rtl8366: make it available on all platforms
[openwrt/openwrt.git] / target / linux / generic-2.6 / files / drivers / net / phy / rtl8366_smi.c
1 /*
2 * Realtek RTL8366 SMI interface driver
3 *
4 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/device.h>
14 #include <linux/delay.h>
15 #include <linux/gpio.h>
16 #include <linux/spinlock.h>
17
18 #include "rtl8366_smi.h"
19
20 #define RTL8366_SMI_ACK_RETRY_COUNT 5
21 #define RTL8366_SMI_CLK_DELAY 10 /* nsec */
22
23 static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi)
24 {
25 ndelay(RTL8366_SMI_CLK_DELAY);
26 }
27
28 static void rtl8366_smi_start(struct rtl8366_smi *smi)
29 {
30 unsigned int sda = smi->gpio_sda;
31 unsigned int sck = smi->gpio_sck;
32
33 /*
34 * Set GPIO pins to output mode, with initial state:
35 * SCK = 0, SDA = 1
36 */
37 gpio_direction_output(sck, 0);
38 gpio_direction_output(sda, 1);
39 rtl8366_smi_clk_delay(smi);
40
41 /* CLK 1: 0 -> 1, 1 -> 0 */
42 gpio_set_value(sck, 1);
43 rtl8366_smi_clk_delay(smi);
44 gpio_set_value(sck, 0);
45 rtl8366_smi_clk_delay(smi);
46
47 /* CLK 2: */
48 gpio_set_value(sck, 1);
49 rtl8366_smi_clk_delay(smi);
50 gpio_set_value(sda, 0);
51 rtl8366_smi_clk_delay(smi);
52 gpio_set_value(sck, 0);
53 rtl8366_smi_clk_delay(smi);
54 gpio_set_value(sda, 1);
55 }
56
57 static void rtl8366_smi_stop(struct rtl8366_smi *smi)
58 {
59 unsigned int sda = smi->gpio_sda;
60 unsigned int sck = smi->gpio_sck;
61
62 rtl8366_smi_clk_delay(smi);
63 gpio_set_value(sda, 0);
64 gpio_set_value(sck, 1);
65 rtl8366_smi_clk_delay(smi);
66 gpio_set_value(sda, 1);
67 rtl8366_smi_clk_delay(smi);
68 gpio_set_value(sck, 1);
69 rtl8366_smi_clk_delay(smi);
70 gpio_set_value(sck, 0);
71 rtl8366_smi_clk_delay(smi);
72 gpio_set_value(sck, 1);
73
74 /* add a click */
75 rtl8366_smi_clk_delay(smi);
76 gpio_set_value(sck, 0);
77 rtl8366_smi_clk_delay(smi);
78 gpio_set_value(sck, 1);
79
80 /* set GPIO pins to input mode */
81 gpio_direction_input(sda);
82 gpio_direction_input(sck);
83 }
84
85 static void rtl8366_smi_write_bits(struct rtl8366_smi *smi, u32 data, u32 len)
86 {
87 unsigned int sda = smi->gpio_sda;
88 unsigned int sck = smi->gpio_sck;
89
90 for (; len > 0; len--) {
91 rtl8366_smi_clk_delay(smi);
92
93 /* prepare data */
94 if ( data & ( 1 << (len - 1)) )
95 gpio_set_value(sda, 1);
96 else
97 gpio_set_value(sda, 0);
98 rtl8366_smi_clk_delay(smi);
99
100 /* clocking */
101 gpio_set_value(sck, 1);
102 rtl8366_smi_clk_delay(smi);
103 gpio_set_value(sck, 0);
104 }
105 }
106
107 static void rtl8366_smi_read_bits(struct rtl8366_smi *smi, u32 len, u32 *data)
108 {
109 unsigned int sda = smi->gpio_sda;
110 unsigned int sck = smi->gpio_sck;
111
112 gpio_direction_input(sda);
113
114 for (*data = 0; len > 0; len--) {
115 u32 u;
116
117 rtl8366_smi_clk_delay(smi);
118
119 /* clocking */
120 gpio_set_value(sck, 1);
121 rtl8366_smi_clk_delay(smi);
122 u = gpio_get_value(sda);
123 gpio_set_value(sck, 0);
124
125 *data |= (u << (len - 1));
126 }
127
128 gpio_direction_output(sda, 0);
129 }
130
131 static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi)
132 {
133 int retry_cnt;
134
135 retry_cnt = 0;
136 do {
137 u32 ack;
138
139 rtl8366_smi_read_bits(smi, 1, &ack);
140 if (ack == 0)
141 break;
142
143 if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT)
144 return -EIO;
145 } while (1);
146
147 return 0;
148 }
149
150 static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data)
151 {
152 rtl8366_smi_write_bits(smi, data, 8);
153 return rtl8366_smi_wait_for_ack(smi);
154 }
155
156 static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data)
157 {
158 u32 t;
159
160 /* read data */
161 rtl8366_smi_read_bits(smi, 8, &t);
162 *data = (t & 0xff);
163
164 /* send an ACK */
165 rtl8366_smi_write_bits(smi, 0x00, 1);
166
167 return 0;
168 }
169
170 static int rtl8366_smi_read_byte1(struct rtl8366_smi *smi, u8 *data)
171 {
172 u32 t;
173
174 /* read data */
175 rtl8366_smi_read_bits(smi, 8, &t);
176 *data = (t & 0xff);
177
178 /* send an ACK */
179 rtl8366_smi_write_bits(smi, 0x01, 1);
180
181 return 0;
182 }
183
184 int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
185 {
186 unsigned long flags;
187 u8 lo = 0;
188 u8 hi = 0;
189 int ret;
190
191 spin_lock_irqsave(&smi->lock, flags);
192
193 rtl8366_smi_start(smi);
194
195 /* send READ command */
196 ret = rtl8366_smi_write_byte(smi, 0x0a << 4 | 0x04 << 1 | 0x01);
197 if (ret)
198 goto out;
199
200 /* set ADDR[7:0] */
201 ret = rtl8366_smi_write_byte(smi, addr & 0xff);
202 if (ret)
203 goto out;
204
205 /* set ADDR[15:8] */
206 ret = rtl8366_smi_write_byte(smi, addr >> 8);
207 if (ret)
208 goto out;
209
210 /* read DATA[7:0] */
211 rtl8366_smi_read_byte0(smi, &lo);
212 /* read DATA[15:8] */
213 rtl8366_smi_read_byte1(smi, &hi);
214
215 *data = ((u32) lo) | (((u32) hi) << 8);
216
217 ret = 0;
218
219 out:
220 rtl8366_smi_stop(smi);
221 spin_unlock_irqrestore(&smi->lock, flags);
222
223 return ret;
224 }
225 EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg);
226
227 int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
228 {
229 unsigned long flags;
230 int ret;
231
232 spin_lock_irqsave(&smi->lock, flags);
233
234 rtl8366_smi_start(smi);
235
236 /* send WRITE command */
237 ret = rtl8366_smi_write_byte(smi, 0x0a << 4 | 0x04 << 1 | 0x00);
238 if (ret)
239 goto out;
240
241 /* set ADDR[7:0] */
242 ret = rtl8366_smi_write_byte(smi, addr & 0xff);
243 if (ret)
244 goto out;
245
246 /* set ADDR[15:8] */
247 ret = rtl8366_smi_write_byte(smi, addr >> 8);
248 if (ret)
249 goto out;
250
251 /* write DATA[7:0] */
252 ret = rtl8366_smi_write_byte(smi, data & 0xff);
253 if (ret)
254 goto out;
255
256 /* write DATA[15:8] */
257 ret = rtl8366_smi_write_byte(smi, data >> 8);
258 if (ret)
259 goto out;
260
261 ret = 0;
262
263 out:
264 rtl8366_smi_stop(smi);
265 spin_unlock_irqrestore(&smi->lock, flags);
266
267 return ret;
268 }
269 EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg);
270
271 int rtl8366_smi_init(struct rtl8366_smi *smi)
272 {
273 int err;
274
275 if (!smi->parent)
276 return -EINVAL;
277
278 err = gpio_request(smi->gpio_sda, dev_name(smi->parent));
279 if (err) {
280 dev_err(smi->parent, "gpio_request failed for %u, err=%d\n",
281 smi->gpio_sda, err);
282 goto err_out;
283 }
284
285 err = gpio_request(smi->gpio_sck, dev_name(smi->parent));
286 if (err) {
287 dev_err(smi->parent, "gpio_request failed for %u, err=%d\n",
288 smi->gpio_sck, err);
289 goto err_free_sda;
290 }
291
292 spin_lock_init(&smi->lock);
293
294 dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n",
295 smi->gpio_sda, smi->gpio_sck);
296
297 return 0;
298
299 err_free_sda:
300 gpio_free(smi->gpio_sda);
301 err_out:
302 return err;
303 }
304 EXPORT_SYMBOL_GPL(rtl8366_smi_init);
305
306 void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
307 {
308 gpio_free(smi->gpio_sck);
309 gpio_free(smi->gpio_sda);
310 }
311 EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
312
313 MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
314 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
315 MODULE_LICENSE("GPL v2");