Commit | Line | Data |
---|---|---|
e4f2379d AB |
1 | /* |
2 | * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com) | |
3 | * | |
4 | * MDIO implementation for ARC EMAC | |
5 | */ | |
6 | ||
7 | #include <linux/delay.h> | |
8 | #include <linux/of_mdio.h> | |
9 | #include <linux/platform_device.h> | |
1bddd96c | 10 | #include <linux/gpio/consumer.h> |
e4f2379d AB |
11 | |
12 | #include "emac.h" | |
13 | ||
14 | /* Number of seconds we wait for "MDIO complete" flag to appear */ | |
15 | #define ARC_MDIO_COMPLETE_POLL_COUNT 1 | |
16 | ||
17 | /** | |
18 | * arc_mdio_complete_wait - Waits until MDIO transaction is completed. | |
19 | * @priv: Pointer to ARC EMAC private data structure. | |
20 | * | |
21 | * returns: 0 on success, -ETIMEDOUT on a timeout. | |
22 | */ | |
23 | static int arc_mdio_complete_wait(struct arc_emac_priv *priv) | |
24 | { | |
25 | unsigned int i; | |
26 | ||
27 | for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) { | |
28 | unsigned int status = arc_reg_get(priv, R_STATUS); | |
29 | ||
30 | status &= MDIO_MASK; | |
31 | ||
32 | if (status) { | |
33 | /* Reset "MDIO complete" flag */ | |
34 | arc_reg_set(priv, R_STATUS, status); | |
35 | return 0; | |
36 | } | |
37 | ||
38 | msleep(25); | |
39 | } | |
40 | ||
41 | return -ETIMEDOUT; | |
42 | } | |
43 | ||
44 | /** | |
45 | * arc_mdio_read - MDIO interface read function. | |
46 | * @bus: Pointer to MII bus structure. | |
47 | * @phy_addr: Address of the PHY device. | |
48 | * @reg_num: PHY register to read. | |
49 | * | |
50 | * returns: The register contents on success, -ETIMEDOUT on a timeout. | |
51 | * | |
52 | * Reads the contents of the requested register from the requested PHY | |
53 | * address. | |
54 | */ | |
55 | static int arc_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) | |
56 | { | |
57 | struct arc_emac_priv *priv = bus->priv; | |
58 | unsigned int value; | |
59 | int error; | |
60 | ||
61 | arc_reg_set(priv, R_MDIO, | |
62 | 0x60020000 | (phy_addr << 23) | (reg_num << 18)); | |
63 | ||
64 | error = arc_mdio_complete_wait(priv); | |
65 | if (error < 0) | |
66 | return error; | |
67 | ||
68 | value = arc_reg_get(priv, R_MDIO) & 0xffff; | |
69 | ||
70 | dev_dbg(priv->dev, "arc_mdio_read(phy_addr=%i, reg_num=%x) = %x\n", | |
71 | phy_addr, reg_num, value); | |
72 | ||
73 | return value; | |
74 | } | |
75 | ||
76 | /** | |
77 | * arc_mdio_write - MDIO interface write function. | |
78 | * @bus: Pointer to MII bus structure. | |
79 | * @phy_addr: Address of the PHY device. | |
80 | * @reg_num: PHY register to write to. | |
81 | * @value: Value to be written into the register. | |
82 | * | |
83 | * returns: 0 on success, -ETIMEDOUT on a timeout. | |
84 | * | |
85 | * Writes the value to the requested register. | |
86 | */ | |
87 | static int arc_mdio_write(struct mii_bus *bus, int phy_addr, | |
88 | int reg_num, u16 value) | |
89 | { | |
90 | struct arc_emac_priv *priv = bus->priv; | |
91 | ||
92 | dev_dbg(priv->dev, | |
93 | "arc_mdio_write(phy_addr=%i, reg_num=%x, value=%x)\n", | |
94 | phy_addr, reg_num, value); | |
95 | ||
96 | arc_reg_set(priv, R_MDIO, | |
663713eb | 97 | 0x50020000 | (phy_addr << 23) | (reg_num << 18) | value); |
e4f2379d AB |
98 | |
99 | return arc_mdio_complete_wait(priv); | |
100 | } | |
101 | ||
1bddd96c CW |
102 | /** |
103 | * arc_mdio_reset | |
104 | * @bus: points to the mii_bus structure | |
105 | * Description: reset the MII bus | |
106 | */ | |
107 | int arc_mdio_reset(struct mii_bus *bus) | |
108 | { | |
109 | struct arc_emac_priv *priv = bus->priv; | |
110 | struct arc_emac_mdio_bus_data *data = &priv->bus_data; | |
111 | ||
112 | if (data->reset_gpio) { | |
113 | gpiod_set_value_cansleep(data->reset_gpio, 1); | |
114 | msleep(data->msec); | |
115 | gpiod_set_value_cansleep(data->reset_gpio, 0); | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
e4f2379d AB |
121 | /** |
122 | * arc_mdio_probe - MDIO probe function. | |
e4f2379d AB |
123 | * @priv: Pointer to ARC EMAC private data structure. |
124 | * | |
125 | * returns: 0 on success, -ENOMEM when mdiobus_alloc | |
126 | * (to allocate memory for MII bus structure) fails. | |
127 | * | |
128 | * Sets up and registers the MDIO interface. | |
129 | */ | |
93e91b3d | 130 | int arc_mdio_probe(struct arc_emac_priv *priv) |
e4f2379d | 131 | { |
1bddd96c CW |
132 | struct arc_emac_mdio_bus_data *data = &priv->bus_data; |
133 | struct device_node *np = priv->dev->of_node; | |
e4f2379d AB |
134 | struct mii_bus *bus; |
135 | int error; | |
136 | ||
137 | bus = mdiobus_alloc(); | |
138 | if (!bus) | |
139 | return -ENOMEM; | |
140 | ||
141 | priv->bus = bus; | |
142 | bus->priv = priv; | |
143 | bus->parent = priv->dev; | |
3424d9be | 144 | bus->name = "Synopsys MII Bus"; |
e4f2379d AB |
145 | bus->read = &arc_mdio_read; |
146 | bus->write = &arc_mdio_write; | |
1bddd96c CW |
147 | bus->reset = &arc_mdio_reset; |
148 | ||
149 | /* optional reset-related properties */ | |
150 | data->reset_gpio = devm_gpiod_get_optional(priv->dev, "phy-reset", | |
151 | GPIOD_OUT_LOW); | |
152 | if (IS_ERR(data->reset_gpio)) { | |
153 | error = PTR_ERR(data->reset_gpio); | |
154 | dev_err(priv->dev, "Failed to request gpio: %d\n", error); | |
155 | return error; | |
156 | } | |
157 | ||
158 | of_property_read_u32(np, "phy-reset-duration", &data->msec); | |
159 | /* A sane reset duration should not be longer than 1s */ | |
160 | if (data->msec > 1000) | |
161 | data->msec = 1; | |
e4f2379d | 162 | |
93e91b3d | 163 | snprintf(bus->id, MII_BUS_ID_SIZE, "%s", bus->name); |
e4f2379d | 164 | |
93e91b3d | 165 | error = of_mdiobus_register(bus, priv->dev->of_node); |
e4f2379d AB |
166 | if (error) { |
167 | dev_err(priv->dev, "cannot register MDIO bus %s\n", bus->name); | |
168 | mdiobus_free(bus); | |
169 | return error; | |
170 | } | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | /** | |
176 | * arc_mdio_remove - MDIO remove function. | |
177 | * @priv: Pointer to ARC EMAC private data structure. | |
178 | * | |
179 | * Unregisters the MDIO and frees any associate memory for MII bus. | |
180 | */ | |
181 | int arc_mdio_remove(struct arc_emac_priv *priv) | |
182 | { | |
183 | mdiobus_unregister(priv->bus); | |
184 | mdiobus_free(priv->bus); | |
185 | priv->bus = NULL; | |
186 | ||
187 | return 0; | |
188 | } |