fs_enet: Be an of_platform device when CONFIG_PPC_CPM_NEW_BINDING is set.
[deliverable/linux.git] / drivers / net / fs_enet / mii-bitbang.c
index 422f828778736f584be0d311a5ca5b471d114f67..7cf132f0f952fa6032a984afb9a59d3d10a2b947 100644 (file)
  */
 
 #include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.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/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>
+#ifdef CONFIG_PPC_CPM_NEW_BINDING
+#include <linux/of_platform.h>
+#endif
 
 #include "fs_enet.h"
 
-static int bitbang_prep_bit(u8 **datp, u8 *mskp,
-               struct fs_mii_bit *mii_bit)
-{
-       void *dat;
-       int adv;
-       u8 msk;
-
-       dat = (void*) mii_bit->offset;
-
-       adv = mii_bit->bit >> 3;
-       dat = (char *)dat + adv;
-
-       msk = 1 << (7 - (mii_bit->bit & 7));
-
-       *datp = dat;
-       *mskp = msk;
-
-       return 0;
-}
+struct bb_info {
+       __be32 __iomem *dir;
+       __be32 __iomem *dat;
+       u32 mdio_msk;
+       u32 mdc_msk;
+       int delay;
+};
 
-static inline void bb_set(u8 *p, u8 m)
+/* FIXME: If any other users of GPIO crop up, then these will have to
+ * have some sort of global synchronization to avoid races with other
+ * pins on the same port.  The ideal solution would probably be to
+ * bind the ports to a GPIO driver, and have this be a client of it.
+ */
+static inline void bb_set(u32 __iomem *p, u32 m)
 {
-       out_8(p, in_8(p) | m);
+       out_be32(p, in_be32(p) | m);
 }
 
-static inline void bb_clr(u8 *p, u8 m)
+static inline void bb_clr(u32 __iomem *p, u32 m)
 {
-       out_8(p, in_8(p) & ~m);
+       out_be32(p, in_be32(p) & ~m);
 }
 
-static inline int bb_read(u8 *p, u8 m)
+static inline int bb_read(u32 __iomem *p, u32 m)
 {
-       return (in_8(p) & m) != 0;
+       return (in_be32(p) & m) != 0;
 }
 
 static inline void mdio_active(struct bb_info *bitbang)
 {
-       bb_set(bitbang->mdio_dir, bitbang->mdio_dir_msk);
+       bb_set(bitbang->dir, bitbang->mdio_msk);
 }
 
-static inline void mdio_tristate(struct bb_info *bitbang )
+static inline void mdio_tristate(struct bb_info *bitbang)
 {
-       bb_clr(bitbang->mdio_dir, bitbang->mdio_dir_msk);
+       bb_clr(bitbang->dir, bitbang->mdio_msk);
 }
 
-static inline int mdio_read(struct bb_info *bitbang )
+static inline int mdio_read(struct bb_info *bitbang)
 {
-       return bb_read(bitbang->mdio_dat, bitbang->mdio_dat_msk);
+       return bb_read(bitbang->dat, bitbang->mdio_msk);
 }
 
-static inline void mdio(struct bb_info *bitbang , int what)
+static inline void mdio(struct bb_info *bitbang, int what)
 {
        if (what)
-               bb_set(bitbang->mdio_dat, bitbang->mdio_dat_msk);
+               bb_set(bitbang->dat, bitbang->mdio_msk);
        else
-               bb_clr(bitbang->mdio_dat, bitbang->mdio_dat_msk);
+               bb_clr(bitbang->dat, bitbang->mdio_msk);
 }
 
-static inline void mdc(struct bb_info *bitbang , int what)
+static inline void mdc(struct bb_info *bitbang, int what)
 {
        if (what)
-               bb_set(bitbang->mdc_dat, bitbang->mdc_msk);
+               bb_set(bitbang->dat, bitbang->mdc_msk);
        else
-               bb_clr(bitbang->mdc_dat, bitbang->mdc_msk);
+               bb_clr(bitbang->dat, bitbang->mdc_msk);
 }
 
-static inline void mii_delay(struct bb_info *bitbang )
+static inline void mii_delay(struct bb_info *bitbang)
 {
        udelay(bitbang->delay);
 }
@@ -280,29 +266,178 @@ static int fs_enet_mii_bb_reset(struct mii_bus *bus)
        return 0;
 }
 
