]>
Commit | Line | Data |
---|---|---|
f9bdedb2 SM |
1 | /* |
2 | * PIKA Warp(tm) board specific routines | |
3 | * | |
4 | * Copyright (c) 2008 PIKA Technologies | |
5 | * Sean MacLennan <smaclennan@pikatech.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | */ | |
12 | #include <linux/init.h> | |
13 | #include <linux/of_platform.h> | |
14 | #include <linux/kthread.h> | |
4ebef31f SM |
15 | #include <linux/i2c.h> |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/delay.h> | |
f9bdedb2 SM |
18 | |
19 | #include <asm/machdep.h> | |
20 | #include <asm/prom.h> | |
21 | #include <asm/udbg.h> | |
22 | #include <asm/time.h> | |
23 | #include <asm/uic.h> | |
93173ce2 | 24 | #include <asm/ppc4xx.h> |
f9bdedb2 SM |
25 | |
26 | static __initdata struct of_device_id warp_of_bus[] = { | |
27 | { .compatible = "ibm,plb4", }, | |
28 | { .compatible = "ibm,opb", }, | |
29 | { .compatible = "ibm,ebc", }, | |
30 | {}, | |
31 | }; | |
32 | ||
33 | static int __init warp_device_probe(void) | |
34 | { | |
35 | of_platform_bus_probe(NULL, warp_of_bus, NULL); | |
36 | return 0; | |
37 | } | |
38 | machine_device_initcall(warp, warp_device_probe); | |
39 | ||
40 | static int __init warp_probe(void) | |
41 | { | |
42 | unsigned long root = of_get_flat_dt_root(); | |
43 | ||
44 | return of_flat_dt_is_compatible(root, "pika,warp"); | |
45 | } | |
46 | ||
47 | define_machine(warp) { | |
48 | .name = "Warp", | |
49 | .probe = warp_probe, | |
50 | .progress = udbg_progress, | |
51 | .init_IRQ = uic_init_tree, | |
52 | .get_irq = uic_get_irq, | |
93173ce2 | 53 | .restart = ppc4xx_reset_system, |
f9bdedb2 SM |
54 | .calibrate_decr = generic_calibrate_decr, |
55 | }; | |
56 | ||
57 | ||
4ebef31f SM |
58 | /* I am not sure this is the best place for this... */ |
59 | static int __init warp_post_info(void) | |
60 | { | |
61 | struct device_node *np; | |
62 | void __iomem *fpga; | |
63 | u32 post1, post2; | |
64 | ||
65 | /* Sighhhh... POST information is in the sd area. */ | |
66 | np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); | |
67 | if (np == NULL) | |
68 | return -ENOENT; | |
69 | ||
70 | fpga = of_iomap(np, 0); | |
71 | of_node_put(np); | |
72 | if (fpga == NULL) | |
73 | return -ENOENT; | |
74 | ||
75 | post1 = in_be32(fpga + 0x40); | |
76 | post2 = in_be32(fpga + 0x44); | |
77 | ||
78 | iounmap(fpga); | |
79 | ||
80 | if (post1 || post2) | |
81 | printk(KERN_INFO "Warp POST %08x %08x\n", post1, post2); | |
82 | else | |
83 | printk(KERN_INFO "Warp POST OK\n"); | |
84 | ||
85 | return 0; | |
86 | } | |
87 | machine_late_initcall(warp, warp_post_info); | |
88 | ||
89 | ||
90 | #ifdef CONFIG_SENSORS_AD7414 | |
91 | ||
92 | static LIST_HEAD(dtm_shutdown_list); | |
93 | static void __iomem *dtm_fpga; | |
94 | static void __iomem *gpio_base; | |
95 | ||
96 | ||
97 | struct dtm_shutdown { | |
98 | struct list_head list; | |
99 | void (*func)(void *arg); | |
100 | void *arg; | |
101 | }; | |
f9bdedb2 SM |
102 | |
103 | ||
4ebef31f | 104 | int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) |
f9bdedb2 | 105 | { |
4ebef31f SM |
106 | struct dtm_shutdown *shutdown; |
107 | ||
108 | shutdown = kmalloc(sizeof(struct dtm_shutdown), GFP_KERNEL); | |
109 | if (shutdown == NULL) | |
110 | return -ENOMEM; | |
111 | ||
112 | shutdown->func = func; | |
113 | shutdown->arg = arg; | |
114 | ||
115 | list_add(&shutdown->list, &dtm_shutdown_list); | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) | |
121 | { | |
122 | struct dtm_shutdown *shutdown; | |
123 | ||
124 | list_for_each_entry(shutdown, &dtm_shutdown_list, list) | |
125 | if (shutdown->func == func && shutdown->arg == arg) { | |
126 | list_del(&shutdown->list); | |
127 | kfree(shutdown); | |
128 | return 0; | |
129 | } | |
130 | ||
131 | return -EINVAL; | |
132 | } | |
133 | ||
134 | static irqreturn_t temp_isr(int irq, void *context) | |
135 | { | |
136 | struct dtm_shutdown *shutdown; | |
137 | ||
138 | local_irq_disable(); | |
139 | ||
140 | /* Run through the shutdown list. */ | |
141 | list_for_each_entry(shutdown, &dtm_shutdown_list, list) | |
142 | shutdown->func(shutdown->arg); | |
143 | ||
144 | printk(KERN_EMERG "\n\nCritical Temperature Shutdown\n"); | |
145 | ||
146 | while (1) { | |
147 | if (dtm_fpga) { | |
148 | unsigned reset = in_be32(dtm_fpga + 0x14); | |
149 | out_be32(dtm_fpga + 0x14, reset); | |
f9bdedb2 SM |
150 | } |
151 | ||
4ebef31f SM |
152 | if (gpio_base) { |
153 | unsigned leds = in_be32(gpio_base); | |
154 | ||
155 | /* green off, red toggle */ | |
156 | leds &= ~0x80000000; | |
157 | leds ^= 0x40000000; | |
158 | ||
159 | out_be32(gpio_base, leds); | |
f9bdedb2 | 160 | } |
4ebef31f SM |
161 | |
162 | mdelay(500); | |
163 | } | |
164 | } | |
165 | ||
166 | static int pika_setup_leds(void) | |
167 | { | |
168 | struct device_node *np; | |
169 | const u32 *gpios; | |
170 | int len; | |
171 | ||
172 | np = of_find_compatible_node(NULL, NULL, "linux,gpio-led"); | |
173 | if (!np) { | |
174 | printk(KERN_ERR __FILE__ ": Unable to find gpio-led\n"); | |
175 | return -ENOENT; | |
f9bdedb2 SM |
176 | } |
177 | ||
4ebef31f SM |
178 | gpios = of_get_property(np, "gpios", &len); |
179 | of_node_put(np); | |
180 | if (!gpios || len < 4) { | |
181 | printk(KERN_ERR __FILE__ | |
182 | ": Unable to get gpios property (%d)\n", len); | |
183 | return -ENOENT; | |
184 | } | |
f9bdedb2 | 185 | |
4ebef31f SM |
186 | np = of_find_node_by_phandle(gpios[0]); |
187 | if (!np) { | |
188 | printk(KERN_ERR __FILE__ ": Unable to find gpio\n"); | |
189 | return -ENOENT; | |
f9bdedb2 | 190 | } |
4ebef31f SM |
191 | |
192 | gpio_base = of_iomap(np, 0); | |
193 | of_node_put(np); | |
194 | if (!gpio_base) { | |
195 | printk(KERN_ERR __FILE__ ": Unable to map gpio"); | |
196 | return -ENOMEM; | |
f9bdedb2 SM |
197 | } |
198 | ||
4ebef31f | 199 | return 0; |
f9bdedb2 | 200 | } |
f9bdedb2 | 201 | |
4ebef31f SM |
202 | static void pika_setup_critical_temp(struct i2c_client *client) |
203 | { | |
204 | struct device_node *np; | |
205 | int irq, rc; | |
206 | ||
207 | /* Do this before enabling critical temp interrupt since we | |
208 | * may immediately interrupt. | |
209 | */ | |
210 | pika_setup_leds(); | |
211 | ||
212 | /* These registers are in 1 degree increments. */ | |
213 | i2c_smbus_write_byte_data(client, 2, 65); /* Thigh */ | |
048040a3 | 214 | i2c_smbus_write_byte_data(client, 3, 0); /* Tlow */ |
4ebef31f SM |
215 | |
216 | np = of_find_compatible_node(NULL, NULL, "adi,ad7414"); | |
217 | if (np == NULL) { | |
218 | printk(KERN_ERR __FILE__ ": Unable to find ad7414\n"); | |
219 | return; | |
220 | } | |
221 | ||
222 | irq = irq_of_parse_and_map(np, 0); | |
223 | of_node_put(np); | |
224 | if (irq == NO_IRQ) { | |
225 | printk(KERN_ERR __FILE__ ": Unable to get ad7414 irq\n"); | |
226 | return; | |
227 | } | |
228 | ||
229 | rc = request_irq(irq, temp_isr, 0, "ad7414", NULL); | |
230 | if (rc) { | |
231 | printk(KERN_ERR __FILE__ | |
232 | ": Unable to request ad7414 irq %d = %d\n", irq, rc); | |
233 | return; | |
234 | } | |
235 | } | |
236 | ||
237 | static inline void pika_dtm_check_fan(void __iomem *fpga) | |
238 | { | |
239 | static int fan_state; | |
240 | u32 fan = in_be32(fpga + 0x34) & (1 << 14); | |
241 | ||
242 | if (fan_state != fan) { | |
243 | fan_state = fan; | |
244 | if (fan) | |
245 | printk(KERN_WARNING "Fan rotation error detected." | |
246 | " Please check hardware.\n"); | |
247 | } | |
248 | } | |
f9bdedb2 | 249 | |
f9bdedb2 SM |
250 | static int pika_dtm_thread(void __iomem *fpga) |
251 | { | |
4ebef31f SM |
252 | struct i2c_adapter *adap; |
253 | struct i2c_client *client; | |
254 | ||
255 | /* We loop in case either driver was compiled as a module and | |
256 | * has not been insmoded yet. | |
257 | */ | |
258 | while (!(adap = i2c_get_adapter(0))) { | |
259 | set_current_state(TASK_INTERRUPTIBLE); | |
260 | schedule_timeout(HZ); | |
261 | } | |
262 | ||
263 | while (1) { | |
264 | list_for_each_entry(client, &adap->clients, list) | |
265 | if (client->addr == 0x4a) | |
266 | goto found_it; | |
267 | ||
268 | set_current_state(TASK_INTERRUPTIBLE); | |
269 | schedule_timeout(HZ); | |
270 | } | |
271 | ||
272 | found_it: | |
273 | i2c_put_adapter(adap); | |
274 | ||
275 | pika_setup_critical_temp(client); | |
276 | ||
277 | printk(KERN_INFO "PIKA DTM thread running.\n"); | |
f9bdedb2 SM |
278 | |
279 | while (!kthread_should_stop()) { | |
048040a3 SM |
280 | int val; |
281 | ||
282 | val = i2c_smbus_read_word_data(client, 0); | |
283 | if (val < 0) | |
284 | dev_dbg(&client->dev, "DTM read temp failed.\n"); | |
285 | else { | |
286 | s16 temp = swab16(val); | |
287 | out_be32(fpga + 0x20, temp); | |
288 | } | |
f9bdedb2 | 289 | |
4ebef31f | 290 | pika_dtm_check_fan(fpga); |
f9bdedb2 SM |
291 | |
292 | set_current_state(TASK_INTERRUPTIBLE); | |
293 | schedule_timeout(HZ); | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
4ebef31f | 299 | |
f9bdedb2 SM |
300 | static int __init pika_dtm_start(void) |
301 | { | |
302 | struct task_struct *dtm_thread; | |
303 | struct device_node *np; | |
f9bdedb2 SM |
304 | |
305 | np = of_find_compatible_node(NULL, NULL, "pika,fpga"); | |
306 | if (np == NULL) | |
307 | return -ENOENT; | |
308 | ||
4ebef31f | 309 | dtm_fpga = of_iomap(np, 0); |
f9bdedb2 | 310 | of_node_put(np); |
4ebef31f | 311 | if (dtm_fpga == NULL) |
f9bdedb2 SM |
312 | return -ENOENT; |
313 | ||
4ebef31f | 314 | dtm_thread = kthread_run(pika_dtm_thread, dtm_fpga, "pika-dtm"); |
f9bdedb2 | 315 | if (IS_ERR(dtm_thread)) { |
4ebef31f | 316 | iounmap(dtm_fpga); |
f9bdedb2 SM |
317 | return PTR_ERR(dtm_thread); |
318 | } | |
319 | ||
320 | return 0; | |
321 | } | |
4ebef31f SM |
322 | machine_late_initcall(warp, pika_dtm_start); |
323 | ||
324 | #else /* !CONFIG_SENSORS_AD7414 */ | |
325 | ||
326 | int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) | |
327 | { | |
328 | return 0; | |
329 | } | |
330 | ||
331 | int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) | |
332 | { | |
333 | return 0; | |
334 | } | |
335 | ||
f9bdedb2 | 336 | #endif |
4ebef31f SM |
337 | |
338 | EXPORT_SYMBOL(pika_dtm_register_shutdown); | |
339 | EXPORT_SYMBOL(pika_dtm_unregister_shutdown); |