diff options
Diffstat (limited to 'arch/ppc/platforms/pmac_low_i2c.c')
-rw-r--r-- | arch/ppc/platforms/pmac_low_i2c.c | 511 |
1 files changed, 0 insertions, 511 deletions
diff --git a/arch/ppc/platforms/pmac_low_i2c.c b/arch/ppc/platforms/pmac_low_i2c.c deleted file mode 100644 index 08583fce1692..000000000000 --- a/arch/ppc/platforms/pmac_low_i2c.c +++ /dev/null | |||
@@ -1,511 +0,0 @@ | |||
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 | void __iomem * 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(host->base + (((unsigned)reg) << host->bsteps)); | ||
158 | } | ||
159 | |||
160 | static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) | ||
161 | { | ||
162 | out_8(host->base + (((unsigned)reg) << host->bsteps), val); | ||
163 | (void)__kw_read_reg(host, reg_subaddr); | ||
164 | } | ||
165 | |||
166 | #define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) | ||
167 | #define kw_read_reg(reg) __kw_read_reg(host, reg) | ||
168 | |||
169 | |||
170 | /* Don't schedule, the g5 fan controller is too | ||
171 | * timing sensitive | ||
172 | */ | ||
173 | static u8 kw_wait_interrupt(struct low_i2c_host* host) | ||
174 | { | ||
175 | int i; | ||
176 | u8 isr; | ||
177 | |||
178 | for (i = 0; i < 200000; i++) { | ||
179 | isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; | ||
180 | if (isr != 0) | ||
181 | return isr; | ||
182 | udelay(1); | ||
183 | } | ||
184 | return isr; | ||
185 | } | ||
186 | |||
187 | static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) | ||
188 | { | ||
189 | u8 ack; | ||
190 | |||
191 | if (isr == 0) { | ||
192 | if (state != state_stop) { | ||
193 | DBG("KW: Timeout !\n"); | ||
194 | *rc = -EIO; | ||
195 | goto stop; | ||
196 | } | ||
197 | if (state == state_stop) { | ||
198 | ack = kw_read_reg(reg_status); | ||
199 | if (!(ack & KW_I2C_STAT_BUSY)) { | ||
200 | state = state_idle; | ||
201 | kw_write_reg(reg_ier, 0x00); | ||
202 | } | ||
203 | } | ||
204 | return state; | ||
205 | } | ||
206 | |||
207 | if (isr & KW_I2C_IRQ_ADDR) { | ||
208 | ack = kw_read_reg(reg_status); | ||
209 | if (state != state_addr) { | ||
210 | kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); | ||
211 | WRONG_STATE("KW_I2C_IRQ_ADDR"); | ||
212 | *rc = -EIO; | ||
213 | goto stop; | ||
214 | } | ||
215 | if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { | ||
216 | *rc = -ENODEV; | ||
217 | DBG("KW: NAK on address\n"); | ||
218 | return state_stop; | ||
219 | } else { | ||
220 | if (rw) { | ||
221 | state = state_read; | ||
222 | if (*len > 1) | ||
223 | kw_write_reg(reg_control, KW_I2C_CTL_AAK); | ||
224 | } else { | ||
225 | state = state_write; | ||
226 | kw_write_reg(reg_data, **data); | ||
227 | (*data)++; (*len)--; | ||
228 | } | ||
229 | } | ||
230 | kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); | ||
231 | } | ||
232 | |||
233 | if (isr & KW_I2C_IRQ_DATA) { | ||
234 | if (state == state_read) { | ||
235 | **data = kw_read_reg(reg_data); | ||
236 | (*data)++; (*len)--; | ||
237 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | ||
238 | if ((*len) == 0) | ||
239 | state = state_stop; | ||
240 | else if ((*len) == 1) | ||
241 | kw_write_reg(reg_control, 0); | ||
242 | } else if (state == state_write) { | ||
243 | ack = kw_read_reg(reg_status); | ||
244 | if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { | ||
245 | DBG("KW: nack on data write\n"); | ||
246 | *rc = -EIO; | ||
247 | goto stop; | ||
248 | } else if (*len) { | ||
249 | kw_write_reg(reg_data, **data); | ||
250 | (*data)++; (*len)--; | ||
251 | } else { | ||
252 | kw_write_reg(reg_control, KW_I2C_CTL_STOP); | ||
253 | state = state_stop; | ||
254 | *rc = 0; | ||
255 | } | ||
256 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | ||
257 | } else { | ||
258 | kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); | ||
259 | WRONG_STATE("KW_I2C_IRQ_DATA"); | ||
260 | if (state != state_stop) { | ||
261 | *rc = -EIO; | ||
262 | goto stop; | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if (isr & KW_I2C_IRQ_STOP) { | ||
268 | kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); | ||
269 | if (state != state_stop) { | ||
270 | WRONG_STATE("KW_I2C_IRQ_STOP"); | ||
271 | *rc = -EIO; | ||
272 | } | ||
273 | return state_idle; | ||
274 | } | ||
275 | |||
276 | if (isr & KW_I2C_IRQ_START) | ||
277 | kw_write_reg(reg_isr, KW_I2C_IRQ_START); | ||
278 | |||
279 | return state; | ||
280 | |||
281 | stop: | ||
282 | kw_write_reg(reg_control, KW_I2C_CTL_STOP); | ||
283 | return state_stop; | ||
284 | } | ||
285 | |||
286 | static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) | ||
287 | { | ||
288 | u8 mode_reg = host->speed; | ||
289 | int state = state_addr; | ||
290 | int rc = 0; | ||
291 | |||
292 | /* Setup mode & subaddress if any */ | ||
293 | switch(host->mode) { | ||
294 | case pmac_low_i2c_mode_dumb: | ||
295 | printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); | ||
296 | return -EINVAL; | ||
297 | case pmac_low_i2c_mode_std: | ||
298 | mode_reg |= KW_I2C_MODE_STANDARD; | ||
299 | break; | ||
300 | case pmac_low_i2c_mode_stdsub: | ||
301 | mode_reg |= KW_I2C_MODE_STANDARDSUB; | ||
302 | kw_write_reg(reg_subaddr, subaddr); | ||
303 | break; | ||
304 | case pmac_low_i2c_mode_combined: | ||
305 | mode_reg |= KW_I2C_MODE_COMBINED; | ||
306 | kw_write_reg(reg_subaddr, subaddr); | ||
307 | break; | ||
308 | } | ||
309 | |||
310 | /* Setup channel & clear pending irqs */ | ||
311 | kw_write_reg(reg_isr, kw_read_reg(reg_isr)); | ||
312 | kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); | ||
313 | kw_write_reg(reg_status, 0); | ||
314 | |||
315 | /* Set up address and r/w bit */ | ||
316 | kw_write_reg(reg_addr, addr); | ||
317 | |||
318 | /* Start sending address & disable interrupt*/ | ||
319 | kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); | ||
320 | kw_write_reg(reg_control, KW_I2C_CTL_XADDR); | ||
321 | |||
322 | /* State machine, to turn into an interrupt handler */ | ||
323 | while(state != state_idle) { | ||
324 | u8 isr = kw_wait_interrupt(host); | ||
325 | state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); | ||
326 | } | ||
327 | |||
328 | return rc; | ||
329 | } | ||
330 | |||
331 | static void keywest_low_i2c_add(struct device_node *np) | ||
332 | { | ||
333 | struct low_i2c_host *host = find_low_i2c_host(NULL); | ||
334 | unsigned long *psteps, *prate, steps, aoffset = 0; | ||
335 | struct device_node *parent; | ||
336 | |||
337 | if (host == NULL) { | ||
338 | printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", | ||
339 | np->full_name); | ||
340 | return; | ||
341 | } | ||
342 | memset(host, 0, sizeof(*host)); | ||
343 | |||
344 | init_MUTEX(&host->mutex); | ||
345 | host->np = of_node_get(np); | ||
346 | psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); | ||
347 | steps = psteps ? (*psteps) : 0x10; | ||
348 | for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) | ||
349 | steps >>= 1; | ||
350 | parent = of_get_parent(np); | ||
351 | host->num_channels = 1; | ||
352 | if (parent && parent->name[0] == 'u') { | ||
353 | host->num_channels = 2; | ||
354 | aoffset = 3; | ||
355 | } | ||
356 | /* Select interface rate */ | ||
357 | host->speed = KW_I2C_MODE_100KHZ; | ||
358 | prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); | ||
359 | if (prate) switch(*prate) { | ||
360 | case 100: | ||
361 | host->speed = KW_I2C_MODE_100KHZ; | ||
362 | break; | ||
363 | case 50: | ||
364 | host->speed = KW_I2C_MODE_50KHZ; | ||
365 | break; | ||
366 | case 25: | ||
367 | host->speed = KW_I2C_MODE_25KHZ; | ||
368 | break; | ||
369 | } | ||
370 | host->mode = pmac_low_i2c_mode_std; | ||
371 | host->base = ioremap(np->addrs[0].address + aoffset, | ||
372 | np->addrs[0].size); | ||
373 | host->func = keywest_low_i2c_func; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * | ||
378 | * PMU implementation | ||
379 | * | ||
380 | */ | ||
381 | |||
382 | |||
383 | #ifdef CONFIG_ADB_PMU | ||
384 | |||
385 | static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) | ||
386 | { | ||
387 | // TODO | ||
388 | return -ENODEV; | ||
389 | } | ||
390 | |||
391 | static void pmu_low_i2c_add(struct device_node *np) | ||
392 | { | ||
393 | struct low_i2c_host *host = find_low_i2c_host(NULL); | ||
394 | |||
395 | if (host == NULL) { | ||
396 | printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", | ||
397 | np->full_name); | ||
398 | return; | ||
399 | } | ||
400 | memset(host, 0, sizeof(*host)); | ||
401 | |||
402 | init_MUTEX(&host->mutex); | ||
403 | host->np = of_node_get(np); | ||
404 | host->num_channels = 3; | ||
405 | host->mode = pmac_low_i2c_mode_std; | ||
406 | host->func = pmu_low_i2c_func; | ||
407 | } | ||
408 | |||
409 | #endif /* CONFIG_ADB_PMU */ | ||
410 | |||
411 | void __init pmac_init_low_i2c(void) | ||
412 | { | ||
413 | struct device_node *np; | ||
414 | |||
415 | /* Probe keywest-i2c busses */ | ||
416 | np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); | ||
417 | while(np) { | ||
418 | keywest_low_i2c_add(np); | ||
419 | np = of_find_compatible_node(np, "i2c", "keywest-i2c"); | ||
420 | } | ||
421 | |||
422 | #ifdef CONFIG_ADB_PMU | ||
423 | /* Probe PMU busses */ | ||
424 | np = of_find_node_by_name(NULL, "via-pmu"); | ||
425 | if (np) | ||
426 | pmu_low_i2c_add(np); | ||
427 | #endif /* CONFIG_ADB_PMU */ | ||
428 | |||
429 | /* TODO: Add CUDA support as well */ | ||
430 | } | ||
431 | |||
432 | int pmac_low_i2c_lock(struct device_node *np) | ||
433 | { | ||
434 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
435 | |||
436 | if (!host) | ||
437 | return -ENODEV; | ||
438 | down(&host->mutex); | ||
439 | return 0; | ||
440 | } | ||
441 | EXPORT_SYMBOL(pmac_low_i2c_lock); | ||
442 | |||
443 | int pmac_low_i2c_unlock(struct device_node *np) | ||
444 | { | ||
445 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
446 | |||
447 | if (!host) | ||
448 | return -ENODEV; | ||
449 | up(&host->mutex); | ||
450 | return 0; | ||
451 | } | ||
452 | EXPORT_SYMBOL(pmac_low_i2c_unlock); | ||
453 | |||
454 | |||
455 | int pmac_low_i2c_open(struct device_node *np, int channel) | ||
456 | { | ||
457 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
458 | |||
459 | if (!host) | ||
460 | return -ENODEV; | ||
461 | |||
462 | if (channel >= host->num_channels) | ||
463 | return -EINVAL; | ||
464 | |||
465 | down(&host->mutex); | ||
466 | host->is_open = 1; | ||
467 | host->channel = channel; | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | EXPORT_SYMBOL(pmac_low_i2c_open); | ||
472 | |||
473 | int pmac_low_i2c_close(struct device_node *np) | ||
474 | { | ||
475 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
476 | |||
477 | if (!host) | ||
478 | return -ENODEV; | ||
479 | |||
480 | host->is_open = 0; | ||
481 | up(&host->mutex); | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | EXPORT_SYMBOL(pmac_low_i2c_close); | ||
486 | |||
487 | int pmac_low_i2c_setmode(struct device_node *np, int mode) | ||
488 | { | ||
489 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
490 | |||
491 | if (!host) | ||
492 | return -ENODEV; | ||
493 | WARN_ON(!host->is_open); | ||
494 | host->mode = mode; | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | EXPORT_SYMBOL(pmac_low_i2c_setmode); | ||
499 | |||
500 | int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) | ||
501 | { | ||
502 | struct low_i2c_host *host = find_low_i2c_host(np); | ||
503 | |||
504 | if (!host) | ||
505 | return -ENODEV; | ||
506 | WARN_ON(!host->is_open); | ||
507 | |||
508 | return host->func(host, addrdir, subaddr, data, len); | ||
509 | } | ||
510 | EXPORT_SYMBOL(pmac_low_i2c_xfer); | ||
511 | |||