]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/net/ethernet/hisilicon/hip04_mdio.c
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[karo-tx-linux.git] / drivers / net / ethernet / hisilicon / hip04_mdio.c
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                 .of_match_table = hip04_mdio_match,
178         },
179 };
180
181 module_platform_driver(hip04_mdio_driver);
182
183 MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
184 MODULE_LICENSE("GPL v2");
185 MODULE_ALIAS("platform:hip04-mdio");