Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2006-2007 PA Semi, Inc
0004  *
0005  * Author: Olof Johansson, PA Semi
0006  *
0007  * Maintained by: Olof Johansson <olof@lixom.net>
0008  *
0009  * Based on drivers/net/fs_enet/mii-bitbang.c.
0010  */
0011 
0012 #include <linux/io.h>
0013 #include <linux/module.h>
0014 #include <linux/types.h>
0015 #include <linux/slab.h>
0016 #include <linux/sched.h>
0017 #include <linux/errno.h>
0018 #include <linux/ioport.h>
0019 #include <linux/interrupt.h>
0020 #include <linux/phy.h>
0021 #include <linux/of_address.h>
0022 #include <linux/of_mdio.h>
0023 #include <linux/of_platform.h>
0024 
0025 #define DELAY 1
0026 
0027 static void __iomem *gpio_regs;
0028 
0029 struct gpio_priv {
0030     int mdc_pin;
0031     int mdio_pin;
0032 };
0033 
0034 #define MDC_PIN(bus)    (((struct gpio_priv *)bus->priv)->mdc_pin)
0035 #define MDIO_PIN(bus)   (((struct gpio_priv *)bus->priv)->mdio_pin)
0036 
0037 static inline void mdio_lo(struct mii_bus *bus)
0038 {
0039     out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
0040 }
0041 
0042 static inline void mdio_hi(struct mii_bus *bus)
0043 {
0044     out_le32(gpio_regs, 1 << MDIO_PIN(bus));
0045 }
0046 
0047 static inline void mdc_lo(struct mii_bus *bus)
0048 {
0049     out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
0050 }
0051 
0052 static inline void mdc_hi(struct mii_bus *bus)
0053 {
0054     out_le32(gpio_regs, 1 << MDC_PIN(bus));
0055 }
0056 
0057 static inline void mdio_active(struct mii_bus *bus)
0058 {
0059     out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
0060 }
0061 
0062 static inline void mdio_tristate(struct mii_bus *bus)
0063 {
0064     out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
0065 }
0066 
0067 static inline int mdio_read(struct mii_bus *bus)
0068 {
0069     return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
0070 }
0071 
0072 static void clock_out(struct mii_bus *bus, int bit)
0073 {
0074     if (bit)
0075         mdio_hi(bus);
0076     else
0077         mdio_lo(bus);
0078     udelay(DELAY);
0079     mdc_hi(bus);
0080     udelay(DELAY);
0081     mdc_lo(bus);
0082 }
0083 
0084 /* Utility to send the preamble, address, and register (common to read and write). */
0085 static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
0086 {
0087     int i;
0088 
0089     /* CFE uses a really long preamble (40 bits). We'll do the same. */
0090     mdio_active(bus);
0091     for (i = 0; i < 40; i++) {
0092         clock_out(bus, 1);
0093     }
0094 
0095     /* send the start bit (01) and the read opcode (10) or write (10) */
0096     clock_out(bus, 0);
0097     clock_out(bus, 1);
0098 
0099     clock_out(bus, read);
0100     clock_out(bus, !read);
0101 
0102     /* send the PHY address */
0103     for (i = 0; i < 5; i++) {
0104         clock_out(bus, (addr & 0x10) != 0);
0105         addr <<= 1;
0106     }
0107 
0108     /* send the register address */
0109     for (i = 0; i < 5; i++) {
0110         clock_out(bus, (reg & 0x10) != 0);
0111         reg <<= 1;
0112     }
0113 }
0114 
0115 static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
0116 {
0117     u16 rdreg;
0118     int ret, i;
0119     u8 addr = phy_id & 0xff;
0120     u8 reg = location & 0xff;
0121 
0122     bitbang_pre(bus, 1, addr, reg);
0123 
0124     /* tri-state our MDIO I/O pin so we can read */
0125     mdio_tristate(bus);
0126     udelay(DELAY);
0127     mdc_hi(bus);
0128     udelay(DELAY);
0129     mdc_lo(bus);
0130 
0131     /* read 16 bits of register data, MSB first */
0132     rdreg = 0;
0133     for (i = 0; i < 16; i++) {
0134         mdc_lo(bus);
0135         udelay(DELAY);
0136         mdc_hi(bus);
0137         udelay(DELAY);
0138         mdc_lo(bus);
0139         udelay(DELAY);
0140         rdreg <<= 1;
0141         rdreg |= mdio_read(bus);
0142     }
0143 
0144     mdc_hi(bus);
0145     udelay(DELAY);
0146     mdc_lo(bus);
0147     udelay(DELAY);
0148 
0149     ret = rdreg;
0150 
0151     return ret;
0152 }
0153 
0154 static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
0155 {
0156     int i;
0157 
0158     u8 addr = phy_id & 0xff;
0159     u8 reg = location & 0xff;
0160     u16 value = val & 0xffff;
0161 
0162     bitbang_pre(bus, 0, addr, reg);
0163 
0164     /* send the turnaround (10) */
0165     mdc_lo(bus);
0166     mdio_hi(bus);
0167     udelay(DELAY);
0168     mdc_hi(bus);
0169     udelay(DELAY);
0170     mdc_lo(bus);
0171     mdio_lo(bus);
0172     udelay(DELAY);
0173     mdc_hi(bus);
0174     udelay(DELAY);
0175 
0176     /* write 16 bits of register data, MSB first */
0177     for (i = 0; i < 16; i++) {
0178         mdc_lo(bus);
0179         if (value & 0x8000)
0180             mdio_hi(bus);
0181         else
0182             mdio_lo(bus);
0183         udelay(DELAY);
0184         mdc_hi(bus);
0185         udelay(DELAY);
0186         value <<= 1;
0187     }
0188 
0189     /*
0190      * Tri-state the MDIO line.
0191      */
0192     mdio_tristate(bus);
0193     mdc_lo(bus);
0194     udelay(DELAY);
0195     mdc_hi(bus);
0196     udelay(DELAY);
0197     return 0;
0198 }
0199 
0200 static int gpio_mdio_reset(struct mii_bus *bus)
0201 {
0202     /*nothing here - dunno how to reset it*/
0203     return 0;
0204 }
0205 
0206 
0207 static int gpio_mdio_probe(struct platform_device *ofdev)
0208 {
0209     struct device *dev = &ofdev->dev;
0210     struct device_node *np = ofdev->dev.of_node;
0211     struct mii_bus *new_bus;
0212     struct gpio_priv *priv;
0213     const unsigned int *prop;
0214     int err;
0215 
0216     err = -ENOMEM;
0217     priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
0218     if (!priv)
0219         goto out;
0220 
0221     new_bus = mdiobus_alloc();
0222 
0223     if (!new_bus)
0224         goto out_free_priv;
0225 
0226     new_bus->name = "pasemi gpio mdio bus";
0227     new_bus->read = &gpio_mdio_read;
0228     new_bus->write = &gpio_mdio_write;
0229     new_bus->reset = &gpio_mdio_reset;
0230 
0231     prop = of_get_property(np, "reg", NULL);
0232     snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
0233     new_bus->priv = priv;
0234 
0235     prop = of_get_property(np, "mdc-pin", NULL);
0236     priv->mdc_pin = *prop;
0237 
0238     prop = of_get_property(np, "mdio-pin", NULL);
0239     priv->mdio_pin = *prop;
0240 
0241     new_bus->parent = dev;
0242     dev_set_drvdata(dev, new_bus);
0243 
0244     err = of_mdiobus_register(new_bus, np);
0245 
0246     if (err != 0) {
0247         pr_err("%s: Cannot register as MDIO bus, err %d\n",
0248                 new_bus->name, err);
0249         goto out_free_irq;
0250     }
0251 
0252     return 0;
0253 
0254 out_free_irq:
0255     kfree(new_bus);
0256 out_free_priv:
0257     kfree(priv);
0258 out:
0259     return err;
0260 }
0261 
0262 
0263 static int gpio_mdio_remove(struct platform_device *dev)
0264 {
0265     struct mii_bus *bus = dev_get_drvdata(&dev->dev);
0266 
0267     mdiobus_unregister(bus);
0268 
0269     dev_set_drvdata(&dev->dev, NULL);
0270 
0271     kfree(bus->priv);
0272     bus->priv = NULL;
0273     mdiobus_free(bus);
0274 
0275     return 0;
0276 }
0277 
0278 static const struct of_device_id gpio_mdio_match[] =
0279 {
0280     {
0281         .compatible      = "gpio-mdio",
0282     },
0283     {},
0284 };
0285 MODULE_DEVICE_TABLE(of, gpio_mdio_match);
0286 
0287 static struct platform_driver gpio_mdio_driver =
0288 {
0289     .probe      = gpio_mdio_probe,
0290     .remove     = gpio_mdio_remove,
0291     .driver = {
0292         .name = "gpio-mdio-bitbang",
0293         .of_match_table = gpio_mdio_match,
0294     },
0295 };
0296 
0297 static int gpio_mdio_init(void)
0298 {
0299     struct device_node *np;
0300 
0301     np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
0302     if (!np)
0303         np = of_find_compatible_node(NULL, NULL,
0304                          "pasemi,pwrficient-gpio");
0305     if (!np)
0306         return -ENODEV;
0307     gpio_regs = of_iomap(np, 0);
0308     of_node_put(np);
0309 
0310     if (!gpio_regs)
0311         return -ENODEV;
0312 
0313     return platform_driver_register(&gpio_mdio_driver);
0314 }
0315 module_init(gpio_mdio_init);
0316 
0317 static void gpio_mdio_exit(void)
0318 {
0319     platform_driver_unregister(&gpio_mdio_driver);
0320     if (gpio_regs)
0321         iounmap(gpio_regs);
0322 }
0323 module_exit(gpio_mdio_exit);
0324 
0325 MODULE_LICENSE("GPL");
0326 MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
0327 MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");