| #include <common.h> |
| #include <config.h> |
| #include <asm/arch/hardware.h> |
| #include <asm/byteorder.h> |
| //#include <malloc.h> |
| #include <net.h> |
| #include <command.h> |
| #include <miiphy.h> |
| #include <asm/arch/clkcore_c2000.h> |
| #include <asm/arch/gpio_c2000.h> |
| #include "c2000_eth.h" |
| #ifdef CONFIG_AR8328_SWITCH |
| #include "ar8328.h" |
| #endif |
| |
| struct gemac_s gem_info[] = { |
| /* PORT_0 configuration */ |
| { |
| /* GEMAC config */ |
| .gemac_mode = RGMII, |
| .gemac_speed = SPEED_1000M, |
| .gemac_duplex = DUPLEX_FULL, |
| .flags = EMAC0_FLAGS, |
| |
| /* phy iface */ |
| .phy_reg_index = EMAC_PORT_0, |
| .phy_address = EMAC0_PHY_ADDR, |
| }, |
| /* PORT_1 configuration */ |
| { |
| /* GEMAC config */ |
| .gemac_mode = RGMII, |
| .gemac_speed = SPEED_1000M, |
| .gemac_duplex = DUPLEX_FULL, |
| .flags = EMAC1_FLAGS, |
| |
| /* phy iface */ |
| .phy_reg_index = EMAC_PORT_0, |
| .phy_address = EMAC1_PHY_ADDR, |
| }, |
| |
| /* PORT_2 configuration */ |
| { |
| /* GEMAC config */ |
| .gemac_mode = RGMII, |
| .gemac_speed = SPEED_1000M, |
| .gemac_duplex = DUPLEX_FULL, |
| .flags = EMAC2_FLAGS, |
| |
| /* phy iface */ |
| .phy_reg_index = EMAC_PORT_0, |
| .phy_address = 0, |
| }, |
| }; |
| |
| #define MAX_GEMACS 3 |
| |
| static struct c2000_eth_dev *gemac_list[MAX_GEMACS]; |
| |
| /******************************************************************** |
| * Helper functions for phy read/write |
| *******************************************************************/ |
| |
| /* Max MII register/address (we support) */ |
| #define MII_REGISTER_MAX 31 |
| #define MII_ADDRESS_MAX 31 |
| |
| #define MDIO_TIMEOUT 5000 |
| |
| /******************************************************************** |
| * gem_phy_man_rd : |
| * Performs phy management read operation. |
| *******************************************************************/ |
| static int gem_phy_man_rd(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg) |
| { |
| u32 write_data; |
| |
| write_data = 0x60020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18); // read_op |
| writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT); |
| |
| return 0; |
| } |
| |
| static int gem_phy_man_wr(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg, u32 val) |
| { |
| u32 write_data; |
| |
| write_data = 0x50020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18) | (val & (u32) 0xffff); // write_op |
| writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT); |
| |
| return 0; |
| } |
| |
| /** gem_phy_man_data |
| * Read the data section of phy management register. |
| * After a successful read opeeration the data will be stored in |
| * in this register in lower 16bits. |
| */ |
| static u32 gem_phy_man_data(struct c2000_eth_dev *priv) |
| { |
| u32 value; |
| |
| value = readl(priv->phyregisters + EMAC_PHY_MANAGEMENT) & 0xFFFF; |
| return value; |
| } |
| |
| #define EMAC_PHY_IDLE (1 << 2) |
| |
| static int gem_phy_man_idle(struct c2000_eth_dev *priv) |
| { |
| u32 value; |
| |
| value = readl(priv->phyregisters + EMAC_NETWORK_STATUS); |
| return ((value & EMAC_PHY_IDLE) == EMAC_PHY_IDLE); |
| } |
| |
| static int gem_phy_timeout(struct c2000_eth_dev *priv, int timeout) |
| { |
| while(!gem_phy_man_idle(priv)) { |
| |
| if (timeout-- <= 0) { |
| printf("Phy MDIO read/write timeout\n"); |
| return -1; |
| } |
| |
| //udelay(1); |
| } |
| return 0; |
| } |
| |
| static struct c2000_eth_dev* get_gemac(char *devname) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_GEMACS; i++) { |
| if (!strcmp(gemac_list[i]->dev->name, devname)) |
| return gemac_list[i]; |
| } |
| |
| return NULL; |
| } |
| |
| |
| /** PHY read function |
| * Reads a 16bit value from a MII register |
| * |
| * @param[in] mdev Pointer to MII device structure |
| * @param[in] phy_addr |
| * @param[in] phy_reg |
| * |
| * @return 16bit value on success, a negivie value (-1) on error |
| */ |
| static int c2000_phy_read(char *devname, unsigned char phy_addr, unsigned char phy_reg, unsigned short *value) |
| { |
| struct c2000_eth_dev *priv = get_gemac(devname); |
| |
| if (!priv) { |
| printf("Unknown device %s\n", devname); |
| return -1; |
| } |
| |
| if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX)) |
| return -1; |
| |
| gem_phy_man_rd(priv, phy_addr, phy_reg); |
| if (gem_phy_timeout(priv, MDIO_TIMEOUT)) |
| return -1; |
| |
| *value = gem_phy_man_data(priv); |
| |
| dprint("%s: Addr: %d, Reg: %d, Val: 0x%x\n", |
| __func__, phy_addr, phy_reg, *value); |
| |
| return 0; |
| } |
| |
| /** PHY write function |
| * Writes a 16bit value to a MII register |
| * |
| * @param[in] mdev Pointer to MII device structure |
| * @param[in] phy_addr |
| * @param[in] phy_reg |
| * @param[in] value Value to be written to Phy |
| * |
| * @return On success returns 0, a negative value (-1) on error |
| */ |
| static int c2000_phy_write(char *devname, unsigned char phy_addr, unsigned char phy_reg, unsigned short value) |
| { |
| struct c2000_eth_dev *priv = get_gemac(devname); |
| |
| if (!priv) { |
| printf("Unknown device %s\n", devname); |
| return -1; |
| } |
| if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX)) |
| return -1; |
| |
| gem_phy_man_wr(priv, phy_addr, phy_reg, value); |
| if (gem_phy_timeout(priv, MDIO_TIMEOUT)) |
| return -1; |
| |
| dprint("%s: Addr: %d, Reg: %d, Val: 0x%x\n", |
| __func__, phy_addr, phy_reg, value); |
| |
| return 0; |
| } |
| |
| /** MAC Address converter |
| * Convert standard byte style ethernet address to format compatible with MAC. |
| * |
| * @param[in] enet_byte_addr Pointer to the mac address in byte format |
| * @param[out] Pointer to MAC_ADDR structure |
| * |
| * @return 0 on success, -1 on failure |
| */ |
| int gemac_enet_addr_byte_mac(u8 *enet_byte_addr, MAC_ADDR *enet_addr) |
| { |
| if ((enet_byte_addr == NULL) || (enet_addr == NULL)) |
| { |
| return -1; |
| } |
| else |
| { |
| enet_addr->bottom = enet_byte_addr[0] | |
| (enet_byte_addr[1] << 8) | |
| (enet_byte_addr[2] << 16) | |
| (enet_byte_addr[3] << 24); |
| enet_addr->top = enet_byte_addr[4] | |
| (enet_byte_addr[5] << 8); |
| return 0; |
| } |
| } |
| |
| /** Configures ethernet address to GEMAC ADDR1 location. |
| * |
| * @param[in] edev Pointer to the eth device structure |
| * @param[in] addr Pointer to ethernet address in byte order |
| * |
| * @return 0 on success (always success) |
| */ |
| static int c2000_set_ethaddr(struct eth_device *dev, u8 *enet_byte_addr) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv; |
| MAC_ADDR enet_address = {0x0, 0x0}; |
| |
| |
| gemac_enet_addr_byte_mac(enet_byte_addr, &enet_address); |
| gemac_set_laddr1(priv->gem->gemac_base, &enet_address); |
| |
| return 0; |
| } |
| |
| /** Get/Read configured ethernet mac address from GEMAC. |
| * |
| * @param[in] edev Pointer to eth device structure |
| * @param[out] addr Pointer to ethernet address to be copied |
| * |
| * @return 0 on success, always success. |
| */ |
| static int c2000_get_ethaddr(struct eth_device *edev, unsigned char *addr) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv; |
| MAC_ADDR enet_address = {0x0, 0x0}; |
| u16 *p = (u16 *)addr; |
| |
| enet_address = gem_get_laddr1(priv->gem->gemac_base); |
| p[0] = enet_address.bottom & 0xffff; |
| p[1] = (enet_address.bottom >> 16) & 0xffff; |
| p[2] = enet_address.top & 0xffff; |
| |
| return 0; |
| } |
| |
| |
| /** Stops or Disables GEMAC pointing to this eth iface. |
| * |
| * @param[in] edev Pointer to eth device structure. |
| * |
| * @return none |
| */ |
| static void c2000_eth_halt(struct eth_device *edev) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv; |
| |
| // printf("%s:\n",__func__); |
| |
| gemac_disable(priv->gem->gemac_base); |
| |
| gpi_disable(priv->gem->egpi_base); |
| |
| return; |
| } |
| |
| #ifdef CONFIG_BOARD_C2KASIC |
| int c2000_eth_board_init(char *devname, int eth_port) |
| { |
| //eth0 port is chosen as criteria for bringingup out of reset because |
| //all MDIO access can happen through EMAC0 and without bringing eth0 first |
| //no PHY configuration can happen and no point in removing reset without eth0 |
| if(eth_port != 0) |
| return 0; |
| |
| // printf("%s: Bringing PHY out of reset\n",__func__); |
| //Bring all PHYs out of reset. The correct way is to assert, deassert and re-assert the GPIO reset signal |
| //with delay in between |
| writel(readl(COMCERTO_GPIO_OE_REG) | GPIO_3, COMCERTO_GPIO_OE_REG); |
| |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_3, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(100000); |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~GPIO_3, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(100000); |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_3, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(100000); |
| |
| } |
| #else |
| int c2000_eth_board_init(char *devname, int eth_port) |
| { |
| //eth0 port is chosen as criteria for bringingup out of reset because |
| //all MDIO access can happen through EMAC0 and without bringing eth0 first |
| //no Switch/PHY configuration can happen and no point in removing reset without eth0 |
| if(eth_port == 0) |
| { |
| |
| //Bring AR8327 switch out of reset. Toggle reset |
| writel(readl(COMCERTO_GPIO_OE_REG) | GPIO_27, COMCERTO_GPIO_OE_REG); |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_27, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(10000); |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~GPIO_27, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(10000); |
| writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_27, COMCERTO_GPIO_OUTPUT_REG); |
| udelay(10000); |
| |
| |
| //AR8327 Switch init |
| athrs17_init(devname); |
| |
| //AR8327 WAN PHY4 init |
| athrs17_phy_setup(devname,EMAC0_PHY_ADDR); |
| } |
| else |
| { |
| //AR8327 LAN PHYs init |
| athrs17_phy_setup(devname,EMAC1_PHY_ADDR); |
| } |
| |
| } |
| #endif |
| |
| static int c2000_eth_init(struct eth_device *dev, bd_t * bd) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv; |
| struct gemac_s *gem = priv->gem; |
| u32 phyaddr; |
| |
| // printf("## %s\n",__func__); |
| |
| /* GEMAC init */ |
| pfe_gemac_init(gem->gemac_base, gem->gemac_mode, gem->gemac_speed, gem->gemac_duplex); |
| |
| /* set ethernet mac address */ |
| c2000_set_ethaddr(dev, dev->enetaddr); |
| |
| phyaddr = gem_info[priv->gemac_port].phy_address; |
| |
| if (!(gem->flags & GEMAC_NO_PHY) && (gem->gemac_mode == RGMII) ) |
| { |
| miiphy_enable_rgmii_rx_delay(dev->name, phyaddr); |
| miiphy_enable_rgmii_tx_delay(dev->name, phyaddr); |
| } |
| |
| |
| /* Re-negotiate speed and duplex */ |
| if (!(gem->flags & GEMAC_NO_PHY)) |
| { |
| int speed = gem->gemac_speed; |
| int duplex = gem->gemac_duplex; |
| |
| miiphy_speed_duplex(dev->name, phyaddr, &speed, &duplex); |
| |
| switch (speed) |
| { |
| case _10BASET: |
| speed = SPEED_10M; |
| break; |
| |
| case _100BASET: |
| speed = SPEED_100M; |
| break; |
| |
| case _1000BASET: |
| speed = SPEED_1000M; |
| break; |
| } |
| |
| duplex = (duplex == HALF) ? DUPLEX_HALF:DUPLEX_FULL; |
| gemac_set_speed(gem->gemac_base, speed); |
| gemac_set_duplex(gem->gemac_base, duplex); |
| } |
| |
| /* Enable GPI */ |
| gpi_enable(priv->gem->egpi_base); |
| |
| /* Enable GEMAC for tx and rx */ |
| gemac_enable(priv->gem->gemac_base); |
| |
| return 0; |
| } |
| |
| static int c2000_eth_send(struct eth_device *dev, volatile void *data, int length) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv; |
| |
| int rc; |
| uint64_t tx_tmo; |
| int i; |
| |
| rc = pfe_send(priv->gemac_port, data, length); |
| |
| if (rc < 0) { |
| printf("Tx Q full\n"); |
| return 0; |
| } |
| |
| while (1) { |
| rc = pfe_tx_done(); |
| if (rc == 0) |
| break; |
| |
| udelay(100); |
| i++; |
| if(i == 30000) |
| printf("Tx timeout, send failed\n"); |
| break; |
| |
| } |
| |
| return 0; |
| } |
| |
| static int c2000_eth_recv(struct eth_device *dev) |
| { |
| struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv; |
| u32 pkt_buf; |
| int len; |
| int phy_port; |
| |
| len = pfe_recv(&pkt_buf, &phy_port); |
| |
| if (len < 0) |
| return 0; //no packet in rx |
| |
| dprint("Rx pkt: pkt_buf(%08x), phy_port(%d), len(%d)\n", pkt_buf, phy_port, len); |
| if (phy_port != priv->gemac_port) { |
| printf("Rx pkt not on expected port\n"); |
| return 0; |
| } |
| |
| // Pass the packet up to the protocol layers. |
| NetReceive((unsigned char *)pkt_buf, len); |
| |
| |
| return 0; |
| } |
| |
| int c2000_gemac_initialize(bd_t * bis, int index, char *devname) |
| { |
| struct eth_device *dev; |
| struct c2000_eth_dev *priv; |
| struct pfe *pfe; |
| int i; |
| |
| //Bring HFE and GEMTX out of reset |
| writel(0x0, HFE_RESET); |
| writel(0x0, GEMTX_RESET); |
| |
| dev = (struct eth_device *)malloc(sizeof(struct eth_device)); |
| if (!dev) |
| return -1; |
| |
| memset(dev, 0, sizeof(struct eth_device)); |
| |
| priv = (struct c2000_eth_dev *)malloc(sizeof(struct c2000_eth_dev)); |
| if (!priv) |
| return -1; |
| |
| gemac_list[index] = priv; |
| priv->gemac_port = index; |
| priv->gem = &gem_info[priv->gemac_port]; |
| priv->dev = dev; |
| |
| pfe = &priv->pfe; |
| |
| pfe->cbus_baseaddr = (void *)COMCERTO_AXI_HFE_CFG_BASE; |
| pfe->ddr_baseaddr = (void *)CONFIG_DDR_BASEADDR; |
| pfe->ddr_phys_baseaddr = (unsigned long)CONFIG_DDR_PHYS_BASEADDR; |
| |
| sprintf(dev->name, devname); |
| dev->priv = priv; |
| dev->init = c2000_eth_init; |
| dev->halt = c2000_eth_halt; |
| dev->send = c2000_eth_send; |
| dev->recv = c2000_eth_recv; |
| |
| /* Tell u-boot to get the addr from the env */ |
| for (i = 0; i < 6; i++) |
| dev->enetaddr[i] = 0; |
| |
| |
| switch(priv->gemac_port) { |
| case EMAC_PORT_0: |
| default: |
| priv->gem->gemac_base = EMAC1_BASE_ADDR; |
| priv->gem->egpi_base = EGPI1_BASE_ADDR; |
| break; |
| case EMAC_PORT_1: |
| priv->gem->gemac_base = EMAC2_BASE_ADDR; |
| priv->gem->egpi_base = EGPI2_BASE_ADDR; |
| break; |
| case EMAC_PORT_2: |
| priv->gem->gemac_base = EMAC3_BASE_ADDR; |
| priv->gem->egpi_base = EGPI3_BASE_ADDR; |
| break; |
| } |
| |
| pfe_probe(pfe); |
| priv->phyregisters = (void *)gem_info[gem_info[priv->gemac_port].phy_reg_index].gemac_base; |
| |
| if(priv->gemac_port == 0) |
| { |
| gemac_set_mdc_div(priv->gem->gemac_base, MDC_DIV_96); |
| gemac_enable_mdio(priv->gem->gemac_base); |
| miiphy_register(dev->name, c2000_phy_read, c2000_phy_write); |
| } |
| |
| c2000_eth_board_init(dev->name, priv->gemac_port); |
| |
| eth_register(dev); |
| } |