diff options
Diffstat (limited to 'drivers/mfd/hd64461.c')
-rw-r--r-- | drivers/mfd/hd64461.c | 225 |
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"); |