diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/i2c/algos/i2c-algo-pca.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/i2c/algos/i2c-algo-pca.c')
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 000000000000..c3d912cbbbc3 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c | |||
@@ -0,0 +1,399 @@ | |||
1 | /* | ||
2 | * i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters | ||
3 | * Copyright (C) 2004 Arcom Control Systems | ||
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., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/moduleparam.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/i2c-algo-pca.h> | ||
29 | #include "i2c-algo-pca.h" | ||
30 | |||
31 | #define DRIVER "i2c-algo-pca" | ||
32 | |||
33 | #define DEB1(fmt, args...) do { if (i2c_debug>=1) printk(fmt, ## args); } while(0) | ||
34 | #define DEB2(fmt, args...) do { if (i2c_debug>=2) printk(fmt, ## args); } while(0) | ||
35 | #define DEB3(fmt, args...) do { if (i2c_debug>=3) printk(fmt, ## args); } while(0) | ||
36 | |||
37 | static int i2c_debug=0; | ||
38 | |||
39 | #define pca_outw(adap, reg, val) adap->write_byte(adap, reg, val) | ||
40 | #define pca_inw(adap, reg) adap->read_byte(adap, reg) | ||
41 | |||
42 | #define pca_status(adap) pca_inw(adap, I2C_PCA_STA) | ||
43 | #define pca_clock(adap) adap->get_clock(adap) | ||
44 | #define pca_own(adap) adap->get_own(adap) | ||
45 | #define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) | ||
46 | #define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) | ||
47 | #define pca_wait(adap) adap->wait_for_interrupt(adap) | ||
48 | |||
49 | /* | ||
50 | * Generate a start condition on the i2c bus. | ||
51 | * | ||
52 | * returns after the start condition has occured | ||
53 | */ | ||
54 | static void pca_start(struct i2c_algo_pca_data *adap) | ||
55 | { | ||
56 | int sta = pca_get_con(adap); | ||
57 | DEB2("=== START\n"); | ||
58 | sta |= I2C_PCA_CON_STA; | ||
59 | sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); | ||
60 | pca_set_con(adap, sta); | ||
61 | pca_wait(adap); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Generate a repeated start condition on the i2c bus | ||
66 | * | ||
67 | * return after the repeated start condition has occured | ||
68 | */ | ||
69 | static void pca_repeated_start(struct i2c_algo_pca_data *adap) | ||
70 | { | ||
71 | int sta = pca_get_con(adap); | ||
72 | DEB2("=== REPEATED START\n"); | ||
73 | sta |= I2C_PCA_CON_STA; | ||
74 | sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); | ||
75 | pca_set_con(adap, sta); | ||
76 | pca_wait(adap); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Generate a stop condition on the i2c bus | ||
81 | * | ||
82 | * returns after the stop condition has been generated | ||
83 | * | ||
84 | * STOPs do not generate an interrupt or set the SI flag, since the | ||
85 | * part returns the the idle state (0xf8). Hence we don't need to | ||
86 | * pca_wait here. | ||
87 | */ | ||
88 | static void pca_stop(struct i2c_algo_pca_data *adap) | ||
89 | { | ||
90 | int sta = pca_get_con(adap); | ||
91 | DEB2("=== STOP\n"); | ||
92 | sta |= I2C_PCA_CON_STO; | ||
93 | sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); | ||
94 | pca_set_con(adap, sta); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Send the slave address and R/W bit | ||
99 | * | ||
100 | * returns after the address has been sent | ||
101 | */ | ||
102 | static void pca_address(struct i2c_algo_pca_data *adap, | ||
103 | struct i2c_msg *msg) | ||
104 | { | ||
105 | int sta = pca_get_con(adap); | ||
106 | int addr; | ||
107 | |||
108 | addr = ( (0x7f & msg->addr) << 1 ); | ||
109 | if (msg->flags & I2C_M_RD ) | ||
110 | addr |= 1; | ||
111 | DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", | ||
112 | msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); | ||
113 | |||
114 | pca_outw(adap, I2C_PCA_DAT, addr); | ||
115 | |||
116 | sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); | ||
117 | pca_set_con(adap, sta); | ||
118 | |||
119 | pca_wait(adap); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Transmit a byte. | ||
124 | * | ||
125 | * Returns after the byte has been transmitted | ||
126 | */ | ||
127 | static void pca_tx_byte(struct i2c_algo_pca_data *adap, | ||
128 | __u8 b) | ||
129 | { | ||
130 | int sta = pca_get_con(adap); | ||
131 | DEB2("=== WRITE %#04x\n", b); | ||
132 | pca_outw(adap, I2C_PCA_DAT, b); | ||
133 | |||
134 | sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); | ||
135 | pca_set_con(adap, sta); | ||
136 | |||
137 | pca_wait(adap); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Receive a byte | ||
142 | * | ||
143 | * returns immediately. | ||
144 | */ | ||
145 | static void pca_rx_byte(struct i2c_algo_pca_data *adap, | ||
146 | __u8 *b, int ack) | ||
147 | { | ||
148 | *b = pca_inw(adap, I2C_PCA_DAT); | ||
149 | DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Setup ACK or NACK for next received byte and wait for it to arrive. | ||
154 | * | ||
155 | * Returns after next byte has arrived. | ||
156 | */ | ||
157 | static void pca_rx_ack(struct i2c_algo_pca_data *adap, | ||
158 | int ack) | ||
159 | { | ||
160 | int sta = pca_get_con(adap); | ||
161 | |||
162 | sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); | ||
163 | |||
164 | if ( ack ) | ||
165 | sta |= I2C_PCA_CON_AA; | ||
166 | |||
167 | pca_set_con(adap, sta); | ||
168 | pca_wait(adap); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * Reset the i2c bus / SIO | ||
173 | */ | ||
174 | static void pca_reset(struct i2c_algo_pca_data *adap) | ||
175 | { | ||
176 | /* apparently only an external reset will do it. not a lot can be done */ | ||
177 | printk(KERN_ERR DRIVER ": Haven't figured out how to do a reset yet\n"); | ||
178 | } | ||
179 | |||
180 | static int pca_xfer(struct i2c_adapter *i2c_adap, | ||
181 | struct i2c_msg *msgs, | ||
182 | int num) | ||
183 | { | ||
184 | struct i2c_algo_pca_data *adap = i2c_adap->algo_data; | ||
185 | struct i2c_msg *msg = NULL; | ||
186 | int curmsg; | ||
187 | int numbytes = 0; | ||
188 | int state; | ||
189 | int ret; | ||
190 | |||
191 | state = pca_status(adap); | ||
192 | if ( state != 0xF8 ) { | ||
193 | dev_dbg(&i2c_adap->dev, "bus is not idle. status is %#04x\n", state ); | ||
194 | /* FIXME: what to do. Force stop ? */ | ||
195 | return -EREMOTEIO; | ||
196 | } | ||
197 | |||
198 | DEB1("{{{ XFER %d messages\n", num); | ||
199 | |||
200 | if (i2c_debug>=2) { | ||
201 | for (curmsg = 0; curmsg < num; curmsg++) { | ||
202 | int addr, i; | ||
203 | msg = &msgs[curmsg]; | ||
204 | |||
205 | addr = (0x7f & msg->addr) ; | ||
206 | |||
207 | if (msg->flags & I2C_M_RD ) | ||
208 | printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", | ||
209 | curmsg, msg->len, addr, (addr<<1) | 1); | ||
210 | else { | ||
211 | printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", | ||
212 | curmsg, msg->len, addr, addr<<1, | ||
213 | msg->len == 0 ? "" : ", "); | ||
214 | for(i=0; i < msg->len; i++) | ||
215 | printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); | ||
216 | printk("]\n"); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | curmsg = 0; | ||
222 | ret = -EREMOTEIO; | ||
223 | while (curmsg < num) { | ||
224 | state = pca_status(adap); | ||
225 | |||
226 | DEB3("STATE is 0x%02x\n", state); | ||
227 | msg = &msgs[curmsg]; | ||
228 | |||
229 | switch (state) { | ||
230 | case 0xf8: /* On reset or stop the bus is idle */ | ||
231 | pca_start(adap); | ||
232 | break; | ||
233 | |||
234 | case 0x08: /* A START condition has been transmitted */ | ||
235 | case 0x10: /* A repeated start condition has been transmitted */ | ||
236 | pca_address(adap, msg); | ||
237 | break; | ||
238 | |||
239 | case 0x18: /* SLA+W has been transmitted; ACK has been received */ | ||
240 | case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ | ||
241 | if (numbytes < msg->len) { | ||
242 | pca_tx_byte(adap, msg->buf[numbytes]); | ||
243 | numbytes++; | ||
244 | break; | ||
245 | } | ||
246 | curmsg++; numbytes = 0; | ||
247 | if (curmsg == num) | ||
248 | pca_stop(adap); | ||
249 | else | ||
250 | pca_repeated_start(adap); | ||
251 | break; | ||
252 | |||
253 | case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ | ||
254 | DEB2("NOT ACK received after SLA+W\n"); | ||
255 | pca_stop(adap); | ||
256 | goto out; | ||
257 | |||
258 | case 0x40: /* SLA+R has been transmitted; ACK has been received */ | ||
259 | pca_rx_ack(adap, msg->len > 1); | ||
260 | break; | ||
261 | |||
262 | case 0x50: /* Data bytes has been received; ACK has been returned */ | ||
263 | if (numbytes < msg->len) { | ||
264 | pca_rx_byte(adap, &msg->buf[numbytes], 1); | ||
265 | numbytes++; | ||
266 | pca_rx_ack(adap, numbytes < msg->len - 1); | ||
267 | break; | ||
268 | } | ||
269 | curmsg++; numbytes = 0; | ||
270 | if (curmsg == num) | ||
271 | pca_stop(adap); | ||
272 | else | ||
273 | pca_repeated_start(adap); | ||
274 | break; | ||
275 | |||
276 | case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ | ||
277 | DEB2("NOT ACK received after SLA+R\n"); | ||
278 | pca_stop(adap); | ||
279 | goto out; | ||
280 | |||
281 | case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ | ||
282 | DEB2("NOT ACK received after data byte\n"); | ||
283 | goto out; | ||
284 | |||
285 | case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ | ||
286 | DEB2("Arbitration lost\n"); | ||
287 | goto out; | ||
288 | |||
289 | case 0x58: /* Data byte has been received; NOT ACK has been returned */ | ||
290 | if ( numbytes == msg->len - 1 ) { | ||
291 | pca_rx_byte(adap, &msg->buf[numbytes], 0); | ||
292 | curmsg++; numbytes = 0; | ||
293 | if (curmsg == num) | ||
294 | pca_stop(adap); | ||
295 | else | ||
296 | pca_repeated_start(adap); | ||
297 | } else { | ||
298 | DEB2("NOT ACK sent after data byte received. " | ||
299 | "Not final byte. numbytes %d. len %d\n", | ||
300 | numbytes, msg->len); | ||
301 | pca_stop(adap); | ||
302 | goto out; | ||
303 | } | ||
304 | break; | ||
305 | case 0x70: /* Bus error - SDA stuck low */ | ||
306 | DEB2("BUS ERROR - SDA Stuck low\n"); | ||
307 | pca_reset(adap); | ||
308 | goto out; | ||
309 | case 0x90: /* Bus error - SCL stuck low */ | ||
310 | DEB2("BUS ERROR - SCL Stuck low\n"); | ||
311 | pca_reset(adap); | ||
312 | goto out; | ||
313 | case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ | ||
314 | DEB2("BUS ERROR - Illegal START or STOP\n"); | ||
315 | pca_reset(adap); | ||
316 | goto out; | ||
317 | default: | ||
318 | printk(KERN_ERR DRIVER ": unhandled SIO state 0x%02x\n", state); | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | } | ||
323 | |||
324 | ret = curmsg; | ||
325 | out: | ||
326 | DEB1(KERN_CRIT "}}} transfered %d/%d messages. " | ||
327 | "status is %#04x. control is %#04x\n", | ||
328 | curmsg, num, pca_status(adap), | ||
329 | pca_get_con(adap)); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | static u32 pca_func(struct i2c_adapter *adap) | ||
334 | { | ||
335 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
336 | } | ||
337 | |||
338 | static int pca_init(struct i2c_algo_pca_data *adap) | ||
339 | { | ||
340 | static int freqs[] = {330,288,217,146,88,59,44,36}; | ||
341 | int own, clock; | ||
342 | |||
343 | own = pca_own(adap); | ||
344 | clock = pca_clock(adap); | ||
345 | DEB1(KERN_INFO DRIVER ": own address is %#04x\n", own); | ||
346 | DEB1(KERN_INFO DRIVER ": clock freqeuncy is %dkHz\n", freqs[clock]); | ||
347 | |||
348 | pca_outw(adap, I2C_PCA_ADR, own << 1); | ||
349 | |||
350 | pca_set_con(adap, I2C_PCA_CON_ENSIO | clock); | ||
351 | udelay(500); /* 500 µs for oscilator to stabilise */ | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static struct i2c_algorithm pca_algo = { | ||
357 | .name = "PCA9564 algorithm", | ||
358 | .id = I2C_ALGO_PCA, | ||
359 | .master_xfer = pca_xfer, | ||
360 | .functionality = pca_func, | ||
361 | }; | ||
362 | |||
363 | /* | ||
364 | * registering functions to load algorithms at runtime | ||
365 | */ | ||
366 | int i2c_pca_add_bus(struct i2c_adapter *adap) | ||
367 | { | ||
368 | struct i2c_algo_pca_data *pca_adap = adap->algo_data; | ||
369 | int rval; | ||
370 | |||
371 | /* register new adapter to i2c module... */ | ||
372 | |||
373 | adap->id |= pca_algo.id; | ||
374 | adap->algo = &pca_algo; | ||
375 | |||
376 | adap->timeout = 100; /* default values, should */ | ||
377 | adap->retries = 3; /* be replaced by defines */ | ||
378 | |||
379 | rval = pca_init(pca_adap); | ||
380 | |||
381 | if (!rval) | ||
382 | i2c_add_adapter(adap); | ||
383 | |||
384 | return rval; | ||
385 | } | ||
386 | |||
387 | int i2c_pca_del_bus(struct i2c_adapter *adap) | ||
388 | { | ||
389 | return i2c_del_adapter(adap); | ||
390 | } | ||
391 | |||
392 | EXPORT_SYMBOL(i2c_pca_add_bus); | ||
393 | EXPORT_SYMBOL(i2c_pca_del_bus); | ||
394 | |||
395 | MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>"); | ||
396 | MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm"); | ||
397 | MODULE_LICENSE("GPL"); | ||
398 | |||
399 | module_param(i2c_debug, int, 0); | ||