aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/hd64461.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/hd64461.c')
-rw-r--r--drivers/mfd/hd64461.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/mfd/hd64461.c b/drivers/mfd/hd64461.c
new file mode 100644
index 00000000000000..65cf4fa89a6242
--- /dev/null
+++ b/drivers/mfd/hd64461.c
@@ -0,0 +1,225 @@
+ /*
+ *
+ * MFD driver for the Hitachi HD64461 companion chip
+ *
+ * HD64461 chip contains 8 interrupts handled by an demuxer
+ * They control pcmcia, compact flash,
+ *
+ * (C) 2008 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ * Based on hd64461.c (C) 2000 YAEGASHI Takeshi
+ */
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include "hd64461.h"
+
+struct hd64461_info {
+ spinlock_t lock;
+
+ void __iomem *base_addr;
+ int base_irq;
+ int irq_end;
+};
+
+/* IO FUNCTIONS */
+void hd64461_chip_writew(u16 value, struct hd64461_info *hd64461, int reg)
+{
+ unsigned long addr = hd64461->base_addr;
+
+ addr += reg;
+
+ iowritew(value, addr);
+}
+EXPORT_SYMBOL(hd64461_chip_writew);
+
+void hd64461_chip_writeb(u8 value, struct hd64461_info *hd64461, int reg)
+{
+ unsigned long addr = hd64461->base_addr;
+
+ addr += reg;
+
+ iowriteb(value, addr);
+}
+EXPORT_SYMBOL(hd64461_chip_writeb);
+
+u16 hd64461_chip_readw(struct hd64461_info *hd64461, int reg)
+{
+ return(ioreadw(hd64461->base_addr + reg));
+}
+EXPORT_SYMBOL(hd64461_chip_readw);
+
+u8 hd64461_chip_readb(struct hd64461_info *hd64461, int reg)
+{
+ return(ioreadb(hd64461->base_addr + reg));
+}
+EXPORT_SYMBOL(hd64461_chip_readb);
+
+/* IRQ DEMUXER */
+static void hd64461_irq_disable(unsigned int irq)
+{
+ struct hd64461_info *hd64461 = get_irq_chip_data(irq);
+ unsigned int nimr;
+ unsigned short mask = (1 << (irq - (hd64461->base_irq)));
+
+ nimr = hd64461_chip_readw(hd64461, HD64461_NIMR);
+ nimr |= mask;
+ hd64461_chip_writew(nimr, hd64461, HD64461_NIMR);
+}
+
+static void hd64461_irq_mask_ack(unsigned int irq)
+{
+ hd64461_irq_disable(irq);
+}
+
+static void hd64461_irq_unmask(unsigned int irq)
+{
+ struct hd64461_info *hd64461 = get_irq_chip_data(irq);
+ unsigned int nimr;
+ unsigned short mask = 1 << (irq - (hd64461->base_irq));
+ nimr &= ~mask;
+ hd64461_chip_writew(nimr, hd64461, HD64461_NIMR);
+}
+
+static void hd64461_demux(unsigned int irq, struct irq_desc *desc)
+{
+ struct hd64461_info *hd64461 = get_irq_data(irq);
+ unsigned short bit;
+ unsigned int nirr, nimr, i;
+
+ nirr = hd64461_chip_readw(hd64461, HD64461_NIRR);
+ nimr = hd64461_chip_readw(hd64461, HD64461_NIMR);
+
+ nirr &= ~nimr;
+
+ /* What irq is causing our interrupt? */
+ for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
+ if (nirr & bit)
+ break;
+
+ /* Lets jump into the correct handler */
+ desc = &irq_desc[(nirr + i)];
+ desc->handle_irq((nirr + 1), desc);
+}
+
+static irqreturn_t hd64461_irq_interrupt(int irq, void *dev_id)
+{
+ struct hd64461_info *hd64461 = get_irq_chip_data(irq);
+
+
+
+static struct irq_chip hd64461_chip = {
+ .name = "hd64461",
+ .ack = hd64461_irq_mask_ack,
+ .mask = hd64461_irq_mask_ack,
+ .unmask = hd64461_irq_unmask,
+ .end = hd64461_irq_end,
+};
+
+static struct irqaction hd64461_irq = {
+ .name = "hd64461_irq",
+ .handler = hd64461_irq_interrupt,
+ .mask = CPU_MASK_NONE,
+ .flags = IRQF_DISABLED,
+};
+
+static int __init hd64461_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct hd64461_info *hd64461;
+
+ int irq;
+ u16 status_data,v;
+
+ /* Allocate our memory */
+ hd64461 = kzalloc(size(*hd64461), GFP_KERNEL);
+ if (!hd64461) {
+ goto fault_0;
+ }
+
+ /* We get our base IRQ from the driver resource */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ /* got nothing, so just bail */
+ if (!res)
+ goto fault_1;
+
+ /* usually 16 IRQ's */
+ hd64461->base_irq = res->start;
+ hd64461->irq_end = res->end;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* got nothing, so just bail */
+ if (!res)
+ goto fault_1;
+
+ /* lets reserve our area's */
+ hd64461->base_addr = ioremap(res->start, (res->end - res->start) - 1);
+
+ platform_set_drvdata(pdev, hd64461);
+
+ /* ack/mask all interrupt sources */
+ hd64461_chip_writeb(0xffff, hd64461, HD64461_NIMR);
+
+ /* make sure all hd64461 interrupts are handled by us */
+ for (irq = hd64461->base_irq; (irq < hd64461->irq_end); irq++) {
+ set_irq_chip(irq, &hd64461_chip);
+ set_irq_chip_data(irq, hd64461);
+ }
+
+ /* now lets set the base_irq handler (which everything runs through) */
+ set_irq_type(hd64461->base_irq, IRQF_DISABLED);
+ set_irq_data(hd64461->base_irq, hd64461);
+ setup_irq(hd64461->base_irq, &hd64461_irq);
+
+ printk(KERN_INFO "hd64461 configured at 0x%x on base irq %d (demux irq %d - %d)\n",
+ hd64461->base_addr, hd64461->base_irq, hd64461->base_irq, hd64461->irq_end);
+
+fault_0:
+ printk(KERN_ERR "Unable to aqcuire memory\n");
+ return -ENOMEM;
+
+fault_1:
+ printk(KERN_ERR "Unable to get resources\n");
+ return -ENODEV;
+}
+
+static int __exit hd64461_remove(struct platform_device *pdev)
+{
+
+return 0;
+}
+
+static struct platform_driver hd64461_driver = {
+ .driver = {
+ .name = "hd64461",
+ },
+ .remove = __exit_p(hd64461_remove),
+};
+
+static int __init hd64461_init(void)
+{
+ return platform_driver_probe(&hd64461_driver, hd64461_probe);
+}
+
+static void __exit hd64461_exit(void)
+{
+ platform_driver_unregister(&hd64461_driver);
+}
+module_init(hd64461_init)
+module_exit(hd64461_exit)
+
+MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
+MODULE_DESCRIPTION("Core Driver for HD64461");
+MODULE_LICENSE("GPL");