]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $ |
2 | * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III | |
3 | * platforms. | |
4 | * | |
5 | * Copyright (C) 2001 David S. Miller (davem@redhat.com) | |
6 | */ | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/types.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/wait.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <asm/oplib.h> | |
18 | #include <asm/ebus.h> | |
19 | #include <asm/spitfire.h> | |
20 | #include <asm/bbc.h> | |
21 | ||
22 | #include "bbc_i2c.h" | |
23 | ||
24 | /* Convert this driver to use i2c bus layer someday... */ | |
25 | #define I2C_PCF_PIN 0x80 | |
26 | #define I2C_PCF_ESO 0x40 | |
27 | #define I2C_PCF_ES1 0x20 | |
28 | #define I2C_PCF_ES2 0x10 | |
29 | #define I2C_PCF_ENI 0x08 | |
30 | #define I2C_PCF_STA 0x04 | |
31 | #define I2C_PCF_STO 0x02 | |
32 | #define I2C_PCF_ACK 0x01 | |
33 | ||
34 | #define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK) | |
35 | #define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) | |
36 | #define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) | |
37 | #define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) | |
38 | ||
39 | #define I2C_PCF_INI 0x40 /* 1 if not initialized */ | |
40 | #define I2C_PCF_STS 0x20 | |
41 | #define I2C_PCF_BER 0x10 | |
42 | #define I2C_PCF_AD0 0x08 | |
43 | #define I2C_PCF_LRB 0x08 | |
44 | #define I2C_PCF_AAS 0x04 | |
45 | #define I2C_PCF_LAB 0x02 | |
46 | #define I2C_PCF_BB 0x01 | |
47 | ||
48 | /* The BBC devices have two I2C controllers. The first I2C controller | |
49 | * connects mainly to configuration proms (NVRAM, cpu configuration, | |
50 | * dimm types, etc.). Whereas the second I2C controller connects to | |
51 | * environmental control devices such as fans and temperature sensors. | |
52 | * The second controller also connects to the smartcard reader, if present. | |
53 | */ | |
54 | ||
55 | #define NUM_CHILDREN 8 | |
56 | struct bbc_i2c_bus { | |
57 | struct bbc_i2c_bus *next; | |
58 | int index; | |
59 | spinlock_t lock; | |
60 | void __iomem *i2c_bussel_reg; | |
61 | void __iomem *i2c_control_regs; | |
62 | unsigned char own, clock; | |
63 | ||
64 | wait_queue_head_t wq; | |
65 | volatile int waiting; | |
66 | ||
67 | struct linux_ebus_device *bus_edev; | |
68 | struct { | |
69 | struct linux_ebus_child *device; | |
70 | int client_claimed; | |
71 | } devs[NUM_CHILDREN]; | |
72 | }; | |
73 | ||
74 | static struct bbc_i2c_bus *all_bbc_i2c; | |
75 | ||
76 | struct bbc_i2c_client { | |
77 | struct bbc_i2c_bus *bp; | |
78 | struct linux_ebus_child *echild; | |
79 | int bus; | |
80 | int address; | |
81 | }; | |
82 | ||
83 | static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild) | |
84 | { | |
85 | int i; | |
86 | ||
87 | for (i = 0; i < NUM_CHILDREN; i++) { | |
88 | if (bp->devs[i].device == echild) { | |
89 | if (bp->devs[i].client_claimed) | |
90 | return 0; | |
91 | return 1; | |
92 | } | |
93 | } | |
94 | return 0; | |
95 | } | |
96 | ||
97 | static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val) | |
98 | { | |
99 | int i; | |
100 | ||
101 | for (i = 0; i < NUM_CHILDREN; i++) { | |
102 | if (bp->devs[i].device == echild) { | |
103 | bp->devs[i].client_claimed = val; | |
104 | return; | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
109 | #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) | |
110 | #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) | |
111 | ||
112 | static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild) | |
113 | { | |
114 | struct bbc_i2c_bus *bp = all_bbc_i2c; | |
115 | ||
116 | while (bp != NULL) { | |
117 | if (find_device(bp, echild) != 0) | |
118 | break; | |
119 | bp = bp->next; | |
120 | } | |
121 | ||
122 | return bp; | |
123 | } | |
124 | ||
125 | struct linux_ebus_child *bbc_i2c_getdev(int index) | |
126 | { | |
127 | struct bbc_i2c_bus *bp = all_bbc_i2c; | |
128 | struct linux_ebus_child *echild = NULL; | |
129 | int curidx = 0; | |
130 | ||
131 | while (bp != NULL) { | |
132 | struct bbc_i2c_bus *next = bp->next; | |
133 | int i; | |
134 | ||
135 | for (i = 0; i < NUM_CHILDREN; i++) { | |
136 | if (!(echild = bp->devs[i].device)) | |
137 | break; | |
138 | if (curidx == index) | |
139 | goto out; | |
140 | echild = NULL; | |
141 | curidx++; | |
142 | } | |
143 | bp = next; | |
144 | } | |
145 | out: | |
146 | if (curidx == index) | |
147 | return echild; | |
148 | return NULL; | |
149 | } | |
150 | ||
151 | struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) | |
152 | { | |
153 | struct bbc_i2c_bus *bp = find_bus_for_device(echild); | |
154 | struct bbc_i2c_client *client; | |
155 | ||
156 | if (!bp) | |
157 | return NULL; | |
158 | client = kmalloc(sizeof(*client), GFP_KERNEL); | |
159 | if (!client) | |
160 | return NULL; | |
161 | memset(client, 0, sizeof(*client)); | |
162 | client->bp = bp; | |
163 | client->echild = echild; | |
164 | client->bus = echild->resource[0].start; | |
165 | client->address = echild->resource[1].start; | |
166 | ||
167 | claim_device(bp, echild); | |
168 | ||
169 | return client; | |
170 | } | |
171 | ||
172 | void bbc_i2c_detach(struct bbc_i2c_client *client) | |
173 | { | |
174 | struct bbc_i2c_bus *bp = client->bp; | |
175 | struct linux_ebus_child *echild = client->echild; | |
176 | ||
177 | release_device(bp, echild); | |
178 | kfree(client); | |
179 | } | |
180 | ||
181 | static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status) | |
182 | { | |
183 | DECLARE_WAITQUEUE(wait, current); | |
184 | int limit = 32; | |
185 | int ret = 1; | |
186 | ||
187 | bp->waiting = 1; | |
188 | add_wait_queue(&bp->wq, &wait); | |
189 | while (limit-- > 0) { | |
190 | u8 val; | |
191 | ||
192 | set_current_state(TASK_INTERRUPTIBLE); | |
193 | *status = val = readb(bp->i2c_control_regs + 0); | |
194 | if ((val & I2C_PCF_PIN) == 0) { | |
195 | ret = 0; | |
196 | break; | |
197 | } | |
198 | msleep_interruptible(250); | |
199 | } | |
200 | remove_wait_queue(&bp->wq, &wait); | |
201 | bp->waiting = 0; | |
202 | current->state = TASK_RUNNING; | |
203 | ||
204 | return ret; | |
205 | } | |
206 | ||
207 | int bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off) | |
208 | { | |
209 | struct bbc_i2c_bus *bp = client->bp; | |
210 | int address = client->address; | |
211 | u8 status; | |
212 | int ret = -1; | |
213 | ||
214 | if (bp->i2c_bussel_reg != NULL) | |
215 | writeb(client->bus, bp->i2c_bussel_reg); | |
216 | ||
217 | writeb(address, bp->i2c_control_regs + 0x1); | |
218 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | |
219 | if (wait_for_pin(bp, &status)) | |
220 | goto out; | |
221 | ||
222 | writeb(off, bp->i2c_control_regs + 0x1); | |
223 | if (wait_for_pin(bp, &status) || | |
224 | (status & I2C_PCF_LRB) != 0) | |
225 | goto out; | |
226 | ||
227 | writeb(val, bp->i2c_control_regs + 0x1); | |
228 | if (wait_for_pin(bp, &status)) | |
229 | goto out; | |
230 | ||
231 | ret = 0; | |
232 | ||
233 | out: | |
234 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | int bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off) | |
239 | { | |
240 | struct bbc_i2c_bus *bp = client->bp; | |
241 | unsigned char address = client->address, status; | |
242 | int ret = -1; | |
243 | ||
244 | if (bp->i2c_bussel_reg != NULL) | |
245 | writeb(client->bus, bp->i2c_bussel_reg); | |
246 | ||
247 | writeb(address, bp->i2c_control_regs + 0x1); | |
248 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | |
249 | if (wait_for_pin(bp, &status)) | |
250 | goto out; | |
251 | ||
252 | writeb(off, bp->i2c_control_regs + 0x1); | |
253 | if (wait_for_pin(bp, &status) || | |
254 | (status & I2C_PCF_LRB) != 0) | |
255 | goto out; | |
256 | ||
257 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | |
258 | ||
259 | address |= 0x1; /* READ */ | |
260 | ||
261 | writeb(address, bp->i2c_control_regs + 0x1); | |
262 | writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); | |
263 | if (wait_for_pin(bp, &status)) | |
264 | goto out; | |
265 | ||
266 | /* Set PIN back to one so the device sends the first | |
267 | * byte. | |
268 | */ | |
269 | (void) readb(bp->i2c_control_regs + 0x1); | |
270 | if (wait_for_pin(bp, &status)) | |
271 | goto out; | |
272 | ||
273 | writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0); | |
274 | *byte = readb(bp->i2c_control_regs + 0x1); | |
275 | if (wait_for_pin(bp, &status)) | |
276 | goto out; | |
277 | ||
278 | ret = 0; | |
279 | ||
280 | out: | |
281 | writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); | |
282 | (void) readb(bp->i2c_control_regs + 0x1); | |
283 | ||
284 | return ret; | |
285 | } | |
286 | ||
287 | int bbc_i2c_write_buf(struct bbc_i2c_client *client, | |
288 | char *buf, int len, int off) | |
289 | { | |
290 | int ret = 0; | |
291 | ||
292 | while (len > 0) { | |
293 | int err = bbc_i2c_writeb(client, *buf, off); | |
294 | ||
295 | if (err < 0) { | |
296 | ret = err; | |
297 | break; | |
298 | } | |
299 | ||
300 | len--; | |
301 | buf++; | |
302 | off++; | |
303 | } | |
304 | return ret; | |
305 | } | |
306 | ||
307 | int bbc_i2c_read_buf(struct bbc_i2c_client *client, | |
308 | char *buf, int len, int off) | |
309 | { | |
310 | int ret = 0; | |
311 | ||
312 | while (len > 0) { | |
313 | int err = bbc_i2c_readb(client, buf, off); | |
314 | if (err < 0) { | |
315 | ret = err; | |
316 | break; | |
317 | } | |
318 | len--; | |
319 | buf++; | |
320 | off++; | |
321 | } | |
322 | ||
323 | return ret; | |
324 | } | |
325 | ||
326 | EXPORT_SYMBOL(bbc_i2c_getdev); | |
327 | EXPORT_SYMBOL(bbc_i2c_attach); | |
328 | EXPORT_SYMBOL(bbc_i2c_detach); | |
329 | EXPORT_SYMBOL(bbc_i2c_writeb); | |
330 | EXPORT_SYMBOL(bbc_i2c_readb); | |
331 | EXPORT_SYMBOL(bbc_i2c_write_buf); | |
332 | EXPORT_SYMBOL(bbc_i2c_read_buf); | |
333 | ||
7d12e780 | 334 | static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id) |
1da177e4 LT |
335 | { |
336 | struct bbc_i2c_bus *bp = dev_id; | |
337 | ||
338 | /* PIN going from set to clear is the only event which | |
339 | * makes the i2c assert an interrupt. | |
340 | */ | |
341 | if (bp->waiting && | |
342 | !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN)) | |
343 | wake_up(&bp->wq); | |
344 | ||
345 | return IRQ_HANDLED; | |
346 | } | |
347 | ||
348 | static void __init reset_one_i2c(struct bbc_i2c_bus *bp) | |
349 | { | |
350 | writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); | |
351 | writeb(bp->own, bp->i2c_control_regs + 0x1); | |
352 | writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); | |
353 | writeb(bp->clock, bp->i2c_control_regs + 0x1); | |
354 | writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); | |
355 | } | |
356 | ||
357 | static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) | |
358 | { | |
359 | struct bbc_i2c_bus *bp = kmalloc(sizeof(*bp), GFP_KERNEL); | |
360 | struct linux_ebus_child *echild; | |
361 | int entry; | |
362 | ||
363 | if (!bp) | |
364 | return -ENOMEM; | |
365 | memset(bp, 0, sizeof(*bp)); | |
366 | ||
367 | bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2); | |
368 | if (!bp->i2c_control_regs) | |
369 | goto fail; | |
370 | ||
371 | if (edev->num_addrs == 2) { | |
372 | bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1); | |
373 | if (!bp->i2c_bussel_reg) | |
374 | goto fail; | |
375 | } | |
376 | ||
377 | bp->waiting = 0; | |
378 | init_waitqueue_head(&bp->wq); | |
379 | if (request_irq(edev->irqs[0], bbc_i2c_interrupt, | |
dace1453 | 380 | IRQF_SHARED, "bbc_i2c", bp)) |
1da177e4 LT |
381 | goto fail; |
382 | ||
383 | bp->index = index; | |
384 | bp->bus_edev = edev; | |
385 | ||
386 | spin_lock_init(&bp->lock); | |
387 | bp->next = all_bbc_i2c; | |
388 | all_bbc_i2c = bp; | |
389 | ||
390 | entry = 0; | |
391 | for (echild = edev->children; | |
392 | echild && entry < 8; | |
393 | echild = echild->next, entry++) { | |
394 | bp->devs[entry].device = echild; | |
395 | bp->devs[entry].client_claimed = 0; | |
396 | } | |
397 | ||
398 | writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); | |
399 | bp->own = readb(bp->i2c_control_regs + 0x01); | |
400 | writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); | |
401 | bp->clock = readb(bp->i2c_control_regs + 0x01); | |
402 | ||
403 | printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n", | |
404 | bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock); | |
405 | ||
406 | reset_one_i2c(bp); | |
407 | ||
408 | return 0; | |
409 | ||
410 | fail: | |
411 | if (bp->i2c_bussel_reg) | |
412 | iounmap(bp->i2c_bussel_reg); | |
413 | if (bp->i2c_control_regs) | |
414 | iounmap(bp->i2c_control_regs); | |
415 | kfree(bp); | |
416 | return -EINVAL; | |
417 | } | |
418 | ||
419 | static int __init bbc_present(void) | |
420 | { | |
421 | struct linux_ebus *ebus = NULL; | |
422 | struct linux_ebus_device *edev = NULL; | |
423 | ||
424 | for_each_ebus(ebus) { | |
425 | for_each_ebusdev(edev, ebus) { | |
690c8fd3 | 426 | if (!strcmp(edev->prom_node->name, "bbc")) |
1da177e4 LT |
427 | return 1; |
428 | } | |
429 | } | |
430 | return 0; | |
431 | } | |
432 | ||
433 | extern int bbc_envctrl_init(void); | |
434 | extern void bbc_envctrl_cleanup(void); | |
435 | static void bbc_i2c_cleanup(void); | |
436 | ||
437 | static int __init bbc_i2c_init(void) | |
438 | { | |
439 | struct linux_ebus *ebus = NULL; | |
440 | struct linux_ebus_device *edev = NULL; | |
441 | int err, index = 0; | |
442 | ||
b5e7ae5d DM |
443 | if ((tlb_type != cheetah && tlb_type != cheetah_plus) || |
444 | !bbc_present()) | |
1da177e4 LT |
445 | return -ENODEV; |
446 | ||
447 | for_each_ebus(ebus) { | |
448 | for_each_ebusdev(edev, ebus) { | |
690c8fd3 | 449 | if (!strcmp(edev->prom_node->name, "i2c")) { |
1da177e4 LT |
450 | if (!attach_one_i2c(edev, index)) |
451 | index++; | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | if (!index) | |
457 | return -ENODEV; | |
458 | ||
459 | err = bbc_envctrl_init(); | |
460 | if (err) | |
461 | bbc_i2c_cleanup(); | |
462 | return err; | |
463 | } | |
464 | ||
465 | static void bbc_i2c_cleanup(void) | |
466 | { | |
467 | struct bbc_i2c_bus *bp = all_bbc_i2c; | |
468 | ||
469 | bbc_envctrl_cleanup(); | |
470 | ||
471 | while (bp != NULL) { | |
472 | struct bbc_i2c_bus *next = bp->next; | |
473 | ||
474 | free_irq(bp->bus_edev->irqs[0], bp); | |
475 | ||
476 | if (bp->i2c_bussel_reg) | |
477 | iounmap(bp->i2c_bussel_reg); | |
478 | if (bp->i2c_control_regs) | |
479 | iounmap(bp->i2c_control_regs); | |
480 | ||
481 | kfree(bp); | |
482 | ||
483 | bp = next; | |
484 | } | |
485 | all_bbc_i2c = NULL; | |
486 | } | |
487 | ||
488 | module_init(bbc_i2c_init); | |
489 | module_exit(bbc_i2c_cleanup); | |
b5e7ae5d | 490 | MODULE_LICENSE("GPL"); |