From eb99c09f7c7edf27188e2d3e70773f9d29b93547 Mon Sep 17 00:00:00 2001 From: Renaud Barbier Date: Tue, 25 Jun 2013 14:09:59 +0100 Subject: [PATCH] ppc: gianfar MDIO buses This commit creates MDIO bus devices to separate the MDIO bus abstraction from the Ethernet device initialisation. It also updates the configuration of the P2020RDB ports. Signed-off-by: Renaud Barbier Signed-off-by: Sascha Hauer --- arch/ppc/boards/freescale-p2020rdb/p2020rdb.c | 13 +- arch/ppc/mach-mpc85xx/eth-devices.c | 46 ++++-- arch/ppc/mach-mpc85xx/include/mach/gianfar.h | 4 + drivers/net/gianfar.c | 146 +++++++++++++----- drivers/net/gianfar.h | 13 +- 5 files changed, 163 insertions(+), 59 deletions(-) diff --git a/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c b/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c index edb9bcd27..6426bd3c7 100644 --- a/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c +++ b/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c @@ -59,12 +59,19 @@ #define SYSCLK_50 50000000 #define SYSCLK_100 100000000 -/* Ethernet. Use eTSEC3 */ +/* Define attributes for eTSEC2 and eTSEC3 */ static struct gfar_info_struct gfar_info[] = { + { + .phyaddr = 0, + .tbiana = 0x1a0, + .tbicr = 0x9140, + .mdiobus_tbi = 1, + }, { .phyaddr = 1, .tbiana = 0, .tbicr = 0, + .mdiobus_tbi = 2, }, }; @@ -82,8 +89,8 @@ static int devices_init(void) add_generic_device("i2c-fsl", 1, NULL, I2C2_BASE_ADDR, 0x100, IORESOURCE_MEM, &i2cplat); - /* eTSEC3 */ - fsl_eth_init(3, &gfar_info[0]); + fsl_eth_init(2, &gfar_info[0]); + fsl_eth_init(3, &gfar_info[1]); devfs_add_partition("nor0", 0xf80000, 0x80000, DEVFS_PARTITION_FIXED, "self0"); diff --git a/arch/ppc/mach-mpc85xx/eth-devices.c b/arch/ppc/mach-mpc85xx/eth-devices.c index c6e8f3660..611a5787f 100644 --- a/arch/ppc/mach-mpc85xx/eth-devices.c +++ b/arch/ppc/mach-mpc85xx/eth-devices.c @@ -22,28 +22,40 @@ #include #include +#include #include #include -int fsl_eth_init(int num, struct gfar_info_struct *gf) +static int fsl_phy_init(void) { - struct resource *res; + int i; + void __iomem *base = IOMEM(GFAR_BASE_ADDR + GFAR_TBIPA_OFFSET); - res = xzalloc(3 * sizeof(struct resource)); - /* TSEC interface registers */ - res[0].start = GFAR_BASE_ADDR + ((num - 1) * 0x1000); - res[0].end = res[0].start + 0x1000 - 1; - res[0].flags = IORESOURCE_MEM; - /* External PHY access always through eTSEC1 */ - res[1].start = MDIO_BASE_ADDR; - res[1].end = res[1].start + 0x1000 - 1; - res[1].flags = IORESOURCE_MEM; - /* Access to TBI/RTBI interface. */ - res[2].start = MDIO_BASE_ADDR + ((num - 1) * 0x1000); - res[2].end = res[2].start + 0x1000 - 1; - res[2].flags = IORESOURCE_MEM; - - add_generic_device_res("gfar", DEVICE_ID_DYNAMIC, res, 3, gf); + /* + * The TBI address must be initialised to enable the PHY to + * link up after the MDIO reset. + */ + out_be32(base, GFAR_TBIPA_END); + /* All ports access external PHYs via the "gfar-mdio" device */ + add_generic_device("gfar-mdio", 0, NULL, MDIO_BASE_ADDR, + 0x1000, IORESOURCE_MEM, NULL); + for (i = 1; i < 3; i++) { + out_be32(base + (i * 0x1000), GFAR_TBIPA_END - i); + /* Use "gfar-tbiphy" devices to access internal PHY. */ + add_generic_device("gfar-tbiphy", i, NULL, + MDIO_BASE_ADDR + (i * 0x1000), + 0x1000, IORESOURCE_MEM, NULL); + } + return 0; +} + +coredevice_initcall(fsl_phy_init); + +int fsl_eth_init(int num, struct gfar_info_struct *gf) +{ + add_generic_device("gfar", DEVICE_ID_DYNAMIC, NULL, + GFAR_BASE_ADDR + ((num - 1) * 0x1000), 0x1000, + IORESOURCE_MEM, gf); return 0; } diff --git a/arch/ppc/mach-mpc85xx/include/mach/gianfar.h b/arch/ppc/mach-mpc85xx/include/mach/gianfar.h index ae3163865..6a7b9e945 100644 --- a/arch/ppc/mach-mpc85xx/include/mach/gianfar.h +++ b/arch/ppc/mach-mpc85xx/include/mach/gianfar.h @@ -22,10 +22,14 @@ * Platform data for the Motorola Triple Speed Ethernet Controller */ +#define GFAR_TBIPA_OFFSET 0x030 /* TBI PHY address */ +#define GFAR_TBIPA_END 0x1f /* Last valid PHY address */ + struct gfar_info_struct { unsigned int phyaddr; unsigned int tbiana; unsigned int tbicr; + unsigned int mdiobus_tbi; }; int fsl_eth_init(int num, struct gfar_info_struct *gf); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 96055bd39..f944c6c06 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -184,10 +184,11 @@ static int gfar_open(struct eth_device *edev) { int ix; struct gfar_private *priv = edev->priv; + struct gfar_phy *phy = priv->gfar_mdio; void __iomem *regs = priv->regs; int ret; - ret = phy_device_connect(edev, &priv->miibus, priv->phyaddr, + ret = phy_device_connect(edev, &phy->miibus, priv->phyaddr, gfar_adjust_link, 0, PHY_INTERFACE_MODE_NA); if (ret) return ret; @@ -305,44 +306,51 @@ static uint gfar_local_mdio_read(void __iomem *phyregs, uint phyid, uint regnum) static void gfar_configure_serdes(struct gfar_private *priv) { - gfar_local_mdio_write(priv->phyregs_sgmii, + struct gfar_phy *phy = priv->gfar_tbi; + + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_ANA, priv->tbiana); - gfar_local_mdio_write(priv->phyregs_sgmii, + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_TBICON, GFAR_TBICON_CLK_SELECT); - gfar_local_mdio_write(priv->phyregs_sgmii, + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_CR, priv->tbicr); } -/* Reset the internal and external PHYs. */ -static void gfar_init_phy(struct eth_device *dev) +static int gfar_bus_reset(struct mii_bus *bus) { - struct gfar_private *priv = dev->priv; - void __iomem *regs = priv->regs; + struct gfar_phy *phy = bus->priv; uint64_t start; - /* Assign a Physical address to the TBI */ - out_be32(regs + GFAR_TBIPA_OFFSET, GFAR_TBIPA_VALUE); - /* Reset MII (due to new addresses) */ - out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_RESET); - out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_INIT_VALUE); + out_be32(phy->regs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_RESET); + out_be32(phy->regs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_INIT_VALUE); start = get_time_ns(); while (!is_timeout(start, 10 * MSECOND)) { - if (!(in_be32(priv->phyregs + GFAR_MIIMMIND_OFFSET) & + if (!(in_be32(phy->regs + GFAR_MIIMMIND_OFFSET) & GFAR_MIIMIND_BUSY)) break; } + return 0; +} - gfar_local_mdio_write(priv->phyregs, priv->phyaddr, GFAR_MIIM_CR, +/* Reset the external PHYs. */ +static void gfar_init_phy(struct eth_device *dev) +{ + struct gfar_private *priv = dev->priv; + struct gfar_phy *phy = priv->gfar_mdio; + void __iomem *regs = priv->regs; + uint64_t start; + + gfar_local_mdio_write(phy->regs, priv->phyaddr, GFAR_MIIM_CR, GFAR_MIIM_CR_RST); start = get_time_ns(); while (!is_timeout(start, 10 * MSECOND)) { - if (!(gfar_local_mdio_read(priv->phyregs, priv->phyaddr, + if (!(gfar_local_mdio_read(phy->regs, priv->phyaddr, GFAR_MIIM_CR) & GFAR_MIIM_CR_RST)) break; } @@ -433,13 +441,12 @@ static int gfar_recv(struct eth_device *edev) /* Read a MII PHY register. */ static int gfar_miiphy_read(struct mii_bus *bus, int addr, int reg) { - struct device_d *dev = bus->parent; - struct gfar_private *priv = bus->priv; + struct gfar_phy *phy = bus->priv; int ret; - ret = gfar_local_mdio_read(priv->phyregs, addr, reg); + ret = gfar_local_mdio_read(phy->regs, addr, reg); if (ret == -EIO) - dev_err(dev, "Can't read PHY at address %d\n", addr); + dev_err(phy->dev, "Can't read PHY at address %d\n", addr); return ret; } @@ -448,15 +455,14 @@ static int gfar_miiphy_read(struct mii_bus *bus, int addr, int reg) static int gfar_miiphy_write(struct mii_bus *bus, int addr, int reg, u16 value) { - struct device_d *dev = bus->parent; - struct gfar_private *priv = bus->priv; + struct gfar_phy *phy = bus->priv; unsigned short val = value; int ret; - ret = gfar_local_mdio_write(priv->phyregs, addr, reg, val); + ret = gfar_local_mdio_write(phy->regs, addr, reg, val); if (ret) - dev_err(dev, "Can't write PHY at address %d\n", addr); + dev_err(phy->dev, "Can't write PHY at address %d\n", addr); return 0; } @@ -470,7 +476,9 @@ static int gfar_probe(struct device_d *dev) struct gfar_info_struct *gfar_info = dev->platform_data; struct eth_device *edev; struct gfar_private *priv; + struct device_d *mdev; size_t size; + char devname[16]; char *p; priv = xzalloc(sizeof(struct gfar_private)); @@ -480,14 +488,28 @@ static int gfar_probe(struct device_d *dev) edev = &priv->edev; - priv->regs = dev_request_mem_region(dev, 0); - priv->phyregs = dev_request_mem_region(dev, 1); - priv->phyregs_sgmii = dev_request_mem_region(dev, 2); - + priv->mdiobus_tbi = gfar_info->mdiobus_tbi; + priv->regs = dev_get_mem_region(dev, 0); priv->phyaddr = gfar_info->phyaddr; priv->tbicr = gfar_info->tbicr; priv->tbiana = gfar_info->tbiana; + mdev = get_device_by_name("gfar-mdio0"); + if (mdev == NULL) { + pr_err("gfar-mdio0 was not found\n"); + return -ENODEV; + } + priv->gfar_mdio = mdev->priv; + + if (priv->mdiobus_tbi != 0) { + sprintf(devname, "%s%d", "gfar-tbiphy", priv->mdiobus_tbi); + mdev = get_device_by_name(devname); + if (mdev == NULL) { + pr_err("%s was not found\n", devname); + return -ENODEV; + } + } + priv->gfar_tbi = mdev->priv; /* * Allocate descriptors 64-bit aligned. Descriptors * are 8 bytes in size. @@ -512,15 +534,8 @@ static int gfar_probe(struct device_d *dev) udelay(2); clrbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET); - priv->miibus.read = gfar_miiphy_read; - priv->miibus.write = gfar_miiphy_write; - priv->miibus.priv = priv; - priv->miibus.parent = dev; - gfar_init_phy(edev); - mdiobus_register(&priv->miibus); - return eth_register(edev); } @@ -529,3 +544,64 @@ static struct driver_d gfar_eth_driver = { .probe = gfar_probe, }; device_platform_driver(gfar_eth_driver); + +static int gfar_phy_probe(struct device_d *dev) +{ + struct gfar_phy *phy; + int ret; + + phy = xzalloc(sizeof(*phy)); + phy->dev = dev; + phy->regs = dev_get_mem_region(dev, 0); + if (!phy->regs) + return -ENOMEM; + + phy->miibus.read = gfar_miiphy_read; + phy->miibus.write = gfar_miiphy_write; + phy->miibus.priv = phy; + phy->miibus.reset = gfar_bus_reset; + phy->miibus.parent = dev; + dev->priv = phy; + + ret = mdiobus_register(&phy->miibus); + if (ret) + return ret; + + return 0; +} + +static struct driver_d gfar_phy_driver = { + .name = "gfar-mdio", + .probe = gfar_phy_probe, +}; +register_driver_macro(coredevice, platform, gfar_phy_driver); + +static int gfar_tbiphy_probe(struct device_d *dev) +{ + struct gfar_phy *phy; + int ret; + + phy = xzalloc(sizeof(*phy)); + phy->dev = dev; + phy->regs = dev_get_mem_region(dev, 0); + if (!phy->regs) + return -ENOMEM; + + phy->miibus.read = gfar_miiphy_read; + phy->miibus.write = gfar_miiphy_write; + phy->miibus.priv = phy; + phy->miibus.parent = dev; + dev->priv = phy; + + ret = mdiobus_register(&phy->miibus); + if (ret) + return ret; + + return 0; +} + +static struct driver_d gfar_tbiphy_driver = { + .name = "gfar-tbiphy", + .probe = gfar_tbiphy_probe, +}; +register_driver_macro(coredevice, platform, gfar_tbiphy_driver); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index b52cc5ab3..1aac47907 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -205,7 +205,6 @@ struct rxbd8 { #define GFAR_ECNTRL_OFFSET 0x020 /* Ethernet Control */ #define GFAR_MINFLR_OFFSET 0x024 /* Minimum Frame Length */ #define GFAR_DMACTRL_OFFSET 0x02c /* DMA Control */ -#define GFAR_TBIPA_OFFSET 0x030 /* TBI PHY address */ /* eTSEC transmit control and status register */ #define GFAR_TSTAT_OFFSET 0x104 /* transmit status register */ @@ -263,13 +262,19 @@ struct rxbd8 { #define GFAR_ATTR_OFFSET 0xbf8 /* Default Attribute Register */ #define GFAR_ATTRELI_OFFSET 0xbfc /* Default Attribute Extract Len/Idx */ +struct gfar_phy { + void __iomem *regs; + struct device_d *dev; + struct mii_bus miibus; +}; + struct gfar_private { struct eth_device edev; void __iomem *regs; - void __iomem *phyregs; - void __iomem *phyregs_sgmii; + int mdiobus_tbi; + struct gfar_phy *gfar_mdio; + struct gfar_phy *gfar_tbi; struct phy_info *phyinfo; - struct mii_bus miibus; volatile struct txbd8 *txbd; volatile struct rxbd8 *rxbd; uint txidx;