diff options
-rw-r--r-- | drivers/sbus/char/Kconfig | 2 | ||||
-rw-r--r-- | drivers/sbus/char/uctrl.c | 216 |
2 files changed, 121 insertions, 97 deletions
diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig index 3fd2bc95b514..1d5d1b4580cd 100644 --- a/drivers/sbus/char/Kconfig +++ b/drivers/sbus/char/Kconfig | |||
@@ -32,7 +32,7 @@ config OBP_FLASH | |||
32 | 32 | ||
33 | config TADPOLE_TS102_UCTRL | 33 | config TADPOLE_TS102_UCTRL |
34 | tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" | 34 | tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" |
35 | depends on EXPERIMENTAL && SPARC32 | 35 | depends on EXPERIMENTAL |
36 | help | 36 | help |
37 | Say Y here to directly support the TS102 Microcontroller interface | 37 | Say Y here to directly support the TS102 Microcontroller interface |
38 | on the Tadpole Sparcbook 3. This device handles power-management | 38 | on the Tadpole Sparcbook 3. This device handles power-management |
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 777637594acd..6cff9777bbc0 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $Id: uctrl.c,v 1.12 2001/10/08 22:19:51 davem Exp $ | 1 | /* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 |
2 | * uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 | ||
3 | * | 2 | * |
4 | * Copyright 1999 Derrick J Brashear (shadow@dementia.org) | 3 | * Copyright 1999 Derrick J Brashear (shadow@dementia.org) |
4 | * Copyright 2008 David S. Miller (davem@davemloft.net) | ||
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
@@ -14,6 +14,8 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/miscdevice.h> | 15 | #include <linux/miscdevice.h> |
16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
17 | #include <linux/of.h> | ||
18 | #include <linux/of_device.h> | ||
17 | 19 | ||
18 | #include <asm/openprom.h> | 20 | #include <asm/openprom.h> |
19 | #include <asm/oplib.h> | 21 | #include <asm/oplib.h> |
@@ -21,7 +23,6 @@ | |||
21 | #include <asm/irq.h> | 23 | #include <asm/irq.h> |
22 | #include <asm/io.h> | 24 | #include <asm/io.h> |
23 | #include <asm/pgtable.h> | 25 | #include <asm/pgtable.h> |
24 | #include <asm/sbus.h> | ||
25 | 26 | ||
26 | #define UCTRL_MINOR 174 | 27 | #define UCTRL_MINOR 174 |
27 | 28 | ||
@@ -33,26 +34,26 @@ | |||
33 | #endif | 34 | #endif |
34 | 35 | ||
35 | struct uctrl_regs { | 36 | struct uctrl_regs { |
36 | volatile u32 uctrl_intr; | 37 | u32 uctrl_intr; |
37 | volatile u32 uctrl_data; | 38 | u32 uctrl_data; |
38 | volatile u32 uctrl_stat; | 39 | u32 uctrl_stat; |
39 | volatile u32 uctrl_xxx[5]; | 40 | u32 uctrl_xxx[5]; |
40 | }; | 41 | }; |
41 | 42 | ||
42 | struct ts102_regs { | 43 | struct ts102_regs { |
43 | volatile u32 card_a_intr; | 44 | u32 card_a_intr; |
44 | volatile u32 card_a_stat; | 45 | u32 card_a_stat; |
45 | volatile u32 card_a_ctrl; | 46 | u32 card_a_ctrl; |
46 | volatile u32 card_a_xxx; | 47 | u32 card_a_xxx; |
47 | volatile u32 card_b_intr; | 48 | u32 card_b_intr; |
48 | volatile u32 card_b_stat; | 49 | u32 card_b_stat; |
49 | volatile u32 card_b_ctrl; | 50 | u32 card_b_ctrl; |
50 | volatile u32 card_b_xxx; | 51 | u32 card_b_xxx; |
51 | volatile u32 uctrl_intr; | 52 | u32 uctrl_intr; |
52 | volatile u32 uctrl_data; | 53 | u32 uctrl_data; |
53 | volatile u32 uctrl_stat; | 54 | u32 uctrl_stat; |
54 | volatile u32 uctrl_xxx; | 55 | u32 uctrl_xxx; |
55 | volatile u32 ts102_xxx[4]; | 56 | u32 ts102_xxx[4]; |
56 | }; | 57 | }; |
57 | 58 | ||
58 | /* Bits for uctrl_intr register */ | 59 | /* Bits for uctrl_intr register */ |
@@ -186,17 +187,15 @@ enum uctrl_opcode { | |||
186 | POWER_RESTART=0x83, | 187 | POWER_RESTART=0x83, |
187 | }; | 188 | }; |
188 | 189 | ||
189 | struct uctrl_driver { | 190 | static struct uctrl_driver { |
190 | struct uctrl_regs *regs; | 191 | struct uctrl_regs __iomem *regs; |
191 | int irq; | 192 | int irq; |
192 | int pending; | 193 | int pending; |
193 | struct uctrl_status status; | 194 | struct uctrl_status status; |
194 | }; | 195 | } *global_driver; |
195 | |||
196 | static struct uctrl_driver drv; | ||
197 | 196 | ||
198 | static void uctrl_get_event_status(void); | 197 | static void uctrl_get_event_status(struct uctrl_driver *); |
199 | static void uctrl_get_external_status(void); | 198 | static void uctrl_get_external_status(struct uctrl_driver *); |
200 | 199 | ||
201 | static int | 200 | static int |
202 | uctrl_ioctl(struct inode *inode, struct file *file, | 201 | uctrl_ioctl(struct inode *inode, struct file *file, |
@@ -213,16 +212,14 @@ static int | |||
213 | uctrl_open(struct inode *inode, struct file *file) | 212 | uctrl_open(struct inode *inode, struct file *file) |
214 | { | 213 | { |
215 | lock_kernel(); | 214 | lock_kernel(); |
216 | uctrl_get_event_status(); | 215 | uctrl_get_event_status(global_driver); |
217 | uctrl_get_external_status(); | 216 | uctrl_get_external_status(global_driver); |
218 | unlock_kernel(); | 217 | unlock_kernel(); |
219 | return 0; | 218 | return 0; |
220 | } | 219 | } |
221 | 220 | ||
222 | static irqreturn_t uctrl_interrupt(int irq, void *dev_id) | 221 | static irqreturn_t uctrl_interrupt(int irq, void *dev_id) |
223 | { | 222 | { |
224 | struct uctrl_driver *driver = (struct uctrl_driver *)dev_id; | ||
225 | printk("in uctrl_interrupt\n"); | ||
226 | return IRQ_HANDLED; | 223 | return IRQ_HANDLED; |
227 | } | 224 | } |
228 | 225 | ||
@@ -244,11 +241,11 @@ static struct miscdevice uctrl_dev = { | |||
244 | { \ | 241 | { \ |
245 | unsigned int i; \ | 242 | unsigned int i; \ |
246 | for (i = 0; i < 10000; i++) { \ | 243 | for (i = 0; i < 10000; i++) { \ |
247 | if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \ | 244 | if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \ |
248 | break; \ | 245 | break; \ |
249 | } \ | 246 | } \ |
250 | dprintk(("write data 0x%02x\n", value)); \ | 247 | dprintk(("write data 0x%02x\n", value)); \ |
251 | driver->regs->uctrl_data = value; \ | 248 | sbus_writel(value, &driver->regs->uctrl_data); \ |
252 | } | 249 | } |
253 | 250 | ||
254 | /* Wait for something to read, read it, then clear the bit */ | 251 | /* Wait for something to read, read it, then clear the bit */ |
@@ -257,24 +254,23 @@ static struct miscdevice uctrl_dev = { | |||
257 | unsigned int i; \ | 254 | unsigned int i; \ |
258 | value = 0; \ | 255 | value = 0; \ |
259 | for (i = 0; i < 10000; i++) { \ | 256 | for (i = 0; i < 10000; i++) { \ |
260 | if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \ | 257 | if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \ |
261 | break; \ | 258 | break; \ |
262 | udelay(1); \ | 259 | udelay(1); \ |
263 | } \ | 260 | } \ |
264 | value = driver->regs->uctrl_data; \ | 261 | value = sbus_readl(&driver->regs->uctrl_data); \ |
265 | dprintk(("read data 0x%02x\n", value)); \ | 262 | dprintk(("read data 0x%02x\n", value)); \ |
266 | driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \ | 263 | sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \ |
267 | } | 264 | } |
268 | 265 | ||
269 | static void uctrl_do_txn(struct uctrl_txn *txn) | 266 | static void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn) |
270 | { | 267 | { |
271 | struct uctrl_driver *driver = &drv; | ||
272 | int stat, incnt, outcnt, bytecnt, intr; | 268 | int stat, incnt, outcnt, bytecnt, intr; |
273 | u32 byte; | 269 | u32 byte; |
274 | 270 | ||
275 | stat = driver->regs->uctrl_stat; | 271 | stat = sbus_readl(&driver->regs->uctrl_stat); |
276 | intr = driver->regs->uctrl_intr; | 272 | intr = sbus_readl(&driver->regs->uctrl_intr); |
277 | driver->regs->uctrl_stat = stat; | 273 | sbus_writel(stat, &driver->regs->uctrl_stat); |
278 | 274 | ||
279 | dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); | 275 | dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); |
280 | 276 | ||
@@ -305,9 +301,8 @@ static void uctrl_do_txn(struct uctrl_txn *txn) | |||
305 | } | 301 | } |
306 | } | 302 | } |
307 | 303 | ||
308 | static void uctrl_get_event_status(void) | 304 | static void uctrl_get_event_status(struct uctrl_driver *driver) |
309 | { | 305 | { |
310 | struct uctrl_driver *driver = &drv; | ||
311 | struct uctrl_txn txn; | 306 | struct uctrl_txn txn; |
312 | u8 outbits[2]; | 307 | u8 outbits[2]; |
313 | 308 | ||
@@ -317,7 +312,7 @@ static void uctrl_get_event_status(void) | |||
317 | txn.inbuf = NULL; | 312 | txn.inbuf = NULL; |
318 | txn.outbuf = outbits; | 313 | txn.outbuf = outbits; |
319 | 314 | ||
320 | uctrl_do_txn(&txn); | 315 | uctrl_do_txn(driver, &txn); |
321 | 316 | ||
322 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); | 317 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); |
323 | driver->status.event_status = | 318 | driver->status.event_status = |
@@ -325,9 +320,8 @@ static void uctrl_get_event_status(void) | |||
325 | dprintk(("ev is %x\n", driver->status.event_status)); | 320 | dprintk(("ev is %x\n", driver->status.event_status)); |
326 | } | 321 | } |
327 | 322 | ||
328 | static void uctrl_get_external_status(void) | 323 | static void uctrl_get_external_status(struct uctrl_driver *driver) |
329 | { | 324 | { |
330 | struct uctrl_driver *driver = &drv; | ||
331 | struct uctrl_txn txn; | 325 | struct uctrl_txn txn; |
332 | u8 outbits[2]; | 326 | u8 outbits[2]; |
333 | int i, v; | 327 | int i, v; |
@@ -338,7 +332,7 @@ static void uctrl_get_external_status(void) | |||
338 | txn.inbuf = NULL; | 332 | txn.inbuf = NULL; |
339 | txn.outbuf = outbits; | 333 | txn.outbuf = outbits; |
340 | 334 | ||
341 | uctrl_do_txn(&txn); | 335 | uctrl_do_txn(driver, &txn); |
342 | 336 | ||
343 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); | 337 | dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); |
344 | driver->status.external_status = | 338 | driver->status.external_status = |
@@ -354,71 +348,101 @@ static void uctrl_get_external_status(void) | |||
354 | 348 | ||
355 | } | 349 | } |
356 | 350 | ||
357 | static int __init ts102_uctrl_init(void) | 351 | static int __devinit uctrl_probe(struct of_device *op, |
352 | const struct of_device_id *match) | ||
358 | { | 353 | { |
359 | struct uctrl_driver *driver = &drv; | 354 | struct uctrl_driver *p; |
360 | int len; | 355 | int err = -ENOMEM; |
361 | struct linux_prom_irqs tmp_irq[2]; | ||
362 | unsigned int vaddr[2] = { 0, 0 }; | ||
363 | int tmpnode, uctrlnode = prom_getchild(prom_root_node); | ||
364 | int err; | ||
365 | 356 | ||
366 | tmpnode = prom_searchsiblings(uctrlnode, "obio"); | 357 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
358 | if (!p) { | ||
359 | printk(KERN_ERR "uctrl: Unable to allocate device struct.\n"); | ||
360 | goto out; | ||
361 | } | ||
367 | 362 | ||
368 | if (tmpnode) | 363 | p->regs = of_ioremap(&op->resource[0], 0, |
369 | uctrlnode = prom_getchild(tmpnode); | 364 | resource_size(&op->resource[0]), |
365 | "uctrl"); | ||
366 | if (!p->regs) { | ||
367 | printk(KERN_ERR "uctrl: Unable to map registers.\n"); | ||
368 | goto out_free; | ||
369 | } | ||
370 | 370 | ||
371 | uctrlnode = prom_searchsiblings(uctrlnode, "uctrl"); | 371 | p->irq = op->irqs[0]; |
372 | err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p); | ||
373 | if (err) { | ||
374 | printk(KERN_ERR "uctrl: Unable to register irq.\n"); | ||
375 | goto out_iounmap; | ||
376 | } | ||
372 | 377 | ||
373 | if (!uctrlnode) | 378 | err = misc_register(&uctrl_dev); |
374 | return -ENODEV; | 379 | if (err) { |
380 | printk(KERN_ERR "uctrl: Unable to register misc device.\n"); | ||
381 | goto out_free_irq; | ||
382 | } | ||
375 | 383 | ||
376 | /* the prom mapped it for us */ | 384 | sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr); |
377 | len = prom_getproperty(uctrlnode, "address", (void *) vaddr, | 385 | printk(KERN_INFO "%s: uctrl regs[0x%p] (irq %d)\n", |
378 | sizeof(vaddr)); | 386 | op->node->full_name, p->regs, p->irq); |
379 | driver->regs = (struct uctrl_regs *)vaddr[0]; | 387 | uctrl_get_event_status(p); |
388 | uctrl_get_external_status(p); | ||
380 | 389 | ||
381 | len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq, | 390 | dev_set_drvdata(&op->dev, p); |
382 | sizeof(tmp_irq)); | 391 | global_driver = p; |
383 | 392 | ||
384 | /* Flush device */ | 393 | out: |
385 | READUCTLDATA(len); | 394 | return err; |
386 | 395 | ||
387 | if(!driver->irq) | 396 | out_free_irq: |
388 | driver->irq = tmp_irq[0].pri; | 397 | free_irq(p->irq, p); |
389 | 398 | ||
390 | err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); | 399 | out_iounmap: |
391 | if (err) { | 400 | of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); |
392 | printk("%s: unable to register irq %d\n", | ||
393 | __func__, driver->irq); | ||
394 | return err; | ||
395 | } | ||
396 | 401 | ||
397 | if (misc_register(&uctrl_dev)) { | 402 | out_free: |
398 | printk("%s: unable to get misc minor %d\n", | 403 | kfree(p); |
399 | __func__, uctrl_dev.minor); | 404 | goto out; |
400 | free_irq(driver->irq, driver); | 405 | } |
401 | return -ENODEV; | ||
402 | } | ||
403 | 406 | ||
404 | driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK; | 407 | static int __devexit uctrl_remove(struct of_device *op) |
405 | printk("uctrl: 0x%p (irq %d)\n", driver->regs, driver->irq); | 408 | { |
406 | uctrl_get_event_status(); | 409 | struct uctrl_driver *p = dev_get_drvdata(&op->dev); |
407 | uctrl_get_external_status(); | 410 | |
408 | return 0; | 411 | if (p) { |
412 | misc_deregister(&uctrl_dev); | ||
413 | free_irq(p->irq, p); | ||
414 | of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); | ||
415 | kfree(p); | ||
416 | } | ||
417 | return 0; | ||
409 | } | 418 | } |
410 | 419 | ||
411 | static void __exit ts102_uctrl_cleanup(void) | 420 | static struct of_device_id uctrl_match[] = { |
421 | { | ||
422 | .name = "uctrl", | ||
423 | }, | ||
424 | {}, | ||
425 | }; | ||
426 | MODULE_DEVICE_TABLE(of, uctrl_match); | ||
427 | |||
428 | static struct of_platform_driver uctrl_driver = { | ||
429 | .name = "uctrl", | ||
430 | .match_table = uctrl_match, | ||
431 | .probe = uctrl_probe, | ||
432 | .remove = __devexit_p(uctrl_remove), | ||
433 | }; | ||
434 | |||
435 | |||
436 | static int __init uctrl_init(void) | ||
412 | { | 437 | { |
413 | struct uctrl_driver *driver = &drv; | 438 | return of_register_driver(&uctrl_driver, &of_bus_type); |
439 | } | ||
414 | 440 | ||
415 | misc_deregister(&uctrl_dev); | 441 | static void __exit uctrl_exit(void) |
416 | if (driver->irq) | 442 | { |
417 | free_irq(driver->irq, driver); | 443 | of_unregister_driver(&uctrl_driver); |
418 | if (driver->regs) | ||
419 | driver->regs = NULL; | ||
420 | } | 444 | } |
421 | 445 | ||
422 | module_init(ts102_uctrl_init); | 446 | module_init(uctrl_init); |
423 | module_exit(ts102_uctrl_cleanup); | 447 | module_exit(uctrl_exit); |
424 | MODULE_LICENSE("GPL"); | 448 | MODULE_LICENSE("GPL"); |