]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/net/sunxi_emac.c
drivers: fsl-mc: Update flibs to mc-0.6.0.1
[karo-tx-uboot.git] / drivers / net / sunxi_emac.c
index 5a06d68af70804b35812b396ea53d58abf54f7e0..e939bf2108a3fbde2c23cd01bdb09f815a4be53c 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <linux/err.h>
 #include <malloc.h>
 #include <miiphy.h>
@@ -153,13 +154,16 @@ struct sunxi_sramc_regs {
 #define EMAC_CRCERR            (0x1 << 4)
 #define EMAC_LENERR            (0x3 << 5)
 
-#define DMA_CPU_TRRESHOLD      2000
+#define EMAC_RX_BUFSIZE                2000
 
 struct emac_eth_dev {
-       u32 speed;
-       u32 duplex;
-       u32 phy_configured;
+       struct emac_regs *regs;
+       struct mii_dev *bus;
+       struct phy_device *phydev;
        int link_printed;
+#ifdef CONFIG_DM_ETH
+       uchar rx_buf[EMAC_RX_BUFSIZE];
+#endif
 };
 
 struct emac_rxhdr {
@@ -195,11 +199,10 @@ static void emac_outblk_32bit(void *reg, void *data, int count)
 }
 
 /* Read a word from phyxcer */
-static int emac_phy_read(const char *devname, unsigned char addr,
-                         unsigned char reg, unsigned short *value)
+static int emac_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
 {
-       struct eth_device *dev = eth_get_dev_by_name(devname);
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_eth_dev *priv = bus->priv;
+       struct emac_regs *regs = priv->regs;
 
        /* issue the phy address and reg */
        writel(addr << 8 | reg, &regs->mac_madr);
@@ -213,18 +216,16 @@ static int emac_phy_read(const char *devname, unsigned char addr,
        /* push down the phy io line */
        writel(0x0, &regs->mac_mcmd);
 
-       /* and write data */
-       *value = readl(&regs->mac_mrdd);
-
-       return 0;
+       /* And read data */
+       return readl(&regs->mac_mrdd);
 }
 
 /* Write a word to phyxcer */
-static int emac_phy_write(const char *devname, unsigned char addr,
-                          unsigned char reg, unsigned short value)
+static int emac_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+                         u16 value)
 {
-       struct eth_device *dev = eth_get_dev_by_name(devname);
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_eth_dev *priv = bus->priv;
+       struct emac_regs *regs = priv->regs;
 
        /* issue the phy address and reg */
        writel(addr << 8 | reg, &regs->mac_madr);
@@ -244,12 +245,44 @@ static int emac_phy_write(const char *devname, unsigned char addr,
        return 0;
 }
 
-static void emac_setup(struct eth_device *dev)
+static int sunxi_emac_init_phy(struct emac_eth_dev *priv, void *dev)
+{
+       int ret, mask = 0xffffffff;
+
+#ifdef CONFIG_PHY_ADDR
+       mask = 1 << CONFIG_PHY_ADDR;
+#endif
+
+       priv->bus = mdio_alloc();
+       if (!priv->bus) {
+               printf("Failed to allocate MDIO bus\n");
+               return -ENOMEM;
+       }
+
+       priv->bus->read = emac_mdio_read;
+       priv->bus->write = emac_mdio_write;
+       priv->bus->priv = priv;
+       strcpy(priv->bus->name, "emac");
+
+       ret = mdio_register(priv->bus);
+       if (ret)
+               return ret;
+
+       priv->phydev = phy_find_by_mask(priv->bus, mask,
+                                       PHY_INTERFACE_MODE_MII);
+       if (!priv->phydev)
+               return -ENODEV;
+
+       phy_connect_dev(priv->phydev, dev);
+       phy_config(priv->phydev);
+
+       return 0;
+}
+
+static void emac_setup(struct emac_eth_dev *priv)
 {
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_regs *regs = priv->regs;
        u32 reg_val;
-       u16 phy_val;
-       u32 duplex_flag;
 
        /* Set up TX */
        writel(EMAC_TX_SETUP, &regs->tx_mode);
@@ -262,12 +295,8 @@ static void emac_setup(struct eth_device *dev)
        writel(EMAC_MAC_CTL0_SETUP, &regs->mac_ctl0);
 
        /* Set MAC CTL1 */
-       emac_phy_read(dev->name, 1, 0, &phy_val);
-       debug("PHY SETUP, reg 0 value: %x\n", phy_val);
-       duplex_flag = !!(phy_val & (1 << 8));
-
        reg_val = 0;
-       if (duplex_flag)
+       if (priv->phydev->duplex == DUPLEX_FULL)
                reg_val = (0x1 << 0);
        writel(EMAC_MAC_CTL1_SETUP | reg_val, &regs->mac_ctl1);
 
@@ -284,9 +313,9 @@ static void emac_setup(struct eth_device *dev)
        writel(EMAC_MAC_MFL, &regs->mac_maxf);
 }
 
-static void emac_reset(struct eth_device *dev)
+static void emac_reset(struct emac_eth_dev *priv)
 {
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_regs *regs = priv->regs;
 
        debug("resetting device\n");
 
@@ -298,11 +327,10 @@ static void emac_reset(struct eth_device *dev)
        udelay(200);
 }
 
-static int sunxi_emac_eth_init(struct eth_device *dev, bd_t *bd)
+static int _sunxi_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr)
 {
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
-       struct emac_eth_dev *priv = dev->priv;
-       u16 phy_reg;
+       struct emac_regs *regs = priv->regs;
+       int ret;
 
        /* Init EMAC */
 
@@ -320,41 +348,44 @@ static int sunxi_emac_eth_init(struct eth_device *dev, bd_t *bd)
        udelay(1);
 
        /* Set up EMAC */
-       emac_setup(dev);
+       emac_setup(priv);
 
-       writel(dev->enetaddr[0] << 16 | dev->enetaddr[1] << 8 |
-              dev->enetaddr[2], &regs->mac_a1);
-       writel(dev->enetaddr[3] << 16 | dev->enetaddr[4] << 8 |
-              dev->enetaddr[5], &regs->mac_a0);
+       writel(enetaddr[0] << 16 | enetaddr[1] << 8 | enetaddr[2],
+              &regs->mac_a1);
+       writel(enetaddr[3] << 16 | enetaddr[4] << 8 | enetaddr[5],
+              &regs->mac_a0);
 
        mdelay(1);
 
-       emac_reset(dev);
+       emac_reset(priv);
 
        /* PHY POWER UP */
-       emac_phy_read(dev->name, 1, 0, &phy_reg);
-       emac_phy_write(dev->name, 1, 0, phy_reg & (~(0x1 << 11)));
-       mdelay(1);
-
-       emac_phy_read(dev->name, 1, 0, &phy_reg);
-
-       priv->speed = miiphy_speed(dev->name, 0);
-       priv->duplex = miiphy_duplex(dev->name, 0);
+       ret = phy_startup(priv->phydev);
+       if (ret) {
+               printf("Could not initialize PHY %s\n",
+                      priv->phydev->dev->name);
+               return ret;
+       }
 
        /* Print link status only once */
        if (!priv->link_printed) {
                printf("ENET Speed is %d Mbps - %s duplex connection\n",
-                      priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL");
+                      priv->phydev->speed,
+                      priv->phydev->duplex ? "FULL" : "HALF");
                priv->link_printed = 1;
        }
 
        /* Set EMAC SPEED depend on PHY */
-       clrsetbits_le32(&regs->mac_supp, 1 << 8,
-                       ((phy_reg & (0x1 << 13)) >> 13) << 8);
+       if (priv->phydev->speed == SPEED_100)
+               setbits_le32(&regs->mac_supp, 1 << 8);
+       else
+               clrbits_le32(&regs->mac_supp, 1 << 8);
 
        /* Set duplex depend on phy */
-       clrsetbits_le32(&regs->mac_ctl1, 1 << 0,
-                       ((phy_reg & (0x1 << 8)) >> 8) << 0);
+       if (priv->phydev->duplex == DUPLEX_FULL)
+               setbits_le32(&regs->mac_ctl1, 1 << 0);
+       else
+               clrbits_le32(&regs->mac_ctl1, 1 << 0);
 
        /* Enable RX/TX */
        setbits_le32(&regs->ctl, 0x7);
@@ -362,14 +393,9 @@ static int sunxi_emac_eth_init(struct eth_device *dev, bd_t *bd)
        return 0;
 }
 
-static void sunxi_emac_eth_halt(struct eth_device *dev)
-{
-       /* Nothing to do here */
-}
-
-static int sunxi_emac_eth_recv(struct eth_device *dev)
+static int _sunxi_emac_eth_recv(struct emac_eth_dev *priv, void *packet)
 {
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_regs *regs = priv->regs;
        struct emac_rxhdr rxhdr;
        u32 rxcount;
        u32 reg_val;
@@ -387,7 +413,7 @@ static int sunxi_emac_eth_recv(struct eth_device *dev)
                /* Had one stuck? */
                rxcount = readl(&regs->rx_fbc);
                if (!rxcount)
-                       return 0;
+                       return -EAGAIN;
        }
 
        reg_val = readl(&regs->rx_io_data);
@@ -403,7 +429,7 @@ static int sunxi_emac_eth_recv(struct eth_device *dev)
                /* Enable RX */
                setbits_le32(&regs->ctl, 0x1 << 2);
 
-               return 0;
+               return -EAGAIN;
        }
 
        /* A packet ready now
@@ -433,24 +459,21 @@ static int sunxi_emac_eth_recv(struct eth_device *dev)
 
        /* Move data from EMAC */
        if (good_packet) {
-               if (rx_len > DMA_CPU_TRRESHOLD) {
+               if (rx_len > EMAC_RX_BUFSIZE) {
                        printf("Received packet is too big (len=%d)\n", rx_len);
-               } else {
-                       emac_inblk_32bit((void *)&regs->rx_io_data,
-                                        NetRxPackets[0], rx_len);
-
-                       /* Pass to upper layer */
-                       NetReceive(NetRxPackets[0], rx_len);
-                       return rx_len;
+                       return -EMSGSIZE;
                }
+               emac_inblk_32bit((void *)&regs->rx_io_data, packet, rx_len);
+               return rx_len;
        }
 
-       return 0;
+       return -EIO; /* Bad packet */
 }
 