-static int fs_mii_bitbang_init(struct bb_info *bitbang, struct fs_mii_bb_platform_info* fmpi)
+#ifdef CONFIG_PPC_CPM_NEW_BINDING
+static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
+                                         struct device_node *np)
 {
-       int r;
+       struct resource res;
+       const u32 *data;
+       int mdio_pin, mdc_pin, len;
+       struct bb_info *bitbang = bus->priv;
 
-       bitbang->delay = fmpi->delay;
+       int ret = of_address_to_resource(np, 0, &res);
+       if (ret)
+               return ret;
+
+       if (res.end - res.start < 13)
+               return -ENODEV;
+
+       /* This should really encode the pin number as well, but all
+        * we get is an int, and the odds of multiple bitbang mdio buses
+        * is low enough that it's not worth going too crazy.
+        */
+       bus->id = res.start;
+
+       data = of_get_property(np, "fsl,mdio-pin", &len);
+       if (!data || len != 4)
+               return -ENODEV;
+       mdio_pin = *data;
+
+       data = of_get_property(np, "fsl,mdc-pin", &len);
+       if (!data || len != 4)
+               return -ENODEV;
+       mdc_pin = *data;
+
+       bitbang->dir = ioremap(res.start, res.end - res.start + 1);
+       if (!bitbang->dir)
+               return -ENOMEM;
+
+       bitbang->dat = bitbang->dir + 4;
+       bitbang->mdio_msk = 1 << (31 - mdio_pin);
+       bitbang->mdc_msk = 1 << (31 - mdc_pin);
+       bitbang->delay = 1; /* 1 us between operations */
 
-       r = bitbang_prep_bit(&bitbang->mdio_dir,
-                        &bitbang->mdio_dir_msk,
-                        &fmpi->mdio_dir);
-       if (r != 0)
-               return r;
-
-       r = bitbang_prep_bit(&bitbang->mdio_dat,
-                        &bitbang->mdio_dat_msk,
-                        &fmpi->mdio_dat);
-       if (r != 0)
-               return r;
-
-       r = bitbang_prep_bit(&bitbang->mdc_dat,
-                        &bitbang->mdc_msk,
-                        &fmpi->mdc_dat);
-       if (r != 0)
-               return r;
+       return 0;
+}
+
+static void __devinit add_phy(struct mii_bus *bus, struct device_node *np)
+{
+       const u32 *data;
+       int len, id, irq;
+
+       data = of_get_property(np, "reg", &len);
+       if (!data || len != 4)
+               return;
+
+       id = *data;
+       bus->phy_mask &= ~(1 << id);
+
+       irq = of_irq_to_resource(np, 0, NULL);
+       if (irq != NO_IRQ)
+               bus->irq[id] = irq;
+}
+
+static int __devinit fs_enet_mdio_probe(struct of_device *ofdev,
+                                        const struct of_device_id *match)
+{
+       struct device_node *np = NULL;
+       struct mii_bus *new_bus;
+       struct bb_info *bitbang;
+       int ret = -ENOMEM;
+       int i;
+
+       new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+       if (!new_bus)
+               goto out;
+
+       bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
+       if (!bitbang)
+               goto out_free_bus;
+
+       new_bus->priv = bitbang;
+       new_bus->name = "CPM2 Bitbanged MII",
+       new_bus->read = &fs_enet_mii_bb_read,
+       new_bus->write = &fs_enet_mii_bb_write,
+       new_bus->reset = &fs_enet_mii_bb_reset,
+
+       ret = fs_mii_bitbang_init(new_bus, ofdev->node);
+       if (ret)
+               goto out_free_bitbang;
+
+       new_bus->phy_mask = ~0;
+       new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!new_bus->irq)
+               goto out_unmap_regs;
+
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               new_bus->irq[i] = -1;
+
+       while ((np = of_get_next_child(ofdev->node, np)))
+               if (!strcmp(np->type, "ethernet-phy"))
+                       add_phy(new_bus, np);
+
+       new_bus->dev = &ofdev->dev;
+       dev_set_drvdata(&ofdev->dev, new_bus);
+
+       ret = mdiobus_register(new_bus);
+       if (ret)
+               goto out_free_irqs;
+
+       return 0;
+
+out_free_irqs:
+       dev_set_drvdata(&ofdev->dev, NULL);
+       kfree(new_bus->irq);
+out_unmap_regs:
+       iounmap(bitbang->dir);
+out_free_bitbang:
+       kfree(bitbang);
+out_free_bus:
+       kfree(new_bus);
+out:
+       return ret;
+}
+
+static int fs_enet_mdio_remove(struct of_device *ofdev)
+{
+       struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
+       struct bb_info *bitbang = bus->priv;
+
+       mdiobus_unregister(bus);
+       dev_set_drvdata(&ofdev->dev, NULL);
+       kfree(bus->irq);
+       iounmap(bitbang->dir);
+       kfree(bitbang);
+       kfree(bus);
+
+       return 0;
+}
+
+static struct of_device_id fs_enet_mdio_bb_match[] = {
+       {
+               .compatible = "fsl,cpm2-mdio-bitbang",
+       },
+       {},
+};
+
+static struct of_platform_driver fs_enet_bb_mdio_driver = {
+       .name = "fsl-bb-mdio",
+       .match_table = fs_enet_mdio_bb_match,
+       .probe = fs_enet_mdio_probe,
+       .remove = fs_enet_mdio_remove,
+};
+
+int fs_enet_mdio_bb_init(void)
+{
+       return of_register_platform_driver(&fs_enet_bb_mdio_driver);
+}
+
+void fs_enet_mdio_bb_exit(void)
+{
+       of_unregister_platform_driver(&fs_enet_bb_mdio_driver);
+}
+
+module_init(fs_enet_mdio_bb_init);
+module_exit(fs_enet_mdio_bb_exit);
+#else
+static int __devinit fs_mii_bitbang_init(struct bb_info *bitbang,
+                                         struct fs_mii_bb_platform_info *fmpi)
+{
+       bitbang->dir = (u32 __iomem *)fmpi->mdio_dir.offset;
+       bitbang->dat = (u32 __iomem *)fmpi->mdio_dat.offset;
+       bitbang->mdio_msk = 1U << (31 - fmpi->mdio_dat.bit);
+       bitbang->mdc_msk = 1U << (31 - fmpi->mdc_dat.bit);
+       bitbang->delay = fmpi->delay;
 
        return 0;
 }
This page took 0.032592 seconds and 5 git commands to generate.