cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gpio_mdio.c (6296B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2006-2007 PA Semi, Inc
      4 *
      5 * Author: Olof Johansson, PA Semi
      6 *
      7 * Maintained by: Olof Johansson <olof@lixom.net>
      8 *
      9 * Based on drivers/net/fs_enet/mii-bitbang.c.
     10 */
     11
     12#include <linux/io.h>
     13#include <linux/module.h>
     14#include <linux/types.h>
     15#include <linux/slab.h>
     16#include <linux/sched.h>
     17#include <linux/errno.h>
     18#include <linux/ioport.h>
     19#include <linux/interrupt.h>
     20#include <linux/phy.h>
     21#include <linux/of_address.h>
     22#include <linux/of_mdio.h>
     23#include <linux/of_platform.h>
     24
     25#define DELAY 1
     26
     27static void __iomem *gpio_regs;
     28
     29struct gpio_priv {
     30	int mdc_pin;
     31	int mdio_pin;
     32};
     33
     34#define MDC_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdc_pin)
     35#define MDIO_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdio_pin)
     36
     37static inline void mdio_lo(struct mii_bus *bus)
     38{
     39	out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
     40}
     41
     42static inline void mdio_hi(struct mii_bus *bus)
     43{
     44	out_le32(gpio_regs, 1 << MDIO_PIN(bus));
     45}
     46
     47static inline void mdc_lo(struct mii_bus *bus)
     48{
     49	out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
     50}
     51
     52static inline void mdc_hi(struct mii_bus *bus)
     53{
     54	out_le32(gpio_regs, 1 << MDC_PIN(bus));
     55}
     56
     57static inline void mdio_active(struct mii_bus *bus)
     58{
     59	out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
     60}
     61
     62static inline void mdio_tristate(struct mii_bus *bus)
     63{
     64	out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
     65}
     66
     67static inline int mdio_read(struct mii_bus *bus)
     68{
     69	return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
     70}
     71
     72static void clock_out(struct mii_bus *bus, int bit)
     73{
     74	if (bit)
     75		mdio_hi(bus);
     76	else
     77		mdio_lo(bus);
     78	udelay(DELAY);
     79	mdc_hi(bus);
     80	udelay(DELAY);
     81	mdc_lo(bus);
     82}
     83
     84/* Utility to send the preamble, address, and register (common to read and write). */
     85static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
     86{
     87	int i;
     88
     89	/* CFE uses a really long preamble (40 bits). We'll do the same. */
     90	mdio_active(bus);
     91	for (i = 0; i < 40; i++) {
     92		clock_out(bus, 1);
     93	}
     94
     95	/* send the start bit (01) and the read opcode (10) or write (10) */
     96	clock_out(bus, 0);
     97	clock_out(bus, 1);
     98
     99	clock_out(bus, read);
    100	clock_out(bus, !read);
    101
    102	/* send the PHY address */
    103	for (i = 0; i < 5; i++) {
    104		clock_out(bus, (addr & 0x10) != 0);
    105		addr <<= 1;
    106	}
    107
    108	/* send the register address */
    109	for (i = 0; i < 5; i++) {
    110		clock_out(bus, (reg & 0x10) != 0);
    111		reg <<= 1;
    112	}
    113}
    114
    115static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
    116{
    117	u16 rdreg;
    118	int ret, i;
    119	u8 addr = phy_id & 0xff;
    120	u8 reg = location & 0xff;
    121
    122	bitbang_pre(bus, 1, addr, reg);
    123
    124	/* tri-state our MDIO I/O pin so we can read */
    125	mdio_tristate(bus);
    126	udelay(DELAY);
    127	mdc_hi(bus);
    128	udelay(DELAY);
    129	mdc_lo(bus);
    130
    131	/* read 16 bits of register data, MSB first */
    132	rdreg = 0;
    133	for (i = 0; i < 16; i++) {
    134		mdc_lo(bus);
    135		udelay(DELAY);
    136		mdc_hi(bus);
    137		udelay(DELAY);
    138		mdc_lo(bus);
    139		udelay(DELAY);
    140		rdreg <<= 1;
    141		rdreg |= mdio_read(bus);
    142	}
    143
    144	mdc_hi(bus);
    145	udelay(DELAY);
    146	mdc_lo(bus);
    147	udelay(DELAY);
    148
    149	ret = rdreg;
    150
    151	return ret;
    152}
    153
    154static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
    155{
    156	int i;
    157
    158	u8 addr = phy_id & 0xff;
    159	u8 reg = location & 0xff;
    160	u16 value = val & 0xffff;
    161
    162	bitbang_pre(bus, 0, addr, reg);
    163
    164	/* send the turnaround (10) */
    165	mdc_lo(bus);
    166	mdio_hi(bus);
    167	udelay(DELAY);
    168	mdc_hi(bus);
    169	udelay(DELAY);
    170	mdc_lo(bus);
    171	mdio_lo(bus);
    172	udelay(DELAY);
    173	mdc_hi(bus);
    174	udelay(DELAY);
    175
    176	/* write 16 bits of register data, MSB first */
    177	for (i = 0; i < 16; i++) {
    178		mdc_lo(bus);
    179		if (value & 0x8000)
    180			mdio_hi(bus);
    181		else
    182			mdio_lo(bus);
    183		udelay(DELAY);
    184		mdc_hi(bus);
    185		udelay(DELAY);
    186		value <<= 1;
    187	}
    188
    189	/*
    190	 * Tri-state the MDIO line.
    191	 */
    192	mdio_tristate(bus);
    193	mdc_lo(bus);
    194	udelay(DELAY);
    195	mdc_hi(bus);
    196	udelay(DELAY);
    197	return 0;
    198}
    199
    200static int gpio_mdio_reset(struct mii_bus *bus)
    201{
    202	/*nothing here - dunno how to reset it*/
    203	return 0;
    204}
    205
    206
    207static int gpio_mdio_probe(struct platform_device *ofdev)
    208{
    209	struct device *dev = &ofdev->dev;
    210	struct device_node *np = ofdev->dev.of_node;
    211	struct mii_bus *new_bus;
    212	struct gpio_priv *priv;
    213	const unsigned int *prop;
    214	int err;
    215
    216	err = -ENOMEM;
    217	priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
    218	if (!priv)
    219		goto out;
    220
    221	new_bus = mdiobus_alloc();
    222
    223	if (!new_bus)
    224		goto out_free_priv;
    225
    226	new_bus->name = "pasemi gpio mdio bus";
    227	new_bus->read = &gpio_mdio_read;
    228	new_bus->write = &gpio_mdio_write;
    229	new_bus->reset = &gpio_mdio_reset;
    230
    231	prop = of_get_property(np, "reg", NULL);
    232	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
    233	new_bus->priv = priv;
    234
    235	prop = of_get_property(np, "mdc-pin", NULL);
    236	priv->mdc_pin = *prop;
    237
    238	prop = of_get_property(np, "mdio-pin", NULL);
    239	priv->mdio_pin = *prop;
    240
    241	new_bus->parent = dev;
    242	dev_set_drvdata(dev, new_bus);
    243
    244	err = of_mdiobus_register(new_bus, np);
    245
    246	if (err != 0) {
    247		pr_err("%s: Cannot register as MDIO bus, err %d\n",
    248				new_bus->name, err);
    249		goto out_free_irq;
    250	}
    251
    252	return 0;
    253
    254out_free_irq:
    255	kfree(new_bus);
    256out_free_priv:
    257	kfree(priv);
    258out:
    259	return err;
    260}
    261
    262
    263static int gpio_mdio_remove(struct platform_device *dev)
    264{
    265	struct mii_bus *bus = dev_get_drvdata(&dev->dev);
    266
    267	mdiobus_unregister(bus);
    268
    269	dev_set_drvdata(&dev->dev, NULL);
    270
    271	kfree(bus->priv);
    272	bus->priv = NULL;
    273	mdiobus_free(bus);
    274
    275	return 0;
    276}
    277
    278static const struct of_device_id gpio_mdio_match[] =
    279{
    280	{
    281		.compatible      = "gpio-mdio",
    282	},
    283	{},
    284};
    285MODULE_DEVICE_TABLE(of, gpio_mdio_match);
    286
    287static struct platform_driver gpio_mdio_driver =
    288{
    289	.probe		= gpio_mdio_probe,
    290	.remove		= gpio_mdio_remove,
    291	.driver = {
    292		.name = "gpio-mdio-bitbang",
    293		.of_match_table = gpio_mdio_match,
    294	},
    295};
    296
    297static int gpio_mdio_init(void)
    298{
    299	struct device_node *np;
    300
    301	np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
    302	if (!np)
    303		np = of_find_compatible_node(NULL, NULL,
    304					     "pasemi,pwrficient-gpio");
    305	if (!np)
    306		return -ENODEV;
    307	gpio_regs = of_iomap(np, 0);
    308	of_node_put(np);
    309
    310	if (!gpio_regs)
    311		return -ENODEV;
    312
    313	return platform_driver_register(&gpio_mdio_driver);
    314}
    315module_init(gpio_mdio_init);
    316
    317static void gpio_mdio_exit(void)
    318{
    319	platform_driver_unregister(&gpio_mdio_driver);
    320	if (gpio_regs)
    321		iounmap(gpio_regs);
    322}
    323module_exit(gpio_mdio_exit);
    324
    325MODULE_LICENSE("GPL");
    326MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
    327MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");