summaryrefslogtreecommitdiffstats
path: root/drivers/net/fs_enet/mii-fec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/fs_enet/mii-fec.c')
-rw-r--r--drivers/net/fs_enet/mii-fec.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
new file mode 100644
index 000000000000..1328e10caa35
--- /dev/null
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -0,0 +1,243 @@
+/*
+ * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
+ *
+ * Copyright (c) 2003 Intracom S.A.
+ * by Pantelis Antoniou <panto@intracom.gr>
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "fs_enet.h"
+#include "fec.h"
+
+/* Make MII read/write commands for the FEC.
+*/
+#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
+#define mk_mii_end 0
+
+#define FEC_MII_LOOPS 10000
+
+static int match_has_phy (struct device *dev, void* data)
+{
+ struct platform_device* pdev = container_of(dev, struct platform_device, dev);
+ struct fs_platform_info* fpi;
+ if(strcmp(pdev->name, (char*)data))
+ {
+ return 0;
+ }
+
+ fpi = pdev->dev.platform_data;
+ if((fpi)&&(fpi->has_phy))
+ return 1;
+ return 0;
+}
+
+static int fs_mii_fec_init(struct fec_info* fec, struct fs_mii_fec_platform_info *fmpi)
+{
+ struct resource *r;
+ fec_t *fecp;
+ char* name = "fsl-cpm-fec";
+
+ /* we need fec in order to be useful */
+ struct platform_device *fec_pdev =
+ container_of(bus_find_device(&platform_bus_type, NULL, name, match_has_phy),
+ struct platform_device, dev);
+
+ if(fec_pdev == NULL) {
+ printk(KERN_ERR"Unable to find PHY for %s", name);
+ return -ENODEV;
+ }
+
+ r = platform_get_resource_byname(fec_pdev, IORESOURCE_MEM, "regs");
+
+ fec->fecp = fecp = (fec_t*)ioremap(r->start,sizeof(fec_t));
+ fec->mii_speed = fmpi->mii_speed;
+
+ setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ setbits32(&fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+ out_be32(&fecp->fec_mii_speed, fec->mii_speed);
+
+ return 0;
+}
+
+static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location)
+{
+ struct fec_info* fec = bus->priv;
+ fec_t *fecp = fec->fecp;
+ int i, ret = -1;
+
+ if ((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
+ BUG();
+
+ /* Add PHY address to register command. */
+ out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_read(location));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS) {
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+ ret = in_be32(&fecp->fec_mii_data) & 0xffff;
+ }
+
+ return ret;
+
+}
+
+static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
+{
+ struct fec_info* fec = bus->priv;
+ fec_t *fecp = fec->fecp;
+ int i;
+
+ /* this must never happen */
+ if ((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
+ BUG();
+
+ /* Add PHY address to register command. */
+ out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_write(location, val));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS)
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+
+ return 0;
+
+}
+
+static int fs_enet_fec_mii_reset(struct mii_bus *bus)
+{
+ /* nothing here - for now */
+ return 0;
+}
+
+static int __devinit fs_enet_fec_mdio_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fs_mii_fec_platform_info *pdata;
+ struct mii_bus *new_bus;
+ struct fec_info *fec;
+ int err = 0;
+ if (NULL == dev)
+ return -EINVAL;
+ new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+ if (NULL == new_bus)
+ return -ENOMEM;
+
+ fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL);
+
+ if (NULL == fec)
+ return -ENOMEM;
+
+ new_bus->name = "FEC MII Bus",
+ new_bus->read = &fs_enet_fec_mii_read,
+ new_bus->write = &fs_enet_fec_mii_write,
+ new_bus->reset = &fs_enet_fec_mii_reset,
+ new_bus->id = pdev->id;
+
+ pdata = (struct fs_mii_fec_platform_info *)pdev->dev.platform_data;
+
+ if (NULL == pdata) {
+ printk(KERN_ERR "fs_enet FEC mdio %d: Missing platform data!\n", pdev->id);
+ return -ENODEV;
+ }
+
+ /*set up workspace*/
+
+ fs_mii_fec_init(fec, pdata);
+ new_bus->priv = fec;
+
+ new_bus->irq = pdata->irq;
+
+ new_bus->dev = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ err = mdiobus_register(new_bus);
+
+ if (0 != err) {
+ printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
+ new_bus->name);
+ goto bus_register_fail;
+ }
+
+ return 0;
+
+bus_register_fail:
+ kfree(new_bus);
+
+ return err;
+}
+
+
+static int fs_enet_fec_mdio_remove(struct device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(dev);
+
+ mdiobus_unregister(bus);
+
+ dev_set_drvdata(dev, NULL);
+ kfree(bus->priv);
+
+ bus->priv = NULL;
+ kfree(bus);
+
+ return 0;
+}
+
+static struct device_driver fs_enet_fec_mdio_driver = {
+ .name = "fsl-cpm-fec-mdio",
+ .bus = &platform_bus_type,
+ .probe = fs_enet_fec_mdio_probe,
+ .remove = fs_enet_fec_mdio_remove,
+};
+
+int fs_enet_mdio_fec_init(void)
+{
+ return driver_register(&fs_enet_fec_mdio_driver);
+}
+
+void fs_enet_mdio_fec_exit(void)
+{
+ driver_unregister(&fs_enet_fec_mdio_driver);
+}
+
OpenPOWER on IntegriCloud