diff options
-rw-r--r-- | drivers/net/ethernet/hisilicon/Kconfig | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/hisilicon/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/hisilicon/hip04_mdio.c | 186 |
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 | ||
27 | config 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 | |||
27 | endif # NET_VENDOR_HISILICON | 36 | endif # 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 | ||
5 | obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o | 5 | obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o |
6 | obj-$(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 | |||
27 | struct hip04_mdio_priv { | ||
28 | void __iomem *base; | ||
29 | }; | ||
30 | |||
31 | #define WAIT_TIMEOUT 10 | ||
32 | static 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 | |||
46 | static 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; | ||
72 | out: | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static 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); | ||
90 | out: | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | static 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 | |||
113 | static 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 | |||
151 | out_mdio: | ||
152 | mdiobus_free(bus); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | static 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 | |||
166 | static const struct of_device_id hip04_mdio_match[] = { | ||
167 | { .compatible = "hisilicon,hip04-mdio" }, | ||
168 | { } | ||
169 | }; | ||
170 | MODULE_DEVICE_TABLE(of, hip04_mdio_match); | ||
171 | |||
172 | static 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 | |||
182 | module_platform_driver(hip04_mdio_driver); | ||
183 | |||
184 | MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver"); | ||
185 | MODULE_LICENSE("GPL v2"); | ||
186 | MODULE_ALIAS("platform:hip04-mdio"); | ||