Path: blob/master/arch/powerpc/platforms/pasemi/gpio_mdio.c
26489 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2006-2007 PA Semi, Inc3*4* Author: Olof Johansson, PA Semi5*6* Maintained by: Olof Johansson <[email protected]>7*8* Based on drivers/net/fs_enet/mii-bitbang.c.9*/1011#include <linux/io.h>12#include <linux/module.h>13#include <linux/types.h>14#include <linux/slab.h>15#include <linux/sched.h>16#include <linux/errno.h>17#include <linux/ioport.h>18#include <linux/interrupt.h>19#include <linux/phy.h>20#include <linux/of_address.h>21#include <linux/of_mdio.h>22#include <linux/platform_device.h>2324#define DELAY 12526static void __iomem *gpio_regs;2728struct gpio_priv {29int mdc_pin;30int mdio_pin;31};3233#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin)34#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin)3536static inline void mdio_lo(struct mii_bus *bus)37{38out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));39}4041static inline void mdio_hi(struct mii_bus *bus)42{43out_le32(gpio_regs, 1 << MDIO_PIN(bus));44}4546static inline void mdc_lo(struct mii_bus *bus)47{48out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));49}5051static inline void mdc_hi(struct mii_bus *bus)52{53out_le32(gpio_regs, 1 << MDC_PIN(bus));54}5556static inline void mdio_active(struct mii_bus *bus)57{58out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));59}6061static inline void mdio_tristate(struct mii_bus *bus)62{63out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));64}6566static inline int mdio_read(struct mii_bus *bus)67{68return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));69}7071static void clock_out(struct mii_bus *bus, int bit)72{73if (bit)74mdio_hi(bus);75else76mdio_lo(bus);77udelay(DELAY);78mdc_hi(bus);79udelay(DELAY);80mdc_lo(bus);81}8283/* Utility to send the preamble, address, and register (common to read and write). */84static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)85{86int i;8788/* CFE uses a really long preamble (40 bits). We'll do the same. */89mdio_active(bus);90for (i = 0; i < 40; i++) {91clock_out(bus, 1);92}9394/* send the start bit (01) and the read opcode (10) or write (10) */95clock_out(bus, 0);96clock_out(bus, 1);9798clock_out(bus, read);99clock_out(bus, !read);100101/* send the PHY address */102for (i = 0; i < 5; i++) {103clock_out(bus, (addr & 0x10) != 0);104addr <<= 1;105}106107/* send the register address */108for (i = 0; i < 5; i++) {109clock_out(bus, (reg & 0x10) != 0);110reg <<= 1;111}112}113114static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)115{116u16 rdreg;117int ret, i;118u8 addr = phy_id & 0xff;119u8 reg = location & 0xff;120121bitbang_pre(bus, 1, addr, reg);122123/* tri-state our MDIO I/O pin so we can read */124mdio_tristate(bus);125udelay(DELAY);126mdc_hi(bus);127udelay(DELAY);128mdc_lo(bus);129130/* read 16 bits of register data, MSB first */131rdreg = 0;132for (i = 0; i < 16; i++) {133mdc_lo(bus);134udelay(DELAY);135mdc_hi(bus);136udelay(DELAY);137mdc_lo(bus);138udelay(DELAY);139rdreg <<= 1;140rdreg |= mdio_read(bus);141}142143mdc_hi(bus);144udelay(DELAY);145mdc_lo(bus);146udelay(DELAY);147148ret = rdreg;149150return ret;151}152153static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)154{155int i;156157u8 addr = phy_id & 0xff;158u8 reg = location & 0xff;159u16 value = val & 0xffff;160161bitbang_pre(bus, 0, addr, reg);162163/* send the turnaround (10) */164mdc_lo(bus);165mdio_hi(bus);166udelay(DELAY);167mdc_hi(bus);168udelay(DELAY);169mdc_lo(bus);170mdio_lo(bus);171udelay(DELAY);172mdc_hi(bus);173udelay(DELAY);174175/* write 16 bits of register data, MSB first */176for (i = 0; i < 16; i++) {177mdc_lo(bus);178if (value & 0x8000)179mdio_hi(bus);180else181mdio_lo(bus);182udelay(DELAY);183mdc_hi(bus);184udelay(DELAY);185value <<= 1;186}187188/*189* Tri-state the MDIO line.190*/191mdio_tristate(bus);192mdc_lo(bus);193udelay(DELAY);194mdc_hi(bus);195udelay(DELAY);196return 0;197}198199static int gpio_mdio_reset(struct mii_bus *bus)200{201/*nothing here - dunno how to reset it*/202return 0;203}204205206static int gpio_mdio_probe(struct platform_device *ofdev)207{208struct device *dev = &ofdev->dev;209struct device_node *np = ofdev->dev.of_node;210struct mii_bus *new_bus;211struct gpio_priv *priv;212const unsigned int *prop;213int err;214215err = -ENOMEM;216priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);217if (!priv)218goto out;219220new_bus = mdiobus_alloc();221222if (!new_bus)223goto out_free_priv;224225new_bus->name = "pasemi gpio mdio bus";226new_bus->read = &gpio_mdio_read;227new_bus->write = &gpio_mdio_write;228new_bus->reset = &gpio_mdio_reset;229230prop = of_get_property(np, "reg", NULL);231snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);232new_bus->priv = priv;233234prop = of_get_property(np, "mdc-pin", NULL);235priv->mdc_pin = *prop;236237prop = of_get_property(np, "mdio-pin", NULL);238priv->mdio_pin = *prop;239240new_bus->parent = dev;241dev_set_drvdata(dev, new_bus);242243err = of_mdiobus_register(new_bus, np);244245if (err != 0) {246pr_err("%s: Cannot register as MDIO bus, err %d\n",247new_bus->name, err);248goto out_free_irq;249}250251return 0;252253out_free_irq:254kfree(new_bus);255out_free_priv:256kfree(priv);257out:258return err;259}260261262static void gpio_mdio_remove(struct platform_device *dev)263{264struct mii_bus *bus = dev_get_drvdata(&dev->dev);265266mdiobus_unregister(bus);267268dev_set_drvdata(&dev->dev, NULL);269270kfree(bus->priv);271bus->priv = NULL;272mdiobus_free(bus);273}274275static const struct of_device_id gpio_mdio_match[] =276{277{278.compatible = "gpio-mdio",279},280{},281};282MODULE_DEVICE_TABLE(of, gpio_mdio_match);283284static struct platform_driver gpio_mdio_driver =285{286.probe = gpio_mdio_probe,287.remove = gpio_mdio_remove,288.driver = {289.name = "gpio-mdio-bitbang",290.of_match_table = gpio_mdio_match,291},292};293294static int __init gpio_mdio_init(void)295{296struct device_node *np;297298np = of_find_compatible_node(NULL, NULL, "1682m-gpio");299if (!np)300np = of_find_compatible_node(NULL, NULL,301"pasemi,pwrficient-gpio");302if (!np)303return -ENODEV;304gpio_regs = of_iomap(np, 0);305of_node_put(np);306307if (!gpio_regs)308return -ENODEV;309310return platform_driver_register(&gpio_mdio_driver);311}312module_init(gpio_mdio_init);313314static void __exit gpio_mdio_exit(void)315{316platform_driver_unregister(&gpio_mdio_driver);317if (gpio_regs)318iounmap(gpio_regs);319}320module_exit(gpio_mdio_exit);321322MODULE_LICENSE("GPL");323MODULE_AUTHOR("Olof Johansson <[email protected]>");324MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");325326327