-static int sunxi_emac_eth_send(struct eth_device *dev, void *packet, int len)
+static int _sunxi_emac_eth_send(struct emac_eth_dev *priv, void *packet,
+                               int len)
 {
-       struct emac_regs *regs = (struct emac_regs *)dev->iobase;
+       struct emac_regs *regs = priv->regs;
 
        /* Select channel 0 */
        writel(0, &regs->tx_ins);
@@ -467,55 +490,98 @@ static int sunxi_emac_eth_send(struct eth_device *dev, void *packet, int len)
        return 0;
 }
 
-int sunxi_emac_initialize(void)
+static void sunxi_emac_board_setup(struct emac_eth_dev *priv)
 {
        struct sunxi_ccm_reg *const ccm =
                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
        struct sunxi_sramc_regs *sram =
                (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE;
-       struct emac_regs *regs =
-               (struct emac_regs *)SUNXI_EMAC_BASE;
-       struct eth_device *dev;
-       struct emac_eth_dev *priv;
+       struct emac_regs *regs = priv->regs;
        int pin;
 
-       dev = malloc(sizeof(*dev));
-       if (dev == NULL)
-               return -ENOMEM;
-
-       priv = (struct emac_eth_dev *)malloc(sizeof(struct emac_eth_dev));
-       if (!priv) {
-               free(dev);
-               return -ENOMEM;
-       }
-
-       memset(dev, 0, sizeof(*dev));
-       memset(priv, 0, sizeof(struct emac_eth_dev));
-
        /* Map SRAM to EMAC */
        setbits_le32(&sram->ctrl1, 0x5 << 2);
 
        /* Configure pin mux settings for MII Ethernet */
        for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++)
-               sunxi_gpio_set_cfgpin(pin, SUNXI_GPA0_EMAC);
+               sunxi_gpio_set_cfgpin(pin, SUNXI_GPA_EMAC);
 
        /* Set up clock gating */
        setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_EMAC);
 
        /* Set MII clock */
        clrsetbits_le32(&regs->mac_mcfg, 0xf << 2, 0xd << 2);
