]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/ppc/platforms/pmac_low_i2c.c | |
3 | * | |
4 | * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This file contains some low-level i2c access routines that | |
12 | * need to be used by various bits of the PowerMac platform code | |
13 | * at times where the real asynchronous & interrupt driven driver | |
14 | * cannot be used. The API borrows some semantics from the darwin | |
15 | * driver in order to ease the implementation of the platform | |
16 | * properties parser | |
17 | */ | |
18 | ||
19 | #include <linux/config.h> | |
20 | #include <linux/types.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/sched.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/module.h> | |
25 | #include <linux/adb.h> | |
26 | #include <linux/pmu.h> | |
27 | #include <asm/keylargo.h> | |
28 | #include <asm/uninorth.h> | |
29 | #include <asm/io.h> | |
30 | #include <asm/prom.h> | |
31 | #include <asm/machdep.h> | |
32 | #include <asm/pmac_low_i2c.h> | |
33 | ||
34 | #define MAX_LOW_I2C_HOST 4 | |
35 | ||
36 | #if 1 | |
37 | #define DBG(x...) do {\ | |
38 | printk(KERN_DEBUG "KW:" x); \ | |
39 | } while(0) | |
40 | #else | |
41 | #define DBGG(x...) | |
42 | #endif | |
43 | ||
44 | struct low_i2c_host; | |
45 | ||
46 | typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); | |
47 | ||
48 | struct low_i2c_host | |
49 | { | |
50 | struct device_node *np; /* OF device node */ | |
51 | struct semaphore mutex; /* Access mutex for use by i2c-keywest */ | |
52 | low_i2c_func_t func; /* Access function */ | |
53 | int is_open : 1; /* Poor man's access control */ | |
54 | int mode; /* Current mode */ | |
55 | int channel; /* Current channel */ | |
56 | int num_channels; /* Number of channels */ | |
57 | unsigned long base; /* For keywest-i2c, base address */ | |
58 | int bsteps; /* And register stepping */ | |
59 | int speed; /* And speed */ | |
60 | }; | |
61 | ||
62 | static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; | |
63 | ||
64 | /* No locking is necessary on allocation, we are running way before | |
65 | * anything can race with us | |
66 | */ | |
67 | static struct low_i2c_host *find_low_i2c_host(struct device_node *np) | |
68 | { | |
69 | int i; | |
70 | ||
71 | for (i = 0; i < MAX_LOW_I2C_HOST; i++) | |
72 | if (low_i2c_hosts[i].np == np) | |
73 | return &low_i2c_hosts[i]; | |
74 | return NULL; | |
75 | } | |
76 | ||
77 | /* | |
78 | * | |
79 | * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) | |
80 | * | |
81 | */ | |
82 | ||
83 | /* | |
84 | * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, | |
85 | * should be moved somewhere in include/asm-ppc/ | |
86 | */ | |
87 | /* Register indices */ | |
88 | typedef enum { | |
89 | reg_mode = 0, | |
90 | reg_control, | |
91 | reg_status, | |
92 | reg_isr, | |
93 | reg_ier, | |
94 | reg_addr, | |
95 | reg_subaddr, | |
96 | reg_data | |
97 | } reg_t; | |
98 | ||
99 | ||
100 | /* Mode register */ | |
101 | #define KW_I2C_MODE_100KHZ 0x00 | |
102 | #define KW_I2C_MODE_50KHZ 0x01 | |
103 | #define KW_I2C_MODE_25KHZ 0x02 | |
104 | #define KW_I2C_MODE_DUMB 0x00 | |
105 | #define KW_I2C_MODE_STANDARD 0x04 | |
106 | #define KW_I2C_MODE_STANDARDSUB 0x08 | |
107 | #define KW_I2C_MODE_COMBINED 0x0C | |
108 | #define KW_I2C_MODE_MODE_MASK 0x0C | |
109 | #define KW_I2C_MODE_CHAN_MASK 0xF0 | |
110 | ||
111 | /* Control register */ | |
112 | #define KW_I2C_CTL_AAK 0x01 | |
113 | #define KW_I2C_CTL_XADDR 0x02 | |
114 | #define KW_I2C_CTL_STOP 0x04 | |
115 | #define KW_I2C_CTL_START 0x08 | |
116 | ||
117 | /* Status register */ | |
118 | #define KW_I2C_STAT_BUSY 0x01 | |
119 | #define KW_I2C_STAT_LAST_AAK 0x02 | |
120 | #define KW_I2C_STAT_LAST_RW 0x04 | |
121 | #define KW_I2C_STAT_SDA 0x08 | |
122 | #define KW_I2C_STAT_SCL 0x10 | |
123 | ||
124 | /* IER & ISR registers */ | |
125 | #define KW_I2C_IRQ_DATA 0x01 | |
126 | #define KW_I2C_IRQ_ADDR 0x02 | |
127 | #define KW_I2C_IRQ_STOP 0x04 | |
128 | #define KW_I2C_IRQ_START 0x08 | |
129 | #define KW_I2C_IRQ_MASK 0x0F | |
130 | ||
131 | /* State machine states */ | |
132 | enum { | |
133 | state_idle, | |
134 | state_addr, | |
135 | state_read, | |
136 | state_write, | |
137 | state_stop, | |
138 | state_dead | |
139 | }; | |
140 | ||
141 | #define WRONG_STATE(name) do {\ | |
142 | printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ | |
143 | name, __kw_state_names[state], isr); \ | |
144 | } while(0) | |
145 | ||
146 | static const char *__kw_state_names[] = { | |
147 | "state_idle", | |
148 | "state_addr", | |
149 | "state_read", | |
150 | "state_write", | |
151 | "state_stop", | |
152 | "state_dead" | |
153 | }; | |
154 | ||
155 | static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) | |
156 | { | |
157 | return in_8(((volatile u8 *)host->base) | |
158 | + (((unsigned)reg) << host->bsteps)); | |
159 | } | |
160 | ||
161 | static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) | |
162 | { | |
163 | out_8(((volatile u8 *)host->base) | |
164 | + (((unsigned)reg) << host->bsteps), val); | |
165 | (void)__kw_read_reg(host, reg_subaddr); | |
166 | } | |
167 | ||
168 | #define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) | |
169 | #define kw_read_reg(reg) __kw_read_reg(host, reg) | |
170 | ||
171 | ||
172 | /* Don't schedule, the g5 fan controller is too | |
173 | * timing sensitive | |
174 | */ | |
175 | static u8 kw_wait_interrupt(struct low_i2c_host* host) | |
176 | { | |
177 | int i; | |
178 | u8 isr; | |
179 | ||
180 | for (i = 0; i < 200000; i++) { | |
181 | isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; | |
182 | if (isr != 0) | |
183 | return isr; | |
184 | udelay(1); | |
185 | } | |
186 | return isr; | |
187 | } | |
188 | ||
189 | static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) | |
190 | { | |
191 | u8 ack; | |
192 | ||
193 | if (isr == 0) { | |
194 | if (state != state_stop) { | |
195 | DBG("KW: Timeout !\n"); | |
196 | *rc = -EIO; | |
197 | goto stop; | |
198 | } | |
199 | if (state == state_stop) { | |
200 | ack = kw_read_reg(reg_status); | |
201 | if (!(ack & KW_I2C_STAT_BUSY)) { | |
202 | state = state_idle; | |
203 | kw_write_reg(reg_ier, 0x00); | |
204 | } | |
205 | } | |
206 | return state; | |
207 | } | |
208 | ||
209 | if (isr & KW_I2C_IRQ_ADDR) { | |
210 | ack = kw_read_reg(reg_status); | |
211 | if (state != state_addr) { | |
212 | kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); | |
213 | WRONG_STATE("KW_I2C_IRQ_ADDR"); | |
214 | *rc = -EIO; | |
215 | goto stop; | |
216 | } | |
217 | if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { | |
218 | *rc = -ENODEV; | |
219 | DBG("KW: NAK on address\n"); | |
220 | return state_stop; | |
221 | } else { | |
222 | if (rw) { | |
223 | state = state_read; | |
224 | if (*len > 1) | |
225 | kw_write_reg(reg_control, KW_I2C_CTL_AAK); | |
226 | } else { | |
227 | state = state_write; | |
228 | kw_write_reg(reg_data, **data); | |
229 | (*data)++; (*len)--; | |
230 | } | |
231 | } | |
232 | kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); | |
233 | } | |
234 | ||
235 | if (isr & KW_I2C_IRQ_DATA) { | |
236 | if (state == state_read) { | |
237 | **data = kw_read_reg(reg_data); | |
238 | (*data)++; (*len)--; | |
239 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | |
240 | if ((*len) == 0) | |
241 | state = state_stop; | |
242 | else if ((*len) == 1) | |
243 | kw_write_reg(reg_control, 0); | |
244 | } else if (state == state_write) { | |
245 | ack = kw_read_reg(reg_status); | |
246 | if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { | |
247 | DBG("KW: nack on data write\n"); | |
248 | *rc = -EIO; | |
249 | goto stop; | |
250 | } else if (*len) { | |
251 | kw_write_reg(reg_data, **data); | |
252 | (*data)++; (*len)--; | |
253 | } else { | |
254 | kw_write_reg(reg_control, KW_I2C_CTL_STOP); | |
255 | state = state_stop; | |
256 | *rc = 0; | |
257 | } | |
258 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | |
259 | } else { | |
260 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | |
261 | WRONG_STATE("KW_I2C_IRQ_DATA"); | |
262 | if (state != state_stop) { | |
263 | *rc = -EIO; | |
264 | goto stop; | |
265 | } | |
266 | } | |
267 | } | |
268 | ||
269 | if (isr & KW_I2C_IRQ_STOP) { | |
270 | kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); | |
271 | if (state != state_stop) { | |
272 | WRONG_STATE("KW_I2C_IRQ_STOP"); | |
273 | *rc = -EIO; | |
274 | } | |
275 | return state_idle; | |
276 | } | |
277 | ||
278 | if (isr & KW_I2C_IRQ_START) | |
279 | kw_write_reg(reg_isr, KW_I2C_IRQ_START); | |
280 | ||
281 | return state; | |
282 | ||
283 | stop: | |
284 | kw_write_reg(reg_control, KW_I2C_CTL_STOP); | |
285 | return state_stop; | |
286 | } | |
287 | ||
288 | static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) | |
289 | { | |
290 | u8 mode_reg = host->speed; | |
291 | int state = state_addr; | |
292 | int rc = 0; | |
293 | ||
294 | /* Setup mode & subaddress if any */ | |
295 | switch(host->mode) { | |
296 | case pmac_low_i2c_mode_dumb: | |
297 | printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); | |
298 | return -EINVAL; | |
299 | case pmac_low_i2c_mode_std: | |
300 | mode_reg |= KW_I2C_MODE_STANDARD; | |
301 | break; | |
302 | case pmac_low_i2c_mode_stdsub: | |
303 | mode_reg |= KW_I2C_MODE_STANDARDSUB; | |
304 | kw_write_reg(reg_subaddr, subaddr); | |
305 | break; | |
306 | case pmac_low_i2c_mode_combined: | |
307 | mode_reg |= KW_I2C_MODE_COMBINED; | |
308 | kw_write_reg(reg_subaddr, subaddr); | |
309 | break; | |
310 | } | |
311 | ||
312 | /* Setup channel & clear pending irqs */ | |
313 | kw_write_reg(reg_isr, kw_read_reg(reg_isr)); | |
314 | kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); | |
315 | kw_write_reg(reg_status, 0); | |
316 | ||
317 | /* Set up address and r/w bit */ | |
318 | kw_write_reg(reg_addr, addr); | |
319 | ||
320 | /* Start sending address & disable interrupt*/ | |
321 | kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); | |
322 | kw_write_reg(reg_control, KW_I2C_CTL_XADDR); | |
323 | ||
324 | /* State machine, to turn into an interrupt handler */ | |
325 | while(state != state_idle) { | |
326 | u8 isr = kw_wait_interrupt(host); | |
327 | state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); | |
328 | } | |
329 | ||
330 | return rc; | |
331 | } | |
332 | ||
333 | static void keywest_low_i2c_add(struct device_node *np) | |
334 | { | |
335 | struct low_i2c_host *host = find_low_i2c_host(NULL); | |
336 | unsigned long *psteps, *prate, steps, aoffset = 0; | |
337 | struct device_node *parent; | |
338 | ||
339 | if (host == NULL) { | |
340 | printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", | |
341 | np->full_name); | |
342 | return; | |
343 | } | |
344 | memset(host, 0, sizeof(*host)); | |
345 | ||
346 | init_MUTEX(&host->mutex); | |
347 | host->np = of_node_get(np); | |
348 | psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); | |
349 | steps = psteps ? (*psteps) : 0x10; | |
350 | for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) | |
351 | steps >>= 1; | |
352 | parent = of_get_parent(np); | |
353 | host->num_channels = 1; | |
354 | if (parent && parent->name[0] == 'u') { | |
355 | host->num_channels = 2; | |
356 | aoffset = 3; | |
357 | } | |
358 | /* Select interface rate */ | |
359 | host->speed = KW_I2C_MODE_100KHZ; | |
360 | prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); | |
361 | if (prate) switch(*prate) { | |
362 | case 100: | |
363 | host->speed = KW_I2C_MODE_100KHZ; | |
364 | break; | |
365 | case 50: | |
366 | host->speed = KW_I2C_MODE_50KHZ; | |
367 | break; | |
368 | case 25: | |
369 | host->speed = KW_I2C_MODE_25KHZ; | |
370 | break; | |
371 | } | |
372 | host->mode = pmac_low_i2c_mode_std; | |
373 | host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset, | |
374 | np->addrs[0].size); | |
375 | host->func = keywest_low_i2c_func; | |
376 | } | |
377 | ||
378 | /* | |
379 | * | |
380 | * PMU implementation | |
381 | * | |
382 | */ | |
383 | ||
384 | ||
385 | #ifdef CONFIG_ADB_PMU | |
386 | ||
387 | static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) | |
388 | { | |
389 | // TODO | |
390 | return -ENODEV; | |
391 | } | |
392 | ||
393 | static void pmu_low_i2c_add(struct device_node *np) | |
394 | { | |
395 | struct low_i2c_host *host = find_low_i2c_host(NULL); | |
396 | ||
397 | if (host == NULL) { | |
398 | printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", | |
399 | np->full_name); | |
400 | return; | |
401 | } | |
402 | memset(host, 0, sizeof(*host)); | |
403 | ||
404 | init_MUTEX(&host->mutex); | |
405 | host->np = of_node_get(np); | |
406 | host->num_channels = 3; | |
407 | host->mode = pmac_low_i2c_mode_std; | |
408 | host->func = pmu_low_i2c_func; | |
409 | } | |
410 | ||
411 | #endif /* CONFIG_ADB_PMU */ | |
412 | ||
413 | void __init pmac_init_low_i2c(void) | |
414 | { | |
415 | struct device_node *np; | |
416 | ||
417 | /* Probe keywest-i2c busses */ | |
418 | np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); | |
419 | while(np) { | |
420 | keywest_low_i2c_add(np); | |
421 | np = of_find_compatible_node(np, "i2c", "keywest-i2c"); | |
422 | } | |
423 | ||
424 | #ifdef CONFIG_ADB_PMU | |
425 | /* Probe PMU busses */ | |
426 | np = of_find_node_by_name(NULL, "via-pmu"); | |
427 | if (np) | |
428 | pmu_low_i2c_add(np); | |
429 | #endif /* CONFIG_ADB_PMU */ | |
430 | ||
431 | /* TODO: Add CUDA support as well */ | |
432 | } | |
433 | ||
434 | int pmac_low_i2c_lock(struct device_node *np) | |
435 | { | |
436 | struct low_i2c_host *host = find_low_i2c_host(np); | |
437 | ||
438 | if (!host) | |
439 | return -ENODEV; | |
440 | down(&host->mutex); | |
441 | return 0; | |
442 | } | |
443 | EXPORT_SYMBOL(pmac_low_i2c_lock); | |
444 | ||
445 | int pmac_low_i2c_unlock(struct device_node *np) | |
446 | { | |
447 | struct low_i2c_host *host = find_low_i2c_host(np); | |
448 | ||
449 | if (!host) | |
450 | return -ENODEV; | |
451 | up(&host->mutex); | |
452 | return 0; | |
453 | } | |
454 | EXPORT_SYMBOL(pmac_low_i2c_unlock); | |
455 | ||
456 | ||
457 | int pmac_low_i2c_open(struct device_node *np, int channel) | |
458 | { | |
459 | struct low_i2c_host *host = find_low_i2c_host(np); | |
460 | ||
461 | if (!host) | |
462 | return -ENODEV; | |
463 | ||
464 | if (channel >= host->num_channels) | |
465 | return -EINVAL; | |
466 | ||
467 | down(&host->mutex); | |
468 | host->is_open = 1; | |
469 | host->channel = channel; | |
470 | ||
471 | return 0; | |
472 | } | |
473 | EXPORT_SYMBOL(pmac_low_i2c_open); | |
474 | ||
475 | int pmac_low_i2c_close(struct device_node *np) | |
476 | { | |
477 | struct low_i2c_host *host = find_low_i2c_host(np); | |
478 | ||
479 | if (!host) | |
480 | return -ENODEV; | |
481 | ||
482 | host->is_open = 0; | |
483 | up(&host->mutex); | |
484 | ||
485 | return 0; | |
486 | } | |
487 | EXPORT_SYMBOL(pmac_low_i2c_close); | |
488 | ||
489 | int pmac_low_i2c_setmode(struct device_node *np, int mode) | |
490 | { | |
491 | struct low_i2c_host *host = find_low_i2c_host(np); | |
492 | ||
493 | if (!host) | |
494 | return -ENODEV; | |
495 | WARN_ON(!host->is_open); | |
496 | host->mode = mode; | |
497 | ||
498 | return 0; | |
499 | } | |
500 | EXPORT_SYMBOL(pmac_low_i2c_setmode); | |
501 | ||
502 | int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) | |
503 | { | |
504 | struct low_i2c_host *host = find_low_i2c_host(np); | |
505 | ||
506 | if (!host) | |
507 | return -ENODEV; | |
508 | WARN_ON(!host->is_open); | |
509 | ||
510 | return host->func(host, addrdir, subaddr, data, len); | |
511 | } | |
512 | EXPORT_SYMBOL(pmac_low_i2c_xfer); | |
513 |