brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 4511bcc Raw
179 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Allwinner EMAC MDIO interface driver4 *5 * Copyright 2012-2013 Stefan Roese <sr@denx.de>6 * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>7 *8 * Based on the Linux driver provided by Allwinner:9 * Copyright (C) 1997  Sten Wang10 */11 12#include <linux/delay.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/mutex.h>16#include <linux/of_address.h>17#include <linux/of_mdio.h>18#include <linux/phy.h>19#include <linux/platform_device.h>20#include <linux/regulator/consumer.h>21 22#define EMAC_MAC_MCMD_REG	(0x00)23#define EMAC_MAC_MADR_REG	(0x04)24#define EMAC_MAC_MWTD_REG	(0x08)25#define EMAC_MAC_MRDD_REG	(0x0c)26#define EMAC_MAC_MIND_REG	(0x10)27#define EMAC_MAC_SSRR_REG	(0x14)28 29#define MDIO_TIMEOUT		(msecs_to_jiffies(100))30 31struct sun4i_mdio_data {32	void __iomem		*membase;33	struct regulator	*regulator;34};35 36static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum)37{38	struct sun4i_mdio_data *data = bus->priv;39	unsigned long timeout_jiffies;40	int value;41 42	/* issue the phy address and reg */43	writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);44	/* pull up the phy io line */45	writel(0x1, data->membase + EMAC_MAC_MCMD_REG);46 47	/* Wait read complete */48	timeout_jiffies = jiffies + MDIO_TIMEOUT;49	while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {50		if (time_is_before_jiffies(timeout_jiffies))51			return -ETIMEDOUT;52		msleep(1);53	}54 55	/* push down the phy io line */56	writel(0x0, data->membase + EMAC_MAC_MCMD_REG);57	/* and read data */58	value = readl(data->membase + EMAC_MAC_MRDD_REG);59 60	return value;61}62 63static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum,64			    u16 value)65{66	struct sun4i_mdio_data *data = bus->priv;67	unsigned long timeout_jiffies;68 69	/* issue the phy address and reg */70	writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);71	/* pull up the phy io line */72	writel(0x1, data->membase + EMAC_MAC_MCMD_REG);73 74	/* Wait read complete */75	timeout_jiffies = jiffies + MDIO_TIMEOUT;76	while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {77		if (time_is_before_jiffies(timeout_jiffies))78			return -ETIMEDOUT;79		msleep(1);80	}81 82	/* push down the phy io line */83	writel(0x0, data->membase + EMAC_MAC_MCMD_REG);84	/* and write data */85	writel(value, data->membase + EMAC_MAC_MWTD_REG);86 87	return 0;88}89 90static int sun4i_mdio_probe(struct platform_device *pdev)91{92	struct device_node *np = pdev->dev.of_node;93	struct mii_bus *bus;94	struct sun4i_mdio_data *data;95	int ret;96 97	bus = mdiobus_alloc_size(sizeof(*data));98	if (!bus)99		return -ENOMEM;100 101	bus->name = "sun4i_mii_bus";102	bus->read = &sun4i_mdio_read;103	bus->write = &sun4i_mdio_write;104	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));105	bus->parent = &pdev->dev;106 107	data = bus->priv;108	data->membase = devm_platform_ioremap_resource(pdev, 0);109	if (IS_ERR(data->membase)) {110		ret = PTR_ERR(data->membase);111		goto err_out_free_mdiobus;112	}113 114	data->regulator = devm_regulator_get(&pdev->dev, "phy");115	if (IS_ERR(data->regulator)) {116		if (PTR_ERR(data->regulator) == -EPROBE_DEFER) {117			ret = -EPROBE_DEFER;118			goto err_out_free_mdiobus;119		}120 121		dev_info(&pdev->dev, "no regulator found\n");122		data->regulator = NULL;123	} else {124		ret = regulator_enable(data->regulator);125		if (ret)126			goto err_out_free_mdiobus;127	}128 129	ret = of_mdiobus_register(bus, np);130	if (ret < 0)131		goto err_out_disable_regulator;132 133	platform_set_drvdata(pdev, bus);134 135	return 0;136 137err_out_disable_regulator:138	if (data->regulator)139		regulator_disable(data->regulator);140err_out_free_mdiobus:141	mdiobus_free(bus);142	return ret;143}144 145static void sun4i_mdio_remove(struct platform_device *pdev)146{147	struct mii_bus *bus = platform_get_drvdata(pdev);148	struct sun4i_mdio_data *data = bus->priv;149 150	mdiobus_unregister(bus);151	if (data->regulator)152		regulator_disable(data->regulator);153	mdiobus_free(bus);154}155 156static const struct of_device_id sun4i_mdio_dt_ids[] = {157	{ .compatible = "allwinner,sun4i-a10-mdio" },158 159	/* Deprecated */160	{ .compatible = "allwinner,sun4i-mdio" },161	{ }162};163MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids);164 165static struct platform_driver sun4i_mdio_driver = {166	.probe = sun4i_mdio_probe,167	.remove_new = sun4i_mdio_remove,168	.driver = {169		.name = "sun4i-mdio",170		.of_match_table = sun4i_mdio_dt_ids,171	},172};173 174module_platform_driver(sun4i_mdio_driver);175 176MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver");177MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");178MODULE_LICENSE("GPL v2");179