diff options
Diffstat (limited to 'drivers/staging/solo6x10/i2c.c')
-rw-r--r-- | drivers/staging/solo6x10/i2c.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/drivers/staging/solo6x10/i2c.c b/drivers/staging/solo6x10/i2c.c new file mode 100644 index 00000000000..ef95a500b4d --- /dev/null +++ b/drivers/staging/solo6x10/i2c.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com | ||
3 | * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | */ | ||
19 | |||
20 | /* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c | ||
21 | * channel. The bus can only handle one i2c event at a time. The below handles | ||
22 | * this all wrong. We should be using the status registers to see if the bus | ||
23 | * is in use, and have a global lock to check the status register. Also, | ||
24 | * the bulk of the work should be handled out-of-interrupt. The ugly loops | ||
25 | * that occur during interrupt scare me. The ISR should merely signal | ||
26 | * thread context, ACK the interrupt, and move on. -- BenC */ | ||
27 | |||
28 | #include <linux/kernel.h> | ||
29 | #include "solo6x10.h" | ||
30 | |||
31 | u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) | ||
32 | { | ||
33 | struct i2c_msg msgs[2]; | ||
34 | u8 data; | ||
35 | |||
36 | msgs[0].flags = 0; | ||
37 | msgs[0].addr = addr; | ||
38 | msgs[0].len = 1; | ||
39 | msgs[0].buf = &off; | ||
40 | |||
41 | msgs[1].flags = I2C_M_RD; | ||
42 | msgs[1].addr = addr; | ||
43 | msgs[1].len = 1; | ||
44 | msgs[1].buf = &data; | ||
45 | |||
46 | i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); | ||
47 | |||
48 | return data; | ||
49 | } | ||
50 | |||
51 | void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, | ||
52 | u8 off, u8 data) | ||
53 | { | ||
54 | struct i2c_msg msgs; | ||
55 | u8 buf[2]; | ||
56 | |||
57 | buf[0] = off; | ||
58 | buf[1] = data; | ||
59 | msgs.flags = 0; | ||
60 | msgs.addr = addr; | ||
61 | msgs.len = 2; | ||
62 | msgs.buf = buf; | ||
63 | |||
64 | i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); | ||
65 | } | ||
66 | |||
67 | static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) | ||
68 | { | ||
69 | u32 ctrl; | ||
70 | |||
71 | ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); | ||
72 | |||
73 | if (solo_dev->i2c_state == IIC_STATE_START) | ||
74 | ctrl |= SOLO_IIC_START; | ||
75 | |||
76 | if (wr) { | ||
77 | ctrl |= SOLO_IIC_WRITE; | ||
78 | } else { | ||
79 | ctrl |= SOLO_IIC_READ; | ||
80 | if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) | ||
81 | ctrl |= SOLO_IIC_ACK_EN; | ||
82 | } | ||
83 | |||
84 | if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) | ||
85 | ctrl |= SOLO_IIC_STOP; | ||
86 | |||
87 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); | ||
88 | } | ||
89 | |||
90 | static void solo_i2c_start(struct solo_dev *solo_dev) | ||
91 | { | ||
92 | u32 addr = solo_dev->i2c_msg->addr << 1; | ||
93 | |||
94 | if (solo_dev->i2c_msg->flags & I2C_M_RD) | ||
95 | addr |= 1; | ||
96 | |||
97 | solo_dev->i2c_state = IIC_STATE_START; | ||
98 | solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); | ||
99 | solo_i2c_flush(solo_dev, 1); | ||
100 | } | ||
101 | |||
102 | static void solo_i2c_stop(struct solo_dev *solo_dev) | ||
103 | { | ||
104 | solo_irq_off(solo_dev, SOLO_IRQ_IIC); | ||
105 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); | ||
106 | solo_dev->i2c_state = IIC_STATE_STOP; | ||
107 | wake_up(&solo_dev->i2c_wait); | ||
108 | } | ||
109 | |||
110 | static int solo_i2c_handle_read(struct solo_dev *solo_dev) | ||
111 | { | ||
112 | prepare_read: | ||
113 | if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { | ||
114 | solo_i2c_flush(solo_dev, 0); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | solo_dev->i2c_msg_ptr = 0; | ||
119 | solo_dev->i2c_msg++; | ||
120 | solo_dev->i2c_msg_num--; | ||
121 | |||
122 | if (solo_dev->i2c_msg_num == 0) { | ||
123 | solo_i2c_stop(solo_dev); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { | ||
128 | solo_i2c_start(solo_dev); | ||
129 | } else { | ||
130 | if (solo_dev->i2c_msg->flags & I2C_M_RD) | ||
131 | goto prepare_read; | ||
132 | else | ||
133 | solo_i2c_stop(solo_dev); | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int solo_i2c_handle_write(struct solo_dev *solo_dev) | ||
140 | { | ||
141 | retry_write: | ||
142 | if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { | ||
143 | solo_reg_write(solo_dev, SOLO_IIC_TXD, | ||
144 | solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); | ||
145 | solo_dev->i2c_msg_ptr++; | ||
146 | solo_i2c_flush(solo_dev, 1); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | solo_dev->i2c_msg_ptr = 0; | ||
151 | solo_dev->i2c_msg++; | ||
152 | solo_dev->i2c_msg_num--; | ||
153 | |||
154 | if (solo_dev->i2c_msg_num == 0) { | ||
155 | solo_i2c_stop(solo_dev); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { | ||
160 | solo_i2c_start(solo_dev); | ||
161 | } else { | ||
162 | if (solo_dev->i2c_msg->flags & I2C_M_RD) | ||
163 | solo_i2c_stop(solo_dev); | ||
164 | else | ||
165 | goto retry_write; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int solo_i2c_isr(struct solo_dev *solo_dev) | ||
172 | { | ||
173 | u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); | ||
174 | int ret = -EINVAL; | ||
175 | |||
176 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); | ||
177 | |||
178 | if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || | ||
179 | solo_dev->i2c_id < 0) { | ||
180 | solo_i2c_stop(solo_dev); | ||
181 | return -ENXIO; | ||
182 | } | ||
183 | |||
184 | switch (solo_dev->i2c_state) { | ||
185 | case IIC_STATE_START: | ||
186 | if (solo_dev->i2c_msg->flags & I2C_M_RD) { | ||
187 | solo_dev->i2c_state = IIC_STATE_READ; | ||
188 | ret = solo_i2c_handle_read(solo_dev); | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | solo_dev->i2c_state = IIC_STATE_WRITE; | ||
193 | case IIC_STATE_WRITE: | ||
194 | ret = solo_i2c_handle_write(solo_dev); | ||
195 | break; | ||
196 | |||
197 | case IIC_STATE_READ: | ||
198 | solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = | ||
199 | solo_reg_read(solo_dev, SOLO_IIC_RXD); | ||
200 | solo_dev->i2c_msg_ptr++; | ||
201 | |||
202 | ret = solo_i2c_handle_read(solo_dev); | ||
203 | break; | ||
204 | |||
205 | default: | ||
206 | solo_i2c_stop(solo_dev); | ||
207 | } | ||
208 | |||
209 | return ret; | ||
210 | } | ||
211 | |||
212 | static int solo_i2c_master_xfer(struct i2c_adapter *adap, | ||
213 | struct i2c_msg msgs[], int num) | ||
214 | { | ||
215 | struct solo_dev *solo_dev = adap->algo_data; | ||
216 | unsigned long timeout; | ||
217 | int ret; | ||
218 | int i; | ||
219 | DEFINE_WAIT(wait); | ||
220 | |||
221 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { | ||
222 | if (&solo_dev->i2c_adap[i] == adap) | ||
223 | break; | ||
224 | } | ||
225 | |||
226 | if (i == SOLO_I2C_ADAPTERS) | ||
227 | return num; /* XXX Right return value for failure? */ | ||
228 | |||
229 | mutex_lock(&solo_dev->i2c_mutex); | ||
230 | solo_dev->i2c_id = i; | ||
231 | solo_dev->i2c_msg = msgs; | ||
232 | solo_dev->i2c_msg_num = num; | ||
233 | solo_dev->i2c_msg_ptr = 0; | ||
234 | |||
235 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); | ||
236 | solo_irq_on(solo_dev, SOLO_IRQ_IIC); | ||
237 | solo_i2c_start(solo_dev); | ||
238 | |||
239 | timeout = HZ / 2; | ||
240 | |||
241 | for (;;) { | ||
242 | prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); | ||
243 | |||
244 | if (solo_dev->i2c_state == IIC_STATE_STOP) | ||
245 | break; | ||
246 | |||
247 | timeout = schedule_timeout(timeout); | ||
248 | if (!timeout) | ||
249 | break; | ||
250 | |||
251 | if (signal_pending(current)) | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | finish_wait(&solo_dev->i2c_wait, &wait); | ||
256 | ret = num - solo_dev->i2c_msg_num; | ||
257 | solo_dev->i2c_state = IIC_STATE_IDLE; | ||
258 | solo_dev->i2c_id = -1; | ||
259 | |||
260 | mutex_unlock(&solo_dev->i2c_mutex); | ||
261 | |||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | static u32 solo_i2c_functionality(struct i2c_adapter *adap) | ||
266 | { | ||
267 | return I2C_FUNC_I2C; | ||
268 | } | ||
269 | |||
270 | static struct i2c_algorithm solo_i2c_algo = { | ||
271 | .master_xfer = solo_i2c_master_xfer, | ||
272 | .functionality = solo_i2c_functionality, | ||
273 | }; | ||
274 | |||
275 | int solo_i2c_init(struct solo_dev *solo_dev) | ||
276 | { | ||
277 | int i; | ||
278 | int ret; | ||
279 | |||
280 | solo_reg_write(solo_dev, SOLO_IIC_CFG, | ||
281 | SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); | ||
282 | |||
283 | solo_dev->i2c_id = -1; | ||
284 | solo_dev->i2c_state = IIC_STATE_IDLE; | ||
285 | init_waitqueue_head(&solo_dev->i2c_wait); | ||
286 | mutex_init(&solo_dev->i2c_mutex); | ||
287 | |||
288 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { | ||
289 | struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; | ||
290 | |||
291 | snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i); | ||
292 | adap->algo = &solo_i2c_algo; | ||
293 | adap->algo_data = solo_dev; | ||
294 | adap->retries = 1; | ||
295 | adap->dev.parent = &solo_dev->pdev->dev; | ||
296 | |||
297 | ret = i2c_add_adapter(adap); | ||
298 | if (ret) { | ||
299 | adap->algo_data = NULL; | ||
300 | break; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | if (ret) { | ||
305 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { | ||
306 | if (!solo_dev->i2c_adap[i].algo_data) | ||
307 | break; | ||
308 | i2c_del_adapter(&solo_dev->i2c_adap[i]); | ||
309 | solo_dev->i2c_adap[i].algo_data = NULL; | ||
310 | } | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", | ||
315 | SOLO_I2C_ADAPTERS); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | void solo_i2c_exit(struct solo_dev *solo_dev) | ||
321 | { | ||
322 | int i; | ||
323 | |||
324 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { | ||
325 | if (!solo_dev->i2c_adap[i].algo_data) | ||
326 | continue; | ||
327 | i2c_del_adapter(&solo_dev->i2c_adap[i]); | ||
328 | solo_dev->i2c_adap[i].algo_data = NULL; | ||
329 | } | ||
330 | } | ||