aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/hisilicon/Kconfig9
-rw-r--r--drivers/net/ethernet/hisilicon/Makefile1
-rw-r--r--drivers/net/ethernet/hisilicon/hip04_mdio.c186
3 files changed, 196 insertions, 0 deletions
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
index e9421731b05e..a54d89791311 100644
--- a/drivers/net/ethernet/hisilicon/Kconfig
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -24,4 +24,13 @@ config HIX5HD2_GMAC
24 help 24 help
25 This selects the hix5hd2 mac family network device. 25 This selects the hix5hd2 mac family network device.
26 26
27config HIP04_ETH
28 tristate "HISILICON P04 Ethernet support"
29 select PHYLIB
30 select MARVELL_PHY
31 select MFD_SYSCON
32 ---help---
33 If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
34 want to use the internal ethernet then you should answer Y to this.
35
27endif # NET_VENDOR_HISILICON 36endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
index 9175e84622d4..40115a7e2ed5 100644
--- a/drivers/net/ethernet/hisilicon/Makefile
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -3,3 +3,4 @@
3# 3#
4 4
5obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o 5obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o
6obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 000000000000..b3bac25db99c
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
1/* Copyright (c) 2014 Linaro Ltd.
2 * Copyright (c) 2014 Hisilicon Limited.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <linux/module.h>
11#include <linux/platform_device.h>
12#include <linux/io.h>
13#include <linux/of_mdio.h>
14#include <linux/delay.h>
15
16#define MDIO_CMD_REG 0x0
17#define MDIO_ADDR_REG 0x4
18#define MDIO_WDATA_REG 0x8
19#define MDIO_RDATA_REG 0xc
20#define MDIO_STA_REG 0x10
21
22#define MDIO_START BIT(14)
23#define MDIO_R_VALID BIT(1)
24#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
25#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
26
27struct hip04_mdio_priv {
28 void __iomem *base;
29};
30
31#define WAIT_TIMEOUT 10
32static int hip04_mdio_wait_ready(struct mii_bus *bus)
33{
34 struct hip04_mdio_priv *priv = bus->priv;
35 int i;
36
37 for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
38 if (i == WAIT_TIMEOUT)
39 return -ETIMEDOUT;
40 msleep(20);
41 }
42
43 return 0;
44}
45
46static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
47{
48 struct hip04_mdio_priv *priv = bus->priv;
49 u32 val;
50 int ret;
51
52 ret = hip04_mdio_wait_ready(bus);
53 if (ret < 0)
54 goto out;
55
56 val = regnum | (mii_id << 5) | MDIO_READ;
57 writel_relaxed(val, priv->base + MDIO_CMD_REG);
58
59 ret = hip04_mdio_wait_ready(bus);
60 if (ret < 0)
61 goto out;
62
63 val = readl_relaxed(priv->base + MDIO_STA_REG);
64 if (val & MDIO_R_VALID) {
65 dev_err(bus->parent, "SMI bus read not valid\n");
66 ret = -ENODEV;
67 goto out;
68 }
69
70 val = readl_relaxed(priv->base + MDIO_RDATA_REG);
71 ret = val & 0xFFFF;
72out:
73 return ret;
74}
75
76static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
77 int regnum, u16 value)
78{
79 struct hip04_mdio_priv *priv = bus->priv;
80 u32 val;
81 int ret;
82
83 ret = hip04_mdio_wait_ready(bus);
84 if (ret < 0)
85 goto out;
86
87 writel_relaxed(value, priv->base + MDIO_WDATA_REG);
88 val = regnum | (mii_id << 5) | MDIO_WRITE;
89 writel_relaxed(val, priv->base + MDIO_CMD_REG);
90out:
91 return ret;
92}
93
94static int hip04_mdio_reset(struct mii_bus *bus)
95{
96 int temp, i;
97
98 for (i = 0; i < PHY_MAX_ADDR; i++) {
99 hip04_mdio_write(bus, i, 22, 0);
100 temp = hip04_mdio_read(bus, i, MII_BMCR);
101 if (temp < 0)
102 continue;
103
104 temp |= BMCR_RESET;
105 if (hip04_mdio_write(bus, i, MII_BMCR, temp) < 0)
106 continue;
107 }
108
109 mdelay(500);
110 return 0;
111}
112
113static int hip04_mdio_probe(struct platform_device *pdev)
114{
115 struct resource *r;
116 struct mii_bus *bus;
117 struct hip04_mdio_priv *priv;
118 int ret;
119
120 bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
121 if (!bus) {
122 dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
123 return -ENOMEM;
124 }
125
126 bus->name = "hip04_mdio_bus";
127 bus->read = hip04_mdio_read;
128 bus->write = hip04_mdio_write;
129 bus->reset = hip04_mdio_reset;
130 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
131 bus->parent = &pdev->dev;
132 priv = bus->priv;
133
134 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
135 priv->base = devm_ioremap_resource(&pdev->dev, r);
136 if (IS_ERR(priv->base)) {
137 ret = PTR_ERR(priv->base);
138 goto out_mdio;
139 }
140
141 ret = of_mdiobus_register(bus, pdev->dev.of_node);
142 if (ret < 0) {
143 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
144 goto out_mdio;
145 }
146
147 platform_set_drvdata(pdev, bus);
148
149 return 0;
150
151out_mdio:
152 mdiobus_free(bus);
153 return ret;
154}
155
156static int hip04_mdio_remove(struct platform_device *pdev)
157{
158 struct mii_bus *bus = platform_get_drvdata(pdev);
159
160 mdiobus_unregister(bus);
161 mdiobus_free(bus);
162
163 return 0;
164}
165
166static const struct of_device_id hip04_mdio_match[] = {
167 { .compatible = "hisilicon,hip04-mdio" },
168 { }
169};
170MODULE_DEVICE_TABLE(of, hip04_mdio_match);
171
172static struct platform_driver hip04_mdio_driver = {
173 .probe = hip04_mdio_probe,
174 .remove = hip04_mdio_remove,
175 .driver = {
176 .name = "hip04-mdio",
177 .owner = THIS_MODULE,
178 .of_match_table = hip04_mdio_match,
179 },
180};
181
182module_platform_driver(hip04_mdio_driver);
183
184MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
185MODULE_LICENSE("GPL v2");
186MODULE_ALIAS("platform:hip04-mdio");