162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 Domen Puncer, Telargo, Inc. 562306a36Sopenharmony_ci * Copyright (C) 2008 Wolfram Sang, Pengutronix 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 862306a36Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 962306a36Sopenharmony_ci * kind, whether express or implied. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci#include <linux/phy.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/of_mdio.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <asm/io.h> 2262306a36Sopenharmony_ci#include <asm/mpc52xx.h> 2362306a36Sopenharmony_ci#include "fec_mpc52xx.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct mpc52xx_fec_mdio_priv { 2662306a36Sopenharmony_ci struct mpc52xx_fec __iomem *regs; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id, 3062306a36Sopenharmony_ci int reg, u32 value) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct mpc52xx_fec_mdio_priv *priv = bus->priv; 3362306a36Sopenharmony_ci struct mpc52xx_fec __iomem *fec = priv->regs; 3462306a36Sopenharmony_ci int tries = 3; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; 3762306a36Sopenharmony_ci value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci out_be32(&fec->ievent, FEC_IEVENT_MII); 4062306a36Sopenharmony_ci out_be32(&fec->mii_data, value); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* wait for it to finish, this takes about 23 us on lite5200b */ 4362306a36Sopenharmony_ci while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries) 4462306a36Sopenharmony_ci msleep(1); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!tries) 4762306a36Sopenharmony_ci return -ETIMEDOUT; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return value & FEC_MII_DATA_OP_RD ? 5062306a36Sopenharmony_ci in_be32(&fec->mii_data) & FEC_MII_DATA_DATAMSK : 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, FEC_MII_READ_FRAME); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, 5962306a36Sopenharmony_ci u16 data) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, 6262306a36Sopenharmony_ci data | FEC_MII_WRITE_FRAME); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int mpc52xx_fec_mdio_probe(struct platform_device *of) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct device *dev = &of->dev; 6862306a36Sopenharmony_ci struct device_node *np = of->dev.of_node; 6962306a36Sopenharmony_ci struct mii_bus *bus; 7062306a36Sopenharmony_ci struct mpc52xx_fec_mdio_priv *priv; 7162306a36Sopenharmony_ci struct resource res; 7262306a36Sopenharmony_ci int err; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci bus = mdiobus_alloc(); 7562306a36Sopenharmony_ci if (bus == NULL) 7662306a36Sopenharmony_ci return -ENOMEM; 7762306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 7862306a36Sopenharmony_ci if (priv == NULL) { 7962306a36Sopenharmony_ci err = -ENOMEM; 8062306a36Sopenharmony_ci goto out_free; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci bus->name = "mpc52xx MII bus"; 8462306a36Sopenharmony_ci bus->read = mpc52xx_fec_mdio_read; 8562306a36Sopenharmony_ci bus->write = mpc52xx_fec_mdio_write; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* setup registers */ 8862306a36Sopenharmony_ci err = of_address_to_resource(np, 0, &res); 8962306a36Sopenharmony_ci if (err) 9062306a36Sopenharmony_ci goto out_free; 9162306a36Sopenharmony_ci priv->regs = ioremap(res.start, resource_size(&res)); 9262306a36Sopenharmony_ci if (priv->regs == NULL) { 9362306a36Sopenharmony_ci err = -ENOMEM; 9462306a36Sopenharmony_ci goto out_free; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); 9862306a36Sopenharmony_ci bus->priv = priv; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci bus->parent = dev; 10162306a36Sopenharmony_ci dev_set_drvdata(dev, bus); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* set MII speed */ 10462306a36Sopenharmony_ci out_be32(&priv->regs->mii_speed, ((mpc5xxx_get_bus_frequency(dev) >> 20) / 5) << 1); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci err = of_mdiobus_register(bus, np); 10762306a36Sopenharmony_ci if (err) 10862306a36Sopenharmony_ci goto out_unmap; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci out_unmap: 11362306a36Sopenharmony_ci iounmap(priv->regs); 11462306a36Sopenharmony_ci out_free: 11562306a36Sopenharmony_ci kfree(priv); 11662306a36Sopenharmony_ci mdiobus_free(bus); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void mpc52xx_fec_mdio_remove(struct platform_device *of) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct mii_bus *bus = platform_get_drvdata(of); 12462306a36Sopenharmony_ci struct mpc52xx_fec_mdio_priv *priv = bus->priv; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci mdiobus_unregister(bus); 12762306a36Sopenharmony_ci iounmap(priv->regs); 12862306a36Sopenharmony_ci kfree(priv); 12962306a36Sopenharmony_ci mdiobus_free(bus); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct of_device_id mpc52xx_fec_mdio_match[] = { 13362306a36Sopenharmony_ci { .compatible = "fsl,mpc5200b-mdio", }, 13462306a36Sopenharmony_ci { .compatible = "fsl,mpc5200-mdio", }, 13562306a36Sopenharmony_ci { .compatible = "mpc5200b-fec-phy", }, 13662306a36Sopenharmony_ci {} 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct platform_driver mpc52xx_fec_mdio_driver = { 14162306a36Sopenharmony_ci .driver = { 14262306a36Sopenharmony_ci .name = "mpc5200b-fec-phy", 14362306a36Sopenharmony_ci .owner = THIS_MODULE, 14462306a36Sopenharmony_ci .of_match_table = mpc52xx_fec_mdio_match, 14562306a36Sopenharmony_ci }, 14662306a36Sopenharmony_ci .probe = mpc52xx_fec_mdio_probe, 14762306a36Sopenharmony_ci .remove_new = mpc52xx_fec_mdio_remove, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* let fec driver call it, since this has to be registered before it */ 15162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 154