Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Hisilicon Fast Ethernet MDIO Bus Driver
  3 *
  4 * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License as published by
  8 * the Free Software Foundation; either version 2 of the License, or
  9 * (at your option) any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful,
 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 * GNU General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU General Public License
 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20#include <linux/clk.h>
 21#include <linux/iopoll.h>
 22#include <linux/kernel.h>
 23#include <linux/module.h>
 24#include <linux/of_address.h>
 25#include <linux/of_mdio.h>
 26#include <linux/platform_device.h>
 27
 28#define MDIO_RWCTRL		0x00
 29#define MDIO_RO_DATA		0x04
 30#define MDIO_WRITE		BIT(13)
 31#define MDIO_RW_FINISH		BIT(15)
 32#define BIT_PHY_ADDR_OFFSET	8
 33#define BIT_WR_DATA_OFFSET	16
 34
 35struct hisi_femac_mdio_data {
 36	struct clk *clk;
 37	void __iomem *membase;
 38};
 39
 40static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data)
 41{
 42	u32 val;
 43
 44	return readl_poll_timeout(data->membase + MDIO_RWCTRL,
 45				  val, val & MDIO_RW_FINISH, 20, 10000);
 46}
 47
 48static int hisi_femac_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 49{
 50	struct hisi_femac_mdio_data *data = bus->priv;
 51	int ret;
 52
 53	ret = hisi_femac_mdio_wait_ready(data);
 54	if (ret)
 55		return ret;
 56
 57	writel((mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
 58	       data->membase + MDIO_RWCTRL);
 59
 60	ret = hisi_femac_mdio_wait_ready(data);
 61	if (ret)
 62		return ret;
 63
 64	return readl(data->membase + MDIO_RO_DATA) & 0xFFFF;
 65}
 66
 67static int hisi_femac_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
 68				 u16 value)
 69{
 70	struct hisi_femac_mdio_data *data = bus->priv;
 71	int ret;
 72
 73	ret = hisi_femac_mdio_wait_ready(data);
 74	if (ret)
 75		return ret;
 76
 77	writel(MDIO_WRITE | (value << BIT_WR_DATA_OFFSET) |
 78	       (mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
 79	       data->membase + MDIO_RWCTRL);
 80
 81	return hisi_femac_mdio_wait_ready(data);
 82}
 83
 84static int hisi_femac_mdio_probe(struct platform_device *pdev)
 85{
 86	struct device_node *np = pdev->dev.of_node;
 87	struct mii_bus *bus;
 88	struct hisi_femac_mdio_data *data;
 89	struct resource *res;
 90	int ret;
 91
 92	bus = mdiobus_alloc_size(sizeof(*data));
 93	if (!bus)
 94		return -ENOMEM;
 95
 96	bus->name = "hisi_femac_mii_bus";
 97	bus->read = &hisi_femac_mdio_read;
 98	bus->write = &hisi_femac_mdio_write;
 99	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
100	bus->parent = &pdev->dev;
101
102	data = bus->priv;
103	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
104	data->membase = devm_ioremap_resource(&pdev->dev, res);
105	if (IS_ERR(data->membase)) {
106		ret = PTR_ERR(data->membase);
107		goto err_out_free_mdiobus;
108	}
109
110	data->clk = devm_clk_get(&pdev->dev, NULL);
111	if (IS_ERR(data->clk)) {
112		ret = PTR_ERR(data->clk);
113		goto err_out_free_mdiobus;
114	}
115
116	ret = clk_prepare_enable(data->clk);
117	if (ret)
118		goto err_out_free_mdiobus;
119
120	ret = of_mdiobus_register(bus, np);
121	if (ret)
122		goto err_out_disable_clk;
123
124	platform_set_drvdata(pdev, bus);
125
126	return 0;
127
128err_out_disable_clk:
129	clk_disable_unprepare(data->clk);
130err_out_free_mdiobus:
131	mdiobus_free(bus);
132	return ret;
133}
134
135static int hisi_femac_mdio_remove(struct platform_device *pdev)
136{
137	struct mii_bus *bus = platform_get_drvdata(pdev);
138	struct hisi_femac_mdio_data *data = bus->priv;
139
140	mdiobus_unregister(bus);
141	clk_disable_unprepare(data->clk);
142	mdiobus_free(bus);
143
144	return 0;
145}
146
147static const struct of_device_id hisi_femac_mdio_dt_ids[] = {
148	{ .compatible = "hisilicon,hisi-femac-mdio" },
149	{ }
150};
151MODULE_DEVICE_TABLE(of, hisi_femac_mdio_dt_ids);
152
153static struct platform_driver hisi_femac_mdio_driver = {
154	.probe = hisi_femac_mdio_probe,
155	.remove = hisi_femac_mdio_remove,
156	.driver = {
157		.name = "hisi-femac-mdio",
158		.of_match_table = hisi_femac_mdio_dt_ids,
159	},
160};
161
162module_platform_driver(hisi_femac_mdio_driver);
163
164MODULE_DESCRIPTION("Hisilicon Fast Ethernet MAC MDIO interface driver");
165MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>");
166MODULE_LICENSE("GPL v2");