net: Add ag7xxx driver for Atheros MIPS
PATCH v2 1/1 net: Add ag7xxx driver for Atheros MIPS
Add ethernet driver for the AR933x and AR934x Atheros MIPS machines. The driver could be easily extended to other WiSoCs.
---
V2: - Drop the printf() in case malloc fails, it's pointless to try
and print something if we cannot allocate memory, since printf also allocates memory.
--- drivers/net/Kconfig | 9 + drivers/net/Makefile | 1 + drivers/net/ag7xxx.c | 980 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 990 insertions(+) create mode 100644 drivers/net/ag7xxx.c
drivers/net/Kconfig +9 -0
@@ -46,6 +46,15 @@ menuconfig NETDEVICES if NETDEVICES +config AG7XXX + bool "Atheros AG7xxx Ethernet MAC support" + depends on DM_ETH + select PHYLIB + help + This driver supports the Atheros AG7xxx Ethernet MAC. This MAC is + present in the Atheros AR7xxx, AR9xxx and QCA9xxx MIPS chips. + + config ALTERA_TSE bool "Altera Triple-Speed Ethernet MAC support" depends on DM_ETH
drivers/net/Makefile +1 -0
@@ -7,6 +7,7 @@ obj-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o obj-$(CONFIG_ALTERA_TSE) += altera_tse.o +obj-$(CONFIG_AG7XXX) += ag7xxx.o obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
drivers/net/ag7xxx.c new file +980 -0
980 lines not on this page · raw message
Signature · 1 line
2.7.0
PATCH v2 1/1 net: Add ag7xxx driver for Atheros MIPS
Quoted 18 lines · Marek Vasut
Add ethernet driver for the AR933x and AR934x Atheros MIPS machines. The driver could be easily extended to other WiSoCs.
Signed-off-by Marek Vasut <[email protected]>Cc Daniel Schwierzeck <[email protected]>Cc Joe Hershberger <[email protected]>Cc Wills Wang <[email protected]>---
V2: - Drop theprintf()in case malloc fails, it's pointless to try
and print something if we cannot allocate memory, since printf also allocates memory.--- drivers/net/Kconfig | 9 + drivers/net/Makefile | 1 + drivers/net/ag7xxx.c | 980 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 990 insertions(+) create mode 100644 drivers/net/ag7xxx.c
<snip>
Quoted 5 lines
drivers/net/ag7xxx.c new file +0
-0
<snip>
Quoted 16 lines
drivers/net/ag7xxx.c new file +16
-0+static int ag7xxx_switch_reg_read (struct mii_dev *bus,int reg,u32 *val) +{ +struct ar7xxx_eth_priv *priv = bus->priv; +u32 phy_addr; +u32 reg_addr; +u32 phy_temp; +u32 reg_temp; +u16 rv =0 ; +int ret; + +if (priv->model ==AG7XXX_MODEL_AG933X ) { + phy_addr =0x1f ; + reg_addr =0x10 ; + }else if (priv->model ==AG7XXX_MODEL_AG934X ) { + phy_addr =0x18 ; + reg_addr =0x00 ;
drivers/net/ag7xxx.c line 203
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ }else +return -EINVAL ; + + ret =ag7xxx_switch_write (bus, phy_addr, reg_addr, reg >>9 ); +if (ret) +return ret; + + phy_temp = ((reg >>6 ) &0x7 ) |0x10 ; + reg_temp = (reg >>1 ) &0x1e ; + *val =0 ;
Why all the magic numbers?
Quoted 53 lines
drivers/net/ag7xxx.c new file +53
-0+ ret =ag7xxx_switch_read (bus, phy_temp, reg_temp |0 , &rv); +if (ret <0 ) +return ret; + *val |= rv; + + ret =ag7xxx_switch_read (bus, phy_temp, reg_temp |1 , &rv); +if (ret <0 ) +return ret; + *val |= (rv <<16 ); + +return 0 ; +} + +static int ag7xxx_switch_reg_write (struct mii_dev *bus,int reg,u32 val) +{ +struct ar7xxx_eth_priv *priv = bus->priv; +u32 phy_addr; +u32 reg_addr; +u32 phy_temp; +u32 reg_temp; +int ret; + +if (priv->model ==AG7XXX_MODEL_AG933X ) { + phy_addr =0x1f ; + reg_addr =0x10 ; + }else if (priv->model ==AG7XXX_MODEL_AG934X ) { + phy_addr =0x18 ; + reg_addr =0x00 ; + }else +return -EINVAL ; + + ret =ag7xxx_switch_write (bus, phy_addr, reg_addr, reg >>9 ); +if (ret) +return ret; + + phy_temp = ((reg >>6 ) &0x7 ) |0x10 ; + reg_temp = (reg >>1 ) &0x1e ; + +/* +* The switch on AR933x has some special register behavior, which +* expects particular write order of their nibbles: +* 0x40 ..... MSB first, LSB second +* 0x50 ..... MSB first, LSB second +* 0x98 ..... LSB first, MSB second +* others ... don't care +*/ +if ((priv->model ==AG7XXX_MODEL_AG933X ) && (reg ==0x98 )) { + ret =ag7xxx_switch_write (bus, phy_temp, reg_temp |0 , val &0xffff ); +if (ret <0 ) +return ret; + + ret =ag7xxx_switch_write (bus, phy_temp, reg_temp |1 , val >>16 ); +if (ret <0 )
drivers/net/ag7xxx.c line 267
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+return ret; + }else { + ret =ag7xxx_switch_write (bus, phy_temp, reg_temp |1 , val >>16 ); +if (ret <0 ) +return ret; + + ret =ag7xxx_switch_write (bus, phy_temp, reg_temp |0 , val &0xffff ); +if (ret <0 ) +return ret; + }
More magic numbers.
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+return 0 ; +} + +static u16 ag7xxx_mdio_rw (struct mii_dev *bus,int addr,int reg,u32 val) +{ +u32 data;
drivers/net/ag7xxx.c line 284
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ +/* Dummy read followed by PHY read/write command. */ +ag7xxx_switch_reg_read (bus,0x98 , &data); + data = val | (reg <<16 ) | (addr <<21 ) |BIT (30 ) |BIT (31 ); +ag7xxx_switch_reg_write (bus,0x98 , data); + +/* Wait for operation to finish */ +do { +ag7xxx_switch_reg_read (bus,0x98 , &data); + }while (data &BIT (31 ));
Magic numbers.
Quoted 71 lines
drivers/net/ag7xxx.c new file +71
-0+return data &0xffff ; +} + +static int ag7xxx_mdio_read (struct mii_dev *bus,int addr,int devad,int reg) +{ +return ag7xxx_mdio_rw (bus, addr, reg,BIT (27 )); +} + +static int ag7xxx_mdio_write (struct mii_dev *bus,int addr,int devad,int reg, +u16 val) +{ +ag7xxx_mdio_rw (bus, addr, reg, val); +return 0 ; +} + +/* +* DMA ring handlers +*/ +static void ag7xxx_dma_clean_tx (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +struct ag7xxx_dma_desc *curr, *next; +u32 start, end; +int i; + +for (i =0 ; i <CONFIG_TX_DESCR_NUM ; i++) { + curr = &priv->tx_mac_descrtable[i]; + next = &priv->tx_mac_descrtable[(i +1 ) %CONFIG_TX_DESCR_NUM ]; + + curr->data_addr =virt_to_phys (&priv->txbuffs[i *CONFIG_ETH_BUFSIZE ]); + curr->config =AG7XXX_DMADESC_IS_EMPTY ; + curr->next_desc =virt_to_phys (next); + } + + priv->tx_currdescnum =0 ; + +/* Cache: Flush descriptors, don't care about buffers. */ + start = (u32)(&priv->tx_mac_descrtable[0 ]); + end = start +sizeof (priv->tx_mac_descrtable); +flush_dcache_range (start, end); +} + +static void ag7xxx_dma_clean_rx (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +struct ag7xxx_dma_desc *curr, *next; +u32 start, end; +int i; + +for (i =0 ; i <CONFIG_RX_DESCR_NUM ; i++) { + curr = &priv->rx_mac_descrtable[i]; + next = &priv->rx_mac_descrtable[(i +1 ) %CONFIG_RX_DESCR_NUM ]; + + curr->data_addr =virt_to_phys (&priv->rxbuffs[i *CONFIG_ETH_BUFSIZE ]); + curr->config =AG7XXX_DMADESC_IS_EMPTY ; + curr->next_desc =virt_to_phys (next); + } + + priv->rx_currdescnum =0 ; + +/* Cache: Flush+Invalidate descriptors, Invalidate buffers. */ + start = (u32)(&priv->rx_mac_descrtable[0 ]); + end = start +sizeof (priv->rx_mac_descrtable); +flush_dcache_range (start, end); +invalidate_dcache_range (start, end); + + start = (u32)&priv->rxbuffs; + end = start +sizeof (priv->rxbuffs); +invalidate_dcache_range (start, end); +} +
<snip>
Quoted 23 lines
drivers/net/ag7xxx.c new file +23
-0+static void ag7xxx_hw_setup (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +u32 speed; + +setbits_be32 (priv->regs +AG7XXX_ETH_CFG1 , +AG7XXX_ETH_CFG1_RX_RST |AG7XXX_ETH_CFG1_TX_RST | +AG7XXX_ETH_CFG1_SOFT_RST ); + +mdelay (10 ); + +writel (AG7XXX_ETH_CFG1_RX_EN |AG7XXX_ETH_CFG1_TX_EN , + priv->regs +AG7XXX_ETH_CFG1 ); + +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) + speed =AG7XXX_ETH_CFG2_IF_10_100 ; +else + speed =AG7XXX_ETH_CFG2_IF_1000 ; + +clrsetbits_be32 (priv->regs +AG7XXX_ETH_CFG2 , +AG7XXX_ETH_CFG2_IF_SPEED_MASK , + speed |AG7XXX_ETH_CFG2_PAD_CRC_EN | +AG7XXX_ETH_CFG2_LEN_CHECK );
drivers/net/ag7xxx.c line 538
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ +writel (0xfff0000 , priv->regs +AG7XXX_ETH_FIFO_CFG_1 ); +writel (0x1fff , priv->regs +AG7XXX_ETH_FIFO_CFG_2 ); + +writel (0x1f00 , priv->regs +AG7XXX_ETH_FIFO_CFG_0 ); +setbits_be32 (priv->regs +AG7XXX_ETH_FIFO_CFG_4 ,0x3ffff ); +writel (0x10ffff , priv->regs +AG7XXX_ETH_FIFO_CFG_1 ); +writel (0xaaa0555 , priv->regs +AG7XXX_ETH_FIFO_CFG_2 ); +writel (0x7eccf , priv->regs +AG7XXX_ETH_FIFO_CFG_5 ); +writel (0x1f00140 , priv->regs +AG7XXX_ETH_FIFO_CFG_3 );
Please comment this function. What is it doing to the FIFOs?
Quoted 4 lines
drivers/net/ag7xxx.c new file +4
-0+} + +static int ag7xxx_mii_get_div (void ) +{
drivers/net/ag7xxx.c line 552
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ulong freq =get_bus_freq (0 ); + +switch (freq /1000000 ) { +case 150 :return 0x7 ; +case 175 :return 0x5 ; +case 200 :return 0x4 ; +case 210 :return 0x9 ; +case 220 :return 0x9 ; +default :return 0x7 ; + }
Strange mapping. Please document.
Quoted 23 lines
drivers/net/ag7xxx.c new file +23
-0+} + +static int ag7xxx_mii_setup (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret, div =ag7xxx_mii_get_div (); +u32 reg; + +if (priv->model ==AG7XXX_MODEL_AG933X ) { +/* Unit 0 is PHY-less on AR9331, see datasheet Figure 2-3 */ +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) +return 0 ; + } + +if (priv->model ==AG7XXX_MODEL_AG934X ) { +writel (AG7XXX_ETH_MII_MGMT_CFG_RESET |0x4 , + priv->regs +AG7XXX_ETH_MII_MGMT_CFG ); +writel (0x4 , priv->regs +AG7XXX_ETH_MII_MGMT_CFG ); +return 0 ; + } + +for (i =0 ; i <10 ; i++) { + writel(AG7XXX_ETH_MII_MGMT_CFG_RESET | div,
drivers/net/ag7xxx.c line 585
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ priv->regs + AG7XXX_ETH_MII_MGMT_CFG); +writel (div, priv->regs +AG7XXX_ETH_MII_MGMT_CFG ); + +/* Check the switch */ + ret =ag7xxx_switch_reg_read (priv->bus,0x10c , ®); +if (ret) +continue ; + +if (reg !=0x18007fff ) +continue ;
What is this?
Quoted 69 lines
drivers/net/ag7xxx.c new file +69
-0+ +return 0 ; + } + +return -EINVAL ; +} + +static int ag933x_phy_setup_wan (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); + +/* Configure switch port 4 (GMAC0) */ +return ag7xxx_mdio_write (priv->bus,4 ,0 ,MII_BMCR ,0x9000 ); +} + +static int ag933x_phy_setup_lan (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret; +u32 reg; + +/* Reset the switch */ + ret =ag7xxx_switch_reg_read (priv->bus,0 , ®); +if (ret) +return ret; + reg |=BIT (31 ); + ret =ag7xxx_switch_reg_write (priv->bus,0 , reg); +if (ret) +return ret; + +do { + ret =ag7xxx_switch_reg_read (priv->bus,0 , ®); +if (ret) +return ret; + }while (reg &BIT (31 )); + +/* Configure switch ports 0...3 (GMAC1) */ +for (i =0 ; i <4 ; i++) { + ret =ag7xxx_mdio_write (priv->bus,0x4 ,0 ,MII_BMCR ,0x9000 ); +if (ret) +return ret; + } + +/* Enable CPU port */ + ret =ag7xxx_switch_reg_write (priv->bus,0x78 ,BIT (8 )); +if (ret) +return ret; + +for (i =0 ; i <4 ; i++) { + ret =ag7xxx_switch_reg_write (priv->bus, i *0x100 ,BIT (9 )); +if (ret) +return ret; + } + +/* QM Control */ + ret =ag7xxx_switch_reg_write (priv->bus,0x38 ,0xc000050e ); +if (ret) +return ret; + +/* Disable Atheros header */ + ret =ag7xxx_switch_reg_write (priv->bus,0x104 ,0x4004 ); +if (ret) +return ret; + +/* Tag priority mapping */ + ret =ag7xxx_switch_reg_write (priv->bus,0x70 ,0xfa50 ); +if (ret) +return ret; +
drivers/net/ag7xxx.c line 664
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+/* Enable ARP packets to the CPU */ + ret =ag7xxx_switch_reg_read (priv->bus,0x5c , ®); +if (ret) +return ret; + reg |=0x100000 ; + ret =ag7xxx_switch_reg_write (priv->bus,0x5c , reg); +if (ret) +return ret; + +return 0 ;
Lots of magic numbers in this function. The documentation is good, but there should be #defines for the registers and bitfield vales to be ORed.
Quoted 52 lines
drivers/net/ag7xxx.c new file +52
-0+} + +static int ag933x_phy_setup_reset_set (struct udevice *dev,int port) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int ret; + + ret =ag7xxx_mdio_write (priv->bus, port,0 ,MII_ADVERTISE , +ADVERTISE_ALL |ADVERTISE_PAUSE_CAP | +ADVERTISE_PAUSE_ASYM ); +if (ret) +return ret; + +if (priv->model ==AG7XXX_MODEL_AG934X ) { + ret =ag7xxx_mdio_write (priv->bus, port,0 ,MII_CTRL1000 , +ADVERTISE_1000FULL ); +if (ret) +return ret; + } + +return ag7xxx_mdio_write (priv->bus, port,0 ,MII_BMCR , +BMCR_ANENABLE |BMCR_RESET ); +} + +static int ag933x_phy_setup_reset_fin (struct udevice *dev,int port) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int ret; + +do { + ret =ag7xxx_mdio_read (priv->bus, port,0 ,MII_BMCR ); +if (ret <0 ) +return ret; +mdelay (10 ); + }while (ret &BMCR_RESET ); + +return 0 ; +} + +static int ag933x_phy_setup_common (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret, phymax; + +if (priv->model ==AG7XXX_MODEL_AG933X ) + phymax =4 ; +else if (priv->model ==AG7XXX_MODEL_AG934X ) + phymax =5 ; +else +return -EINVAL ; + +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) {
drivers/net/ag7xxx.c line 726
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ ret =ag933x_phy_setup_reset_set (dev, phymax); +if (ret) +return ret; + + ret =ag933x_phy_setup_reset_fin (dev, phymax); +if (ret) +return ret; + +/* Read out link status */ + ret =ag7xxx_mdio_read (priv->bus, phymax,0 ,0x11 );
What is this? Is this intended to be MII_MIPSCR?
Quoted 12 lines
drivers/net/ag7xxx.c new file +12
-0+if (ret <0 ) +return ret; + +return 0 ; + } + +/* Switch ports */ +for (i =0 ; i < phymax; i++) { + ret =ag933x_phy_setup_reset_set (dev, i); +if (ret) +return ret; + }
drivers/net/ag7xxx.c line 748
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ +for (i =0 ; i < phymax; i++) { + ret =ag933x_phy_setup_reset_fin (dev, i); +if (ret) +return ret; + } + +for (i =0 ; i < phymax; i++) { +/* Read out link status */ + ret =ag7xxx_mdio_read (priv->bus, i,0 ,0x11 );
Same here.
Quoted 49 lines
drivers/net/ag7xxx.c new file +49
-0+if (ret <0 ) +return ret; + } + +return 0 ; +} + +static int ag934x_phy_setup (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret; +u32 reg; + + ret =ag7xxx_switch_reg_write (priv->bus,0x624 ,0x7f7f7f7f ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x10 ,0x40000000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x4 ,0x07600000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0xc ,0x01000000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x7c ,0x0000007e ); +if (ret) +return ret; + +/* AR8327/AR8328 v1.0 fixup */ + ret =ag7xxx_switch_reg_read (priv->bus,0 , ®); +if (ret) +return ret; +if ((reg &0xffff ) ==0x1201 ) { +for (i =0 ; i <5 ; i++) { + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1d ,0x0 ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1e ,0x02ea ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1d ,0x3d ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1e ,0x68a0 ); +if (ret) +return ret; + } + }
drivers/net/ag7xxx.c line 807
Quoted 10 lines
drivers/net/ag7xxx.c new file +10
-0+ + ret =ag7xxx_switch_reg_read (priv->bus,0x66c , ®); +if (ret) +return ret; + reg &= ~0x70000 ; + ret =ag7xxx_switch_reg_write (priv->bus,0x66c , reg); +if (ret) +return ret; + +return 0 ;
Many more magic numbers.
drivers/net/ag7xxx.c line 820
Quoted 13 lines
drivers/net/ag7xxx.c new file +13
-0+} + +static int ag7xxx_mac_probe (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int ret; + +ag7xxx_hw_setup (dev); + ret =ag7xxx_mii_setup (dev); +if (ret) +return ret; + +ag7xxx_eth_write_hwaddr (dev);
Any reason not to let the framework call this?
Quoted 167 lines
drivers/net/ag7xxx.c new file +160
-1+ +if (priv->model ==AG7XXX_MODEL_AG933X ) { +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) + ret =ag933x_phy_setup_wan (dev); +else + ret =ag933x_phy_setup_lan (dev); + }else if (priv->model ==AG7XXX_MODEL_AG934X ) { + ret =ag934x_phy_setup (dev); + }else { +return -EINVAL ; + } + +if (ret) +return ret; + +return ag933x_phy_setup_common (dev); +} + +static int ag7xxx_mdio_probe (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +struct mii_dev *bus =mdio_alloc (); + +if (!bus) +return -ENOMEM ; + + bus->read = ag7xxx_mdio_read; + bus->write = ag7xxx_mdio_write; +snprintf (bus->name,sizeof (bus->name), dev->name); + + bus->priv = (void *)priv; + +return mdio_register (bus); +} + +static int ag7xxx_get_phy_iface_offset (struct udevice *dev) +{ +int offset; + + offset =fdtdec_lookup_phandle (gd->fdt_blob, dev->of_offset,"phy" ); +if (offset <=0 ) { +debug ("%s: PHY OF node not found (ret=%i)\n" , __func__, offset); +return -EINVAL ; + } + + offset =fdt_parent_offset (gd->fdt_blob, offset); +if (offset <=0 ) { +debug ("%s: PHY OF node parent MDIO bus not found (ret=%i)\n" , + __func__, offset); +return -EINVAL ; + } + + offset =fdt_parent_offset (gd->fdt_blob, offset); +if (offset <=0 ) { +debug ("%s: PHY MDIO OF node parent MAC not found (ret=%i)\n" , + __func__, offset); +return -EINVAL ; + } + +return offset; +} + +static int ag7xxx_eth_probe (struct udevice *dev) +{ +struct eth_pdata *pdata =dev_get_platdata (dev); +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +void __iomem *iobase, *phyiobase; +int ret, phyreg; + +/* Decoding of convoluted PHY wiring on Atheros MIPS. */ + ret =ag7xxx_get_phy_iface_offset (dev); +if (ret <=0 ) +return ret; + phyreg =fdtdec_get_int (gd->fdt_blob, ret,"reg" ,-1 ); + + iobase =map_physmem (pdata->iobase,0x200 ,MAP_NOCACHE ); + phyiobase =map_physmem (phyreg,0x200 ,MAP_NOCACHE ); + +debug ("%s, iobase=%p, phyiobase=%p, priv=%p\n" , + __func__, iobase, phyiobase, priv); + priv->regs = iobase; + priv->phyregs = phyiobase; + priv->interface = pdata->phy_interface; + priv->model =dev_get_driver_data (dev); + + ret =ag7xxx_mdio_probe (dev); +if (ret) +return ret; + + priv->bus =miiphy_get_dev_by_name (dev->name); + + ret =ag7xxx_mac_probe (dev); +debug ("%s, ret=%d\n" , __func__, ret); + +return ret; +} + +static int ag7xxx_eth_remove (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); + +free (priv->phydev); +mdio_unregister (priv->bus); +mdio_free (priv->bus); + +return 0 ; +} + +static const struct eth_ops ag7xxx_eth_ops = { + .start = ag7xxx_eth_start, + .send = ag7xxx_eth_send, + .recv = ag7xxx_eth_recv, + .free_pkt = ag7xxx_eth_free_pkt, + .stop = ag7xxx_eth_stop, + .write_hwaddr = ag7xxx_eth_write_hwaddr, +}; + +static int ag7xxx_eth_ofdata_to_platdata (struct udevice *dev) +{ +struct eth_pdata *pdata =dev_get_platdata (dev); +const char *phy_mode; +int ret; + + pdata->iobase =dev_get_addr (dev); + pdata->phy_interface =-1 ; + +/* Decoding of convoluted PHY wiring on Atheros MIPS. */ + ret =ag7xxx_get_phy_iface_offset (dev); +if (ret <=0 ) +return ret; + + phy_mode =fdt_getprop (gd->fdt_blob, ret,"phy-mode" ,NULL ); +if (phy_mode) + pdata->phy_interface =phy_get_interface_by_name (phy_mode); +if (pdata->phy_interface ==-1 ) { +debug ("%s: Invalid PHY interface '%s'\n" , __func__, phy_mode); +return -EINVAL ; + } + +return 0 ; +} + +static const struct udevice_id ag7xxx_eth_ids[] = { + { .compatible ="qca,ag933x-mac" , .data =AG7XXX_MODEL_AG933X }, + { .compatible ="qca,ag934x-mac" , .data =AG7XXX_MODEL_AG934X }, + { } +}; + +U_BOOT_DRIVER (eth_ag7xxx) = { + .name ="eth_ag7xxx" , + .id =UCLASS_ETH , + .of_match = ag7xxx_eth_ids, + .ofdata_to_platdata = ag7xxx_eth_ofdata_to_platdata, + .probe = ag7xxx_eth_probe, + .remove = ag7xxx_eth_remove, + .ops = &ag7xxx_eth_ops, + .priv_auto_alloc_size =sizeof (struct ar7xxx_eth_priv ), + .platdata_auto_alloc_size =sizeof (struct eth_pdata ), + .flags =DM_FLAG_ALLOC_PRIV_DMA , +}; --2.7.0
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
PATCH v2 1/1 net: Add ag7xxx driver for Atheros MIPS
Quoted 50 lines · Joe Hershberger
Quoted 18 lines · Marek Vasut
Add ethernet driver for the AR933x and AR934x Atheros MIPS machines. The driver could be easily extended to other WiSoCs.
Signed-off-by Marek Vasut <[email protected]>Cc Daniel Schwierzeck <[email protected]>Cc Joe Hershberger <[email protected]>Cc Wills Wang <[email protected]>---
V2: - Drop theprintf()in case malloc fails, it's pointless to try
and print something if we cannot allocate memory, since printf also allocates memory.--- drivers/net/Kconfig | 9 + drivers/net/Makefile | 1 + drivers/net/ag7xxx.c | 980 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 990 insertions(+) create mode 100644 drivers/net/ag7xxx.c<snip>
Quoted 5 lines
drivers/net/ag7xxx.c new file +0
-0<snip>
Quoted 20 lines
drivers/net/ag7xxx.c new file +20
-0+static int ag7xxx_switch_reg_read (struct mii_dev *bus,int reg,u32 *val) +{ +struct ar7xxx_eth_priv *priv = bus->priv; +u32 phy_addr; +u32 reg_addr; +u32 phy_temp; +u32 reg_temp; +u16 rv =0 ; +int ret; + +if (priv->model ==AG7XXX_MODEL_AG933X ) { + phy_addr =0x1f ; + reg_addr =0x10 ; + }else if (priv->model ==AG7XXX_MODEL_AG934X ) { + phy_addr =0x18 ; + reg_addr =0x00 ; + }else +return -EINVAL ; + + ret =ag7xxx_switch_write (bus, phy_addr, reg_addr, reg >>9 );
Quoted 8 lines
drivers/net/ag7xxx.c line 203
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+if (ret) +return ret; + + phy_temp = ((reg >>6 ) &0x7 ) |0x10 ; + reg_temp = (reg >>1 ) &0x1e ; + *val =0 ;Why all the magic numbers?
Because there is no decent documentation for this block/chip :-(
To summarize all of it, most of the PHY init is mysterious register poking salvaged from the obscure u-boot ports for various boards. It does work, which I verified and the comments try to clarify what it is supposed to do, but the documentation for that sequence is just not available.
I tried plucking out as many of the magic constants as possible, but this is as good as it gets. I _hope_ that getting some support for the ath79 mips into mainline will induce some more attention and maybe push qca to be more open regarding these chips, but I wouldn't bet on it.
Quoted 12 lines
Quoted 12 lines
drivers/net/ag7xxx.c new file +12
-0+ ret =ag7xxx_switch_read (bus, phy_temp, reg_temp |0 , &rv); +if (ret <0 ) +return ret; + *val |= rv; + + ret =ag7xxx_switch_read (bus, phy_temp, reg_temp |1 , &rv); +if (ret <0 ) +return ret; + *val |= (rv <<16 ); + +return 0 ; +}
[...]
Quoted 17 lines
Quoted 17 lines
drivers/net/ag7xxx.c new file +17
-0+static int ag933x_phy_setup_common (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret, phymax; + +if (priv->model ==AG7XXX_MODEL_AG933X ) + phymax =4 ; +else if (priv->model ==AG7XXX_MODEL_AG934X ) + phymax =5 ; +else +return -EINVAL ; + +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) { + ret =ag933x_phy_setup_reset_set (dev, phymax); +if (ret) +return ret; +
Quoted 8 lines
drivers/net/ag7xxx.c line 726
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+ ret =ag933x_phy_setup_reset_fin (dev, phymax); +if (ret) +return ret; + +/* Read out link status */ + ret =ag7xxx_mdio_read (priv->bus, phymax,0 ,0x11 );What is this? Is this intended to be
MII_MIPSCR?
Fixed, I didn't find this register before, thanks.
Quoted 16 lines
Quoted 16 lines
drivers/net/ag7xxx.c new file +16
-0+if (ret <0 ) +return ret; + +return 0 ; + } + +/* Switch ports */ +for (i =0 ; i < phymax; i++) { + ret =ag933x_phy_setup_reset_set (dev, i); +if (ret) +return ret; + } + +for (i =0 ; i < phymax; i++) { + ret =ag933x_phy_setup_reset_fin (dev, i); +if (ret)
Quoted 8 lines
drivers/net/ag7xxx.c line 748
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+return ret; + } + +for (i =0 ; i < phymax; i++) { +/* Read out link status */ + ret =ag7xxx_mdio_read (priv->bus, i,0 ,0x11 );Same here.
Fixed
Quoted 53 lines
Quoted 53 lines
drivers/net/ag7xxx.c new file +53
-0+if (ret <0 ) +return ret; + } + +return 0 ; +} + +static int ag934x_phy_setup (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int i, ret; +u32 reg; + + ret =ag7xxx_switch_reg_write (priv->bus,0x624 ,0x7f7f7f7f ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x10 ,0x40000000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x4 ,0x07600000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0xc ,0x01000000 ); +if (ret) +return ret; + ret =ag7xxx_switch_reg_write (priv->bus,0x7c ,0x0000007e ); +if (ret) +return ret; + +/* AR8327/AR8328 v1.0 fixup */ + ret =ag7xxx_switch_reg_read (priv->bus,0 , ®); +if (ret) +return ret; +if ((reg &0xffff ) ==0x1201 ) { +for (i =0 ; i <5 ; i++) { + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1d ,0x0 ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1e ,0x02ea ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1d ,0x3d ); +if (ret) +return ret; + ret =ag7xxx_mdio_write (priv->bus, i,0 ,0x1e ,0x68a0 ); +if (ret) +return ret; + } + } + + ret =ag7xxx_switch_reg_read (priv->bus,0x66c , ®); +if (ret) +return ret;
Quoted 8 lines
drivers/net/ag7xxx.c line 807
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+ reg &= ~0x70000 ; + ret =ag7xxx_switch_reg_write (priv->bus,0x66c , reg); +if (ret) +return ret; + +return 0 ;Many more magic numbers.
More undocumented goop salvaged from the interwebs, yes.
Quoted 7 lines
Quoted 7 lines
drivers/net/ag7xxx.c new file +7
-0+} + +static int ag7xxx_mac_probe (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +int ret; +
Quoted 8 lines
drivers/net/ag7xxx.c line 820
Quoted 6 lines
drivers/net/ag7xxx.c new file +6
-0+ag7xxx_hw_setup (dev); + ret =ag7xxx_mii_setup (dev); +if (ret) +return ret; + +ag7xxx_eth_write_hwaddr (dev);Any reason not to let the framework call this?
For reason unknown, the MAC needs to be loaded into the device before you start setting up the PHY.
Quoted 154 lines
Quoted 154 lines
drivers/net/ag7xxx.c new file +154
-0+ +if (priv->model ==AG7XXX_MODEL_AG933X ) { +if (priv->interface ==PHY_INTERFACE_MODE_RMII ) + ret =ag933x_phy_setup_wan (dev); +else + ret =ag933x_phy_setup_lan (dev); + }else if (priv->model ==AG7XXX_MODEL_AG934X ) { + ret =ag934x_phy_setup (dev); + }else { +return -EINVAL ; + } + +if (ret) +return ret; + +return ag933x_phy_setup_common (dev); +} + +static int ag7xxx_mdio_probe (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +struct mii_dev *bus =mdio_alloc (); + +if (!bus) +return -ENOMEM ; + + bus->read = ag7xxx_mdio_read; + bus->write = ag7xxx_mdio_write; +snprintf (bus->name,sizeof (bus->name), dev->name); + + bus->priv = (void *)priv; + +return mdio_register (bus); +} + +static int ag7xxx_get_phy_iface_offset (struct udevice *dev) +{ +int offset; + + offset =fdtdec_lookup_phandle (gd->fdt_blob, dev->of_offset,"phy" ); +if (offset <=0 ) { +debug ("%s: PHY OF node not found (ret=%i)\n" , __func__, offset); +return -EINVAL ; + } + + offset =fdt_parent_offset (gd->fdt_blob, offset); +if (offset <=0 ) { +debug ("%s: PHY OF node parent MDIO bus not found (ret=%i)\n" , + __func__, offset); +return -EINVAL ; + } + + offset =fdt_parent_offset (gd->fdt_blob, offset); +if (offset <=0 ) { +debug ("%s: PHY MDIO OF node parent MAC not found (ret=%i)\n" , + __func__, offset); +return -EINVAL ; + } + +return offset; +} + +static int ag7xxx_eth_probe (struct udevice *dev) +{ +struct eth_pdata *pdata =dev_get_platdata (dev); +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); +void __iomem *iobase, *phyiobase; +int ret, phyreg; + +/* Decoding of convoluted PHY wiring on Atheros MIPS. */ + ret =ag7xxx_get_phy_iface_offset (dev); +if (ret <=0 ) +return ret; + phyreg =fdtdec_get_int (gd->fdt_blob, ret,"reg" ,-1 ); + + iobase =map_physmem (pdata->iobase,0x200 ,MAP_NOCACHE ); + phyiobase =map_physmem (phyreg,0x200 ,MAP_NOCACHE ); + +debug ("%s, iobase=%p, phyiobase=%p, priv=%p\n" , + __func__, iobase, phyiobase, priv); + priv->regs = iobase; + priv->phyregs = phyiobase; + priv->interface = pdata->phy_interface; + priv->model =dev_get_driver_data (dev); + + ret =ag7xxx_mdio_probe (dev); +if (ret) +return ret; + + priv->bus =miiphy_get_dev_by_name (dev->name); + + ret =ag7xxx_mac_probe (dev); +debug ("%s, ret=%d\n" , __func__, ret); + +return ret; +} + +static int ag7xxx_eth_remove (struct udevice *dev) +{ +struct ar7xxx_eth_priv *priv =dev_get_priv (dev); + +free (priv->phydev); +mdio_unregister (priv->bus); +mdio_free (priv->bus); + +return 0 ; +} + +static const struct eth_ops ag7xxx_eth_ops = { + .start = ag7xxx_eth_start, + .send = ag7xxx_eth_send, + .recv = ag7xxx_eth_recv, + .free_pkt = ag7xxx_eth_free_pkt, + .stop = ag7xxx_eth_stop, + .write_hwaddr = ag7xxx_eth_write_hwaddr, +}; + +static int ag7xxx_eth_ofdata_to_platdata (struct udevice *dev) +{ +struct eth_pdata *pdata =dev_get_platdata (dev); +const char *phy_mode; +int ret; + + pdata->iobase =dev_get_addr (dev); + pdata->phy_interface =-1 ; + +/* Decoding of convoluted PHY wiring on Atheros MIPS. */ + ret =ag7xxx_get_phy_iface_offset (dev); +if (ret <=0 ) +return ret; + + phy_mode =fdt_getprop (gd->fdt_blob, ret,"phy-mode" ,NULL ); +if (phy_mode) + pdata->phy_interface =phy_get_interface_by_name (phy_mode); +if (pdata->phy_interface ==-1 ) { +debug ("%s: Invalid PHY interface '%s'\n" , __func__, phy_mode); +return -EINVAL ; + } + +return 0 ; +} + +static const struct udevice_id ag7xxx_eth_ids[] = { + { .compatible ="qca,ag933x-mac" , .data =AG7XXX_MODEL_AG933X }, + { .compatible ="qca,ag934x-mac" , .data =AG7XXX_MODEL_AG934X }, + { } +}; + +U_BOOT_DRIVER (eth_ag7xxx) = { + .name ="eth_ag7xxx" , + .id =UCLASS_ETH , + .of_match = ag7xxx_eth_ids, + .ofdata_to_platdata = ag7xxx_eth_ofdata_to_platdata, + .probe = ag7xxx_eth_probe,
Quoted 8 lines
Quoted 8 lines
drivers/net/ag7xxx.c new file +6
-1+ .remove = ag7xxx_eth_remove, + .ops = &ag7xxx_eth_ops, + .priv_auto_alloc_size = sizeof(struct ar7xxx_eth_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; --2.7.0
Sorry I couldn't give you any better answer(s) to the questions about the magic numbers.
Signature · 2 lines
Best regards, Marek Vasut
PATCH v2 1/1 net: Add ag7xxx driver for Atheros MIPS
Quoted 14 lines · Marek Vasut
Quoted 13 lines · Joe Hershberger
Quoted 12 lines · Marek Vasut
Add ethernet driver for the AR933x and AR934x Atheros MIPS machines. The driver could be easily extended to other WiSoCs.
Signed-off-by Marek Vasut <[email protected]>Cc Daniel Schwierzeck <[email protected]>Cc Joe Hershberger <[email protected]>Cc Wills Wang <[email protected]>---
V2: - Drop theprintf()in case malloc fails, it's pointless to try
and print something if we cannot allocate memory, since printf also allocates memory.
---
<snip>
Quoted 2 lines
Sorry I couldn't give you any better answer(s) to the questions about the magic numbers.
OK, Please send a v3 with the one register change and I'll apply it.
Cheers,
-Joe
PATCH v2 1/1 net: Add ag7xxx driver for Atheros MIPS
Quoted 16 lines · Joe Hershberger
Quoted 5 lines · Marek Vasut
Quoted 4 lines · Joe Hershberger
Quoted 3 lines · Marek Vasut
Add ethernet driver for the AR933x and AR934x Atheros MIPS machines. The driver could be easily extended to other WiSoCs.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Signed-off-by Marek Vasut <[email protected]>Cc Daniel Schwierzeck <[email protected]>Cc Joe Hershberger <[email protected]>Cc Wills Wang <[email protected]>---
V2: - Drop theprintf()in case malloc fails, it's pointless to try
and print something if we cannot allocate memory, since printf also allocates memory.
---
Quoted 6 lines
<snip>
Quoted 2 lines
Sorry I couldn't give you any better answer(s) to the questions about the magic numbers.
OK, Please send a v3 with the one register change and I'll apply it.
Just went out. Thanks
Signature · 2 lines
Best regards, Marek Vasut