+}
+
+static int sunxi_emac_eth_start(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+
+       return _sunxi_emac_eth_init(dev->priv, pdata->enetaddr);
+}
+
+static int sunxi_emac_eth_send(struct udevice *dev, void *packet, int length)
+{
+       struct emac_eth_dev *priv = dev_get_priv(dev);
 
-       dev->iobase = (int)regs;
-       dev->priv = priv;
-       dev->init = sunxi_emac_eth_init;
-       dev->halt = sunxi_emac_eth_halt;
-       dev->send = sunxi_emac_eth_send;
-       dev->recv = sunxi_emac_eth_recv;
-       strcpy(dev->name, "emac");
+       return _sunxi_emac_eth_send(priv, packet, length);
+}
+
+static int sunxi_emac_eth_recv(struct udevice *dev, uchar **packetp)
+{
+       struct emac_eth_dev *priv = dev_get_priv(dev);
+       int rx_len;
 
-       eth_register(dev);
+       rx_len = _sunxi_emac_eth_recv(priv, priv->rx_buf);
+       *packetp = priv->rx_buf;
 
-       miiphy_register(dev->name, emac_phy_read, emac_phy_write);
+       return rx_len;
+}
+
+static void sunxi_emac_eth_stop(struct udevice *dev)
+{
+       /* Nothing to do here */
+}
+
+static int sunxi_emac_eth_probe(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct emac_eth_dev *priv = dev_get_priv(dev);
+
+       priv->regs = (struct emac_regs *)pdata->iobase;
+       sunxi_emac_board_setup(priv);
+
+       return sunxi_emac_init_phy(priv, dev);
+}
+
+static const struct eth_ops sunxi_emac_eth_ops = {
+       .start                  = sunxi_emac_eth_start,
+       .send                   = sunxi_emac_eth_send,
+       .recv                   = sunxi_emac_eth_recv,
+       .stop                   = sunxi_emac_eth_stop,
+};
+
+static int sunxi_emac_eth_ofdata_to_platdata(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+
+       pdata->iobase = dev_get_addr(dev);
 
        return 0;
 }
+
+static const struct udevice_id sunxi_emac_eth_ids[] = {
+       { .compatible = "allwinner,sun4i-a10-emac" },
+       { }
+};
+
+U_BOOT_DRIVER(eth_sunxi_emac) = {
+       .name   = "eth_sunxi_emac",
+       .id     = UCLASS_ETH,
+       .of_match = sunxi_emac_eth_ids,
+       .ofdata_to_platdata = sunxi_emac_eth_ofdata_to_platdata,
+       .probe  = sunxi_emac_eth_probe,
+       .ops    = &sunxi_emac_eth_ops,
+       .priv_auto_alloc_size = sizeof(struct emac_eth_dev),
+       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};