|
@@ -1,7 +1,7 @@
|
|
|
-/* $Id: uctrl.c,v 1.12 2001/10/08 22:19:51 davem Exp $
|
|
|
- * uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
|
|
|
+/* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
|
|
|
*
|
|
|
* Copyright 1999 Derrick J Brashear (shadow@dementia.org)
|
|
|
+ * Copyright 2008 David S. Miller (davem@davemloft.net)
|
|
|
*/
|
|
|
|
|
|
#include <linux/module.h>
|
|
@@ -14,6 +14,8 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/miscdevice.h>
|
|
|
#include <linux/mm.h>
|
|
|
+#include <linux/of.h>
|
|
|
+#include <linux/of_device.h>
|
|
|
|
|
|
#include <asm/openprom.h>
|
|
|
#include <asm/oplib.h>
|
|
@@ -21,7 +23,6 @@
|
|
|
#include <asm/irq.h>
|
|
|
#include <asm/io.h>
|
|
|
#include <asm/pgtable.h>
|
|
|
-#include <asm/sbus.h>
|
|
|
|
|
|
#define UCTRL_MINOR 174
|
|
|
|
|
@@ -33,26 +34,26 @@
|
|
|
#endif
|
|
|
|
|
|
struct uctrl_regs {
|
|
|
- volatile u32 uctrl_intr;
|
|
|
- volatile u32 uctrl_data;
|
|
|
- volatile u32 uctrl_stat;
|
|
|
- volatile u32 uctrl_xxx[5];
|
|
|
+ u32 uctrl_intr;
|
|
|
+ u32 uctrl_data;
|
|
|
+ u32 uctrl_stat;
|
|
|
+ u32 uctrl_xxx[5];
|
|
|
};
|
|
|
|
|
|
struct ts102_regs {
|
|
|
- volatile u32 card_a_intr;
|
|
|
- volatile u32 card_a_stat;
|
|
|
- volatile u32 card_a_ctrl;
|
|
|
- volatile u32 card_a_xxx;
|
|
|
- volatile u32 card_b_intr;
|
|
|
- volatile u32 card_b_stat;
|
|
|
- volatile u32 card_b_ctrl;
|
|
|
- volatile u32 card_b_xxx;
|
|
|
- volatile u32 uctrl_intr;
|
|
|
- volatile u32 uctrl_data;
|
|
|
- volatile u32 uctrl_stat;
|
|
|
- volatile u32 uctrl_xxx;
|
|
|
- volatile u32 ts102_xxx[4];
|
|
|
+ u32 card_a_intr;
|
|
|
+ u32 card_a_stat;
|
|
|
+ u32 card_a_ctrl;
|
|
|
+ u32 card_a_xxx;
|
|
|
+ u32 card_b_intr;
|
|
|
+ u32 card_b_stat;
|
|
|
+ u32 card_b_ctrl;
|
|
|
+ u32 card_b_xxx;
|
|
|
+ u32 uctrl_intr;
|
|
|
+ u32 uctrl_data;
|
|
|
+ u32 uctrl_stat;
|
|
|
+ u32 uctrl_xxx;
|
|
|
+ u32 ts102_xxx[4];
|
|
|
};
|
|
|
|
|
|
/* Bits for uctrl_intr register */
|
|
@@ -186,17 +187,15 @@ enum uctrl_opcode {
|
|
|
POWER_RESTART=0x83,
|
|
|
};
|
|
|
|
|
|
-struct uctrl_driver {
|
|
|
- struct uctrl_regs *regs;
|
|
|
+static struct uctrl_driver {
|
|
|
+ struct uctrl_regs __iomem *regs;
|
|
|
int irq;
|
|
|
int pending;
|
|
|
struct uctrl_status status;
|
|
|
-};
|
|
|
-
|
|
|
-static struct uctrl_driver drv;
|
|
|
+} *global_driver;
|
|
|
|
|
|
-static void uctrl_get_event_status(void);
|
|
|
-static void uctrl_get_external_status(void);
|
|
|
+static void uctrl_get_event_status(struct uctrl_driver *);
|
|
|
+static void uctrl_get_external_status(struct uctrl_driver *);
|
|
|
|
|
|
static int
|
|
|
uctrl_ioctl(struct inode *inode, struct file *file,
|
|
@@ -213,16 +212,14 @@ static int
|
|
|
uctrl_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
lock_kernel();
|
|
|
- uctrl_get_event_status();
|
|
|
- uctrl_get_external_status();
|
|
|
+ uctrl_get_event_status(global_driver);
|
|
|
+ uctrl_get_external_status(global_driver);
|
|
|
unlock_kernel();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static irqreturn_t uctrl_interrupt(int irq, void *dev_id)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = (struct uctrl_driver *)dev_id;
|
|
|
- printk("in uctrl_interrupt\n");
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
@@ -244,11 +241,11 @@ static struct miscdevice uctrl_dev = {
|
|
|
{ \
|
|
|
unsigned int i; \
|
|
|
for (i = 0; i < 10000; i++) { \
|
|
|
- if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \
|
|
|
+ if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \
|
|
|
break; \
|
|
|
} \
|
|
|
dprintk(("write data 0x%02x\n", value)); \
|
|
|
- driver->regs->uctrl_data = value; \
|
|
|
+ sbus_writel(value, &driver->regs->uctrl_data); \
|
|
|
}
|
|
|
|
|
|
/* Wait for something to read, read it, then clear the bit */
|
|
@@ -257,24 +254,23 @@ static struct miscdevice uctrl_dev = {
|
|
|
unsigned int i; \
|
|
|
value = 0; \
|
|
|
for (i = 0; i < 10000; i++) { \
|
|
|
- if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \
|
|
|
+ if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \
|
|
|
break; \
|
|
|
udelay(1); \
|
|
|
} \
|
|
|
- value = driver->regs->uctrl_data; \
|
|
|
+ value = sbus_readl(&driver->regs->uctrl_data); \
|
|
|
dprintk(("read data 0x%02x\n", value)); \
|
|
|
- driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \
|
|
|
+ sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \
|
|
|
}
|
|
|
|
|
|
-static void uctrl_do_txn(struct uctrl_txn *txn)
|
|
|
+static void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = &drv;
|
|
|
int stat, incnt, outcnt, bytecnt, intr;
|
|
|
u32 byte;
|
|
|
|
|
|
- stat = driver->regs->uctrl_stat;
|
|
|
- intr = driver->regs->uctrl_intr;
|
|
|
- driver->regs->uctrl_stat = stat;
|
|
|
+ stat = sbus_readl(&driver->regs->uctrl_stat);
|
|
|
+ intr = sbus_readl(&driver->regs->uctrl_intr);
|
|
|
+ sbus_writel(stat, &driver->regs->uctrl_stat);
|
|
|
|
|
|
dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr));
|
|
|
|
|
@@ -305,9 +301,8 @@ static void uctrl_do_txn(struct uctrl_txn *txn)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void uctrl_get_event_status(void)
|
|
|
+static void uctrl_get_event_status(struct uctrl_driver *driver)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = &drv;
|
|
|
struct uctrl_txn txn;
|
|
|
u8 outbits[2];
|
|
|
|
|
@@ -317,7 +312,7 @@ static void uctrl_get_event_status(void)
|
|
|
txn.inbuf = NULL;
|
|
|
txn.outbuf = outbits;
|
|
|
|
|
|
- uctrl_do_txn(&txn);
|
|
|
+ uctrl_do_txn(driver, &txn);
|
|
|
|
|
|
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
|
|
|
driver->status.event_status =
|
|
@@ -325,9 +320,8 @@ static void uctrl_get_event_status(void)
|
|
|
dprintk(("ev is %x\n", driver->status.event_status));
|
|
|
}
|
|
|
|
|
|
-static void uctrl_get_external_status(void)
|
|
|
+static void uctrl_get_external_status(struct uctrl_driver *driver)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = &drv;
|
|
|
struct uctrl_txn txn;
|
|
|
u8 outbits[2];
|
|
|
int i, v;
|
|
@@ -338,7 +332,7 @@ static void uctrl_get_external_status(void)
|
|
|
txn.inbuf = NULL;
|
|
|
txn.outbuf = outbits;
|
|
|
|
|
|
- uctrl_do_txn(&txn);
|
|
|
+ uctrl_do_txn(driver, &txn);
|
|
|
|
|
|
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
|
|
|
driver->status.external_status =
|
|
@@ -354,71 +348,101 @@ static void uctrl_get_external_status(void)
|
|
|
|
|
|
}
|
|
|
|
|
|
-static int __init ts102_uctrl_init(void)
|
|
|
+static int __devinit uctrl_probe(struct of_device *op,
|
|
|
+ const struct of_device_id *match)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = &drv;
|
|
|
- int len;
|
|
|
- struct linux_prom_irqs tmp_irq[2];
|
|
|
- unsigned int vaddr[2] = { 0, 0 };
|
|
|
- int tmpnode, uctrlnode = prom_getchild(prom_root_node);
|
|
|
- int err;
|
|
|
+ struct uctrl_driver *p;
|
|
|
+ int err = -ENOMEM;
|
|
|
|
|
|
- tmpnode = prom_searchsiblings(uctrlnode, "obio");
|
|
|
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
|
+ if (!p) {
|
|
|
+ printk(KERN_ERR "uctrl: Unable to allocate device struct.\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- if (tmpnode)
|
|
|
- uctrlnode = prom_getchild(tmpnode);
|
|
|
+ p->regs = of_ioremap(&op->resource[0], 0,
|
|
|
+ resource_size(&op->resource[0]),
|
|
|
+ "uctrl");
|
|
|
+ if (!p->regs) {
|
|
|
+ printk(KERN_ERR "uctrl: Unable to map registers.\n");
|
|
|
+ goto out_free;
|
|
|
+ }
|
|
|
|
|
|
- uctrlnode = prom_searchsiblings(uctrlnode, "uctrl");
|
|
|
+ p->irq = op->irqs[0];
|
|
|
+ err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p);
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_ERR "uctrl: Unable to register irq.\n");
|
|
|
+ goto out_iounmap;
|
|
|
+ }
|
|
|
|
|
|
- if (!uctrlnode)
|
|
|
- return -ENODEV;
|
|
|
+ err = misc_register(&uctrl_dev);
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_ERR "uctrl: Unable to register misc device.\n");
|
|
|
+ goto out_free_irq;
|
|
|
+ }
|
|
|
|
|
|
- /* the prom mapped it for us */
|
|
|
- len = prom_getproperty(uctrlnode, "address", (void *) vaddr,
|
|
|
- sizeof(vaddr));
|
|
|
- driver->regs = (struct uctrl_regs *)vaddr[0];
|
|
|
+ sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr);
|
|
|
+ printk(KERN_INFO "%s: uctrl regs[0x%p] (irq %d)\n",
|
|
|
+ op->node->full_name, p->regs, p->irq);
|
|
|
+ uctrl_get_event_status(p);
|
|
|
+ uctrl_get_external_status(p);
|
|
|
|
|
|
- len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq,
|
|
|
- sizeof(tmp_irq));
|
|
|
+ dev_set_drvdata(&op->dev, p);
|
|
|
+ global_driver = p;
|
|
|
|
|
|
- /* Flush device */
|
|
|
- READUCTLDATA(len);
|
|
|
+out:
|
|
|
+ return err;
|
|
|
|
|
|
- if(!driver->irq)
|
|
|
- driver->irq = tmp_irq[0].pri;
|
|
|
+out_free_irq:
|
|
|
+ free_irq(p->irq, p);
|
|
|
|
|
|
- err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver);
|
|
|
- if (err) {
|
|
|
- printk("%s: unable to register irq %d\n",
|
|
|
- __func__, driver->irq);
|
|
|
- return err;
|
|
|
- }
|
|
|
+out_iounmap:
|
|
|
+ of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
|
|
|
|
|
|
- if (misc_register(&uctrl_dev)) {
|
|
|
- printk("%s: unable to get misc minor %d\n",
|
|
|
- __func__, uctrl_dev.minor);
|
|
|
- free_irq(driver->irq, driver);
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
+out_free:
|
|
|
+ kfree(p);
|
|
|
+ goto out;
|
|
|
+}
|
|
|
|
|
|
- driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK;
|
|
|
- printk("uctrl: 0x%p (irq %d)\n", driver->regs, driver->irq);
|
|
|
- uctrl_get_event_status();
|
|
|
- uctrl_get_external_status();
|
|
|
- return 0;
|
|
|
+static int __devexit uctrl_remove(struct of_device *op)
|
|
|
+{
|
|
|
+ struct uctrl_driver *p = dev_get_drvdata(&op->dev);
|
|
|
+
|
|
|
+ if (p) {
|
|
|
+ misc_deregister(&uctrl_dev);
|
|
|
+ free_irq(p->irq, p);
|
|
|
+ of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
|
|
|
+ kfree(p);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-static void __exit ts102_uctrl_cleanup(void)
|
|
|
+static struct of_device_id uctrl_match[] = {
|
|
|
+ {
|
|
|
+ .name = "uctrl",
|
|
|
+ },
|
|
|
+ {},
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(of, uctrl_match);
|
|
|
+
|
|
|
+static struct of_platform_driver uctrl_driver = {
|
|
|
+ .name = "uctrl",
|
|
|
+ .match_table = uctrl_match,
|
|
|
+ .probe = uctrl_probe,
|
|
|
+ .remove = __devexit_p(uctrl_remove),
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static int __init uctrl_init(void)
|
|
|
{
|
|
|
- struct uctrl_driver *driver = &drv;
|
|
|
+ return of_register_driver(&uctrl_driver, &of_bus_type);
|
|
|
+}
|
|
|
|
|
|
- misc_deregister(&uctrl_dev);
|
|
|
- if (driver->irq)
|
|
|
- free_irq(driver->irq, driver);
|
|
|
- if (driver->regs)
|
|
|
- driver->regs = NULL;
|
|
|
+static void __exit uctrl_exit(void)
|
|
|
+{
|
|
|
+ of_unregister_driver(&uctrl_driver);
|
|
|
}
|
|
|
|
|
|
-module_init(ts102_uctrl_init);
|
|
|
-module_exit(ts102_uctrl_cleanup);
|
|
|
+module_init(uctrl_init);
|
|
|
+module_exit(uctrl_exit);
|
|
|
MODULE_LICENSE("GPL");
|