Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at e83f1eb6bfc4004c19a99ee5f5aa65bd3fbecec3 184 lines 4.4 kB view raw
1/* 2 * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver 3 * 4 * Copyright (C) 2007 Domen Puncer, Telargo, Inc. 5 * Copyright (C) 2008 Wolfram Sang, Pengutronix 6 * 7 * This file is licensed under the terms of the GNU General Public License 8 * version 2. This program is licensed "as is" without any warranty of any 9 * kind, whether express or implied. 10 */ 11 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/netdevice.h> 15#include <linux/phy.h> 16#include <linux/of_platform.h> 17#include <asm/io.h> 18#include <asm/mpc52xx.h> 19#include "fec_mpc52xx.h" 20 21struct mpc52xx_fec_mdio_priv { 22 struct mpc52xx_fec __iomem *regs; 23}; 24 25static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id, 26 int reg, u32 value) 27{ 28 struct mpc52xx_fec_mdio_priv *priv = bus->priv; 29 struct mpc52xx_fec __iomem *fec; 30 int tries = 100; 31 32 value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; 33 value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; 34 35 fec = priv->regs; 36 out_be32(&fec->ievent, FEC_IEVENT_MII); 37 out_be32(&priv->regs->mii_data, value); 38 39 /* wait for it to finish, this takes about 23 us on lite5200b */ 40 while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries) 41 udelay(5); 42 43 if (!tries) 44 return -ETIMEDOUT; 45 46 return value & FEC_MII_DATA_OP_RD ? 47 in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK : 0; 48} 49 50static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) 51{ 52 return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, FEC_MII_READ_FRAME); 53} 54 55static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, 56 u16 data) 57{ 58 return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, 59 data | FEC_MII_WRITE_FRAME); 60} 61 62static int mpc52xx_fec_mdio_probe(struct of_device *of, 63 const struct of_device_id *match) 64{ 65 struct device *dev = &of->dev; 66 struct device_node *np = of->node; 67 struct device_node *child = NULL; 68 struct mii_bus *bus; 69 struct mpc52xx_fec_mdio_priv *priv; 70 struct resource res = {}; 71 int err; 72 int i; 73 74 bus = mdiobus_alloc(); 75 if (bus == NULL) 76 return -ENOMEM; 77 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 78 if (priv == NULL) { 79 err = -ENOMEM; 80 goto out_free; 81 } 82 83 bus->name = "mpc52xx MII bus"; 84 bus->read = mpc52xx_fec_mdio_read; 85 bus->write = mpc52xx_fec_mdio_write; 86 87 /* setup irqs */ 88 bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL); 89 if (bus->irq == NULL) { 90 err = -ENOMEM; 91 goto out_free; 92 } 93 for (i=0; i<PHY_MAX_ADDR; i++) 94 bus->irq[i] = PHY_POLL; 95 96 while ((child = of_get_next_child(np, child)) != NULL) { 97 int irq = irq_of_parse_and_map(child, 0); 98 if (irq != NO_IRQ) { 99 const u32 *id = of_get_property(child, "reg", NULL); 100 if (id) 101 bus->irq[*id] = irq; 102 } 103 } 104 105 /* setup registers */ 106 err = of_address_to_resource(np, 0, &res); 107 if (err) 108 goto out_free; 109 priv->regs = ioremap(res.start, res.end - res.start + 1); 110 if (priv->regs == NULL) { 111 err = -ENOMEM; 112 goto out_free; 113 } 114 115 snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); 116 bus->priv = priv; 117 118 bus->parent = dev; 119 dev_set_drvdata(dev, bus); 120 121 /* set MII speed */ 122 out_be32(&priv->regs->mii_speed, 123 ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1); 124 125 err = mdiobus_register(bus); 126 if (err) 127 goto out_unmap; 128 129 return 0; 130 131 out_unmap: 132 iounmap(priv->regs); 133 out_free: 134 for (i=0; i<PHY_MAX_ADDR; i++) 135 if (bus->irq[i] != PHY_POLL) 136 irq_dispose_mapping(bus->irq[i]); 137 kfree(bus->irq); 138 kfree(priv); 139 mdiobus_free(bus); 140 141 return err; 142} 143 144static int mpc52xx_fec_mdio_remove(struct of_device *of) 145{ 146 struct device *dev = &of->dev; 147 struct mii_bus *bus = dev_get_drvdata(dev); 148 struct mpc52xx_fec_mdio_priv *priv = bus->priv; 149 int i; 150 151 mdiobus_unregister(bus); 152 dev_set_drvdata(dev, NULL); 153 154 iounmap(priv->regs); 155 for (i=0; i<PHY_MAX_ADDR; i++) 156 if (bus->irq[i] != PHY_POLL) 157 irq_dispose_mapping(bus->irq[i]); 158 kfree(priv); 159 kfree(bus->irq); 160 mdiobus_free(bus); 161 162 return 0; 163} 164 165 166static struct of_device_id mpc52xx_fec_mdio_match[] = { 167 { .compatible = "fsl,mpc5200b-mdio", }, 168 { .compatible = "fsl,mpc5200-mdio", }, 169 { .compatible = "mpc5200b-fec-phy", }, 170 {} 171}; 172 173struct of_platform_driver mpc52xx_fec_mdio_driver = { 174 .name = "mpc5200b-fec-phy", 175 .probe = mpc52xx_fec_mdio_probe, 176 .remove = mpc52xx_fec_mdio_remove, 177 .match_table = mpc52xx_fec_mdio_match, 178}; 179 180/* let fec driver call it, since this has to be registered before it */ 181EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver); 182 183 184MODULE_LICENSE("Dual BSD/GPL");