diff options
67 files changed, 3716 insertions, 3080 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4b15d91..4ade2d8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -759,6 +759,7 @@ config ARCH_SA1100 select ARCH_REQUIRE_GPIOLIB select HAVE_IDE select NEED_MACH_MEMORY_H + select SPARSE_IRQ help Support for StrongARM 11x0 based boards. diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index 61691cd..9173d11 100644 --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -16,6 +16,7 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/errno.h> @@ -28,9 +29,8 @@ #include <linux/io.h> #include <mach/hardware.h> -#include <asm/mach-types.h> -#include <asm/irq.h> #include <asm/mach/irq.h> +#include <asm/mach-types.h> #include <asm/sizes.h> #include <asm/hardware/sa1111.h> @@ -86,8 +86,10 @@ #define IRQ_S1_CD_VALID (52) #define IRQ_S0_BVD1_STSCHG (53) #define IRQ_S1_BVD1_STSCHG (54) +#define SA1111_IRQ_NR (55) -extern void __init sa1110_mb_enable(void); +extern void sa1110_mb_enable(void); +extern void sa1110_mb_disable(void); /* * We keep the following data for the overall SA1111. Note that the @@ -104,6 +106,7 @@ struct sa1111 { int irq_base; /* base for cascaded on-chip IRQs */ spinlock_t lock; void __iomem *base; + struct sa1111_platform_data *pdata; #ifdef CONFIG_PM void *saved_state; #endif @@ -118,6 +121,7 @@ static struct sa1111 *g_sa1111; struct sa1111_dev_info { unsigned long offset; unsigned long skpcr_mask; + bool dma; unsigned int devid; unsigned int irq[6]; }; @@ -126,6 +130,7 @@ static struct sa1111_dev_info sa1111_devices[] = { { .offset = SA1111_USB, .skpcr_mask = SKPCR_UCLKEN, + .dma = true, .devid = SA1111_DEVID_USB, .irq = { IRQ_USBPWR, @@ -139,6 +144,7 @@ static struct sa1111_dev_info sa1111_devices[] = { { .offset = 0x0600, .skpcr_mask = SKPCR_I2SCLKEN | SKPCR_L3CLKEN, + .dma = true, .devid = SA1111_DEVID_SAC, .irq = { AUDXMTDMADONEA, @@ -155,7 +161,7 @@ static struct sa1111_dev_info sa1111_devices[] = { { .offset = SA1111_KBD, .skpcr_mask = SKPCR_PTCLKEN, - .devid = SA1111_DEVID_PS2, + .devid = SA1111_DEVID_PS2_KBD, .irq = { IRQ_TPRXINT, IRQ_TPTXINT @@ -164,7 +170,7 @@ static struct sa1111_dev_info sa1111_devices[] = { { .offset = SA1111_MSE, .skpcr_mask = SKPCR_PMCLKEN, - .devid = SA1111_DEVID_PS2, + .devid = SA1111_DEVID_PS2_MSE, .irq = { IRQ_MSRXINT, IRQ_MSTXINT @@ -434,16 +440,28 @@ static struct irq_chip sa1111_high_chip = { .irq_set_wake = sa1111_wake_highirq, }; -static void sa1111_setup_irq(struct sa1111 *sachip) +static int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base) { void __iomem *irqbase = sachip->base + SA1111_INTC; - unsigned int irq; + unsigned i, irq; + int ret; /* * We're guaranteed that this region hasn't been taken. */ request_mem_region(sachip->phys + SA1111_INTC, 512, "irq"); + ret = irq_alloc_descs(-1, irq_base, SA1111_IRQ_NR, -1); + if (ret <= 0) { + dev_err(sachip->dev, "unable to allocate %u irqs: %d\n", + SA1111_IRQ_NR, ret); + if (ret == 0) + ret = -EINVAL; + return ret; + } + + sachip->irq_base = ret; + /* disable all IRQs */ sa1111_writel(0, irqbase + SA1111_INTEN0); sa1111_writel(0, irqbase + SA1111_INTEN1); @@ -463,14 +481,16 @@ static void sa1111_setup_irq(struct sa1111 *sachip) sa1111_writel(~0, irqbase + SA1111_INTSTATCLR0); sa1111_writel(~0, irqbase + SA1111_INTSTATCLR1); - for (irq = IRQ_GPAIN0; irq <= SSPROR; irq++) { + for (i = IRQ_GPAIN0; i <= SSPROR; i++) { + irq = sachip->irq_base + i; irq_set_chip_and_handler(irq, &sa1111_low_chip, handle_edge_irq); irq_set_chip_data(irq, sachip); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - for (irq = AUDXMTDMADONEA; irq <= IRQ_S1_BVD1_STSCHG; irq++) { + for (i = AUDXMTDMADONEA; i <= IRQ_S1_BVD1_STSCHG; i++) { + irq = sachip->irq_base + i; irq_set_chip_and_handler(irq, &sa1111_high_chip, handle_edge_irq); irq_set_chip_data(irq, sachip); @@ -483,6 +503,11 @@ static void sa1111_setup_irq(struct sa1111 *sachip) irq_set_irq_type(sachip->irq, IRQ_TYPE_EDGE_RISING); irq_set_handler_data(sachip->irq, sachip); irq_set_chained_handler(sachip->irq, sa1111_irq_handler); + + dev_info(sachip->dev, "Providing IRQ%u-%u\n", + sachip->irq_base, sachip->irq_base + SA1111_IRQ_NR - 1); + + return 0; } /* @@ -581,41 +606,10 @@ sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac, } #endif -#ifdef CONFIG_DMABOUNCE -/* - * According to the "Intel StrongARM SA-1111 Microprocessor Companion - * Chip Specification Update" (June 2000), erratum #7, there is a - * significant bug in the SA1111 SDRAM shared memory controller. If - * an access to a region of memory above 1MB relative to the bank base, - * it is important that address bit 10 _NOT_ be asserted. Depending - * on the configuration of the RAM, bit 10 may correspond to one - * of several different (processor-relative) address bits. - * - * This routine only identifies whether or not a given DMA address - * is susceptible to the bug. - * - * This should only get called for sa1111_device types due to the - * way we configure our device dma_masks. - */ -static int sa1111_needs_bounce(struct device *dev, dma_addr_t addr, size_t size) -{ - /* - * Section 4.6 of the "Intel StrongARM SA-1111 Development Module - * User's Guide" mentions that jumpers R51 and R52 control the - * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or - * SDRAM bank 1 on Neponset). The default configuration selects - * Assabet, so any address in bank 1 is necessarily invalid. - */ - return (machine_is_assabet() || machine_is_pfs168()) && - (addr >= 0xc8000000 || (addr + size) >= 0xc8000000); -} -#endif - static void sa1111_dev_release(struct device *_dev) { struct sa1111_dev *dev = SA1111_DEV(_dev); - release_resource(&dev->res); kfree(dev); } @@ -624,67 +618,58 @@ sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent, struct sa1111_dev_info *info) { struct sa1111_dev *dev; + unsigned i; int ret; dev = kzalloc(sizeof(struct sa1111_dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; - goto out; + goto err_alloc; } + device_initialize(&dev->dev); dev_set_name(&dev->dev, "%4.4lx", info->offset); dev->devid = info->devid; dev->dev.parent = sachip->dev; dev->dev.bus = &sa1111_bus_type; dev->dev.release = sa1111_dev_release; - dev->dev.coherent_dma_mask = sachip->dev->coherent_dma_mask; dev->res.start = sachip->phys + info->offset; dev->res.end = dev->res.start + 511; dev->res.name = dev_name(&dev->dev); dev->res.flags = IORESOURCE_MEM; dev->mapbase = sachip->base + info->offset; dev->skpcr_mask = info->skpcr_mask; - memmove(dev->irq, info->irq, sizeof(dev->irq)); - - ret = request_resource(parent, &dev->res); - if (ret) { - printk("SA1111: failed to allocate resource for %s\n", - dev->res.name); - dev_set_name(&dev->dev, NULL); - kfree(dev); - goto out; - } - - ret = device_register(&dev->dev); - if (ret) { - release_resource(&dev->res); - kfree(dev); - goto out; - } + for (i = 0; i < ARRAY_SIZE(info->irq); i++) + dev->irq[i] = sachip->irq_base + info->irq[i]; -#ifdef CONFIG_DMABOUNCE /* - * If the parent device has a DMA mask associated with it, - * propagate it down to the children. + * If the parent device has a DMA mask associated with it, and + * this child supports DMA, propagate it down to the children. */ - if (sachip->dev->dma_mask) { + if (info->dma && sachip->dev->dma_mask) { dev->dma_mask = *sachip->dev->dma_mask; dev->dev.dma_mask = &dev->dma_mask; + dev->dev.coherent_dma_mask = sachip->dev->coherent_dma_mask; + } - if (dev->dma_mask != 0xffffffffUL) { - ret = dmabounce_register_dev(&dev->dev, 1024, 4096, - sa1111_needs_bounce); - if (ret) { - dev_err(&dev->dev, "SA1111: Failed to register" - " with dmabounce\n"); - device_unregister(&dev->dev); - } - } + ret = request_resource(parent, &dev->res); + if (ret) { + dev_err(sachip->dev, "failed to allocate resource for %s\n", + dev->res.name); + goto err_resource; } -#endif -out: + ret = device_add(&dev->dev); + if (ret) + goto err_add; + return 0; + + err_add: + release_resource(&dev->res); + err_resource: + put_device(&dev->dev); + err_alloc: return ret; } @@ -698,16 +683,21 @@ out: * Returns: * %-ENODEV device not found. * %-EBUSY physical address already marked in-use. + * %-EINVAL no platform data passed * %0 successful. */ static int __devinit __sa1111_probe(struct device *me, struct resource *mem, int irq) { + struct sa1111_platform_data *pd = me->platform_data; struct sa1111 *sachip; unsigned long id; unsigned int has_devs; int i, ret = -ENODEV; + if (!pd) + return -EINVAL; + sachip = kzalloc(sizeof(struct sa1111), GFP_KERNEL); if (!sachip) return -ENOMEM; @@ -727,6 +717,7 @@ __sa1111_probe(struct device *me, struct resource *mem, int irq) sachip->dev = me; dev_set_drvdata(sachip->dev, sachip); + sachip->pdata = pd; sachip->phys = mem->start; sachip->irq = irq; @@ -759,6 +750,16 @@ __sa1111_probe(struct device *me, struct resource *mem, int irq) */ sa1111_wake(sachip); + /* + * The interrupt controller must be initialised before any + * other device to ensure that the interrupts are available. + */ + if (sachip->irq != NO_IRQ) { + ret = sa1111_setup_irq(sachip, pd->irq_base); + if (ret) + goto err_unmap; + } + #ifdef CONFIG_ARCH_SA1100 { unsigned int val; @@ -789,24 +790,14 @@ __sa1111_probe(struct device *me, struct resource *mem, int irq) } #endif - /* - * The interrupt controller must be initialised before any - * other device to ensure that the interrupts are available. - */ - if (sachip->irq != NO_IRQ) - sa1111_setup_irq(sachip); - g_sa1111 = sachip; has_devs = ~0; - if (machine_is_assabet() || machine_is_jornada720() || - machine_is_badge4()) - has_devs &= ~(1 << 4); - else - has_devs &= ~(1 << 1); + if (pd) + has_devs &= ~pd->disable_devs; for (i = 0; i < ARRAY_SIZE(sa1111_devices); i++) - if (has_devs & (1 << i)) + if (sa1111_devices[i].devid & has_devs) sa1111_init_one_child(sachip, mem, &sa1111_devices[i]); return 0; @@ -824,7 +815,10 @@ __sa1111_probe(struct device *me, struct resource *mem, int irq) static int sa1111_remove_one(struct device *dev, void *data) { - device_unregister(dev); + struct sa1111_dev *sadev = SA1111_DEV(dev); + device_del(&sadev->dev); + release_resource(&sadev->res); + put_device(&sadev->dev); return 0; } @@ -846,6 +840,7 @@ static void __sa1111_remove(struct sa1111 *sachip) if (sachip->irq != NO_IRQ) { irq_set_chained_handler(sachip->irq, NULL); irq_set_handler_data(sachip->irq, NULL); + irq_free_descs(sachip->irq_base, SA1111_IRQ_NR); release_mem_region(sachip->phys + SA1111_INTC, 512); } @@ -904,6 +899,9 @@ static int sa1111_suspend(struct platform_device *dev, pm_message_t state) save->skpwm0 = sa1111_readl(base + SA1111_SKPWM0); save->skpwm1 = sa1111_readl(base + SA1111_SKPWM1); + sa1111_writel(0, sachip->base + SA1111_SKPWM0); + sa1111_writel(0, sachip->base + SA1111_SKPWM1); + base = sachip->base + SA1111_INTC; save->intpol0 = sa1111_readl(base + SA1111_INTPOL0); save->intpol1 = sa1111_readl(base + SA1111_INTPOL1); @@ -919,13 +917,15 @@ static int sa1111_suspend(struct platform_device *dev, pm_message_t state) */ val = sa1111_readl(sachip->base + SA1111_SKCR); sa1111_writel(val | SKCR_SLEEP, sachip->base + SA1111_SKCR); - sa1111_writel(0, sachip->base + SA1111_SKPWM0); - sa1111_writel(0, sachip->base + SA1111_SKPWM1); clk_disable(sachip->clk); spin_unlock_irqrestore(&sachip->lock, flags); +#ifdef CONFIG_ARCH_SA1100 + sa1110_mb_disable(); +#endif + return 0; } @@ -966,6 +966,11 @@ static int sa1111_resume(struct platform_device *dev) */ sa1111_wake(sachip); +#ifdef CONFIG_ARCH_SA1100 + /* Enable the memory bus request/grant signals */ + sa1110_mb_enable(); +#endif + /* * Only lock for write ops. Also, sa1111_wake must be called with * released spinlock! @@ -1053,6 +1058,7 @@ static struct platform_driver sa1111_device_driver = { .resume = sa1111_resume, .driver = { .name = "sa1111", + .owner = THIS_MODULE, }, }; @@ -1238,16 +1244,23 @@ EXPORT_SYMBOL(sa1111_set_sleep_io); * sa1111_enable_device - enable an on-chip SA1111 function block * @sadev: SA1111 function block device to enable */ -void sa1111_enable_device(struct sa1111_dev *sadev) +int sa1111_enable_device(struct sa1111_dev *sadev) { struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; + int ret = 0; - spin_lock_irqsave(&sachip->lock, flags); - val = sa1111_readl(sachip->base + SA1111_SKPCR); - sa1111_writel(val | sadev->skpcr_mask, sachip->base + SA1111_SKPCR); - spin_unlock_irqrestore(&sachip->lock, flags); + if (sachip->pdata && sachip->pdata->enable) + ret = sachip->pdata->enable(sachip->pdata->data, sadev->devid); + + if (ret == 0) { + spin_lock_irqsave(&sachip->lock, flags); + val = sa1111_readl(sachip->base + SA1111_SKPCR); + sa1111_writel(val | sadev->skpcr_mask, sachip->base + SA1111_SKPCR); + spin_unlock_irqrestore(&sachip->lock, flags); + } + return ret; } EXPORT_SYMBOL(sa1111_enable_device); @@ -1265,6 +1278,9 @@ void sa1111_disable_device(struct sa1111_dev *sadev) val = sa1111_readl(sachip->base + SA1111_SKPCR); sa1111_writel(val & ~sadev->skpcr_mask, sachip->base + SA1111_SKPCR); spin_unlock_irqrestore(&sachip->lock, flags); + + if (sachip->pdata && sachip->pdata->disable) + sachip->pdata->disable(sachip->pdata->data, sadev->devid); } EXPORT_SYMBOL(sa1111_disable_device); @@ -1279,7 +1295,7 @@ static int sa1111_match(struct device *_dev, struct device_driver *_drv) struct sa1111_dev *dev = SA1111_DEV(_dev); struct sa1111_driver *drv = SA1111_DRV(_drv); - return dev->devid == drv->devid; + return dev->devid & drv->devid; } static int sa1111_bus_suspend(struct device *dev, pm_message_t state) @@ -1304,6 +1320,14 @@ static int sa1111_bus_resume(struct device *dev) return ret; } +static void sa1111_bus_shutdown(struct device *dev) +{ + struct sa1111_driver *drv = SA1111_DRV(dev->driver); + + if (drv && drv->shutdown) + drv->shutdown(SA1111_DEV(dev)); +} + static int sa1111_bus_probe(struct device *dev) { struct sa1111_dev *sadev = SA1111_DEV(dev); @@ -1333,6 +1357,7 @@ struct bus_type sa1111_bus_type = { .remove = sa1111_bus_remove, .suspend = sa1111_bus_suspend, .resume = sa1111_bus_resume, + .shutdown = sa1111_bus_shutdown, }; EXPORT_SYMBOL(sa1111_bus_type); @@ -1349,9 +1374,70 @@ void sa1111_driver_unregister(struct sa1111_driver *driver) } EXPORT_SYMBOL(sa1111_driver_unregister); +#ifdef CONFIG_DMABOUNCE +/* + * According to the "Intel StrongARM SA-1111 Microprocessor Companion + * Chip Specification Update" (June 2000), erratum #7, there is a + * significant bug in the SA1111 SDRAM shared memory controller. If + * an access to a region of memory above 1MB relative to the bank base, + * it is important that address bit 10 _NOT_ be asserted. Depending + * on the configuration of the RAM, bit 10 may correspond to one + * of several different (processor-relative) address bits. + * + * This routine only identifies whether or not a given DMA address + * is susceptible to the bug. + * + * This should only get called for sa1111_device types due to the + * way we configure our device dma_masks. + */ +static int sa1111_needs_bounce(struct device *dev, dma_addr_t addr, size_t size) +{ + /* + * Section 4.6 of the "Intel StrongARM SA-1111 Development Module + * User's Guide" mentions that jumpers R51 and R52 control the + * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or + * SDRAM bank 1 on Neponset). The default configuration selects + * Assabet, so any address in bank 1 is necessarily invalid. + */ + return (machine_is_assabet() || machine_is_pfs168()) && + (addr >= 0xc8000000 || (addr + size) >= 0xc8000000); +} + +static int sa1111_notifier_call(struct notifier_block *n, unsigned long action, + void *data) +{ + struct sa1111_dev *dev = SA1111_DEV(data); + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + if (dev->dev.dma_mask && dev->dma_mask < 0xffffffffUL) { + int ret = dmabounce_register_dev(&dev->dev, 1024, 4096, + sa1111_needs_bounce); + if (ret) + dev_err(&dev->dev, "failed to register with dmabounce: %d\n", ret); + } + break; + + case BUS_NOTIFY_DEL_DEVICE: + if (dev->dev.dma_mask && dev->dma_mask < 0xffffffffUL) + dmabounce_unregister_dev(&dev->dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block sa1111_bus_notifier = { + .notifier_call = sa1111_notifier_call, +}; +#endif + static int __init sa1111_init(void) { int ret = bus_register(&sa1111_bus_type); +#ifdef CONFIG_DMABOUNCE + if (ret == 0) + bus_register_notifier(&sa1111_bus_type, &sa1111_bus_notifier); +#endif if (ret == 0) platform_driver_register(&sa1111_device_driver); return ret; @@ -1360,6 +1446,9 @@ static int __init sa1111_init(void) static void __exit sa1111_exit(void) { platform_driver_unregister(&sa1111_device_driver); +#ifdef CONFIG_DMABOUNCE + bus_unregister_notifier(&sa1111_bus_type, &sa1111_bus_notifier); +#endif bus_unregister(&sa1111_bus_type); } diff --git a/arch/arm/include/asm/hardware/sa1111.h b/arch/arm/include/asm/hardware/sa1111.h index 92ed254..7c2bbc7 100644 --- a/arch/arm/include/asm/hardware/sa1111.h +++ b/arch/arm/include/asm/hardware/sa1111.h @@ -132,34 +132,10 @@ #define SKPCR_DCLKEN (1<<7) #define SKPCR_PWMCLKEN (1<<8) -/* - * USB Host controller - */ +/* USB Host controller */ #define SA1111_USB 0x0400 /* - * Offsets from SA1111_USB_BASE - */ -#define SA1111_USB_STATUS 0x0118 -#define SA1111_USB_RESET 0x011c -#define SA1111_USB_IRQTEST 0x0120 - -#define USB_RESET_FORCEIFRESET (1 << 0) -#define USB_RESET_FORCEHCRESET (1 << 1) -#define USB_RESET_CLKGENRESET (1 << 2) -#define USB_RESET_SIMSCALEDOWN (1 << 3) -#define USB_RESET_USBINTTEST (1 << 4) -#define USB_RESET_SLEEPSTBYEN (1 << 5) -#define USB_RESET_PWRSENSELOW (1 << 6) -#define USB_RESET_PWRCTRLLOW (1 << 7) - -#define USB_STATUS_IRQHCIRMTWKUP (1 << 7) -#define USB_STATUS_IRQHCIBUFFACC (1 << 8) -#define USB_STATUS_NIRQHCIM (1 << 9) -#define USB_STATUS_NHCIMFCLR (1 << 10) -#define USB_STATUS_USBPWRSENSE (1 << 11) - -/* * Serial Audio Controller * * Registers @@ -327,22 +303,6 @@ * PC_SSR GPIO Block C Sleep State */ -#define _PA_DDR _SA1111( 0x1000 ) -#define _PA_DRR _SA1111( 0x1004 ) -#define _PA_DWR _SA1111( 0x1004 ) -#define _PA_SDR _SA1111( 0x1008 ) -#define _PA_SSR _SA1111( 0x100c ) -#define _PB_DDR _SA1111( 0x1010 ) -#define _PB_DRR _SA1111( 0x1014 ) -#define _PB_DWR _SA1111( 0x1014 ) -#define _PB_SDR _SA1111( 0x1018 ) -#define _PB_SSR _SA1111( 0x101c ) -#define _PC_DDR _SA1111( 0x1020 ) -#define _PC_DRR _SA1111( 0x1024 ) -#define _PC_DWR _SA1111( 0x1024 ) -#define _PC_SDR _SA1111( 0x1028 ) -#define _PC_SSR _SA1111( 0x102c ) - #define SA1111_GPIO 0x1000 #define SA1111_GPIO_PADDR (0x000) @@ -425,106 +385,30 @@ #define SA1111_WAKEPOL0 0x0034 #define SA1111_WAKEPOL1 0x0038 -/* - * PS/2 Trackpad and Mouse Interfaces - * - * Registers - * PS2CR Control Register - * PS2STAT Status Register - * PS2DATA Transmit/Receive Data register - * PS2CLKDIV Clock Division Register - * PS2PRECNT Clock Precount Register - * PS2TEST1 Test register 1 - * PS2TEST2 Test register 2 - * PS2TEST3 Test register 3 - * PS2TEST4 Test register 4 - */ - +/* PS/2 Trackpad and Mouse Interfaces */ #define SA1111_KBD 0x0a00 #define SA1111_MSE 0x0c00 -/* - * These are offsets from the above bases. - */ -#define SA1111_PS2CR 0x0000 -#define SA1111_PS2STAT 0x0004 -#define SA1111_PS2DATA 0x0008 -#define SA1111_PS2CLKDIV 0x000c -#define SA1111_PS2PRECNT 0x0010 - -#define PS2CR_ENA 0x08 -#define PS2CR_FKD 0x02 -#define PS2CR_FKC 0x01 - -#define PS2STAT_STP 0x0100 -#define PS2STAT_TXE 0x0080 -#define PS2STAT_TXB 0x0040 -#define PS2STAT_RXF 0x0020 -#define PS2STAT_RXB 0x0010 -#define PS2STAT_ENA 0x0008 -#define PS2STAT_RXP 0x0004 -#define PS2STAT_KBD 0x0002 -#define PS2STAT_KBC 0x0001 +/* PCMCIA Interface */ +#define SA1111_PCMCIA 0x1600 -/* - * PCMCIA Interface - * - * Registers - * PCSR Status Register - * PCCR Control Register - * PCSSR Sleep State Register - */ - -#define SA1111_PCMCIA 0x1600 - -/* - * These are offsets from the above base. - */ -#define SA1111_PCCR 0x0000 -#define SA1111_PCSSR 0x0004 -#define SA1111_PCSR 0x0008 - -#define PCSR_S0_READY (1<<0) -#define PCSR_S1_READY (1<<1) -#define PCSR_S0_DETECT (1<<2) -#define PCSR_S1_DETECT (1<<3) -#define PCSR_S0_VS1 (1<<4) -#define PCSR_S0_VS2 (1<<5) -#define PCSR_S1_VS1 (1<<6) -#define PCSR_S1_VS2 (1<<7) -#define PCSR_S0_WP (1<<8) -#define PCSR_S1_WP (1<<9) -#define PCSR_S0_BVD1 (1<<10) -#define PCSR_S0_BVD2 (1<<11) -#define PCSR_S1_BVD1 (1<<12) -#define PCSR_S1_BVD2 (1<<13) - -#define PCCR_S0_RST (1<<0) -#define PCCR_S1_RST (1<<1) -#define PCCR_S0_FLT (1<<2) -#define PCCR_S1_FLT (1<<3) -#define PCCR_S0_PWAITEN (1<<4) -#define PCCR_S1_PWAITEN (1<<5) -#define PCCR_S0_PSE (1<<6) -#define PCCR_S1_PSE (1<<7) - -#define PCSSR_S0_SLEEP (1<<0) -#define PCSSR_S1_SLEEP (1<<1) extern struct bus_type sa1111_bus_type; -#define SA1111_DEVID_SBI 0 -#define SA1111_DEVID_SK 1 -#define SA1111_DEVID_USB 2 -#define SA1111_DEVID_SAC 3 -#define SA1111_DEVID_SSP 4 -#define SA1111_DEVID_PS2 5 -#define SA1111_DEVID_GPIO 6 -#define SA1111_DEVID_INT 7 -#define SA1111_DEVID_PCMCIA 8 +#define SA1111_DEVID_SBI (1 << 0) +#define SA1111_DEVID_SK (1 << 1) +#define SA1111_DEVID_USB (1 << 2) +#define SA1111_DEVID_SAC (1 << 3) +#define SA1111_DEVID_SSP (1 << 4) +#define SA1111_DEVID_PS2 (3 << 5) +#define SA1111_DEVID_PS2_KBD (1 << 5) +#define SA1111_DEVID_PS2_MSE (1 << 6) +#define SA1111_DEVID_GPIO (1 << 7) +#define SA1111_DEVID_INT (1 << 8) +#define SA1111_DEVID_PCMCIA (1 << 9) struct sa1111_dev { struct device dev; @@ -548,6 +432,7 @@ struct sa1111_driver { int (*remove)(struct sa1111_dev *); int (*suspend)(struct sa1111_dev *, pm_message_t); int (*resume)(struct sa1111_dev *); + void (*shutdown)(struct sa1111_dev *); }; #define SA1111_DRV(_d) container_of((_d), struct sa1111_driver, drv) @@ -555,9 +440,10 @@ struct sa1111_driver { #define SA1111_DRIVER_NAME(_sadev) ((_sadev)->dev.driver->name) /* - * These frob the SKPCR register. + * These frob the SKPCR register, and call platform specific + * enable/disable functions. */ -void sa1111_enable_device(struct sa1111_dev *); +int sa1111_enable_device(struct sa1111_dev *); void sa1111_disable_device(struct sa1111_dev *); unsigned int sa1111_pll_clock(struct sa1111_dev *); @@ -580,6 +466,10 @@ void sa1111_set_sleep_io(struct sa1111_dev *sadev, unsigned int bits, unsigned i struct sa1111_platform_data { int irq_base; /* base for cascaded on-chip IRQs */ + unsigned disable_devs; + void *data; + int (*enable)(void *, unsigned); + void (*disable)(void *, unsigned); }; #endif /* _ASM_ARCH_SA1111 */ diff --git a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c index 294aad0..6235efb 100644 --- a/arch/arm/mach-ebsa110/core.c +++ b/arch/arm/mach-ebsa110/core.c @@ -30,10 +30,7 @@ #include <asm/mach/time.h> -#define IRQ_MASK 0xfe000000 /* read */ -#define IRQ_MSET 0xfe000000 /* write */ -#define IRQ_STAT 0xff000000 /* read */ -#define IRQ_MCLR 0xff000000 /* write */ +#include "core.h" static void ebsa110_mask_irq(struct irq_data *d) { @@ -79,22 +76,22 @@ static struct map_desc ebsa110_io_desc[] __initdata = { { /* IRQ_STAT/IRQ_MCLR */ .virtual = IRQ_STAT, .pfn = __phys_to_pfn(TRICK4_PHYS), - .length = PGDIR_SIZE, + .length = TRICK4_SIZE, .type = MT_DEVICE }, { /* IRQ_MASK/IRQ_MSET */ .virtual = IRQ_MASK, .pfn = __phys_to_pfn(TRICK3_PHYS), - .length = PGDIR_SIZE, + .length = TRICK3_SIZE, .type = MT_DEVICE }, { /* SOFT_BASE */ .virtual = SOFT_BASE, .pfn = __phys_to_pfn(TRICK1_PHYS), - .length = PGDIR_SIZE, + .length = TRICK1_SIZE, .type = MT_DEVICE }, { /* PIT_BASE */ .virtual = PIT_BASE, .pfn = __phys_to_pfn(TRICK0_PHYS), - .length = PGDIR_SIZE, + .length = TRICK0_SIZE, .type = MT_DEVICE }, diff --git a/arch/arm/mach-ebsa110/core.h b/arch/arm/mach-ebsa110/core.h new file mode 100644 index 0000000..c93c9e4 --- /dev/null +++ b/arch/arm/mach-ebsa110/core.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1996-2000 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains the core hardware definitions of the EBSA-110. + */ +#ifndef CORE_H +#define CORE_H + +/* Physical addresses/sizes */ +#define ISAMEM_PHYS 0xe0000000 +#define ISAMEM_SIZE 0x10000000 + +#define ISAIO_PHYS 0xf0000000 +#define ISAIO_SIZE PGDIR_SIZE + +#define TRICK0_PHYS 0xf2000000 +#define TRICK0_SIZE PGDIR_SIZE +#define TRICK1_PHYS 0xf2400000 +#define TRICK1_SIZE PGDIR_SIZE +#define TRICK2_PHYS 0xf2800000 +#define TRICK3_PHYS 0xf2c00000 +#define TRICK3_SIZE PGDIR_SIZE +#define TRICK4_PHYS 0xf3000000 +#define TRICK4_SIZE PGDIR_SIZE +#define TRICK5_PHYS 0xf3400000 +#define TRICK6_PHYS 0xf3800000 +#define TRICK7_PHYS 0xf3c00000 + +/* Virtual addresses */ +#define PIT_BASE 0xfc000000 /* trick 0 */ +#define SOFT_BASE 0xfd000000 /* trick 1 */ +#define IRQ_MASK 0xfe000000 /* trick 3 - read */ +#define IRQ_MSET 0xfe000000 /* trick 3 - write */ +#define IRQ_STAT 0xff000000 /* trick 4 - read */ +#define IRQ_MCLR 0xff000000 /* trick 4 - write */ + +#endif diff --git a/arch/arm/mach-ebsa110/include/mach/hardware.h b/arch/arm/mach-ebsa110/include/mach/hardware.h index 4b2fb77..f4e5407 100644 --- a/arch/arm/mach-ebsa110/include/mach/hardware.h +++ b/arch/arm/mach-ebsa110/include/mach/hardware.h @@ -12,48 +12,9 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -/* - * The EBSA110 has a weird "ISA IO" region: - * - * Region 0 (addr = 0xf0000000 + io << 2) - * -------------------------------------------------------- - * Physical region IO region - * f0000fe0 - f0000ffc 3f8 - 3ff ttyS0 - * f0000e60 - f0000e64 398 - 399 - * f0000de0 - f0000dfc 378 - 37f lp0 - * f0000be0 - f0000bfc 2f8 - 2ff ttyS1 - * - * Region 1 (addr = 0xf0000000 + (io & ~1) << 1 + (io & 1)) - * -------------------------------------------------------- - * Physical region IO region - * f00014f1 a79 pnp write data - * f00007c0 - f00007c1 3e0 - 3e1 pcmcia - * f00004f1 279 pnp address - * f0000440 - f000046c 220 - 236 eth0 - * f0000405 203 pnp read data - */ - -#define ISAMEM_PHYS 0xe0000000 -#define ISAMEM_SIZE 0x10000000 - -#define ISAIO_PHYS 0xf0000000 -#define ISAIO_SIZE PGDIR_SIZE - -#define TRICK0_PHYS 0xf2000000 -#define TRICK1_PHYS 0xf2400000 -#define TRICK2_PHYS 0xf2800000 -#define TRICK3_PHYS 0xf2c00000 -#define TRICK4_PHYS 0xf3000000 -#define TRICK5_PHYS 0xf3400000 -#define TRICK6_PHYS 0xf3800000 -#define TRICK7_PHYS 0xf3c00000 - #define ISAMEM_BASE 0xe0000000 #define ISAIO_BASE 0xf0000000 -#define PIT_BASE 0xfc000000 -#define SOFT_BASE 0xfd000000 - /* * RAM definitions */ diff --git a/arch/arm/mach-ebsa110/io.c b/arch/arm/mach-ebsa110/io.c index c52e304..756cc37 100644 --- a/arch/arm/mach-ebsa110/io.c +++ b/arch/arm/mach-ebsa110/io.c @@ -177,6 +177,26 @@ void writesl(void __iomem *addr, const void *data, int len) } EXPORT_SYMBOL(writesl); +/* + * The EBSA110 has a weird "ISA IO" region: + * + * Region 0 (addr = 0xf0000000 + io << 2) + * -------------------------------------------------------- + * Physical region IO region + * f0000fe0 - f0000ffc 3f8 - 3ff ttyS0 + * f0000e60 - f0000e64 398 - 399 + * f0000de0 - f0000dfc 378 - 37f lp0 + * f0000be0 - f0000bfc 2f8 - 2ff ttyS1 + * + * Region 1 (addr = 0xf0000000 + (io & ~1) << 1 + (io & 1)) + * -------------------------------------------------------- + * Physical region IO region + * f00014f1 a79 pnp write data + * f00007c0 - f00007c1 3e0 - 3e1 pcmcia + * f00004f1 279 pnp address + * f0000440 - f000046c 220 - 236 eth0 + * f0000405 203 pnp read data + */ #define SUPERIO_PORT(p) \ (((p) >> 3) == (0x3f8 >> 3) || \ ((p) >> 3) == (0x2f8 >> 3) || \ diff --git a/arch/arm/mach-ebsa110/leds.c b/arch/arm/mach-ebsa110/leds.c index 6a6ea57..d43121a 100644 --- a/arch/arm/mach-ebsa110/leds.c +++ b/arch/arm/mach-ebsa110/leds.c @@ -20,6 +20,8 @@ #include <asm/system.h> #include <asm/mach-types.h> +#include "core.h" + static spinlock_t leds_lock; static void ebsa110_leds_event(led_event_t ledevt) diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c index 6ebd276..6bb3f47 100644 --- a/arch/arm/mach-pxa/lubbock.c +++ b/arch/arm/mach-pxa/lubbock.c @@ -223,6 +223,7 @@ static struct resource sa1111_resources[] = { static struct sa1111_platform_data sa1111_info = { .irq_base = LUBBOCK_SA1111_IRQ_BASE, + .disable_devs = SA1111_DEVID_SAC, }; static struct platform_device sa1111_device = { diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index ed7408d..60b97ec 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -3,7 +3,7 @@ # # Common support -obj-y := clock.o generic.o irq.o dma.o time.o #nmi-oopser.o +obj-y := clock.o generic.o irq.o time.o #nmi-oopser.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index 0c4b76a..375d3f7 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -15,14 +15,16 @@ #include <linux/errno.h> #include <linux/ioport.h> #include <linux/serial_core.h> +#include <linux/mfd/ucb1x00.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/delay.h> #include <linux/mm.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> -#include <asm/irq.h> #include <asm/setup.h> #include <asm/page.h> #include <asm/pgtable-hwdef.h> @@ -36,17 +38,18 @@ #include <asm/mach/serial_sa1100.h> #include <mach/assabet.h> #include <mach/mcp.h> +#include <mach/irqs.h> #include "generic.h" #define ASSABET_BCR_DB1110 \ - (ASSABET_BCR_SPK_OFF | ASSABET_BCR_QMUTE | \ + (ASSABET_BCR_SPK_OFF | \ ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED | \ ASSABET_BCR_RS232EN | ASSABET_BCR_LCD_12RGB | \ ASSABET_BCR_IRDA_MD0) #define ASSABET_BCR_DB1111 \ - (ASSABET_BCR_SPK_OFF | ASSABET_BCR_QMUTE | \ + (ASSABET_BCR_SPK_OFF | \ ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED | \ ASSABET_BCR_RS232EN | ASSABET_BCR_LCD_12RGB | \ ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_STEREO_LB | \ @@ -69,31 +72,10 @@ void ASSABET_BCR_frob(unsigned int mask, unsigned int val) EXPORT_SYMBOL(ASSABET_BCR_frob); -static void assabet_backlight_power(int on) -{ -#ifndef ASSABET_PAL_VIDEO - if (on) - ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON); - else -#endif - ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON); -} - -/* - * Turn on/off the backlight. When turning the backlight on, - * we wait 500us after turning it on so we don't cause the - * supplies to droop when we enable the LCD controller (and - * cause a hard reset.) - */ -static void assabet_lcd_power(int on) +static void assabet_ucb1x00_reset(enum ucb1x00_reset state) { -#ifndef ASSABET_PAL_VIDEO - if (on) { - ASSABET_BCR_set(ASSABET_BCR_LCD_ON); - udelay(500); - } else -#endif - ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); + if (state == UCB_RST_PROBE) + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); } @@ -152,15 +134,8 @@ static struct flash_platform_data assabet_flash_data = { }; static struct resource assabet_flash_resources[] = { - { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, - }, { - .start = SA1100_CS1_PHYS, - .end = SA1100_CS1_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, - } + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M), + DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_32M), }; @@ -199,18 +174,126 @@ static struct irda_platform_data assabet_irda_data = { .set_speed = assabet_irda_set_speed, }; +static struct ucb1x00_plat_data assabet_ucb1x00_data = { + .reset = assabet_ucb1x00_reset, + .gpio_base = -1, +}; + static struct mcp_plat_data assabet_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, + .codec_pdata = &assabet_ucb1x00_data, +}; + +static void assabet_lcd_set_visual(u32 visual) +{ + u_int is_true_color = visual == FB_VISUAL_TRUECOLOR; + + if (machine_is_assabet()) { +#if 1 // phase 4 or newer Assabet's + if (is_true_color) + ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); + else + ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); +#else + // older Assabet's + if (is_true_color) + ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); + else + ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); +#endif + } +} + +#ifndef ASSABET_PAL_VIDEO +static void assabet_lcd_backlight_power(int on) +{ + if (on) + ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON); + else + ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON); +} + +/* + * Turn on/off the backlight. When turning the backlight on, we wait + * 500us after turning it on so we don't cause the supplies to droop + * when we enable the LCD controller (and cause a hard reset.) + */ +static void assabet_lcd_power(int on) +{ + if (on) { + ASSABET_BCR_set(ASSABET_BCR_LCD_ON); + udelay(500); + } else + ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); +} + +/* + * The assabet uses a sharp LQ039Q2DS54 LCD module. It is actually + * takes an RGB666 signal, but we provide it with an RGB565 signal + * instead (def_rgb_16). + */ +static struct sa1100fb_mach_info lq039q2ds54_info = { + .pixclock = 171521, .bpp = 16, + .xres = 320, .yres = 240, + + .hsync_len = 5, .vsync_len = 1, + .left_margin = 61, .upper_margin = 3, + .right_margin = 9, .lower_margin = 0, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), + + .backlight_power = assabet_lcd_backlight_power, + .lcd_power = assabet_lcd_power, + .set_visual = assabet_lcd_set_visual, +}; +#else +static void assabet_pal_backlight_power(int on) +{ + ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON); +} + +static void assabet_pal_power(int on) +{ + ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); +} + +static struct sa1100fb_mach_info pal_info = { + .pixclock = 67797, .bpp = 16, + .xres = 640, .yres = 512, + + .hsync_len = 64, .vsync_len = 6, + .left_margin = 125, .upper_margin = 70, + .right_margin = 115, .lower_margin = 36, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), + + .backlight_power = assabet_pal_backlight_power, + .lcd_power = assabet_pal_power, + .set_visual = assabet_lcd_set_visual, }; +#endif + +#ifdef CONFIG_ASSABET_NEPONSET +static struct resource neponset_resources[] = { + DEFINE_RES_MEM(0x10000000, 0x08000000), + DEFINE_RES_MEM(0x18000000, 0x04000000), + DEFINE_RES_MEM(0x40000000, SZ_8K), + DEFINE_RES_IRQ(IRQ_GPIO25), +}; +#endif static void __init assabet_init(void) { /* * Ensure that the power supply is in "high power" mode. */ - GPDR |= GPIO_GPIO16; GPSR = GPIO_GPIO16; + GPDR |= GPIO_GPIO16; /* * Ensure that these pins are set as outputs and are driving @@ -218,8 +301,16 @@ static void __init assabet_init(void) * the WS latch in the CPLD, and we don't float causing * excessive power drain. --rmk */ - GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; GPCR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + + /* + * Also set GPIO27 as an output; this is used to clock UART3 + * via the FPGA and as otherwise has no pullups or pulldowns, + * so stop it floating. + */ + GPCR = GPIO_GPIO27; + GPDR |= GPIO_GPIO27; /* * Set up registers for sleep mode. @@ -231,8 +322,7 @@ static void __init assabet_init(void) PPDR |= PPC_TXD3 | PPC_TXD1; PPSR |= PPC_TXD3 | PPC_TXD1; - sa1100fb_lcd_power = assabet_lcd_power; - sa1100fb_backlight_power = assabet_backlight_power; + sa11x0_ppc_configure_mcp(); if (machine_has_neponset()) { /* @@ -246,9 +336,17 @@ static void __init assabet_init(void) #ifndef CONFIG_ASSABET_NEPONSET printk( "Warning: Neponset detected but full support " "hasn't been configured in the kernel\n" ); +#else + platform_device_register_simple("neponset", 0, + neponset_resources, ARRAY_SIZE(neponset_resources)); #endif } +#ifndef ASSABET_PAL_VIDEO + sa11x0_register_lcd(&lq039q2ds54_info); +#else + sa11x0_register_lcd(&pal_video); +#endif sa11x0_register_mtd(&assabet_flash_data, assabet_flash_resources, ARRAY_SIZE(assabet_flash_resources)); sa11x0_register_irda(&assabet_irda_data); @@ -412,21 +510,8 @@ static void __init assabet_map_io(void) */ Ser1SDCR0 |= SDCR0_SUS; - if (machine_has_neponset()) { -#ifdef CONFIG_ASSABET_NEPONSET - extern void neponset_map_io(void); - - /* - * We map Neponset registers even if it isn't present since - * many drivers will try to probe their stuff (and fail). - * This is still more friendly than a kernel paging request - * crash. - */ - neponset_map_io(); -#endif - } else { + if (!machine_has_neponset()) sa1100_register_uart_fns(&assabet_port_fns); - } /* * When Neponset is attached, the first UART should be @@ -449,6 +534,7 @@ MACHINE_START(ASSABET, "Intel-Assabet") .atag_offset = 0x100, .fixup = fixup_assabet, .map_io = assabet_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = assabet_init, diff --git a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c index b07a2c0..e0f0c03 100644 --- a/arch/arm/mach-sa1100/badge4.c +++ b/arch/arm/mach-sa1100/badge4.c @@ -39,20 +39,27 @@ #include "generic.h" static struct resource sa1111_resources[] = { - [0] = { - .start = BADGE4_SA1111_BASE, - .end = BADGE4_SA1111_BASE + 0x00001fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = BADGE4_IRQ_GPIO_SA1111, - .end = BADGE4_IRQ_GPIO_SA1111, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(BADGE4_SA1111_BASE, 0x2000), + [1] = DEFINE_RES_IRQ(BADGE4_IRQ_GPIO_SA1111), }; +static int badge4_sa1111_enable(void *data, unsigned devid) +{ + if (devid == SA1111_DEVID_USB) + badge4_set_5V(BADGE4_5V_USB, 1); + return 0; +} + +static void badge4_sa1111_disable(void *data, unsigned devid) +{ + if (devid == SA1111_DEVID_USB) + badge4_set_5V(BADGE4_5V_USB, 0); +} + static struct sa1111_platform_data sa1111_info = { - .irq_base = IRQ_BOARD_END, + .disable_devs = SA1111_DEVID_PS2_MSE, + .enable = badge4_sa1111_enable, + .disable = badge4_sa1111_disable, }; static u64 sa1111_dmamask = 0xffffffffUL; @@ -121,11 +128,8 @@ static struct flash_platform_data badge4_flash_data = { .nr_parts = ARRAY_SIZE(badge4_partitions), }; -static struct resource badge4_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_64M - 1, - .flags = IORESOURCE_MEM, -}; +static struct resource badge4_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_64M); static int five_v_on __initdata = 0; @@ -269,11 +273,6 @@ static struct map_desc badge4_io_desc[] __initdata = { .pfn = __phys_to_pfn(0x10000000), .length = 0x00100000, .type = MT_DEVICE - }, { /* SA-1111 */ - .virtual = 0xf4000000, - .pfn = __phys_to_pfn(0x48000000), - .length = 0x00100000, - .type = MT_DEVICE } }; @@ -304,6 +303,7 @@ static void __init badge4_map_io(void) MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4") .atag_offset = 0x100, .map_io = badge4_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, #ifdef CONFIG_SA1111 diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c index 11bb6d0..4a61f60 100644 --- a/arch/arm/mach-sa1100/cerf.c +++ b/arch/arm/mach-sa1100/cerf.c @@ -18,7 +18,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <asm/irq.h> #include <mach/hardware.h> #include <asm/setup.h> @@ -30,14 +29,11 @@ #include <mach/cerf.h> #include <mach/mcp.h> +#include <mach/irqs.h> #include "generic.h" static struct resource cerfuart2_resources[] = { - [0] = { - .start = 0x80030000, - .end = 0x8003ffff, - .flags = IORESOURCE_MEM, - }, + [0] = DEFINE_RES_MEM(0x80030000, SZ_64K), }; static struct platform_device cerfuart2_device = { @@ -87,11 +83,8 @@ static struct flash_platform_data cerf_flash_data = { .nr_parts = ARRAY_SIZE(cerf_partitions), }; -static struct resource cerf_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; +static struct resource cerf_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M); static void __init cerf_init_irq(void) { @@ -128,6 +121,7 @@ static struct mcp_plat_data cerf_mcp_data = { static void __init cerf_init(void) { + sa11x0_ppc_configure_mcp(); platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices)); sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1); sa11x0_register_mcp(&cerf_mcp_data); @@ -136,6 +130,7 @@ static void __init cerf_init(void) MACHINE_START(CERF, "Intrinsyc CerfBoard/CerfCube") /* Maintainer: support@intrinsyc.com */ .map_io = cerf_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = cerf_init_irq, .timer = &sa1100_timer, .init_machine = cerf_init, diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index fd56521..48885b7 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -22,15 +22,17 @@ #include <linux/tty.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/mfd/ucb1x00.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/timer.h> #include <linux/gpio.h> #include <linux/pda_power.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> -#include <asm/irq.h> #include <asm/page.h> #include <asm/setup.h> #include <mach/collie.h> @@ -44,15 +46,12 @@ #include <asm/mach/sharpsl_param.h> #include <asm/hardware/locomo.h> #include <mach/mcp.h> +#include <mach/irqs.h> #include "generic.h" static struct resource collie_scoop_resources[] = { - [0] = { - .start = 0x40800000, - .end = 0x40800fff, - .flags = IORESOURCE_MEM, - }, + [0] = DEFINE_RES_MEM(0x40800000, SZ_4K), }; static struct scoop_config collie_scoop_setup = { @@ -85,10 +84,14 @@ static struct scoop_pcmcia_config collie_pcmcia_config = { .num_devs = 1, }; +static struct ucb1x00_plat_data collie_ucb1x00_data = { + .gpio_base = COLLIE_TC35143_GPIO_BASE, +}; + static struct mcp_plat_data collie_mcp_data = { .mccr0 = MCCR0_ADM | MCCR0_ExtClk, .sclk_rate = 9216000, - .gpio_base = COLLIE_TC35143_GPIO_BASE, + .codec_pdata = &collie_ucb1x00_data, }; /* @@ -221,16 +224,8 @@ device_initcall(collie_uart_init); static struct resource locomo_resources[] = { - [0] = { - .start = 0x40000000, - .end = 0x40001fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_GPIO25, - .end = IRQ_GPIO25, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(0x40000000, SZ_8K), + [1] = DEFINE_RES_IRQ(IRQ_GPIO25), }; static struct locomo_platform_data locomo_info = { @@ -303,11 +298,21 @@ static struct flash_platform_data collie_flash_data = { }; static struct resource collie_flash_resources[] = { - { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, - } + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M), +}; + +static struct sa1100fb_mach_info collie_lcd_info = { + .pixclock = 171521, .bpp = 16, + .xres = 320, .yres = 240, + + .hsync_len = 5, .vsync_len = 1, + .left_margin = 11, .upper_margin = 2, + .right_margin = 30, .lower_margin = 0, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), }; static void __init collie_init(void) @@ -341,6 +346,10 @@ static void __init collie_init(void) collie_power_resource[0].start = gpio_to_irq(COLLIE_GPIO_AC_IN); collie_power_resource[0].end = gpio_to_irq(COLLIE_GPIO_AC_IN); + + sa11x0_ppc_configure_mcp(); + + platform_scoop_config = &collie_pcmcia_config; ret = platform_add_devices(devices, ARRAY_SIZE(devices)); @@ -348,6 +357,7 @@ static void __init collie_init(void) printk(KERN_WARNING "collie: Unable to register LoCoMo device\n"); } + sa11x0_register_lcd(&collie_lcd_info); sa11x0_register_mtd(&collie_flash_data, collie_flash_resources, ARRAY_SIZE(collie_flash_resources)); sa11x0_register_mcp(&collie_mcp_data); @@ -383,6 +393,7 @@ static void __init collie_map_io(void) MACHINE_START(COLLIE, "Sharp-Collie") .map_io = collie_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = collie_init, diff --git a/arch/arm/mach-sa1100/dma.c b/arch/arm/mach-sa1100/dma.c deleted file mode 100644 index ad66035..0000000 --- a/arch/arm/mach-sa1100/dma.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * arch/arm/mach-sa1100/dma.c - * - * Support functions for the SA11x0 internal DMA channels. - * - * Copyright (C) 2000, 2001 by Nicolas Pitre - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/errno.h> - -#include <asm/system.h> -#include <asm/irq.h> -#include <mach/hardware.h> -#include <mach/dma.h> - - -#undef DEBUG -#ifdef DEBUG -#define DPRINTK( s, arg... ) printk( "dma<%p>: " s, regs , ##arg ) -#else -#define DPRINTK( x... ) -#endif - - -typedef struct { - const char *device_id; /* device name */ - u_long device; /* this channel device, 0 if unused*/ - dma_callback_t callback; /* to call when DMA completes */ - void *data; /* ... with private data ptr */ -} sa1100_dma_t; - -static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS]; - -static DEFINE_SPINLOCK(dma_list_lock); - - -static irqreturn_t dma_irq_handler(int irq, void *dev_id) -{ - dma_regs_t *dma_regs = dev_id; - sa1100_dma_t *dma = dma_chan + (((u_int)dma_regs >> 5) & 7); - int status = dma_regs->RdDCSR; - - if (status & (DCSR_ERROR)) { - printk(KERN_CRIT "DMA on \"%s\" caused an error\n", dma->device_id); - dma_regs->ClrDCSR = DCSR_ERROR; - } - - dma_regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB); - if (dma->callback) { - if (status & DCSR_DONEA) - dma->callback(dma->data); - if (status & DCSR_DONEB) - dma->callback(dma->data); - } - return IRQ_HANDLED; -} - - -/** - * sa1100_request_dma - allocate one of the SA11x0's DMA channels - * @device: The SA11x0 peripheral targeted by this request - * @device_id: An ascii name for the claiming device - * @callback: Function to be called when the DMA completes - * @data: A cookie passed back to the callback function - * @dma_regs: Pointer to the location of the allocated channel's identifier - * - * This function will search for a free DMA channel and returns the - * address of the hardware registers for that channel as the channel - * identifier. This identifier is written to the location pointed by - * @dma_regs. The list of possible values for @device are listed into - * arch/arm/mach-sa1100/include/mach/dma.h as a dma_device_t enum. - * - * Note that reading from a port and writing to the same port are - * actually considered as two different streams requiring separate - * DMA registrations. - * - * The @callback function is called from interrupt context when one - * of the two possible DMA buffers in flight has terminated. That - * function has to be small and efficient while posponing more complex - * processing to a lower priority execution context. - * - * If no channels are available, or if the desired @device is already in - * use by another DMA channel, then an error code is returned. This - * function must be called before any other DMA calls. - **/ - -int sa1100_request_dma (dma_device_t device, const char *device_id, - dma_callback_t callback, void *data, - dma_regs_t **dma_regs) -{ - sa1100_dma_t *dma = NULL; - dma_regs_t *regs; - int i, err; - - *dma_regs = NULL; - - err = 0; - spin_lock(&dma_list_lock); - for (i = 0; i < SA1100_DMA_CHANNELS; i++) { - if (dma_chan[i].device == device) { - err = -EBUSY; - break; - } else if (!dma_chan[i].device && !dma) { - dma = &dma_chan[i]; - } - } - if (!err) { - if (dma) - dma->device = device; - else - err = -ENOSR; - } - spin_unlock(&dma_list_lock); - if (err) - return err; - - i = dma - dma_chan; - regs = (dma_regs_t *)&DDAR(i); - err = request_irq(IRQ_DMA0 + i, dma_irq_handler, IRQF_DISABLED, - device_id, regs); - if (err) { - printk(KERN_ERR - "%s: unable to request IRQ %d for %s\n", - __func__, IRQ_DMA0 + i, device_id); - dma->device = 0; - return err; - } - - *dma_regs = regs; - dma->device_id = device_id; - dma->callback = callback; - dma->data = data; - - regs->ClrDCSR = - (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | - DCSR_IE | DCSR_ERROR | DCSR_RUN); - regs->DDAR = device; - - return 0; -} - - -/** - * sa1100_free_dma - free a SA11x0 DMA channel - * @regs: identifier for the channel to free - * - * This clears all activities on a given DMA channel and releases it - * for future requests. The @regs identifier is provided by a - * successful call to sa1100_request_dma(). - **/ - -void sa1100_free_dma(dma_regs_t *regs) -{ - int i; - - for (i = 0; i < SA1100_DMA_CHANNELS; i++) - if (regs == (dma_regs_t *)&DDAR(i)) - break; - if (i >= SA1100_DMA_CHANNELS) { - printk(KERN_ERR "%s: bad DMA identifier\n", __func__); - return; - } - - if (!dma_chan[i].device) { - printk(KERN_ERR "%s: Trying to free free DMA\n", __func__); - return; - } - - regs->ClrDCSR = - (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | - DCSR_IE | DCSR_ERROR | DCSR_RUN); - free_irq(IRQ_DMA0 + i, regs); - dma_chan[i].device = 0; -} - - -/** - * sa1100_start_dma - submit a data buffer for DMA - * @regs: identifier for the channel to use - * @dma_ptr: buffer physical (or bus) start address - * @size: buffer size - * - * This function hands the given data buffer to the hardware for DMA - * access. If another buffer is already in flight then this buffer - * will be queued so the DMA engine will switch to it automatically - * when the previous one is done. The DMA engine is actually toggling - * between two buffers so at most 2 successful calls can be made before - * one of them terminates and the callback function is called. - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - * - * The @size must not be larger than %MAX_DMA_SIZE. If a given buffer - * is larger than that then it's the caller's responsibility to split - * it into smaller chunks and submit them separately. If this is the - * case then a @size of %CUT_DMA_SIZE is recommended to avoid ending - * up with too small chunks. The callback function can be used to chain - * submissions of buffer chunks. - * - * Error return values: - * %-EOVERFLOW: Given buffer size is too big. - * %-EBUSY: Both DMA buffers are already in use. - * %-EAGAIN: Both buffers were busy but one of them just completed - * but the interrupt handler has to execute first. - * - * This function returs 0 on success. - **/ - -int sa1100_start_dma(dma_regs_t *regs, dma_addr_t dma_ptr, u_int size) -{ - unsigned long flags; - u_long status; - int ret; - - if (dma_ptr & 3) - printk(KERN_WARNING "DMA: unaligned start address (0x%08lx)\n", - (unsigned long)dma_ptr); - - if (size > MAX_DMA_SIZE) - return -EOVERFLOW; - - local_irq_save(flags); - status = regs->RdDCSR; - - /* If both DMA buffers are started, there's nothing else we can do. */ - if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) { - DPRINTK("start: st %#x busy\n", status); - ret = -EBUSY; - goto out; - } - - if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || - (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) { - if (status & DCSR_DONEA) { - /* give a chance for the interrupt to be processed */ - ret = -EAGAIN; - goto out; - } - regs->DBSA = dma_ptr; - regs->DBTA = size; - regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN; - DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size); - } else { - if (status & DCSR_DONEB) { - /* give a chance for the interrupt to be processed */ - ret = -EAGAIN; - goto out; - } - regs->DBSB = dma_ptr; - regs->DBTB = size; - regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN; - DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size); - } - ret = 0; - -out: - local_irq_restore(flags); - return ret; -} - - -/** - * sa1100_get_dma_pos - return current DMA position - * @regs: identifier for the channel to use - * - * This function returns the current physical (or bus) address for the - * given DMA channel. If the channel is running i.e. not in a stopped - * state then the caller must disable interrupts prior calling this - * function and process the returned value before re-enabling them to - * prevent races with the completion interrupt handler and the callback - * function. The validation of the returned value is the caller's - * responsibility as well -- the hardware seems to return out of range - * values when the DMA engine completes a buffer. - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - **/ - -dma_addr_t sa1100_get_dma_pos(dma_regs_t *regs) -{ - int status; - - /* - * We must determine whether buffer A or B is active. - * Two possibilities: either we are in the middle of - * a buffer, or the DMA controller just switched to the - * next toggle but the interrupt hasn't been serviced yet. - * The former case is straight forward. In the later case, - * we'll do like if DMA is just at the end of the previous - * toggle since all registers haven't been reset yet. - * This goes around the edge case and since we're always - * a little behind anyways it shouldn't make a big difference. - * If DMA has been stopped prior calling this then the - * position is exact. - */ - status = regs->RdDCSR; - if ((!(status & DCSR_BIU) && (status & DCSR_STRTA)) || - ( (status & DCSR_BIU) && !(status & DCSR_STRTB))) - return regs->DBSA; - else - return regs->DBSB; -} - - -/** - * sa1100_reset_dma - reset a DMA channel - * @regs: identifier for the channel to use - * - * This function resets and reconfigure the given DMA channel. This is - * particularly useful after a sleep/wakeup event. - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - **/ - -void sa1100_reset_dma(dma_regs_t *regs) -{ - int i; - - for (i = 0; i < SA1100_DMA_CHANNELS; i++) - if (regs == (dma_regs_t *)&DDAR(i)) - break; - if (i >= SA1100_DMA_CHANNELS) { - printk(KERN_ERR "%s: bad DMA identifier\n", __func__); - return; - } - - regs->ClrDCSR = - (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | - DCSR_IE | DCSR_ERROR | DCSR_RUN); - regs->DDAR = dma_chan[i].device; -} - - -EXPORT_SYMBOL(sa1100_request_dma); -EXPORT_SYMBOL(sa1100_free_dma); -EXPORT_SYMBOL(sa1100_start_dma); -EXPORT_SYMBOL(sa1100_get_dma_pos); -EXPORT_SYMBOL(sa1100_reset_dma); - diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index bb10ee2..b184704 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -14,18 +14,23 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/pm.h> #include <linux/cpufreq.h> #include <linux/ioport.h> #include <linux/platform_device.h> +#include <video/sa1100fb.h> + #include <asm/div64.h> -#include <mach/hardware.h> #include <asm/system.h> #include <asm/mach/map.h> #include <asm/mach/flash.h> #include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/irqs.h> + #include "generic.h" unsigned int reset_status; @@ -149,16 +154,8 @@ static void sa11x0_register_device(struct platform_device *dev, void *data) static struct resource sa11x0udc_resources[] = { - [0] = { - .start = __PREG(Ser0UDCCR), - .end = __PREG(Ser0UDCCR) + 0xffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_Ser0UDC, - .end = IRQ_Ser0UDC, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(__PREG(Ser0UDCCR), SZ_64K), + [1] = DEFINE_RES_IRQ(IRQ_Ser0UDC), }; static u64 sa11x0udc_dma_mask = 0xffffffffUL; @@ -175,16 +172,8 @@ static struct platform_device sa11x0udc_device = { }; static struct resource sa11x0uart1_resources[] = { - [0] = { - .start = __PREG(Ser1UTCR0), - .end = __PREG(Ser1UTCR0) + 0xffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_Ser1UART, - .end = IRQ_Ser1UART, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(__PREG(Ser1UTCR0), SZ_64K), + [1] = DEFINE_RES_IRQ(IRQ_Ser1UART), }; static struct platform_device sa11x0uart1_device = { @@ -195,16 +184,8 @@ static struct platform_device sa11x0uart1_device = { }; static struct resource sa11x0uart3_resources[] = { - [0] = { - .start = __PREG(Ser3UTCR0), - .end = __PREG(Ser3UTCR0) + 0xffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_Ser3UART, - .end = IRQ_Ser3UART, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(__PREG(Ser3UTCR0), SZ_64K), + [1] = DEFINE_RES_IRQ(IRQ_Ser3UART), }; static struct platform_device sa11x0uart3_device = { @@ -215,16 +196,9 @@ static struct platform_device sa11x0uart3_device = { }; static struct resource sa11x0mcp_resources[] = { - [0] = { - .start = __PREG(Ser4MCCR0), - .end = __PREG(Ser4MCCR0) + 0xffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_Ser4MCP, - .end = IRQ_Ser4MCP, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(__PREG(Ser4MCCR0), SZ_64K), + [1] = DEFINE_RES_MEM(__PREG(Ser4MCCR1), 4), + [2] = DEFINE_RES_IRQ(IRQ_Ser4MCP), }; static u64 sa11x0mcp_dma_mask = 0xffffffffUL; @@ -240,22 +214,24 @@ static struct platform_device sa11x0mcp_device = { .resource = sa11x0mcp_resources, }; +void __init sa11x0_ppc_configure_mcp(void) +{ + /* Setup the PPC unit for the MCP */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); +} + void sa11x0_register_mcp(struct mcp_plat_data *data) { sa11x0_register_device(&sa11x0mcp_device, data); } static struct resource sa11x0ssp_resources[] = { - [0] = { - .start = 0x80070000, - .end = 0x8007ffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_Ser4SSP, - .end = IRQ_Ser4SSP, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(0x80070000, SZ_64K), + [1] = DEFINE_RES_IRQ(IRQ_Ser4SSP), }; static u64 sa11x0ssp_dma_mask = 0xffffffffUL; @@ -272,16 +248,8 @@ static struct platform_device sa11x0ssp_device = { }; static struct resource sa11x0fb_resources[] = { - [0] = { - .start = 0xb0100000, - .end = 0xb010ffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_LCD, - .end = IRQ_LCD, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(0xb0100000, SZ_64K), + [1] = DEFINE_RES_IRQ(IRQ_LCD), }; static struct platform_device sa11x0fb_device = { @@ -294,6 +262,11 @@ static struct platform_device sa11x0fb_device = { .resource = sa11x0fb_resources, }; +void sa11x0_register_lcd(struct sa1100fb_mach_info *inf) +{ + sa11x0_register_device(&sa11x0fb_device, inf); +} + static struct platform_device sa11x0pcmcia_device = { .name = "sa11x0-pcmcia", .id = -1, @@ -314,23 +287,10 @@ void sa11x0_register_mtd(struct flash_platform_data *flash, } static struct resource sa11x0ir_resources[] = { - { - .start = __PREG(Ser2UTCR0), - .end = __PREG(Ser2UTCR0) + 0x24 - 1, - .flags = IORESOURCE_MEM, - }, { - .start = __PREG(Ser2HSCR0), - .end = __PREG(Ser2HSCR0) + 0x1c - 1, - .flags = IORESOURCE_MEM, - }, { - .start = __PREG(Ser2HSCR2), - .end = __PREG(Ser2HSCR2) + 0x04 - 1, - .flags = IORESOURCE_MEM, - }, { - .start = IRQ_Ser2ICP, - .end = IRQ_Ser2ICP, - .flags = IORESOURCE_IRQ, - } + DEFINE_RES_MEM(__PREG(Ser2UTCR0), 0x24), + DEFINE_RES_MEM(__PREG(Ser2HSCR0), 0x1c), + DEFINE_RES_MEM(__PREG(Ser2HSCR2), 0x04), + DEFINE_RES_IRQ(IRQ_Ser2ICP), }; static struct platform_device sa11x0ir_device = { @@ -350,14 +310,37 @@ static struct platform_device sa11x0rtc_device = { .id = -1, }; +static struct resource sa11x0dma_resources[] = { + DEFINE_RES_MEM(DMA_PHYS, DMA_SIZE), + DEFINE_RES_IRQ(IRQ_DMA0), + DEFINE_RES_IRQ(IRQ_DMA1), + DEFINE_RES_IRQ(IRQ_DMA2), + DEFINE_RES_IRQ(IRQ_DMA3), + DEFINE_RES_IRQ(IRQ_DMA4), + DEFINE_RES_IRQ(IRQ_DMA5), +}; + +static u64 sa11x0dma_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device sa11x0dma_device = { + .name = "sa11x0-dma", + .id = -1, + .dev = { + .dma_mask = &sa11x0dma_dma_mask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa11x0dma_resources), + .resource = sa11x0dma_resources, +}; + static struct platform_device *sa11x0_devices[] __initdata = { &sa11x0udc_device, &sa11x0uart1_device, &sa11x0uart3_device, &sa11x0ssp_device, &sa11x0pcmcia_device, - &sa11x0fb_device, &sa11x0rtc_device, + &sa11x0dma_device, }; static int __init sa1100_init(void) @@ -368,12 +351,6 @@ static int __init sa1100_init(void) arch_initcall(sa1100_init); -void (*sa1100fb_backlight_power)(int on); -void (*sa1100fb_lcd_power)(int on); - -EXPORT_SYMBOL(sa1100fb_backlight_power); -EXPORT_SYMBOL(sa1100fb_lcd_power); - /* * Common I/O mapping: @@ -428,7 +405,7 @@ void __init sa1100_map_io(void) * the MBGNT signal false to ensure the SA1111 doesn't own the * SDRAM bus. */ -void __init sa1110_mb_disable(void) +void sa1110_mb_disable(void) { unsigned long flags; @@ -447,7 +424,7 @@ void __init sa1110_mb_disable(void) * If the system is going to use the SA-1111 DMA engines, set up * the memory bus request/grant pins. */ -void __devinit sa1110_mb_enable(void) +void sa1110_mb_enable(void) { unsigned long flags; diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h index 33268cf..9eb3b3c 100644 --- a/arch/arm/mach-sa1100/generic.h +++ b/arch/arm/mach-sa1100/generic.h @@ -16,9 +16,6 @@ extern void sa11x0_restart(char, const char *); mi->bank[__nr].start = (__start), \ mi->bank[__nr].size = (__size) -extern void (*sa1100fb_backlight_power)(int on); -extern void (*sa1100fb_lcd_power)(int on); - extern void sa1110_mb_enable(void); extern void sa1110_mb_disable(void); @@ -39,4 +36,8 @@ struct irda_platform_data; void sa11x0_register_irda(struct irda_platform_data *irda); struct mcp_plat_data; +void sa11x0_ppc_configure_mcp(void); void sa11x0_register_mcp(struct mcp_plat_data *data); + +struct sa1100fb_mach_info; +void sa11x0_register_lcd(struct sa1100fb_mach_info *inf); diff --git a/arch/arm/mach-sa1100/h3100.c b/arch/arm/mach-sa1100/h3100.c index 1e6b3c1..b2e8d0f 100644 --- a/arch/arm/mach-sa1100/h3100.c +++ b/arch/arm/mach-sa1100/h3100.c @@ -14,11 +14,14 @@ #include <linux/kernel.h> #include <linux/gpio.h> +#include <video/sa1100fb.h> + #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/irda.h> #include <mach/h3xxx.h> +#include <mach/irqs.h> #include "generic.h" @@ -36,13 +39,28 @@ static void h3100_lcd_power(int enable) } } +static struct sa1100fb_mach_info h3100_lcd_info = { + .pixclock = 406977, .bpp = 4, + .xres = 320, .yres = 240, + + .hsync_len = 26, .vsync_len = 41, + .left_margin = 4, .upper_margin = 0, + .right_margin = 4, .lower_margin = 0, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .cmap_greyscale = 1, + .cmap_inverse = 1, + + .lccr0 = LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), + + .lcd_power = h3100_lcd_power, +}; static void __init h3100_map_io(void) { h3xxx_map_io(); - sa1100fb_lcd_power = h3100_lcd_power; - /* Older bootldrs put GPIO2-9 in alternate mode on the assumption that they are used for video */ GAFR &= ~0x000001fb; @@ -80,12 +98,15 @@ static void __init h3100_mach_init(void) { h3xxx_init_gpio(h3100_default_gpio, ARRAY_SIZE(h3100_default_gpio)); h3xxx_mach_init(); + + sa11x0_register_lcd(&h3100_lcd_info); sa11x0_register_irda(&h3100_irda_data); } MACHINE_START(H3100, "Compaq iPAQ H3100") .atag_offset = 0x100, .map_io = h3100_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = h3100_mach_init, diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c index 6b58e74..cb6659f 100644 --- a/arch/arm/mach-sa1100/h3600.c +++ b/arch/arm/mach-sa1100/h3600.c @@ -14,11 +14,14 @@ #include <linux/kernel.h> #include <linux/gpio.h> +#include <video/sa1100fb.h> + #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/irda.h> #include <mach/h3xxx.h> +#include <mach/irqs.h> #include "generic.h" @@ -56,11 +59,35 @@ err2: gpio_free(H3XXX_EGPIO_LCD_ON); err1: return; } +static const struct sa1100fb_rgb h3600_rgb_16 = { + .red = { .offset = 12, .length = 4, }, + .green = { .offset = 7, .length = 4, }, + .blue = { .offset = 1, .length = 4, }, + .transp = { .offset = 0, .length = 0, }, +}; + +static struct sa1100fb_mach_info h3600_lcd_info = { + .pixclock = 174757, .bpp = 16, + .xres = 320, .yres = 240, + + .hsync_len = 3, .vsync_len = 3, + .left_margin = 12, .upper_margin = 10, + .right_margin = 17, .lower_margin = 1, + + .cmap_static = 1, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), + + .rgb[RGB_16] = &h3600_rgb_16, + + .lcd_power = h3600_lcd_power, +}; + + static void __init h3600_map_io(void) { h3xxx_map_io(); - - sa1100fb_lcd_power = h3600_lcd_power; } /* @@ -121,12 +148,15 @@ static void __init h3600_mach_init(void) { h3xxx_init_gpio(h3600_default_gpio, ARRAY_SIZE(h3600_default_gpio)); h3xxx_mach_init(); + + sa11x0_register_lcd(&h3600_lcd_info); sa11x0_register_irda(&h3600_irda_data); } MACHINE_START(H3600, "Compaq iPAQ H3600") .atag_offset = 0x100, .map_io = h3600_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = h3600_mach_init, diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c index b0784c9..63150e1 100644 --- a/arch/arm/mach-sa1100/h3xxx.c +++ b/arch/arm/mach-sa1100/h3xxx.c @@ -109,11 +109,8 @@ static struct flash_platform_data h3xxx_flash_data = { .nr_parts = ARRAY_SIZE(h3xxx_partitions), }; -static struct resource h3xxx_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; +static struct resource h3xxx_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M); /* @@ -186,11 +183,7 @@ static struct sa1100_port_fns h3xxx_port_fns __initdata = { */ static struct resource egpio_resources[] = { - [0] = { - .start = H3600_EGPIO_PHYS, - .end = H3600_EGPIO_PHYS + 0x4 - 1, - .flags = IORESOURCE_MEM, - }, + [0] = DEFINE_RES_MEM(H3600_EGPIO_PHYS, 0x4), }; static struct htc_egpio_chip egpio_chips[] = { diff --git a/arch/arm/mach-sa1100/hackkit.c b/arch/arm/mach-sa1100/hackkit.c index c01bb36..5535475 100644 --- a/arch/arm/mach-sa1100/hackkit.c +++ b/arch/arm/mach-sa1100/hackkit.c @@ -22,12 +22,10 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/setup.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/irq.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> @@ -35,6 +33,9 @@ #include <asm/mach/irq.h> #include <asm/mach/serial_sa1100.h> +#include <mach/hardware.h> +#include <mach/irqs.h> + #include "generic.h" /********************************************************************** @@ -179,11 +180,8 @@ static struct flash_platform_data hackkit_flash_data = { .nr_parts = ARRAY_SIZE(hackkit_partitions), }; -static struct resource hackkit_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M, - .flags = IORESOURCE_MEM, -}; +static struct resource hackkit_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M); static void __init hackkit_init(void) { @@ -197,6 +195,7 @@ static void __init hackkit_init(void) MACHINE_START(HACKKIT, "HackKit Cpu Board") .atag_offset = 0x100, .map_io = hackkit_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = hackkit_init, diff --git a/arch/arm/mach-sa1100/include/mach/SA-1100.h b/arch/arm/mach-sa1100/include/mach/SA-1100.h index bae8296..3f2d1b6 100644 --- a/arch/arm/mach-sa1100/include/mach/SA-1100.h +++ b/arch/arm/mach-sa1100/include/mach/SA-1100.h @@ -1590,224 +1590,9 @@ /* * Direct Memory Access (DMA) control registers - * - * Registers - * DDAR0 Direct Memory Access (DMA) Device Address Register - * channel 0 (read/write). - * DCSR0 Direct Memory Access (DMA) Control and Status - * Register channel 0 (read/write). - * DBSA0 Direct Memory Access (DMA) Buffer Start address - * register A channel 0 (read/write). - * DBTA0 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 0 (read/write). - * DBSB0 Direct Memory Access (DMA) Buffer Start address - * register B channel 0 (read/write). - * DBTB0 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 0 (read/write). - * - * DDAR1 Direct Memory Access (DMA) Device Address Register - * channel 1 (read/write). - * DCSR1 Direct Memory Access (DMA) Control and Status - * Register channel 1 (read/write). - * DBSA1 Direct Memory Access (DMA) Buffer Start address - * register A channel 1 (read/write). - * DBTA1 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 1 (read/write). - * DBSB1 Direct Memory Access (DMA) Buffer Start address - * register B channel 1 (read/write). - * DBTB1 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 1 (read/write). - * - * DDAR2 Direct Memory Access (DMA) Device Address Register - * channel 2 (read/write). - * DCSR2 Direct Memory Access (DMA) Control and Status - * Register channel 2 (read/write). - * DBSA2 Direct Memory Access (DMA) Buffer Start address - * register A channel 2 (read/write). - * DBTA2 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 2 (read/write). - * DBSB2 Direct Memory Access (DMA) Buffer Start address - * register B channel 2 (read/write). - * DBTB2 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 2 (read/write). - * - * DDAR3 Direct Memory Access (DMA) Device Address Register - * channel 3 (read/write). - * DCSR3 Direct Memory Access (DMA) Control and Status - * Register channel 3 (read/write). - * DBSA3 Direct Memory Access (DMA) Buffer Start address - * register A channel 3 (read/write). - * DBTA3 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 3 (read/write). - * DBSB3 Direct Memory Access (DMA) Buffer Start address - * register B channel 3 (read/write). - * DBTB3 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 3 (read/write). - * - * DDAR4 Direct Memory Access (DMA) Device Address Register - * channel 4 (read/write). - * DCSR4 Direct Memory Access (DMA) Control and Status - * Register channel 4 (read/write). - * DBSA4 Direct Memory Access (DMA) Buffer Start address - * register A channel 4 (read/write). - * DBTA4 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 4 (read/write). - * DBSB4 Direct Memory Access (DMA) Buffer Start address - * register B channel 4 (read/write). - * DBTB4 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 4 (read/write). - * - * DDAR5 Direct Memory Access (DMA) Device Address Register - * channel 5 (read/write). - * DCSR5 Direct Memory Access (DMA) Control and Status - * Register channel 5 (read/write). - * DBSA5 Direct Memory Access (DMA) Buffer Start address - * register A channel 5 (read/write). - * DBTA5 Direct Memory Access (DMA) Buffer Transfer count - * register A channel 5 (read/write). - * DBSB5 Direct Memory Access (DMA) Buffer Start address - * register B channel 5 (read/write). - * DBTB5 Direct Memory Access (DMA) Buffer Transfer count - * register B channel 5 (read/write). */ - -#define DMASp 0x00000020 /* DMA control reg. Space [byte] */ - -#define DDAR(Nb) __REG(0xB0000000 + (Nb)*DMASp) /* DMA Device Address Reg. channel [0..5] */ -#define SetDCSR(Nb) __REG(0xB0000004 + (Nb)*DMASp) /* Set DMA Control & Status Reg. channel [0..5] (write) */ -#define ClrDCSR(Nb) __REG(0xB0000008 + (Nb)*DMASp) /* Clear DMA Control & Status Reg. channel [0..5] (write) */ -#define RdDCSR(Nb) __REG(0xB000000C + (Nb)*DMASp) /* Read DMA Control & Status Reg. channel [0..5] (read) */ -#define DBSA(Nb) __REG(0xB0000010 + (Nb)*DMASp) /* DMA Buffer Start address reg. A channel [0..5] */ -#define DBTA(Nb) __REG(0xB0000014 + (Nb)*DMASp) /* DMA Buffer Transfer count reg. A channel [0..5] */ -#define DBSB(Nb) __REG(0xB0000018 + (Nb)*DMASp) /* DMA Buffer Start address reg. B channel [0..5] */ -#define DBTB(Nb) __REG(0xB000001C + (Nb)*DMASp) /* DMA Buffer Transfer count reg. B channel [0..5] */ - -#define DDAR_RW 0x00000001 /* device data Read/Write */ -#define DDAR_DevWr (DDAR_RW*0) /* Device data Write */ - /* (memory -> device) */ -#define DDAR_DevRd (DDAR_RW*1) /* Device data Read */ - /* (device -> memory) */ -#define DDAR_E 0x00000002 /* big/little Endian device */ -#define DDAR_LtlEnd (DDAR_E*0) /* Little Endian device */ -#define DDAR_BigEnd (DDAR_E*1) /* Big Endian device */ -#define DDAR_BS 0x00000004 /* device Burst Size */ -#define DDAR_Brst4 (DDAR_BS*0) /* Burst-of-4 device */ -#define DDAR_Brst8 (DDAR_BS*1) /* Burst-of-8 device */ -#define DDAR_DW 0x00000008 /* device Data Width */ -#define DDAR_8BitDev (DDAR_DW*0) /* 8-Bit Device */ -#define DDAR_16BitDev (DDAR_DW*1) /* 16-Bit Device */ -#define DDAR_DS Fld (4, 4) /* Device Select */ -#define DDAR_Ser0UDCTr /* Ser. port 0 UDC Transmit */ \ - (0x0 << FShft (DDAR_DS)) -#define DDAR_Ser0UDCRc /* Ser. port 0 UDC Receive */ \ - (0x1 << FShft (DDAR_DS)) -#define DDAR_Ser1SDLCTr /* Ser. port 1 SDLC Transmit */ \ - (0x2 << FShft (DDAR_DS)) -#define DDAR_Ser1SDLCRc /* Ser. port 1 SDLC Receive */ \ - (0x3 << FShft (DDAR_DS)) -#define DDAR_Ser1UARTTr /* Ser. port 1 UART Transmit */ \ - (0x4 << FShft (DDAR_DS)) -#define DDAR_Ser1UARTRc /* Ser. port 1 UART Receive */ \ - (0x5 << FShft (DDAR_DS)) -#define DDAR_Ser2ICPTr /* Ser. port 2 ICP Transmit */ \ - (0x6 << FShft (DDAR_DS)) -#define DDAR_Ser2ICPRc /* Ser. port 2 ICP Receive */ \ - (0x7 << FShft (DDAR_DS)) -#define DDAR_Ser3UARTTr /* Ser. port 3 UART Transmit */ \ - (0x8 << FShft (DDAR_DS)) -#define DDAR_Ser3UARTRc /* Ser. port 3 UART Receive */ \ - (0x9 << FShft (DDAR_DS)) -#define DDAR_Ser4MCP0Tr /* Ser. port 4 MCP 0 Transmit */ \ - /* (audio) */ \ - (0xA << FShft (DDAR_DS)) -#define DDAR_Ser4MCP0Rc /* Ser. port 4 MCP 0 Receive */ \ - /* (audio) */ \ - (0xB << FShft (DDAR_DS)) -#define DDAR_Ser4MCP1Tr /* Ser. port 4 MCP 1 Transmit */ \ - /* (telecom) */ \ - (0xC << FShft (DDAR_DS)) -#define DDAR_Ser4MCP1Rc /* Ser. port 4 MCP 1 Receive */ \ - /* (telecom) */ \ - (0xD << FShft (DDAR_DS)) -#define DDAR_Ser4SSPTr /* Ser. port 4 SSP Transmit */ \ - (0xE << FShft (DDAR_DS)) -#define DDAR_Ser4SSPRc /* Ser. port 4 SSP Receive */ \ - (0xF << FShft (DDAR_DS)) -#define DDAR_DA Fld (24, 8) /* Device Address */ -#define DDAR_DevAdd(Add) /* Device Address */ \ - (((Add) & 0xF0000000) | \ - (((Add) & 0X003FFFFC) << (FShft (DDAR_DA) - 2))) -#define DDAR_Ser0UDCWr /* Ser. port 0 UDC Write */ \ - (DDAR_DevWr + DDAR_Brst8 + DDAR_8BitDev + \ - DDAR_Ser0UDCTr + DDAR_DevAdd (__PREG(Ser0UDCDR))) -#define DDAR_Ser0UDCRd /* Ser. port 0 UDC Read */ \ - (DDAR_DevRd + DDAR_Brst8 + DDAR_8BitDev + \ - DDAR_Ser0UDCRc + DDAR_DevAdd (__PREG(Ser0UDCDR))) -#define DDAR_Ser1UARTWr /* Ser. port 1 UART Write */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser1UARTTr + DDAR_DevAdd (__PREG(Ser1UTDR))) -#define DDAR_Ser1UARTRd /* Ser. port 1 UART Read */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser1UARTRc + DDAR_DevAdd (__PREG(Ser1UTDR))) -#define DDAR_Ser1SDLCWr /* Ser. port 1 SDLC Write */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser1SDLCTr + DDAR_DevAdd (__PREG(Ser1SDDR))) -#define DDAR_Ser1SDLCRd /* Ser. port 1 SDLC Read */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser1SDLCRc + DDAR_DevAdd (__PREG(Ser1SDDR))) -#define DDAR_Ser2UARTWr /* Ser. port 2 UART Write */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser2ICPTr + DDAR_DevAdd (__PREG(Ser2UTDR))) -#define DDAR_Ser2UARTRd /* Ser. port 2 UART Read */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser2ICPRc + DDAR_DevAdd (__PREG(Ser2UTDR))) -#define DDAR_Ser2HSSPWr /* Ser. port 2 HSSP Write */ \ - (DDAR_DevWr + DDAR_Brst8 + DDAR_8BitDev + \ - DDAR_Ser2ICPTr + DDAR_DevAdd (__PREG(Ser2HSDR))) -#define DDAR_Ser2HSSPRd /* Ser. port 2 HSSP Read */ \ - (DDAR_DevRd + DDAR_Brst8 + DDAR_8BitDev + \ - DDAR_Ser2ICPRc + DDAR_DevAdd (__PREG(Ser2HSDR))) -#define DDAR_Ser3UARTWr /* Ser. port 3 UART Write */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser3UARTTr + DDAR_DevAdd (__PREG(Ser3UTDR))) -#define DDAR_Ser3UARTRd /* Ser. port 3 UART Read */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev + \ - DDAR_Ser3UARTRc + DDAR_DevAdd (__PREG(Ser3UTDR))) -#define DDAR_Ser4MCP0Wr /* Ser. port 4 MCP 0 Write (audio) */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4MCP0Tr + DDAR_DevAdd (__PREG(Ser4MCDR0))) -#define DDAR_Ser4MCP0Rd /* Ser. port 4 MCP 0 Read (audio) */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4MCP0Rc + DDAR_DevAdd (__PREG(Ser4MCDR0))) -#define DDAR_Ser4MCP1Wr /* Ser. port 4 MCP 1 Write */ \ - /* (telecom) */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4MCP1Tr + DDAR_DevAdd (__PREG(Ser4MCDR1))) -#define DDAR_Ser4MCP1Rd /* Ser. port 4 MCP 1 Read */ \ - /* (telecom) */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4MCP1Rc + DDAR_DevAdd (__PREG(Ser4MCDR1))) -#define DDAR_Ser4SSPWr /* Ser. port 4 SSP Write (16 bits) */ \ - (DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4SSPTr + DDAR_DevAdd (__PREG(Ser4SSDR))) -#define DDAR_Ser4SSPRd /* Ser. port 4 SSP Read (16 bits) */ \ - (DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \ - DDAR_Ser4SSPRc + DDAR_DevAdd (__PREG(Ser4SSDR))) - -#define DCSR_RUN 0x00000001 /* DMA running */ -#define DCSR_IE 0x00000002 /* DMA Interrupt Enable */ -#define DCSR_ERROR 0x00000004 /* DMA ERROR */ -#define DCSR_DONEA 0x00000008 /* DONE DMA transfer buffer A */ -#define DCSR_STRTA 0x00000010 /* STaRTed DMA transfer buffer A */ -#define DCSR_DONEB 0x00000020 /* DONE DMA transfer buffer B */ -#define DCSR_STRTB 0x00000040 /* STaRTed DMA transfer buffer B */ -#define DCSR_BIU 0x00000080 /* DMA Buffer In Use */ -#define DCSR_BufA (DCSR_BIU*0) /* DMA Buffer A in use */ -#define DCSR_BufB (DCSR_BIU*1) /* DMA Buffer B in use */ - -#define DBT_TC Fld (13, 0) /* Transfer Count */ -#define DBTA_TCA DBT_TC /* Transfer Count buffer A */ -#define DBTB_TCB DBT_TC /* Transfer Count buffer B */ +#define DMA_SIZE (6 * 0x20) +#define DMA_PHYS 0xb0000000 /* @@ -1903,16 +1688,6 @@ #define LCD_Int100_0A 0xF /* LCD Intensity = 100.0% = 1 */ /* (Alternative) */ -#define LCCR0 __REG(0xB0100000) /* LCD Control Reg. 0 */ -#define LCSR __REG(0xB0100004) /* LCD Status Reg. */ -#define DBAR1 __REG(0xB0100010) /* LCD DMA Base Address Reg. channel 1 */ -#define DCAR1 __REG(0xB0100014) /* LCD DMA Current Address Reg. channel 1 */ -#define DBAR2 __REG(0xB0100018) /* LCD DMA Base Address Reg. channel 2 */ -#define DCAR2 __REG(0xB010001C) /* LCD DMA Current Address Reg. channel 2 */ -#define LCCR1 __REG(0xB0100020) /* LCD Control Reg. 1 */ -#define LCCR2 __REG(0xB0100024) /* LCD Control Reg. 2 */ -#define LCCR3 __REG(0xB0100028) /* LCD Control Reg. 3 */ - #define LCCR0_LEN 0x00000001 /* LCD ENable */ #define LCCR0_CMS 0x00000002 /* Color/Monochrome display Select */ #define LCCR0_Color (LCCR0_CMS*0) /* Color display */ diff --git a/arch/arm/mach-sa1100/include/mach/dma.h b/arch/arm/mach-sa1100/include/mach/dma.h deleted file mode 100644 index dda1b35..0000000 --- a/arch/arm/mach-sa1100/include/mach/dma.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * arch/arm/mach-sa1100/include/mach/dma.h - * - * Generic SA1100 DMA support - * - * Copyright (C) 2000 Nicolas Pitre - * - */ - -#ifndef __ASM_ARCH_DMA_H -#define __ASM_ARCH_DMA_H - -#include "hardware.h" - - -/* - * The SA1100 has six internal DMA channels. - */ -#define SA1100_DMA_CHANNELS 6 - -/* - * Maximum physical DMA buffer size - */ -#define MAX_DMA_SIZE 0x1fff -#define CUT_DMA_SIZE 0x1000 - -/* - * All possible SA1100 devices a DMA channel can be attached to. - */ -typedef enum { - DMA_Ser0UDCWr = DDAR_Ser0UDCWr, /* Ser. port 0 UDC Write */ - DMA_Ser0UDCRd = DDAR_Ser0UDCRd, /* Ser. port 0 UDC Read */ - DMA_Ser1UARTWr = DDAR_Ser1UARTWr, /* Ser. port 1 UART Write */ - DMA_Ser1UARTRd = DDAR_Ser1UARTRd, /* Ser. port 1 UART Read */ - DMA_Ser1SDLCWr = DDAR_Ser1SDLCWr, /* Ser. port 1 SDLC Write */ - DMA_Ser1SDLCRd = DDAR_Ser1SDLCRd, /* Ser. port 1 SDLC Read */ - DMA_Ser2UARTWr = DDAR_Ser2UARTWr, /* Ser. port 2 UART Write */ - DMA_Ser2UARTRd = DDAR_Ser2UARTRd, /* Ser. port 2 UART Read */ - DMA_Ser2HSSPWr = DDAR_Ser2HSSPWr, /* Ser. port 2 HSSP Write */ - DMA_Ser2HSSPRd = DDAR_Ser2HSSPRd, /* Ser. port 2 HSSP Read */ - DMA_Ser3UARTWr = DDAR_Ser3UARTWr, /* Ser. port 3 UART Write */ - DMA_Ser3UARTRd = DDAR_Ser3UARTRd, /* Ser. port 3 UART Read */ - DMA_Ser4MCP0Wr = DDAR_Ser4MCP0Wr, /* Ser. port 4 MCP 0 Write (audio) */ - DMA_Ser4MCP0Rd = DDAR_Ser4MCP0Rd, /* Ser. port 4 MCP 0 Read (audio) */ - DMA_Ser4MCP1Wr = DDAR_Ser4MCP1Wr, /* Ser. port 4 MCP 1 Write */ - DMA_Ser4MCP1Rd = DDAR_Ser4MCP1Rd, /* Ser. port 4 MCP 1 Read */ - DMA_Ser4SSPWr = DDAR_Ser4SSPWr, /* Ser. port 4 SSP Write (16 bits) */ - DMA_Ser4SSPRd = DDAR_Ser4SSPRd /* Ser. port 4 SSP Read (16 bits) */ -} dma_device_t; - -typedef struct { - volatile u_long DDAR; - volatile u_long SetDCSR; - volatile u_long ClrDCSR; - volatile u_long RdDCSR; - volatile dma_addr_t DBSA; - volatile u_long DBTA; - volatile dma_addr_t DBSB; - volatile u_long DBTB; -} dma_regs_t; - -typedef void (*dma_callback_t)(void *data); - -/* - * DMA function prototypes - */ - -extern int sa1100_request_dma( dma_device_t device, const char *device_id, - dma_callback_t callback, void *data, - dma_regs_t **regs ); -extern void sa1100_free_dma( dma_regs_t *regs ); -extern int sa1100_start_dma( dma_regs_t *regs, dma_addr_t dma_ptr, u_int size ); -extern dma_addr_t sa1100_get_dma_pos(dma_regs_t *regs); -extern void sa1100_reset_dma(dma_regs_t *regs); - -/** - * sa1100_stop_dma - stop DMA in progress - * @regs: identifier for the channel to use - * - * This stops DMA without clearing buffer pointers. Unlike - * sa1100_clear_dma() this allows subsequent use of sa1100_resume_dma() - * or sa1100_get_dma_pos(). - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - **/ - -#define sa1100_stop_dma(regs) ((regs)->ClrDCSR = DCSR_IE|DCSR_RUN) - -/** - * sa1100_resume_dma - resume DMA on a stopped channel - * @regs: identifier for the channel to use - * - * This resumes DMA on a channel previously stopped with - * sa1100_stop_dma(). - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - **/ - -#define sa1100_resume_dma(regs) ((regs)->SetDCSR = DCSR_IE|DCSR_RUN) - -/** - * sa1100_clear_dma - clear DMA pointers - * @regs: identifier for the channel to use - * - * This clear any DMA state so the DMA engine is ready to restart - * with new buffers through sa1100_start_dma(). Any buffers in flight - * are discarded. - * - * The @regs identifier is provided by a successful call to - * sa1100_request_dma(). - **/ - -#define sa1100_clear_dma(regs) ((regs)->ClrDCSR = DCSR_IE|DCSR_RUN|DCSR_STRTA|DCSR_STRTB) - -#endif /* _ASM_ARCH_DMA_H */ diff --git a/arch/arm/mach-sa1100/include/mach/irqs.h b/arch/arm/mach-sa1100/include/mach/irqs.h index d18f21a..3790298 100644 --- a/arch/arm/mach-sa1100/include/mach/irqs.h +++ b/arch/arm/mach-sa1100/include/mach/irqs.h @@ -71,22 +71,19 @@ /* * Figure out the MAX IRQ number. * - * If we have an SA1111, the max IRQ is S1_BVD1_STSCHG+1. - * If we have an LoCoMo, the max IRQ is IRQ_BOARD_START + 4 - * Otherwise, we have the standard IRQs only. + * Neponset, SA1111 and UCB1x00 are sparse IRQ aware, so can dynamically + * allocate their IRQs above NR_IRQS. + * + * LoCoMo has 4 additional IRQs, but is not sparse IRQ aware, and so has + * to be included in the NR_IRQS calculation. */ -#ifdef CONFIG_SA1111 -#define NR_IRQS (IRQ_BOARD_END + 55) -#elif defined(CONFIG_SHARP_LOCOMO) -#define NR_IRQS (IRQ_BOARD_START + 4) +#ifdef CONFIG_SHARP_LOCOMO +#define NR_IRQS_LOCOMO 4 #else -#define NR_IRQS (IRQ_BOARD_START) +#define NR_IRQS_LOCOMO 0 #endif -/* - * Board specific IRQs. Define them here. - * Do not surround them with ifdefs. - */ -#define IRQ_NEPONSET_SMC9196 (IRQ_BOARD_START + 0) -#define IRQ_NEPONSET_USAR (IRQ_BOARD_START + 1) -#define IRQ_NEPONSET_SA1111 (IRQ_BOARD_START + 2) +#ifndef NR_IRQS +#define NR_IRQS (IRQ_BOARD_START + NR_IRQS_LOCOMO) +#endif +#define SA1100_NR_IRQS (IRQ_BOARD_START + NR_IRQS_LOCOMO) diff --git a/arch/arm/mach-sa1100/include/mach/mcp.h b/arch/arm/mach-sa1100/include/mach/mcp.h index ed1a331..4b2860ae 100644 --- a/arch/arm/mach-sa1100/include/mach/mcp.h +++ b/arch/arm/mach-sa1100/include/mach/mcp.h @@ -16,7 +16,7 @@ struct mcp_plat_data { u32 mccr0; u32 mccr1; unsigned int sclk_rate; - int gpio_base; + void *codec_pdata; }; #endif diff --git a/arch/arm/mach-sa1100/include/mach/neponset.h b/arch/arm/mach-sa1100/include/mach/neponset.h index ffe2bc4..5516a52 100644 --- a/arch/arm/mach-sa1100/include/mach/neponset.h +++ b/arch/arm/mach-sa1100/include/mach/neponset.h @@ -15,54 +15,6 @@ /* * Neponset definitions: */ - -#define NEPONSET_CPLD_BASE (0x10000000) -#define Nep_p2v( x ) ((x) - NEPONSET_CPLD_BASE + 0xf3000000) -#define Nep_v2p( x ) ((x) - 0xf3000000 + NEPONSET_CPLD_BASE) - -#define _IRR 0x10000024 /* Interrupt Reason Register */ -#define _AUD_CTL 0x100000c0 /* Audio controls (RW) */ -#define _MDM_CTL_0 0x100000b0 /* Modem control 0 (RW) */ -#define _MDM_CTL_1 0x100000b4 /* Modem control 1 (RW) */ -#define _NCR_0 0x100000a0 /* Control Register (RW) */ -#define _KP_X_OUT 0x10000090 /* Keypad row write (RW) */ -#define _KP_Y_IN 0x10000080 /* Keypad column read (RO) */ -#define _SWPK 0x10000020 /* Switch pack (RO) */ -#define _WHOAMI 0x10000000 /* System ID Register (RO) */ - -#define _LEDS 0x10000010 /* LEDs [31:0] (WO) */ - -#define IRR (*((volatile u_char *) Nep_p2v(_IRR))) -#define AUD_CTL (*((volatile u_char *) Nep_p2v(_AUD_CTL))) -#define MDM_CTL_0 (*((volatile u_char *) Nep_p2v(_MDM_CTL_0))) -#define MDM_CTL_1 (*((volatile u_char *) Nep_p2v(_MDM_CTL_1))) -#define NCR_0 (*((volatile u_char *) Nep_p2v(_NCR_0))) -#define KP_X_OUT (*((volatile u_char *) Nep_p2v(_KP_X_OUT))) -#define KP_Y_IN (*((volatile u_char *) Nep_p2v(_KP_Y_IN))) -#define SWPK (*((volatile u_char *) Nep_p2v(_SWPK))) -#define WHOAMI (*((volatile u_char *) Nep_p2v(_WHOAMI))) - -#define LEDS (*((volatile Word *) Nep_p2v(_LEDS))) - -#define IRR_ETHERNET (1<<0) -#define IRR_USAR (1<<1) -#define IRR_SA1111 (1<<2) - -#define AUD_SEL_1341 (1<<0) -#define AUD_MUTE_1341 (1<<1) - -#define MDM_CTL0_RTS1 (1 << 0) -#define MDM_CTL0_DTR1 (1 << 1) -#define MDM_CTL0_RTS2 (1 << 2) -#define MDM_CTL0_DTR2 (1 << 3) - -#define MDM_CTL1_CTS1 (1 << 0) -#define MDM_CTL1_DSR1 (1 << 1) -#define MDM_CTL1_DCD1 (1 << 2) -#define MDM_CTL1_CTS2 (1 << 3) -#define MDM_CTL1_DSR2 (1 << 4) -#define MDM_CTL1_DCD2 (1 << 5) - #define NCR_GP01_OFF (1<<0) #define NCR_TP_PWR_EN (1<<1) #define NCR_MS_PWR_EN (1<<2) @@ -71,4 +23,8 @@ #define NCR_A0VPP (1<<5) #define NCR_A1VPP (1<<6) +void neponset_ncr_frob(unsigned int, unsigned int); +#define neponset_ncr_set(v) neponset_ncr_frob(0, v) +#define neponset_ncr_clear(v) neponset_ncr_frob(v, 0) + #endif diff --git a/arch/arm/mach-sa1100/include/mach/shannon.h b/arch/arm/mach-sa1100/include/mach/shannon.h index ec27d6e..a0d1114 100644 --- a/arch/arm/mach-sa1100/include/mach/shannon.h +++ b/arch/arm/mach-sa1100/include/mach/shannon.h @@ -21,7 +21,7 @@ #define SHANNON_GPIO_U3_RTS GPIO_GPIO (19) /* ?? */ #define SHANNON_GPIO_U3_CTS GPIO_GPIO (20) /* ?? */ #define SHANNON_GPIO_SENSE_12V GPIO_GPIO (21) /* Input, 12v flash unprotect detected */ -#define SHANNON_GPIO_DISP_EN GPIO_GPIO (22) /* out */ +#define SHANNON_GPIO_DISP_EN 22 /* out */ /* XXX GPIO 23 unaccounted for */ #define SHANNON_GPIO_EJECT_0 GPIO_GPIO (24) /* in */ #define SHANNON_IRQ_GPIO_EJECT_0 IRQ_GPIO24 diff --git a/arch/arm/mach-sa1100/irq.c b/arch/arm/mach-sa1100/irq.c index dfbf824..516ccc2 100644 --- a/arch/arm/mach-sa1100/irq.c +++ b/arch/arm/mach-sa1100/irq.c @@ -17,6 +17,7 @@ #include <linux/syscore_ops.h> #include <mach/hardware.h> +#include <mach/irqs.h> #include <asm/mach/irq.h> #include "generic.h" @@ -221,11 +222,8 @@ static struct irq_chip sa1100_normal_chip = { .irq_set_wake = sa1100_set_wake, }; -static struct resource irq_resource = { - .name = "irqs", - .start = 0x90050000, - .end = 0x9005ffff, -}; +static struct resource irq_resource = + DEFINE_RES_MEM_NAMED(0x90050000, SZ_64K, "irqs"); static struct sa1100irq_state { unsigned int saved; diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c index ee121d6..ca7a7e8 100644 --- a/arch/arm/mach-sa1100/jornada720.c +++ b/arch/arm/mach-sa1100/jornada720.c @@ -23,9 +23,7 @@ #include <linux/mtd/partitions.h> #include <video/s1d13xxxfb.h> -#include <mach/hardware.h> #include <asm/hardware/sa1111.h> -#include <asm/irq.h> #include <asm/page.h> #include <asm/mach-types.h> #include <asm/setup.h> @@ -34,6 +32,9 @@ #include <asm/mach/map.h> #include <asm/mach/serial_sa1100.h> +#include <mach/hardware.h> +#include <mach/irqs.h> + #include "generic.h" /* @@ -46,7 +47,7 @@ /* memory space (line 52 of HP's doc) */ #define SA1111REGSTART 0x40000000 -#define SA1111REGLEN 0x00001fff +#define SA1111REGLEN 0x00002000 #define EPSONREGSTART 0x48000000 #define EPSONREGLEN 0x00100000 #define EPSONFBSTART 0x48200000 @@ -174,16 +175,8 @@ static struct s1d13xxxfb_pdata s1d13xxxfb_data = { }; static struct resource s1d13xxxfb_resources[] = { - [0] = { - .start = EPSONFBSTART, - .end = EPSONFBSTART + EPSONFBLEN, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = EPSONREGSTART, - .end = EPSONREGSTART + EPSONREGLEN, - .flags = IORESOURCE_MEM, - } + [0] = DEFINE_RES_MEM(EPSONFBSTART, EPSONFBLEN), + [1] = DEFINE_RES_MEM(EPSONREGSTART, EPSONREGLEN), }; static struct platform_device s1d13xxxfb_device = { @@ -197,20 +190,12 @@ static struct platform_device s1d13xxxfb_device = { }; static struct resource sa1111_resources[] = { - [0] = { - .start = SA1111REGSTART, - .end = SA1111REGSTART + SA1111REGLEN, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_GPIO1, - .end = IRQ_GPIO1, - .flags = IORESOURCE_IRQ, - }, + [0] = DEFINE_RES_MEM(SA1111REGSTART, SA1111REGLEN), + [1] = DEFINE_RES_IRQ(IRQ_GPIO1), }; static struct sa1111_platform_data sa1111_info = { - .irq_base = IRQ_BOARD_END, + .disable_devs = SA1111_DEVID_PS2_MSE, }; static u64 sa1111_dmamask = 0xffffffffUL; @@ -284,11 +269,6 @@ static struct map_desc jornada720_io_desc[] __initdata = { .pfn = __phys_to_pfn(EPSONFBSTART), .length = EPSONFBLEN, .type = MT_DEVICE - }, { /* SA-1111 */ - .virtual = 0xf4000000, - .pfn = __phys_to_pfn(SA1111REGSTART), - .length = SA1111REGLEN, - .type = MT_DEVICE } }; @@ -352,11 +332,8 @@ static struct flash_platform_data jornada720_flash_data = { .nr_parts = ARRAY_SIZE(jornada720_partitions), }; -static struct resource jornada720_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; +static struct resource jornada720_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M); static void __init jornada720_mach_init(void) { @@ -367,6 +344,7 @@ MACHINE_START(JORNADA720, "HP Jornada 720") /* Maintainer: Kristoffer Ericson <Kristoffer.Ericson@gmail.com> */ .atag_offset = 0x100, .map_io = jornada720_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = jornada720_mach_init, diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c index af4e276..eb6534e 100644 --- a/arch/arm/mach-sa1100/lart.c +++ b/arch/arm/mach-sa1100/lart.c @@ -6,6 +6,8 @@ #include <linux/kernel.h> #include <linux/tty.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/setup.h> #include <asm/mach-types.h> @@ -15,6 +17,7 @@ #include <asm/mach/map.h> #include <asm/mach/serial_sa1100.h> #include <mach/mcp.h> +#include <mach/irqs.h> #include "generic.h" @@ -26,8 +29,86 @@ static struct mcp_plat_data lart_mcp_data = { .sclk_rate = 11981000, }; +#ifdef LART_GREY_LCD +static struct sa1100fb_mach_info lart_grey_info = { + .pixclock = 150000, .bpp = 4, + .xres = 320, .yres = 240, + + .hsync_len = 1, .vsync_len = 1, + .left_margin = 4, .upper_margin = 0, + .right_margin = 2, .lower_margin = 0, + + .cmap_greyscale = 1, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono, + .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), +}; +#endif +#ifdef LART_COLOR_LCD +static struct sa1100fb_mach_info lart_color_info = { + .pixclock = 150000, .bpp = 16, + .xres = 320, .yres = 240, + + .hsync_len = 2, .vsync_len = 3, + .left_margin = 69, .upper_margin = 14, + .right_margin = 8, .lower_margin = 4, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), +}; +#endif +#ifdef LART_VIDEO_OUT +static struct sa1100fb_mach_info lart_video_info = { + .pixclock = 39721, .bpp = 16, + .xres = 640, .yres = 480, + + .hsync_len = 95, .vsync_len = 2, + .left_margin = 40, .upper_margin = 32, + .right_margin = 24, .lower_margin = 11, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), +}; +#endif + +#ifdef LART_KIT01_LCD +static struct sa1100fb_mach_info lart_kit01_info = { + .pixclock = 63291, .bpp = 16, + .xres = 640, .yres = 480, + + .hsync_len = 64, .vsync_len = 3, + .left_margin = 122, .upper_margin = 45, + .right_margin = 10, .lower_margin = 10, + + .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, + .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg +}; +#endif + static void __init lart_init(void) { + struct sa1100fb_mach_info *inf = NULL; + +#ifdef LART_GREY_LCD + inf = &lart_grey_info; +#endif +#ifdef LART_COLOR_LCD + inf = &lart_color_info; +#endif +#ifdef LART_VIDEO_OUT + inf = &lart_video_info; +#endif +#ifdef LART_KIT01_LCD + inf = &lart_kit01_info; +#endif + + if (inf) + sa11x0_register_lcd(inf); + + sa11x0_ppc_configure_mcp(); sa11x0_register_mcp(&lart_mcp_data); } @@ -63,6 +144,7 @@ static void __init lart_map_io(void) MACHINE_START(LART, "LART") .atag_offset = 0x100, .map_io = lart_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .init_machine = lart_init, .timer = &sa1100_timer, diff --git a/arch/arm/mach-sa1100/nanoengine.c b/arch/arm/mach-sa1100/nanoengine.c index 85f6ee6..8f6446b 100644 --- a/arch/arm/mach-sa1100/nanoengine.c +++ b/arch/arm/mach-sa1100/nanoengine.c @@ -28,6 +28,7 @@ #include <mach/hardware.h> #include <mach/nanoengine.h> +#include <mach/irqs.h> #include "generic.h" @@ -58,15 +59,8 @@ static struct flash_platform_data nanoengine_flash_data = { }; static struct resource nanoengine_flash_resources[] = { - { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, - }, { - .start = SA1100_CS1_PHYS, - .end = SA1100_CS1_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, - } + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M), + DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_32M), }; static struct map_desc nanoengine_io_desc[] __initdata = { @@ -114,6 +108,7 @@ static void __init nanoengine_init(void) MACHINE_START(NANOENGINE, "BSE nanoEngine") .atag_offset = 0x100, .map_io = nanoengine_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = nanoengine_init, diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c index b4fa53a..6c58f01 100644 --- a/arch/arm/mach-sa1100/neponset.c +++ b/arch/arm/mach-sa1100/neponset.c @@ -1,89 +1,104 @@ /* * linux/arch/arm/mach-sa1100/neponset.c - * */ -#include <linux/kernel.h> +#include <linux/err.h> #include <linux/init.h> -#include <linux/tty.h> #include <linux/ioport.h> -#include <linux/serial_core.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/serial_core.h> +#include <linux/slab.h> -#include <mach/hardware.h> #include <asm/mach-types.h> -#include <asm/irq.h> #include <asm/mach/map.h> -#include <asm/mach/irq.h> #include <asm/mach/serial_sa1100.h> -#include <mach/assabet.h> -#include <mach/neponset.h> #include <asm/hardware/sa1111.h> #include <asm/sizes.h> -/* - * Install handler for Neponset IRQ. Note that we have to loop here - * since the ETHERNET and USAR IRQs are level based, and we need to - * ensure that the IRQ signal is deasserted before returning. This - * is rather unfortunate. - */ -static void -neponset_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - unsigned int irr; - - while (1) { - /* - * Acknowledge the parent IRQ. - */ - desc->irq_data.chip->irq_ack(&desc->irq_data); - - /* - * Read the interrupt reason register. Let's have all - * active IRQ bits high. Note: there is a typo in the - * Neponset user's guide for the SA1111 IRR level. - */ - irr = IRR ^ (IRR_ETHERNET | IRR_USAR); - - if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) - break; - - /* - * Since there is no individual mask, we have to - * mask the parent IRQ. This is safe, since we'll - * recheck the register for any pending IRQs. - */ - if (irr & (IRR_ETHERNET | IRR_USAR)) { - desc->irq_data.chip->irq_mask(&desc->irq_data); - - /* - * Ack the interrupt now to prevent re-entering - * this neponset handler. Again, this is safe - * since we'll check the IRR register prior to - * leaving. - */ - desc->irq_data.chip->irq_ack(&desc->irq_data); +#include <mach/hardware.h> +#include <mach/assabet.h> +#include <mach/neponset.h> +#include <mach/irqs.h> + +#define NEP_IRQ_SMC91X 0 +#define NEP_IRQ_USAR 1 +#define NEP_IRQ_SA1111 2 +#define NEP_IRQ_NR 3 + +#define WHOAMI 0x00 +#define LEDS 0x10 +#define SWPK 0x20 +#define IRR 0x24 +#define KP_Y_IN 0x80 +#define KP_X_OUT 0x90 +#define NCR_0 0xa0 +#define MDM_CTL_0 0xb0 +#define MDM_CTL_1 0xb4 +#define AUD_CTL 0xc0 + +#define IRR_ETHERNET (1 << 0) +#define IRR_USAR (1 << 1) +#define IRR_SA1111 (1 << 2) + +#define MDM_CTL0_RTS1 (1 << 0) +#define MDM_CTL0_DTR1 (1 << 1) +#define MDM_CTL0_RTS2 (1 << 2) +#define MDM_CTL0_DTR2 (1 << 3) + +#define MDM_CTL1_CTS1 (1 << 0) +#define MDM_CTL1_DSR1 (1 << 1) +#define MDM_CTL1_DCD1 (1 << 2) +#define MDM_CTL1_CTS2 (1 << 3) +#define MDM_CTL1_DSR2 (1 << 4) +#define MDM_CTL1_DCD2 (1 << 5) + +#define AUD_SEL_1341 (1 << 0) +#define AUD_MUTE_1341 (1 << 1) - if (irr & IRR_ETHERNET) { - generic_handle_irq(IRQ_NEPONSET_SMC9196); - } +extern void sa1110_mb_disable(void); - if (irr & IRR_USAR) { - generic_handle_irq(IRQ_NEPONSET_USAR); - } +struct neponset_drvdata { + void __iomem *base; + struct platform_device *sa1111; + struct platform_device *smc91x; + unsigned irq_base; +#ifdef CONFIG_PM_SLEEP + u32 ncr0; + u32 mdm_ctl_0; +#endif +}; - desc->irq_data.chip->irq_unmask(&desc->irq_data); - } +static void __iomem *nep_base; - if (irr & IRR_SA1111) { - generic_handle_irq(IRQ_NEPONSET_SA1111); - } +void neponset_ncr_frob(unsigned int mask, unsigned int val) +{ + void __iomem *base = nep_base; + + if (base) { + unsigned long flags; + unsigned v; + + local_irq_save(flags); + v = readb_relaxed(base + NCR_0); + writeb_relaxed((v & ~mask) | val, base + NCR_0); + local_irq_restore(flags); + } else { + WARN(1, "nep_base unset\n"); } } static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) { - u_int mdm_ctl0 = MDM_CTL_0; + void __iomem *base = nep_base; + u_int mdm_ctl0; + if (!base) + return; + + mdm_ctl0 = readb_relaxed(base + MDM_CTL_0); if (port->mapbase == _Ser1UTCR0) { if (mctrl & TIOCM_RTS) mdm_ctl0 &= ~MDM_CTL0_RTS2; @@ -106,14 +121,19 @@ static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) mdm_ctl0 |= MDM_CTL0_DTR1; } - MDM_CTL_0 = mdm_ctl0; + writeb_relaxed(mdm_ctl0, base + MDM_CTL_0); } static u_int neponset_get_mctrl(struct uart_port *port) { + void __iomem *base = nep_base; u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; - u_int mdm_ctl1 = MDM_CTL_1; + u_int mdm_ctl1; + + if (!base) + return ret; + mdm_ctl1 = readb_relaxed(base + MDM_CTL_1); if (port->mapbase == _Ser1UTCR0) { if (mdm_ctl1 & MDM_CTL1_DCD2) ret &= ~TIOCM_CD; @@ -138,209 +158,278 @@ static struct sa1100_port_fns neponset_port_fns __devinitdata = { .get_mctrl = neponset_get_mctrl, }; -static int __devinit neponset_probe(struct platform_device *dev) +/* + * Install handler for Neponset IRQ. Note that we have to loop here + * since the ETHERNET and USAR IRQs are level based, and we need to + * ensure that the IRQ signal is deasserted before returning. This + * is rather unfortunate. + */ +static void neponset_irq_handler(unsigned int irq, struct irq_desc *desc) { - sa1100_register_uart_fns(&neponset_port_fns); + struct neponset_drvdata *d = irq_desc_get_handler_data(desc); + unsigned int irr; - /* - * Install handler for GPIO25. - */ - irq_set_irq_type(IRQ_GPIO25, IRQ_TYPE_EDGE_RISING); - irq_set_chained_handler(IRQ_GPIO25, neponset_irq_handler); + while (1) { + /* + * Acknowledge the parent IRQ. + */ + desc->irq_data.chip->irq_ack(&desc->irq_data); - /* - * We would set IRQ_GPIO25 to be a wake-up IRQ, but - * unfortunately something on the Neponset activates - * this IRQ on sleep (ethernet?) - */ -#if 0 - enable_irq_wake(IRQ_GPIO25); -#endif + /* + * Read the interrupt reason register. Let's have all + * active IRQ bits high. Note: there is a typo in the + * Neponset user's guide for the SA1111 IRR level. + */ + irr = readb_relaxed(d->base + IRR); + irr ^= IRR_ETHERNET | IRR_USAR; - /* - * Setup other Neponset IRQs. SA1111 will be done by the - * generic SA1111 code. - */ - irq_set_handler(IRQ_NEPONSET_SMC9196, handle_simple_irq); - set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); - irq_set_handler(IRQ_NEPONSET_USAR, handle_simple_irq); - set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); + if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) + break; - /* - * Disable GPIO 0/1 drivers so the buttons work on the module. - */ - NCR_0 = NCR_GP01_OFF; + /* + * Since there is no individual mask, we have to + * mask the parent IRQ. This is safe, since we'll + * recheck the register for any pending IRQs. + */ + if (irr & (IRR_ETHERNET | IRR_USAR)) { + desc->irq_data.chip->irq_mask(&desc->irq_data); - return 0; -} + /* + * Ack the interrupt now to prevent re-entering + * this neponset handler. Again, this is safe + * since we'll check the IRR register prior to + * leaving. + */ + desc->irq_data.chip->irq_ack(&desc->irq_data); -#ifdef CONFIG_PM + if (irr & IRR_ETHERNET) + generic_handle_irq(d->irq_base + NEP_IRQ_SMC91X); -/* - * LDM power management. - */ -static unsigned int neponset_saved_state; + if (irr & IRR_USAR) + generic_handle_irq(d->irq_base + NEP_IRQ_USAR); -static int neponset_suspend(struct platform_device *dev, pm_message_t state) -{ - /* - * Save state. - */ - neponset_saved_state = NCR_0; + desc->irq_data.chip->irq_unmask(&desc->irq_data); + } - return 0; + if (irr & IRR_SA1111) + generic_handle_irq(d->irq_base + NEP_IRQ_SA1111); + } } -static int neponset_resume(struct platform_device *dev) +/* Yes, we really do not have any kind of masking or unmasking */ +static void nochip_noop(struct irq_data *irq) { - NCR_0 = neponset_saved_state; - - return 0; } -#else -#define neponset_suspend NULL -#define neponset_resume NULL -#endif - -static struct platform_driver neponset_device_driver = { - .probe = neponset_probe, - .suspend = neponset_suspend, - .resume = neponset_resume, - .driver = { - .name = "neponset", - }, -}; - -static struct resource neponset_resources[] = { - [0] = { - .start = 0x10000000, - .end = 0x17ffffff, - .flags = IORESOURCE_MEM, - }, -}; - -static struct platform_device neponset_device = { - .name = "neponset", - .id = 0, - .num_resources = ARRAY_SIZE(neponset_resources), - .resource = neponset_resources, -}; - -static struct resource sa1111_resources[] = { - [0] = { - .start = 0x40000000, - .end = 0x40001fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_NEPONSET_SA1111, - .end = IRQ_NEPONSET_SA1111, - .flags = IORESOURCE_IRQ, - }, +static struct irq_chip nochip = { + .name = "neponset", + .irq_ack = nochip_noop, + .irq_mask = nochip_noop, + .irq_unmask = nochip_noop, }; static struct sa1111_platform_data sa1111_info = { - .irq_base = IRQ_BOARD_END, + .disable_devs = SA1111_DEVID_PS2_MSE, }; -static u64 sa1111_dmamask = 0xffffffffUL; +static int __devinit neponset_probe(struct platform_device *dev) +{ + struct neponset_drvdata *d; + struct resource *nep_res, *sa1111_res, *smc91x_res; + struct resource sa1111_resources[] = { + DEFINE_RES_MEM(0x40000000, SZ_8K), + { .flags = IORESOURCE_IRQ }, + }; + struct platform_device_info sa1111_devinfo = { + .parent = &dev->dev, + .name = "sa1111", + .id = 0, + .res = sa1111_resources, + .num_res = ARRAY_SIZE(sa1111_resources), + .data = &sa1111_info, + .size_data = sizeof(sa1111_info), + .dma_mask = 0xffffffffUL, + }; + struct resource smc91x_resources[] = { + DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS, + 0x02000000, "smc91x-regs"), + DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS + 0x02000000, + 0x02000000, "smc91x-attrib"), + { .flags = IORESOURCE_IRQ }, + }; + struct platform_device_info smc91x_devinfo = { + .parent = &dev->dev, + .name = "smc91x", + .id = 0, + .res = smc91x_resources, + .num_res = ARRAY_SIZE(smc91x_resources), + }; + int ret, irq; + + if (nep_base) + return -EBUSY; + + irq = ret = platform_get_irq(dev, 0); + if (ret < 0) + goto err_alloc; + + nep_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + smc91x_res = platform_get_resource(dev, IORESOURCE_MEM, 1); + sa1111_res = platform_get_resource(dev, IORESOURCE_MEM, 2); + if (!nep_res || !smc91x_res || !sa1111_res) { + ret = -ENXIO; + goto err_alloc; + } -static struct platform_device sa1111_device = { - .name = "sa1111", - .id = 0, - .dev = { - .dma_mask = &sa1111_dmamask, - .coherent_dma_mask = 0xffffffff, - .platform_data = &sa1111_info, - }, - .num_resources = ARRAY_SIZE(sa1111_resources), - .resource = sa1111_resources, -}; + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto err_alloc; + } -static struct resource smc91x_resources[] = { - [0] = { - .name = "smc91x-regs", - .start = SA1100_CS3_PHYS, - .end = SA1100_CS3_PHYS + 0x01ffffff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_NEPONSET_SMC9196, - .end = IRQ_NEPONSET_SMC9196, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .name = "smc91x-attrib", - .start = SA1100_CS3_PHYS + 0x02000000, - .end = SA1100_CS3_PHYS + 0x03ffffff, - .flags = IORESOURCE_MEM, - }, -}; + d->base = ioremap(nep_res->start, SZ_4K); + if (!d->base) { + ret = -ENOMEM; + goto err_ioremap; + } -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; + if (readb_relaxed(d->base + WHOAMI) != 0x11) { + dev_warn(&dev->dev, "Neponset board detected, but wrong ID: %02x\n", + readb_relaxed(d->base + WHOAMI)); + ret = -ENODEV; + goto err_id; + } -static struct platform_device *devices[] __initdata = { - &neponset_device, - &sa1111_device, - &smc91x_device, -}; + ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1); + if (ret <= 0) { + dev_err(&dev->dev, "unable to allocate %u irqs: %d\n", + NEP_IRQ_NR, ret); + if (ret == 0) + ret = -ENOMEM; + goto err_irq_alloc; + } -extern void sa1110_mb_disable(void); + d->irq_base = ret; -static int __init neponset_init(void) -{ - platform_driver_register(&neponset_device_driver); + irq_set_chip_and_handler(d->irq_base + NEP_IRQ_SMC91X, &nochip, + handle_simple_irq); + set_irq_flags(d->irq_base + NEP_IRQ_SMC91X, IRQF_VALID | IRQF_PROBE); + irq_set_chip_and_handler(d->irq_base + NEP_IRQ_USAR, &nochip, + handle_simple_irq); + set_irq_flags(d->irq_base + NEP_IRQ_USAR, IRQF_VALID | IRQF_PROBE); + irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip); - /* - * The Neponset is only present on the Assabet machine type. - */ - if (!machine_is_assabet()) - return -ENODEV; + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(irq, d); + irq_set_chained_handler(irq, neponset_irq_handler); /* - * Ensure that the memory bus request/grant signals are setup, - * and the grant is held in its inactive state, whether or not - * we actually have a Neponset attached. + * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately + * something on the Neponset activates this IRQ on sleep (eth?) */ +#if 0 + enable_irq_wake(irq); +#endif + + dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n", + d->irq_base, d->irq_base + NEP_IRQ_NR - 1); + nep_base = d->base; + + sa1100_register_uart_fns(&neponset_port_fns); + + /* Ensure that the memory bus request/grant signals are setup */ sa1110_mb_disable(); - if (!machine_has_neponset()) { - printk(KERN_DEBUG "Neponset expansion board not present\n"); - return -ENODEV; - } + /* Disable GPIO 0/1 drivers so the buttons work on the Assabet */ + writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0); - if (WHOAMI != 0x11) { - printk(KERN_WARNING "Neponset board detected, but " - "wrong ID: %02x\n", WHOAMI); - return -ENODEV; - } + sa1111_resources[0].parent = sa1111_res; + sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111; + sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111; + d->sa1111 = platform_device_register_full(&sa1111_devinfo); + + smc91x_resources[0].parent = smc91x_res; + smc91x_resources[1].parent = smc91x_res; + smc91x_resources[2].start = d->irq_base + NEP_IRQ_SMC91X; + smc91x_resources[2].end = d->irq_base + NEP_IRQ_SMC91X; + d->smc91x = platform_device_register_full(&smc91x_devinfo); + + platform_set_drvdata(dev, d); - return platform_add_devices(devices, ARRAY_SIZE(devices)); + return 0; + + err_irq_alloc: + err_id: + iounmap(d->base); + err_ioremap: + kfree(d); + err_alloc: + return ret; } -subsys_initcall(neponset_init); +static int __devexit neponset_remove(struct platform_device *dev) +{ + struct neponset_drvdata *d = platform_get_drvdata(dev); + int irq = platform_get_irq(dev, 0); + + if (!IS_ERR(d->sa1111)) + platform_device_unregister(d->sa1111); + if (!IS_ERR(d->smc91x)) + platform_device_unregister(d->smc91x); + irq_set_chained_handler(irq, NULL); + irq_free_descs(d->irq_base, NEP_IRQ_NR); + nep_base = NULL; + iounmap(d->base); + kfree(d); -static struct map_desc neponset_io_desc[] __initdata = { - { /* System Registers */ - .virtual = 0xf3000000, - .pfn = __phys_to_pfn(0x10000000), - .length = SZ_1M, - .type = MT_DEVICE - }, { /* SA-1111 */ - .virtual = 0xf4000000, - .pfn = __phys_to_pfn(0x40000000), - .length = SZ_1M, - .type = MT_DEVICE - } + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int neponset_suspend(struct device *dev) +{ + struct neponset_drvdata *d = dev_get_drvdata(dev); + + d->ncr0 = readb_relaxed(d->base + NCR_0); + d->mdm_ctl_0 = readb_relaxed(d->base + MDM_CTL_0); + + return 0; +} + +static int neponset_resume(struct device *dev) +{ + struct neponset_drvdata *d = dev_get_drvdata(dev); + + writeb_relaxed(d->ncr0, d->base + NCR_0); + writeb_relaxed(d->mdm_ctl_0, d->base + MDM_CTL_0); + + return 0; +} + +static const struct dev_pm_ops neponset_pm_ops = { + .suspend_noirq = neponset_suspend, + .resume_noirq = neponset_resume, + .freeze_noirq = neponset_suspend, + .restore_noirq = neponset_resume, +}; +#define PM_OPS &neponset_pm_ops +#else +#define PM_OPS NULL +#endif + +static struct platform_driver neponset_device_driver = { + .probe = neponset_probe, + .remove = __devexit_p(neponset_remove), + .driver = { + .name = "neponset", + .owner = THIS_MODULE, + .pm = PM_OPS, + }, }; -void __init neponset_map_io(void) +static int __init neponset_init(void) { - iotable_init(neponset_io_desc, ARRAY_SIZE(neponset_io_desc)); + return platform_driver_register(&neponset_device_driver); } + +subsys_initcall(neponset_init); diff --git a/arch/arm/mach-sa1100/pci-nanoengine.c b/arch/arm/mach-sa1100/pci-nanoengine.c index 0d01ca7..41bb018 100644 --- a/arch/arm/mach-sa1100/pci-nanoengine.c +++ b/arch/arm/mach-sa1100/pci-nanoengine.c @@ -135,12 +135,8 @@ struct pci_bus * __init pci_nanoengine_scan_bus(int nr, struct pci_sys_data *sys &sys->resources); } -static struct resource pci_io_ports = { - .name = "PCI IO", - .start = 0x400, - .end = 0x7FF, - .flags = IORESOURCE_IO, -}; +static struct resource pci_io_ports = + DEFINE_RES_IO_NAMED(0x400, 0x400, "PCI IO"); static struct resource pci_non_prefetchable_memory = { .name = "PCI non-prefetchable", diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c index 9307df0..1602575 100644 --- a/arch/arm/mach-sa1100/pleb.c +++ b/arch/arm/mach-sa1100/pleb.c @@ -37,17 +37,9 @@ #define IRQ_GPIO_ETH0_IRQ IRQ_GPIO21 static struct resource smc91x_resources[] = { - [0] = { - .start = PLEB_ETH0_P, - .end = PLEB_ETH0_P | 0x03ffffff, - .flags = IORESOURCE_MEM, - }, + [0] = DEFINE_RES_MEM(PLEB_ETH0_P, 0x04000000), #if 0 /* Autoprobe instead, to get rising/falling edge characteristic right */ - [1] = { - .start = IRQ_GPIO_ETH0_IRQ, - .end = IRQ_GPIO_ETH0_IRQ, - .flags = IORESOURCE_IRQ, - }, + [1] = DEFINE_RES_IRQ(IRQ_GPIO_ETH0_IRQ), #endif }; @@ -70,16 +62,8 @@ static struct platform_device *devices[] __initdata = { * the two SA1100 lowest chip select outputs. */ static struct resource pleb_flash_resources[] = { - [0] = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_8M - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = SA1100_CS1_PHYS, - .end = SA1100_CS1_PHYS + SZ_8M - 1, - .flags = IORESOURCE_MEM, - } + [0] = DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_8M), + [1] = DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_8M), }; @@ -147,6 +131,7 @@ static void __init pleb_map_io(void) MACHINE_START(PLEB, "PLEB") .map_io = pleb_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = pleb_init, diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c index 318b2b7..ca8bf59 100644 --- a/arch/arm/mach-sa1100/shannon.c +++ b/arch/arm/mach-sa1100/shannon.c @@ -9,6 +9,8 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/setup.h> @@ -19,6 +21,7 @@ #include <asm/mach/serial_sa1100.h> #include <mach/mcp.h> #include <mach/shannon.h> +#include <mach/irqs.h> #include "generic.h" @@ -46,19 +49,32 @@ static struct flash_platform_data shannon_flash_data = { .nr_parts = ARRAY_SIZE(shannon_partitions), }; -static struct resource shannon_flash_resource = { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_4M - 1, - .flags = IORESOURCE_MEM, -}; +static struct resource shannon_flash_resource = + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_4M); static struct mcp_plat_data shannon_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, }; +static struct sa1100fb_mach_info shannon_lcd_info = { + .pixclock = 152500, .bpp = 8, + .xres = 640, .yres = 480, + + .hsync_len = 4, .vsync_len = 3, + .left_margin = 2, .upper_margin = 0, + .right_margin = 1, .lower_margin = 0, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, + .lccr3 = LCCR3_ACBsDiv(512), +}; + static void __init shannon_init(void) { + sa11x0_ppc_configure_mcp(); + sa11x0_register_lcd(&shannon_lcd_info); sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1); sa11x0_register_mcp(&shannon_mcp_data); } @@ -84,6 +100,7 @@ static void __init shannon_map_io(void) MACHINE_START(SHANNON, "Shannon (AKA: Tuxscreen)") .atag_offset = 0x100, .map_io = shannon_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .init_machine = shannon_init, diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index e17c04d..3efae03 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -7,15 +7,15 @@ #include <linux/kernel.h> #include <linux/tty.h> #include <linux/proc_fs.h> -#include <linux/string.h> +#include <linux/string.h> #include <linux/pm.h> #include <linux/platform_device.h> +#include <linux/mfd/ucb1x00.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/io.h> #include <linux/gpio.h> -#include <asm/irq.h> #include <mach/hardware.h> #include <asm/setup.h> @@ -26,6 +26,7 @@ #include <asm/mach/serial_sa1100.h> #include <mach/mcp.h> #include <mach/simpad.h> +#include <mach/irqs.h> #include <linux/serial_core.h> #include <linux/ioport.h> @@ -176,21 +177,18 @@ static struct flash_platform_data simpad_flash_data = { static struct resource simpad_flash_resources [] = { - { - .start = SA1100_CS0_PHYS, - .end = SA1100_CS0_PHYS + SZ_16M -1, - .flags = IORESOURCE_MEM, - }, { - .start = SA1100_CS1_PHYS, - .end = SA1100_CS1_PHYS + SZ_16M -1, - .flags = IORESOURCE_MEM, - } + DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_16M), + DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_16M), +}; + +static struct ucb1x00_plat_data simpad_ucb1x00_data = { + .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, }; static struct mcp_plat_data simpad_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, + .codec_pdata = &simpad_ucb1x00_data, }; @@ -376,6 +374,7 @@ static int __init simpad_init(void) pm_power_off = simpad_power_off; + sa11x0_ppc_configure_mcp(); sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources, ARRAY_SIZE(simpad_flash_resources)); sa11x0_register_mcp(&simpad_mcp_data); @@ -394,6 +393,7 @@ MACHINE_START(SIMPAD, "Simpad") /* Maintainer: Holger Freyther */ .atag_offset = 0x100, .map_io = simpad_map_io, + .nr_irqs = SA1100_NR_IRQS, .init_irq = sa1100_init_irq, .timer = &sa1100_timer, .restart = sa11x0_restart, diff --git a/arch/arm/mach-sa1100/sleep.S b/arch/arm/mach-sa1100/sleep.S index e822331..30cc672 100644 --- a/arch/arm/mach-sa1100/sleep.S +++ b/arch/arm/mach-sa1100/sleep.S @@ -26,27 +26,36 @@ * * Causes sa11x0 to enter sleep state * + * Must be aligned to a cacheline. */ - + .balign 32 ENTRY(sa1100_finish_suspend) @ disable clock switching mcr p15, 0, r1, c15, c2, 2 - @ Adjust memory timing before lowering CPU clock - @ Clock speed adjustment without changing memory timing makes - @ CPU hang in some cases - ldr r0, =MDREFR - ldr r1, [r0] - orr r1, r1, #MDREFR_K1DB2 - str r1, [r0] + ldr r6, =MDREFR + ldr r4, [r6] + orr r4, r4, #MDREFR_K1DB2 + ldr r5, =PPCR + + @ Pre-load __udelay into the I-cache + mov r0, #1 + bl __udelay + mov r0, r0 + + @ The following must all exist in a single cache line to + @ avoid accessing memory until this sequence is complete, + @ otherwise we occasionally hang. + + @ Adjust memory timing before lowering CPU clock + str r4, [r6] @ delay 90us and set CPU PLL to lowest speed @ fixes resume problem on high speed SA1110 mov r0, #90 bl __udelay - ldr r0, =PPCR mov r1, #0 - str r1, [r0] + str r1, [r5] mov r0, #90 bl __udelay @@ -85,12 +94,10 @@ ENTRY(sa1100_finish_suspend) bic r5, r5, #FMsk(MSC_RT) bic r5, r5, #FMsk(MSC_RT)<<16 - ldr r6, =MDREFR - ldr r7, [r6] -bic r7, r7, #0x0000FF00 -bic r7, r7, #0x000000F0 -orr r8, r7, #MDREFR_SLFRSH + bic r7, r7, #0x0000FF00 + bic r7, r7, #0x000000F0 + orr r8, r7, #MDREFR_SLFRSH ldr r9, =MDCNFG ldr r10, [r9] diff --git a/arch/arm/mach-sa1100/ssp.c b/arch/arm/mach-sa1100/ssp.c index b20ff93..e22fca9 100644 --- a/arch/arm/mach-sa1100/ssp.c +++ b/arch/arm/mach-sa1100/ssp.c @@ -19,8 +19,8 @@ #include <linux/init.h> #include <linux/io.h> -#include <asm/irq.h> #include <mach/hardware.h> +#include <mach/irqs.h> #include <asm/hardware/ssp.h> #define TIMEOUT 100000 diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c index 69e3353..6af26e8 100644 --- a/arch/arm/mach-sa1100/time.c +++ b/arch/arm/mach-sa1100/time.c @@ -18,6 +18,7 @@ #include <asm/mach/time.h> #include <asm/sched_clock.h> #include <mach/hardware.h> +#include <mach/irqs.h> static u32 notrace sa1100_read_sched_clock(void) { diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f1a2749..4a6c46de 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -252,6 +252,15 @@ config EP93XX_DMA help Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller. +config DMA_SA11X0 + tristate "SA-11x0 DMA support" + depends on ARCH_SA1100 + select DMA_ENGINE + help + Support the DMA engine found on Intel StrongARM SA-1100 and + SA-1110 SoCs. This DMA engine can only be used with on-chip + devices. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 009a222..86b795b 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o +obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c new file mode 100644 index 0000000..16a6b48 --- /dev/null +++ b/drivers/dma/sa11x0-dma.c @@ -0,0 +1,1109 @@ +/* + * SA11x0 DMAengine support + * + * Copyright (C) 2012 Russell King + * Derived in part from arch/arm/mach-sa1100/dma.c, + * Copyright (C) 2000, 2001 by Nicolas Pitre + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sa11x0-dma.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define NR_PHY_CHAN 6 +#define DMA_ALIGN 3 +#define DMA_MAX_SIZE 0x1fff +#define DMA_CHUNK_SIZE 0x1000 + +#define DMA_DDAR 0x00 +#define DMA_DCSR_S 0x04 +#define DMA_DCSR_C 0x08 +#define DMA_DCSR_R 0x0c +#define DMA_DBSA 0x10 +#define DMA_DBTA 0x14 +#define DMA_DBSB 0x18 +#define DMA_DBTB 0x1c +#define DMA_SIZE 0x20 + +#define DCSR_RUN (1 << 0) +#define DCSR_IE (1 << 1) +#define DCSR_ERROR (1 << 2) +#define DCSR_DONEA (1 << 3) +#define DCSR_STRTA (1 << 4) +#define DCSR_DONEB (1 << 5) +#define DCSR_STRTB (1 << 6) +#define DCSR_BIU (1 << 7) + +#define DDAR_RW (1 << 0) /* 0 = W, 1 = R */ +#define DDAR_E (1 << 1) /* 0 = LE, 1 = BE */ +#define DDAR_BS (1 << 2) /* 0 = BS4, 1 = BS8 */ +#define DDAR_DW (1 << 3) /* 0 = 8b, 1 = 16b */ +#define DDAR_Ser0UDCTr (0x0 << 4) +#define DDAR_Ser0UDCRc (0x1 << 4) +#define DDAR_Ser1SDLCTr (0x2 << 4) +#define DDAR_Ser1SDLCRc (0x3 << 4) +#define DDAR_Ser1UARTTr (0x4 << 4) +#define DDAR_Ser1UARTRc (0x5 << 4) +#define DDAR_Ser2ICPTr (0x6 << 4) +#define DDAR_Ser2ICPRc (0x7 << 4) +#define DDAR_Ser3UARTTr (0x8 << 4) +#define DDAR_Ser3UARTRc (0x9 << 4) +#define DDAR_Ser4MCP0Tr (0xa << 4) +#define DDAR_Ser4MCP0Rc (0xb << 4) +#define DDAR_Ser4MCP1Tr (0xc << 4) +#define DDAR_Ser4MCP1Rc (0xd << 4) +#define DDAR_Ser4SSPTr (0xe << 4) +#define DDAR_Ser4SSPRc (0xf << 4) + +struct sa11x0_dma_sg { + u32 addr; + u32 len; +}; + +struct sa11x0_dma_desc { + struct dma_async_tx_descriptor tx; + u32 ddar; + size_t size; + + /* maybe protected by c->lock */ + struct list_head node; + unsigned sglen; + struct sa11x0_dma_sg sg[0]; +}; + +struct sa11x0_dma_phy; + +struct sa11x0_dma_chan { + struct dma_chan chan; + spinlock_t lock; + dma_cookie_t lc; + + /* protected by c->lock */ + struct sa11x0_dma_phy *phy; + enum dma_status status; + struct list_head desc_submitted; + struct list_head desc_issued; + + /* protected by d->lock */ + struct list_head node; + + u32 ddar; + const char *name; +}; + +struct sa11x0_dma_phy { + void __iomem *base; + struct sa11x0_dma_dev *dev; + unsigned num; + + struct sa11x0_dma_chan *vchan; + + /* Protected by c->lock */ + unsigned sg_load; + struct sa11x0_dma_desc *txd_load; + unsigned sg_done; + struct sa11x0_dma_desc *txd_done; +#ifdef CONFIG_PM_SLEEP + u32 dbs[2]; + u32 dbt[2]; + u32 dcsr; +#endif +}; + +struct sa11x0_dma_dev { + struct dma_device slave; + void __iomem *base; + spinlock_t lock; + struct tasklet_struct task; + struct list_head chan_pending; + struct list_head desc_complete; + struct sa11x0_dma_phy phy[NR_PHY_CHAN]; +}; + +static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct sa11x0_dma_chan, chan); +} + +static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev) +{ + return container_of(dmadev, struct sa11x0_dma_dev, slave); +} + +static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct sa11x0_dma_desc, tx); +} + +static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c) +{ + if (list_empty(&c->desc_issued)) + return NULL; + + return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node); +} + +static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd) +{ + list_del(&txd->node); + p->txd_load = txd; + p->sg_load = 0; + + dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n", + p->num, txd, txd->tx.cookie, txd->ddar); +} + +static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_load; + struct sa11x0_dma_sg *sg; + void __iomem *base = p->base; + unsigned dbsx, dbtx; + u32 dcsr; + + if (!txd) + return; + + dcsr = readl_relaxed(base + DMA_DCSR_R); + + /* Don't try to load the next transfer if both buffers are started */ + if ((dcsr & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) + return; + + if (p->sg_load == txd->sglen) { + struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); + + /* + * We have reached the end of the current descriptor. + * Peek at the next descriptor, and if compatible with + * the current, start processing it. + */ + if (txn && txn->ddar == txd->ddar) { + txd = txn; + sa11x0_dma_start_desc(p, txn); + } else { + p->txd_load = NULL; + return; + } + } + + sg = &txd->sg[p->sg_load++]; + + /* Select buffer to load according to channel status */ + if (((dcsr & (DCSR_BIU | DCSR_STRTB)) == (DCSR_BIU | DCSR_STRTB)) || + ((dcsr & (DCSR_BIU | DCSR_STRTA)) == 0)) { + dbsx = DMA_DBSA; + dbtx = DMA_DBTA; + dcsr = DCSR_STRTA | DCSR_IE | DCSR_RUN; + } else { + dbsx = DMA_DBSB; + dbtx = DMA_DBTB; + dcsr = DCSR_STRTB | DCSR_IE | DCSR_RUN; + } + + writel_relaxed(sg->addr, base + dbsx); + writel_relaxed(sg->len, base + dbtx); + writel(dcsr, base + DMA_DCSR_S); + + dev_dbg(p->dev->slave.dev, "pchan %u: load: DCSR:%02x DBS%c:%08x DBT%c:%08x\n", + p->num, dcsr, + 'A' + (dbsx == DMA_DBSB), sg->addr, + 'A' + (dbtx == DMA_DBTB), sg->len); +} + +static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, + struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = p->txd_done; + + if (++p->sg_done == txd->sglen) { + struct sa11x0_dma_dev *d = p->dev; + + dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n", + p->num, p->txd_done, p->txd_done->tx.cookie); + + c->lc = txd->tx.cookie; + + spin_lock(&d->lock); + list_add_tail(&txd->node, &d->desc_complete); + spin_unlock(&d->lock); + + p->sg_done = 0; + p->txd_done = p->txd_load; + + tasklet_schedule(&d->task); + } + + sa11x0_dma_start_sg(p, c); +} + +static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id) +{ + struct sa11x0_dma_phy *p = dev_id; + struct sa11x0_dma_dev *d = p->dev; + struct sa11x0_dma_chan *c; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (!(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB))) + return IRQ_NONE; + + /* Clear reported status bits */ + writel_relaxed(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB), + p->base + DMA_DCSR_C); + + dev_dbg(d->slave.dev, "pchan %u: irq: DCSR:%02x\n", p->num, dcsr); + + if (dcsr & DCSR_ERROR) { + dev_err(d->slave.dev, "pchan %u: error. DCSR:%02x DDAR:%08x DBSA:%08x DBTA:%08x DBSB:%08x DBTB:%08x\n", + p->num, dcsr, + readl_relaxed(p->base + DMA_DDAR), + readl_relaxed(p->base + DMA_DBSA), + readl_relaxed(p->base + DMA_DBTA), + readl_relaxed(p->base + DMA_DBSB), + readl_relaxed(p->base + DMA_DBTB)); + } + + c = p->vchan; + if (c) { + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + /* + * Now that we're holding the lock, check that the vchan + * really is associated with this pchan before touching the + * hardware. This should always succeed, because we won't + * change p->vchan or c->phy while the channel is actively + * transferring. + */ + if (c->phy == p) { + if (dcsr & DCSR_DONEA) + sa11x0_dma_complete(p, c); + if (dcsr & DCSR_DONEB) + sa11x0_dma_complete(p, c); + } + spin_unlock_irqrestore(&c->lock, flags); + } + + return IRQ_HANDLED; +} + +static void sa11x0_dma_start_txd(struct sa11x0_dma_chan *c) +{ + struct sa11x0_dma_desc *txd = sa11x0_dma_next_desc(c); + + /* If the issued list is empty, we have no further txds to process */ + if (txd) { + struct sa11x0_dma_phy *p = c->phy; + + sa11x0_dma_start_desc(p, txd); + p->txd_done = txd; + p->sg_done = 0; + + /* The channel should not have any transfers started */ + WARN_ON(readl_relaxed(p->base + DMA_DCSR_R) & + (DCSR_STRTA | DCSR_STRTB)); + + /* Clear the run and start bits before changing DDAR */ + writel_relaxed(DCSR_RUN | DCSR_STRTA | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + /* Try to start both buffers */ + sa11x0_dma_start_sg(p, c); + sa11x0_dma_start_sg(p, c); + } +} + +static void sa11x0_dma_tasklet(unsigned long arg) +{ + struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg; + struct sa11x0_dma_phy *p; + struct sa11x0_dma_chan *c; + struct sa11x0_dma_desc *txd, *txn; + LIST_HEAD(head); + unsigned pch, pch_alloc = 0; + + dev_dbg(d->slave.dev, "tasklet enter\n"); + + /* Get the completed tx descriptors */ + spin_lock_irq(&d->lock); + list_splice_init(&d->desc_complete, &head); + spin_unlock_irq(&d->lock); + + list_for_each_entry(txd, &head, node) { + c = to_sa11x0_dma_chan(txd->tx.chan); + + dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n", + c, txd, txd->tx.cookie); + + spin_lock_irq(&c->lock); + p = c->phy; + if (p) { + if (!p->txd_done) + sa11x0_dma_start_txd(c); + if (!p->txd_done) { + /* No current txd associated with this channel */ + dev_dbg(d->slave.dev, "pchan %u: free\n", p->num); + + /* Mark this channel free */ + c->phy = NULL; + p->vchan = NULL; + } + } + spin_unlock_irq(&c->lock); + } + + spin_lock_irq(&d->lock); + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + p = &d->phy[pch]; + + if (p->vchan == NULL && !list_empty(&d->chan_pending)) { + c = list_first_entry(&d->chan_pending, + struct sa11x0_dma_chan, node); + list_del_init(&c->node); + + pch_alloc |= 1 << pch; + + /* Mark this channel allocated */ + p->vchan = c; + + dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c); + } + } + spin_unlock_irq(&d->lock); + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + if (pch_alloc & (1 << pch)) { + p = &d->phy[pch]; + c = p->vchan; + + spin_lock_irq(&c->lock); + c->phy = p; + + sa11x0_dma_start_txd(c); + spin_unlock_irq(&c->lock); + } + } + + /* Now free the completed tx descriptor, and call their callbacks */ + list_for_each_entry_safe(txd, txn, &head, node) { + dma_async_tx_callback callback = txd->tx.callback; + void *callback_param = txd->tx.callback_param; + + dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n", + txd, txd->tx.cookie); + + kfree(txd); + + if (callback) + callback(callback_param); + } + + dev_dbg(d->slave.dev, "tasklet exit\n"); +} + + +static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head) +{ + struct sa11x0_dma_desc *txd, *txn; + + list_for_each_entry_safe(txd, txn, head, node) { + dev_dbg(d->slave.dev, "txd %p: freeing\n", txd); + kfree(txd); + } +} + +static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void sa11x0_dma_free_chan_resources(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&c->lock, flags); + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + spin_unlock_irqrestore(&c->lock, flags); + + sa11x0_dma_desc_free(d, &head); +} + +static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p) +{ + unsigned reg; + u32 dcsr; + + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + if ((dcsr & (DCSR_BIU | DCSR_STRTA)) == DCSR_STRTA || + (dcsr & (DCSR_BIU | DCSR_STRTB)) == DCSR_BIU) + reg = DMA_DBSA; + else + reg = DMA_DBSB; + + return readl_relaxed(p->base + reg); +} + +static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + struct sa11x0_dma_desc *txd; + dma_cookie_t last_used, last_complete; + unsigned long flags; + enum dma_status ret; + size_t bytes = 0; + + last_used = c->chan.cookie; + last_complete = c->lc; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret == DMA_SUCCESS) { + dma_set_tx_state(state, last_complete, last_used, 0); + return ret; + } + + spin_lock_irqsave(&c->lock, flags); + p = c->phy; + ret = c->status; + if (p) { + dma_addr_t addr = sa11x0_dma_pos(p); + + dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); + + txd = p->txd_done; + if (txd) { + unsigned i; + + for (i = 0; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n", + i, txd->sg[i].addr, txd->sg[i].len); + if (addr >= txd->sg[i].addr && + addr < txd->sg[i].addr + txd->sg[i].len) { + unsigned len; + + len = txd->sg[i].len - + (addr - txd->sg[i].addr); + dev_vdbg(d->slave.dev, "tx_status: [%u] +%x\n", + i, len); + bytes += len; + i++; + break; + } + } + for (; i < txd->sglen; i++) { + dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x ++\n", + i, txd->sg[i].addr, txd->sg[i].len); + bytes += txd->sg[i].len; + } + } + if (txd != p->txd_load && p->txd_load) + bytes += p->txd_load->size; + } + list_for_each_entry(txd, &c->desc_issued, node) { + bytes += txd->size; + } + spin_unlock_irqrestore(&c->lock, flags); + + dma_set_tx_state(state, last_complete, last_used, bytes); + + dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes); + + return ret; +} + +/* + * Move pending txds to the issued list, and re-init pending list. + * If not already pending, add this channel to the list of pending + * channels and trigger the tasklet to run. + */ +static void sa11x0_dma_issue_pending(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &c->desc_issued); + if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + if (!c->phy && list_empty(&c->node)) { + list_add_tail(&c->node, &d->chan_pending); + tasklet_schedule(&d->task); + dev_dbg(d->slave.dev, "vchan %p: issued\n", c); + } + spin_unlock(&d->lock); + } else + dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c); + spin_unlock_irqrestore(&c->lock, flags); +} + +static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan); + struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + c->chan.cookie += 1; + if (c->chan.cookie < 0) + c->chan.cookie = 1; + txd->tx.cookie = c->chan.cookie; + + list_add_tail(&txd->node, &c->desc_submitted); + spin_unlock_irqrestore(&c->lock, flags); + + dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n", + c, txd, txd->tx.cookie); + + return txd->tx.cookie; +} + +static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sg, unsigned int sglen, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_desc *txd; + struct scatterlist *sgent; + unsigned i, j = sglen; + size_t size = 0; + + /* SA11x0 channels can only operate in their native direction */ + if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", + c, c->ddar, dir); + return NULL; + } + + /* Do not allow zero-sized txds */ + if (sglen == 0) + return NULL; + + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned int len = sg_dma_len(sgent); + + if (len > DMA_MAX_SIZE) + j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1; + if (addr & DMA_ALIGN) { + dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n", + c, addr); + return NULL; + } + } + + txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC); + if (!txd) { + dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c); + return NULL; + } + + j = 0; + for_each_sg(sg, sgent, sglen, i) { + dma_addr_t addr = sg_dma_address(sgent); + unsigned len = sg_dma_len(sgent); + + size += len; + + do { + unsigned tlen = len; + + /* + * Check whether the transfer will fit. If not, try + * to split the transfer up such that we end up with + * equal chunks - but make sure that we preserve the + * alignment. This avoids small segments. + */ + if (tlen > DMA_MAX_SIZE) { + unsigned mult = DIV_ROUND_UP(tlen, + DMA_MAX_SIZE & ~DMA_ALIGN); + + tlen = (tlen / mult) & ~DMA_ALIGN; + } + + txd->sg[j].addr = addr; + txd->sg[j].len = tlen; + + addr += tlen; + len -= tlen; + j++; + } while (len); + } + + dma_async_tx_descriptor_init(&txd->tx, &c->chan); + txd->tx.flags = flags; + txd->tx.tx_submit = sa11x0_dma_tx_submit; + txd->ddar = c->ddar; + txd->size = size; + txd->sglen = j; + + dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n", + c, txd, txd->size, txd->sglen); + + return &txd->tx; +} + +static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) +{ + u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); + dma_addr_t addr; + enum dma_slave_buswidth width; + u32 maxburst; + + if (ddar & DDAR_RW) { + addr = cfg->src_addr; + width = cfg->src_addr_width; + maxburst = cfg->src_maxburst; + } else { + addr = cfg->dst_addr; + width = cfg->dst_addr_width; + maxburst = cfg->dst_maxburst; + } + + if ((width != DMA_SLAVE_BUSWIDTH_1_BYTE && + width != DMA_SLAVE_BUSWIDTH_2_BYTES) || + (maxburst != 4 && maxburst != 8)) + return -EINVAL; + + if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) + ddar |= DDAR_DW; + if (maxburst == 8) + ddar |= DDAR_BS; + + dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n", + c, addr, width, maxburst); + + c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6; + + return 0; +} + +static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + LIST_HEAD(head); + unsigned long flags; + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg); + + case DMA_TERMINATE_ALL: + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c); + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + + p = c->phy; + if (p) { + struct sa11x0_dma_desc *txd, *txn; + + dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); + /* vchan is assigned to a pchan - stop the channel */ + writel(DCSR_RUN | DCSR_IE | + DCSR_STRTA | DCSR_DONEA | + DCSR_STRTB | DCSR_DONEB, + p->base + DMA_DCSR_C); + + list_for_each_entry_safe(txd, txn, &d->desc_complete, node) + if (txd->tx.chan == &c->chan) + list_move(&txd->node, &head); + + if (p->txd_load) { + if (p->txd_load != p->txd_done) + list_add_tail(&p->txd_load->node, &head); + p->txd_load = NULL; + } + if (p->txd_done) { + list_add_tail(&p->txd_done->node, &head); + p->txd_done = NULL; + } + c->phy = NULL; + spin_lock(&d->lock); + p->vchan = NULL; + spin_unlock(&d->lock); + tasklet_schedule(&d->task); + } + spin_unlock_irqrestore(&c->lock, flags); + sa11x0_dma_desc_free(d, &head); + ret = 0; + break; + + case DMA_PAUSE: + dev_dbg(d->slave.dev, "vchan %p: pause\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + } else { + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + case DMA_RESUME: + dev_dbg(d->slave.dev, "vchan %p: resume\n", c); + spin_lock_irqsave(&c->lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); + } else if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->lock, flags); + ret = 0; + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +struct sa11x0_dma_channel_desc { + u32 ddar; + const char *name; +}; + +#define CD(d1, d2) { .ddar = DDAR_##d1 | d2, .name = #d1 } +static const struct sa11x0_dma_channel_desc chan_desc[] = { + CD(Ser0UDCTr, 0), + CD(Ser0UDCRc, DDAR_RW), + CD(Ser1SDLCTr, 0), + CD(Ser1SDLCRc, DDAR_RW), + CD(Ser1UARTTr, 0), + CD(Ser1UARTRc, DDAR_RW), + CD(Ser2ICPTr, 0), + CD(Ser2ICPRc, DDAR_RW), + CD(Ser3UARTTr, 0), + CD(Ser3UARTRc, DDAR_RW), + CD(Ser4MCP0Tr, 0), + CD(Ser4MCP0Rc, DDAR_RW), + CD(Ser4MCP1Tr, 0), + CD(Ser4MCP1Rc, DDAR_RW), + CD(Ser4SSPTr, 0), + CD(Ser4SSPRc, DDAR_RW), +}; + +static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev, + struct device *dev) +{ + unsigned i; + + dmadev->chancnt = ARRAY_SIZE(chan_desc); + INIT_LIST_HEAD(&dmadev->channels); + dmadev->dev = dev; + dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; + dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; + dmadev->device_control = sa11x0_dma_control; + dmadev->device_tx_status = sa11x0_dma_tx_status; + dmadev->device_issue_pending = sa11x0_dma_issue_pending; + + for (i = 0; i < dmadev->chancnt; i++) { + struct sa11x0_dma_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + dev_err(dev, "no memory for channel %u\n", i); + return -ENOMEM; + } + + c->chan.device = dmadev; + c->status = DMA_IN_PROGRESS; + c->ddar = chan_desc[i].ddar; + c->name = chan_desc[i].name; + spin_lock_init(&c->lock); + INIT_LIST_HEAD(&c->desc_submitted); + INIT_LIST_HEAD(&c->desc_issued); + INIT_LIST_HEAD(&c->node); + list_add_tail(&c->chan.device_node, &dmadev->channels); + } + + return dma_async_device_register(dmadev); +} + +static int sa11x0_dma_request_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + + if (irq <= 0) + return -ENXIO; + + return request_irq(irq, sa11x0_dma_irq, 0, dev_name(&pdev->dev), data); +} + +static void sa11x0_dma_free_irq(struct platform_device *pdev, int nr, + void *data) +{ + int irq = platform_get_irq(pdev, nr); + if (irq > 0) + free_irq(irq, data); +} + +static void sa11x0_dma_free_channels(struct dma_device *dmadev) +{ + struct sa11x0_dma_chan *c, *cn; + + list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) { + list_del(&c->chan.device_node); + kfree(c); + } +} + +static int __devinit sa11x0_dma_probe(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d; + struct resource *res; + unsigned i; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto err_alloc; + } + + spin_lock_init(&d->lock); + INIT_LIST_HEAD(&d->chan_pending); + INIT_LIST_HEAD(&d->desc_complete); + + d->base = ioremap(res->start, resource_size(res)); + if (!d->base) { + ret = -ENOMEM; + goto err_ioremap; + } + + tasklet_init(&d->task, sa11x0_dma_tasklet, (unsigned long)d); + + for (i = 0; i < NR_PHY_CHAN; i++) { + struct sa11x0_dma_phy *p = &d->phy[i]; + + p->dev = d; + p->num = i; + p->base = d->base + i * DMA_SIZE; + writel_relaxed(DCSR_RUN | DCSR_IE | DCSR_ERROR | + DCSR_DONEA | DCSR_STRTA | DCSR_DONEB | DCSR_STRTB, + p->base + DMA_DCSR_C); + writel_relaxed(0, p->base + DMA_DDAR); + + ret = sa11x0_dma_request_irq(pdev, i, p); + if (ret) { + while (i) { + i--; + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + } + goto err_irq; + } + } + + dma_cap_set(DMA_SLAVE, d->slave.cap_mask); + d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; + ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); + if (ret) { + dev_warn(d->slave.dev, "failed to register slave async device: %d\n", + ret); + goto err_slave_reg; + } + + platform_set_drvdata(pdev, d); + return 0; + + err_slave_reg: + sa11x0_dma_free_channels(&d->slave); + for (i = 0; i < NR_PHY_CHAN; i++) + sa11x0_dma_free_irq(pdev, i, &d->phy[i]); + err_irq: + tasklet_kill(&d->task); + iounmap(d->base); + err_ioremap: + kfree(d); + err_alloc: + return ret; +} + +static int __devexit sa11x0_dma_remove(struct platform_device *pdev) +{ + struct sa11x0_dma_dev *d = platform_get_drvdata(pdev); + unsigned pch; + + dma_async_device_unregister(&d->slave); + + sa11x0_dma_free_channels(&d->slave); + for (pch = 0; pch < NR_PHY_CHAN; pch++) + sa11x0_dma_free_irq(pdev, pch, &d->phy[pch]); + tasklet_kill(&d->task); + iounmap(d->base); + kfree(d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sa11x0_dma_suspend(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + u32 dcsr, saved_dcsr; + + dcsr = saved_dcsr = readl_relaxed(p->base + DMA_DCSR_R); + if (dcsr & DCSR_RUN) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + dcsr = readl_relaxed(p->base + DMA_DCSR_R); + } + + saved_dcsr &= DCSR_RUN | DCSR_IE; + if (dcsr & DCSR_BIU) { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTB); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTA); + saved_dcsr |= (dcsr & DCSR_STRTA ? DCSR_STRTB : 0) | + (dcsr & DCSR_STRTB ? DCSR_STRTA : 0); + } else { + p->dbs[0] = readl_relaxed(p->base + DMA_DBSA); + p->dbt[0] = readl_relaxed(p->base + DMA_DBTA); + p->dbs[1] = readl_relaxed(p->base + DMA_DBSB); + p->dbt[1] = readl_relaxed(p->base + DMA_DBTB); + saved_dcsr |= dcsr & (DCSR_STRTA | DCSR_STRTB); + } + p->dcsr = saved_dcsr; + + writel(DCSR_STRTA | DCSR_STRTB, p->base + DMA_DCSR_C); + } + + return 0; +} + +static int sa11x0_dma_resume(struct device *dev) +{ + struct sa11x0_dma_dev *d = dev_get_drvdata(dev); + unsigned pch; + + for (pch = 0; pch < NR_PHY_CHAN; pch++) { + struct sa11x0_dma_phy *p = &d->phy[pch]; + struct sa11x0_dma_desc *txd = NULL; + u32 dcsr = readl_relaxed(p->base + DMA_DCSR_R); + + WARN_ON(dcsr & (DCSR_BIU | DCSR_STRTA | DCSR_STRTB | DCSR_RUN)); + + if (p->txd_done) + txd = p->txd_done; + else if (p->txd_load) + txd = p->txd_load; + + if (!txd) + continue; + + writel_relaxed(txd->ddar, p->base + DMA_DDAR); + + writel_relaxed(p->dbs[0], p->base + DMA_DBSA); + writel_relaxed(p->dbt[0], p->base + DMA_DBTA); + writel_relaxed(p->dbs[1], p->base + DMA_DBSB); + writel_relaxed(p->dbt[1], p->base + DMA_DBTB); + writel_relaxed(p->dcsr, p->base + DMA_DCSR_S); + } + + return 0; +} +#endif + +static const struct dev_pm_ops sa11x0_dma_pm_ops = { + .suspend_noirq = sa11x0_dma_suspend, + .resume_noirq = sa11x0_dma_resume, + .freeze_noirq = sa11x0_dma_suspend, + .thaw_noirq = sa11x0_dma_resume, + .poweroff_noirq = sa11x0_dma_suspend, + .restore_noirq = sa11x0_dma_resume, +}; + +static struct platform_driver sa11x0_dma_driver = { + .driver = { + .name = "sa11x0-dma", + .owner = THIS_MODULE, + .pm = &sa11x0_dma_pm_ops, + }, + .probe = sa11x0_dma_probe, + .remove = __devexit_p(sa11x0_dma_remove), +}; + +bool sa11x0_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &sa11x0_dma_driver.driver) { + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + const char *p = param; + + return !strcmp(c->name, p); + } + return false; +} +EXPORT_SYMBOL(sa11x0_dma_filter_fn); + +static int __init sa11x0_dma_init(void) +{ + return platform_driver_register(&sa11x0_dma_driver); +} +subsys_initcall(sa11x0_dma_init); + +static void __exit sa11x0_dma_exit(void) +{ + platform_driver_unregister(&sa11x0_dma_driver); +} +module_exit(sa11x0_dma_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SA-11x0 DMA driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sa11x0-dma"); diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 7eecf69..8ea3b33 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <mach/hardware.h> +#include <mach/irqs.h> static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) { diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index eeafc30..9d639fa 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -27,6 +27,7 @@ #include <mach/jornada720.h> #include <mach/hardware.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 44fc8b4..5ebabe3 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -24,6 +24,26 @@ #include <asm/hardware/sa1111.h> +#define PS2CR 0x0000 +#define PS2STAT 0x0004 +#define PS2DATA 0x0008 +#define PS2CLKDIV 0x000c +#define PS2PRECNT 0x0010 + +#define PS2CR_ENA 0x08 +#define PS2CR_FKD 0x02 +#define PS2CR_FKC 0x01 + +#define PS2STAT_STP 0x0100 +#define PS2STAT_TXE 0x0080 +#define PS2STAT_TXB 0x0040 +#define PS2STAT_RXF 0x0020 +#define PS2STAT_RXB 0x0010 +#define PS2STAT_ENA 0x0008 +#define PS2STAT_RXP 0x0004 +#define PS2STAT_KBD 0x0002 +#define PS2STAT_KBC 0x0001 + struct ps2if { struct serio *io; struct sa1111_dev *dev; @@ -45,22 +65,22 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id) struct ps2if *ps2if = dev_id; unsigned int scancode, flag, status; - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); while (status & PS2STAT_RXF) { if (status & PS2STAT_STP) - sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT); + sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT); flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) | (status & PS2STAT_RXP ? 0 : SERIO_PARITY); - scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff; + scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff; if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; serio_interrupt(ps2if->io, scancode, flag); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); } return IRQ_HANDLED; @@ -75,12 +95,12 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) unsigned int status; spin_lock(&ps2if->lock); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); if (ps2if->head == ps2if->tail) { disable_irq_nosync(irq); /* done */ } else if (status & PS2STAT_TXE) { - sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA); + sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); } spin_unlock(&ps2if->lock); @@ -103,8 +123,8 @@ static int ps2_write(struct serio *io, unsigned char val) /* * If the TX register is empty, we can go straight out. */ - if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) { - sa1111_writel(val, ps2if->base + SA1111_PS2DATA); + if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) { + sa1111_writel(val, ps2if->base + PS2DATA); } else { if (ps2if->head == ps2if->tail) enable_irq(ps2if->dev->irq[1]); @@ -124,13 +144,16 @@ static int ps2_open(struct serio *io) struct ps2if *ps2if = io->port_data; int ret; - sa1111_enable_device(ps2if->dev); + ret = sa1111_enable_device(ps2if->dev); + if (ret) + return ret; ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0, SA1111_DRIVER_NAME(ps2if->dev), ps2if); if (ret) { printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[0], ret); + sa1111_disable_device(ps2if->dev); return ret; } @@ -140,6 +163,7 @@ static int ps2_open(struct serio *io) printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[1], ret); free_irq(ps2if->dev->irq[0], ps2if); + sa1111_disable_device(ps2if->dev); return ret; } @@ -147,7 +171,7 @@ static int ps2_open(struct serio *io) enable_irq_wake(ps2if->dev->irq[0]); - sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR); return 0; } @@ -155,7 +179,7 @@ static void ps2_close(struct serio *io) { struct ps2if *ps2if = io->port_data; - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); disable_irq_wake(ps2if->dev->irq[0]); @@ -175,7 +199,7 @@ static void __devinit ps2_clear_input(struct ps2if *ps2if) int maxread = 100; while (maxread--) { - if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff) + if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff) break; } } @@ -185,11 +209,11 @@ static unsigned int __devinit ps2_test_one(struct ps2if *ps2if, { unsigned int val; - sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR); udelay(2); - val = sa1111_readl(ps2if->base + SA1111_PS2STAT); + val = sa1111_readl(ps2if->base + PS2STAT); return val & (PS2STAT_KBC | PS2STAT_KBD); } @@ -220,7 +244,7 @@ static int __devinit ps2_test(struct ps2if *ps2if) ret = -ENODEV; } - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); return ret; } @@ -274,8 +298,8 @@ static int __devinit ps2_probe(struct sa1111_dev *dev) sa1111_enable_device(ps2if->dev); /* Incoming clock is 8MHz */ - sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV); - sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT); + sa1111_writel(0, ps2if->base + PS2CLKDIV); + sa1111_writel(127, ps2if->base + PS2PRECNT); /* * Flush any pending input. @@ -330,6 +354,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev) static struct sa1111_driver ps2_driver = { .drv = { .name = "sa1111-ps2", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_PS2, .probe = ps2_probe, diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index c3848ad..d9be6ea 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -22,6 +22,7 @@ #include <mach/hardware.h> #include <mach/jornada720.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cd13e9f..28a301b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -847,8 +847,9 @@ config MCP_SA11X0 # Chip drivers config MCP_UCB1200 - tristate "Support for UCB1200 / UCB1300" - depends on MCP + bool "Support for UCB1200 / UCB1300" + depends on MCP_SA11X0 + select MCP config MCP_UCB1200_TS tristate "Touchscreen interface support" diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 86cc3f7..6acf2e0 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -19,7 +19,6 @@ #include <linux/string.h> #include <linux/mfd/mcp.h> -#include <mach/dma.h> #include <asm/system.h> @@ -48,39 +47,11 @@ static int mcp_bus_remove(struct device *dev) return 0; } -static int mcp_bus_suspend(struct device *dev, pm_message_t state) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->suspend(mcp, state); - } - return ret; -} - -static int mcp_bus_resume(struct device *dev) -{ - struct mcp *mcp = to_mcp(dev); - int ret = 0; - - if (dev->driver) { - struct mcp_driver *drv = to_mcp_driver(dev->driver); - - ret = drv->resume(mcp); - } - return ret; -} - static struct bus_type mcp_bus_type = { .name = "mcp", .match = mcp_bus_match, .probe = mcp_bus_probe, .remove = mcp_bus_remove, - .suspend = mcp_bus_suspend, - .resume = mcp_bus_resume, }; /** @@ -208,6 +179,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL); if (mcp) { spin_lock_init(&mcp->lock); + device_initialize(&mcp->attached_device); mcp->attached_device.parent = parent; mcp->attached_device.bus = &mcp_bus_type; mcp->attached_device.dma_mask = parent->dma_mask; @@ -217,18 +189,25 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_register(struct mcp *mcp) +int mcp_host_add(struct mcp *mcp, void *pdata) { + mcp->attached_device.platform_data = pdata; dev_set_name(&mcp->attached_device, "mcp0"); - return device_register(&mcp->attached_device); + return device_add(&mcp->attached_device); +} +EXPORT_SYMBOL(mcp_host_add); + +void mcp_host_del(struct mcp *mcp) +{ + device_del(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_del); -void mcp_host_unregister(struct mcp *mcp) +void mcp_host_free(struct mcp *mcp) { - device_unregister(&mcp->attached_device); + put_device(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_host_free); int mcp_driver_register(struct mcp_driver *mcpdrv) { diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 02c53a0..1c0ceac 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -13,51 +13,61 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/mfd/mcp.h> -#include <mach/dma.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/system.h> #include <mach/mcp.h> -#include <mach/assabet.h> - +#define DRIVER_NAME "sa11x0-mcp" struct mcp_sa11x0 { - u32 mccr0; - u32 mccr1; + void __iomem *base0; + void __iomem *base1; + u32 mccr0; + u32 mccr1; }; +/* Register offsets */ +#define MCCR0(m) ((m)->base0 + 0x00) +#define MCDR0(m) ((m)->base0 + 0x08) +#define MCDR1(m) ((m)->base0 + 0x0c) +#define MCDR2(m) ((m)->base0 + 0x10) +#define MCSR(m) ((m)->base0 + 0x18) +#define MCCR1(m) ((m)->base1 + 0x00) + #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) static void mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *m = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x00007f00; - mccr0 |= divisor << 8; - Ser4MCCR0 = mccr0; + m->mccr0 &= ~0x00007f00; + m->mccr0 |= divisor << 8; + writel_relaxed(m->mccr0, MCCR0(m)); } static void mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *m = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x0000007f; - mccr0 |= divisor; - Ser4MCCR0 = mccr0; + m->mccr0 &= ~0x0000007f; + m->mccr0 |= divisor; + writel_relaxed(m->mccr0, MCCR0(m)); } /* @@ -69,14 +79,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) static void mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) { + struct mcp_sa11x0 *m = priv(mcp); int ret = -ETIME; int i; - Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m)); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CWC) { + if (readl_relaxed(MCSR(m)) & MCSR_CWC) { ret = 0; break; } @@ -95,15 +106,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) static unsigned int mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) { + struct mcp_sa11x0 *m = priv(mcp); int ret = -ETIME; int i; - Ser4MCDR2 = reg << 17 | MCDR2_Rd; + writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m)); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CRC) { - ret = Ser4MCDR2 & 0xffff; + if (readl_relaxed(MCSR(m)) & MCSR_CRC) { + ret = readl_relaxed(MCDR2(m)) & 0xffff; break; } } @@ -116,13 +128,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) static void mcp_sa11x0_enable(struct mcp *mcp) { - Ser4MCSR = -1; - Ser4MCCR0 |= MCCR0_MCE; + struct mcp_sa11x0 *m = priv(mcp); + + writel(-1, MCSR(m)); + m->mccr0 |= MCCR0_MCE; + writel_relaxed(m->mccr0, MCCR0(m)); } static void mcp_sa11x0_disable(struct mcp *mcp) { - Ser4MCCR0 &= ~MCCR0_MCE; + struct mcp_sa11x0 *m = priv(mcp); + + m->mccr0 &= ~MCCR0_MCE; + writel_relaxed(m->mccr0, MCCR0(m)); } /* @@ -137,55 +155,64 @@ static struct mcp_ops mcp_sa11x0 = { .disable = mcp_sa11x0_disable, }; -static int mcp_sa11x0_probe(struct platform_device *pdev) +static int mcp_sa11x0_probe(struct platform_device *dev) { - struct mcp_plat_data *data = pdev->dev.platform_data; + struct mcp_plat_data *data = dev->dev.platform_data; + struct resource *mem0, *mem1; + struct mcp_sa11x0 *m; struct mcp *mcp; int ret; if (!data) return -ENODEV; - if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) - return -EBUSY; + mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); + if (!mem0 || !mem1) + return -ENXIO; + + if (!request_mem_region(mem0->start, resource_size(mem0), + DRIVER_NAME)) { + ret = -EBUSY; + goto err_mem0; + } - mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); + if (!request_mem_region(mem1->start, resource_size(mem1), + DRIVER_NAME)) { + ret = -EBUSY; + goto err_mem1; + } + + mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0)); if (!mcp) { ret = -ENOMEM; - goto release; + goto err_alloc; } mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->dma_audio_rd = DMA_Ser4MCP0Rd; - mcp->dma_audio_wr = DMA_Ser4MCP0Wr; - mcp->dma_telco_rd = DMA_Ser4MCP1Rd; - mcp->dma_telco_wr = DMA_Ser4MCP1Wr; - mcp->gpio_base = data->gpio_base; - platform_set_drvdata(pdev, mcp); + m = priv(mcp); + m->mccr0 = data->mccr0 | 0x7f7f; + m->mccr1 = data->mccr1; - if (machine_is_assabet()) { - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + m->base0 = ioremap(mem0->start, resource_size(mem0)); + m->base1 = ioremap(mem1->start, resource_size(mem1)); + if (!m->base0 || !m->base1) { + ret = -ENOMEM; + goto err_ioremap; } - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + platform_set_drvdata(dev, mcp); /* * Initialise device. Note that we initially * set the sampling rate to minimum. */ - Ser4MCSR = -1; - Ser4MCCR1 = data->mccr1; - Ser4MCCR0 = data->mccr0 | 0x7f7f; + writel_relaxed(-1, MCSR(m)); + writel_relaxed(m->mccr1, MCCR1(m)); + writel_relaxed(m->mccr0, MCCR0(m)); /* * Calculate the read/write timeout (us) from the bit clock @@ -195,62 +222,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_register(mcp); + ret = mcp_host_add(mcp, data->codec_pdata); if (ret == 0) - goto out; + return 0; - release: - release_mem_region(0x80060000, 0x60); - platform_set_drvdata(pdev, NULL); + platform_set_drvdata(dev, NULL); - out: + err_ioremap: + iounmap(m->base1); + iounmap(m->base0); + mcp_host_free(mcp); + err_alloc: + release_mem_region(mem1->start, resource_size(mem1)); + err_mem1: + release_mem_region(mem0->start, resource_size(mem0)); + err_mem0: return ret; } static int mcp_sa11x0_remove(struct platform_device *dev) { struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(mcp); + struct resource *mem0, *mem1; + + if (m->mccr0 & MCCR0_MCE) + dev_warn(&dev->dev, + "device left active (missing disable call?)\n"); + + mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); platform_set_drvdata(dev, NULL); - mcp_host_unregister(mcp); - release_mem_region(0x80060000, 0x60); + mcp_host_del(mcp); + iounmap(m->base1); + iounmap(m->base0); + mcp_host_free(mcp); + release_mem_region(mem1->start, resource_size(mem1)); + release_mem_region(mem0->start, resource_size(mem0)); return 0; } -static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int mcp_sa11x0_suspend(struct device *dev) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); + + if (m->mccr0 & MCCR0_MCE) + dev_warn(dev, "device left active (missing disable call?)\n"); - priv(mcp)->mccr0 = Ser4MCCR0; - priv(mcp)->mccr1 = Ser4MCCR1; - Ser4MCCR0 &= ~MCCR0_MCE; + writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m)); return 0; } -static int mcp_sa11x0_resume(struct platform_device *dev) +static int mcp_sa11x0_resume(struct device *dev) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); - Ser4MCCR1 = priv(mcp)->mccr1; - Ser4MCCR0 = priv(mcp)->mccr0; + writel_relaxed(m->mccr1, MCCR1(m)); + writel_relaxed(m->mccr0, MCCR0(m)); return 0; } - -/* - * The driver for the SA11x0 MCP port. - */ -MODULE_ALIAS("platform:sa11x0-mcp"); +#endif + +static const struct dev_pm_ops mcp_sa11x0_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = mcp_sa11x0_suspend, + .freeze = mcp_sa11x0_suspend, + .poweroff = mcp_sa11x0_suspend, + .resume_noirq = mcp_sa11x0_resume, + .thaw_noirq = mcp_sa11x0_resume, + .restore_noirq = mcp_sa11x0_resume, +#endif +}; static struct platform_driver mcp_sa11x0_driver = { .probe = mcp_sa11x0_probe, .remove = mcp_sa11x0_remove, - .suspend = mcp_sa11x0_suspend, - .resume = mcp_sa11x0_resume, .driver = { - .name = "sa11x0-mcp", + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &mcp_sa11x0_pm_ops, }, }; @@ -259,6 +314,7 @@ static struct platform_driver mcp_sa11x0_driver = { */ module_platform_driver(mcp_sa11x0_driver); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index cea9da6..b63c075 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -11,14 +11,15 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> #include <linux/fs.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> +#include <linux/platform_device.h> #include <linux/proc_fs.h> -#include <linux/device.h> #include <linux/mfd/ucb1x00.h> -#include <mach/dma.h> - - #define UCB1X00_ATTR(name,input)\ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -38,14 +39,45 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2); static int ucb1x00_assabet_add(struct ucb1x00_dev *dev) { - device_create_file(&dev->ucb->dev, &dev_attr_vbatt); - device_create_file(&dev->ucb->dev, &dev_attr_vcharger); - device_create_file(&dev->ucb->dev, &dev_attr_batt_temp); + struct ucb1x00 *ucb = dev->ucb; + struct platform_device *pdev; + struct gpio_keys_platform_data keys; + static struct gpio_keys_button buttons[6]; + unsigned i; + + memset(buttons, 0, sizeof(buttons)); + memset(&keys, 0, sizeof(keys)); + + for (i = 0; i < ARRAY_SIZE(buttons); i++) { + buttons[i].code = BTN_0 + i; + buttons[i].gpio = ucb->gpio.base + i; + buttons[i].type = EV_KEY; + buttons[i].can_disable = true; + } + + keys.buttons = buttons; + keys.nbuttons = ARRAY_SIZE(buttons); + keys.poll_interval = 50; + keys.name = "ucb1x00"; + + pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1, + &keys, sizeof(keys)); + + device_create_file(&ucb->dev, &dev_attr_vbatt); + device_create_file(&ucb->dev, &dev_attr_vcharger); + device_create_file(&ucb->dev, &dev_attr_batt_temp); + + dev->priv = pdev; return 0; } static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev) { + struct platform_device *pdev = dev->priv; + + if (!IS_ERR(pdev)) + platform_device_unregister(pdev); + device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp); device_remove_file(&dev->ucb->dev, &dev_attr_vcharger); device_remove_file(&dev->ucb->dev, &dev_attr_vbatt); diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index febc90c..70f02da 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -23,14 +23,12 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/mfd/ucb1x00.h> +#include <linux/pm.h> #include <linux/gpio.h> -#include <linux/semaphore.h> - -#include <mach/dma.h> -#include <mach/hardware.h> static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); @@ -102,7 +100,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) * ucb1x00_enable must have been called to enable the comms * before using this function. * - * This function does not take any semaphores or spinlocks. + * This function does not take any mutexes or spinlocks. */ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) { @@ -120,14 +118,22 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value) else ucb->io_out &= ~(1 << offset); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); } static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset) { struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); - return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset); + unsigned val; + + ucb1x00_enable(ucb); + val = ucb1x00_reg_read(ucb, UCB_IO_DATA); + ucb1x00_disable(ucb); + + return val & (1 << offset); } static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -137,7 +143,9 @@ static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) spin_lock_irqsave(&ucb->io_lock, flags); ucb->io_dir &= ~(1 << offset); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; @@ -157,6 +165,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset else ucb->io_out &= ~mask; + ucb1x00_enable(ucb); if (old != ucb->io_out) ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); @@ -164,11 +173,19 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset ucb->io_dir |= mask; ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); } + ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; } +static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + + return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO; +} + /* * UCB1300 data sheet says we must: * 1. enable ADC => 5us (including reference startup time) @@ -186,7 +203,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset * Any code wishing to use the ADC converter must call this * function prior to using it. * - * This function takes the ADC semaphore to prevent two or more + * This function takes the ADC mutex to prevent two or more * concurrent uses, and therefore may sleep. As a result, it * can only be called from process context, not interrupt * context. @@ -196,7 +213,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset */ void ucb1x00_adc_enable(struct ucb1x00 *ucb) { - down(&ucb->adc_sem); + mutex_lock(&ucb->adc_mutex); ucb->adc_cr |= UCB_ADC_ENA; @@ -218,7 +235,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb) * complete (2 frames max without sync). * * If called for a synchronised ADC conversion, it may sleep - * with the ADC semaphore held. + * with the ADC mutex held. */ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) { @@ -246,7 +263,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) * ucb1x00_adc_disable - disable the ADC converter * @ucb: UCB1x00 structure describing chip * - * Disable the ADC converter and release the ADC semaphore. + * Disable the ADC converter and release the ADC mutex. */ void ucb1x00_adc_disable(struct ucb1x00 *ucb) { @@ -254,7 +271,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ucb1x00_disable(ucb); - up(&ucb->adc_sem); + mutex_unlock(&ucb->adc_mutex); } /* @@ -265,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) * SIBCLK to talk to the chip. We leave the clock running until * we have finished processing all interrupts from the chip. */ -static irqreturn_t ucb1x00_irq(int irqnr, void *devid) +static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc) { - struct ucb1x00 *ucb = devid; - struct ucb1x00_irq *irq; + struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); unsigned int isr, i; ucb1x00_enable(ucb); @@ -276,157 +292,104 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid) ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); - for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) - if (isr & 1 && irq->fn) - irq->fn(i, irq->devid); + for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1) + generic_handle_irq(ucb->irq_base + i); ucb1x00_disable(ucb); - - return IRQ_HANDLED; } -/** - * ucb1x00_hook_irq - hook a UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @fn: function to call when interrupt is triggered - * @devid: device id to pass to interrupt handler - * - * Hook the specified interrupt. You can only register one handler - * for each interrupt source. The interrupt source is not enabled - * by this function; use ucb1x00_enable_irq instead. - * - * Interrupt handlers will be called with other interrupts enabled. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -EBUSY if the interrupt has already been hooked - */ -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask) { - struct ucb1x00_irq *irq; - int ret = -EINVAL; - - if (idx < 16) { - irq = ucb->irq_handler + idx; - ret = -EBUSY; - - spin_lock_irq(&ucb->lock); - if (irq->fn == NULL) { - irq->devid = devid; - irq->fn = fn; - ret = 0; - } - spin_unlock_irq(&ucb->lock); - } - return ret; + ucb1x00_enable(ucb); + if (ucb->irq_ris_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + if (ucb->irq_fal_enbl & mask) + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + ucb1x00_disable(ucb); } -/** - * ucb1x00_enable_irq - enable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @edges: interrupt edges to enable - * - * Enable the specified interrupt to trigger on %UCB_RISING, - * %UCB_FALLING or both edges. The interrupt should have been - * hooked by ucb1x00_hook_irq. - */ -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_noop(struct irq_data *data) { - unsigned long flags; +} - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); +static void ucb1x00_irq_mask(struct irq_data *data) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask &= ~mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); } -/** - * ucb1x00_disable_irq - disable an UCB1x00 interrupt source - * @ucb: UCB1x00 structure describing chip - * @edges: interrupt edges to disable - * - * Disable the specified interrupt triggering on the specified - * (%UCB_RISING, %UCB_FALLING or both) edges. - */ -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +static void ucb1x00_irq_unmask(struct irq_data *data) { - unsigned long flags; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx < 16) { - spin_lock_irqsave(&ucb->lock, flags); - - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl &= ~(1 << idx); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask |= mask; + ucb1x00_irq_update(ucb, mask); + raw_spin_unlock(&ucb->irq_lock); } -/** - * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt - * @ucb: UCB1x00 structure describing chip - * @idx: interrupt index - * @devid: device id. - * - * Disable the interrupt source and remove the handler. devid must - * match the devid passed when hooking the interrupt. - * - * Returns zero on success, or one of the following errors: - * -EINVAL if the interrupt index is invalid - * -ENOENT if devid does not match - */ -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) { - struct ucb1x00_irq *irq; - int ret; + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - if (idx >= 16) - goto bad; + raw_spin_lock(&ucb->irq_lock); + if (type & IRQ_TYPE_EDGE_RISING) + ucb->irq_ris_enbl |= mask; + else + ucb->irq_ris_enbl &= ~mask; - irq = ucb->irq_handler + idx; - ret = -ENOENT; + if (type & IRQ_TYPE_EDGE_FALLING) + ucb->irq_fal_enbl |= mask; + else + ucb->irq_fal_enbl &= ~mask; + if (ucb->irq_mask & mask) { + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + } + raw_spin_unlock(&ucb->irq_lock); - spin_lock_irq(&ucb->lock); - if (irq->devid == devid) { - ucb->irq_ris_enbl &= ~(1 << idx); - ucb->irq_fal_enbl &= ~(1 << idx); + return 0; +} - ucb1x00_enable(ucb); - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - ucb1x00_disable(ucb); +static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data; + unsigned mask = 1 << (data->irq - ucb->irq_base); - irq->fn = NULL; - irq->devid = NULL; - ret = 0; - } - spin_unlock_irq(&ucb->lock); - return ret; + if (!pdata || !pdata->can_wakeup) + return -EINVAL; -bad: - printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); - return -EINVAL; + raw_spin_lock(&ucb->irq_lock); + if (on) + ucb->irq_wake |= mask; + else + ucb->irq_wake &= ~mask; + raw_spin_unlock(&ucb->irq_lock); + + return 0; } +static struct irq_chip ucb1x00_irqchip = { + .name = "ucb1x00", + .irq_ack = ucb1x00_irq_noop, + .irq_mask = ucb1x00_irq_mask, + .irq_unmask = ucb1x00_irq_unmask, + .irq_set_type = ucb1x00_irq_set_type, + .irq_set_wake = ucb1x00_irq_set_wake, +}; + static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) { struct ucb1x00_dev *dev; @@ -440,8 +403,8 @@ static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) ret = drv->add(dev); if (ret == 0) { - list_add(&dev->dev_node, &ucb->devs); - list_add(&dev->drv_node, &drv->devs); + list_add_tail(&dev->dev_node, &ucb->devs); + list_add_tail(&dev->drv_node, &drv->devs); } else { kfree(dev); } @@ -533,98 +496,126 @@ static struct class ucb1x00_class = { static int ucb1x00_probe(struct mcp *mcp) { - struct ucb1x00 *ucb; + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00_driver *drv; - unsigned int id; + struct ucb1x00 *ucb; + unsigned id, i, irq_base; int ret = -ENODEV; - int temp; + + /* Tell the platform to deassert the UCB1x00 reset */ + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE); mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); + mcp_disable(mcp); if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) { printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); - goto err_disable; + goto out; } ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL); ret = -ENOMEM; if (!ucb) - goto err_disable; - + goto out; + device_initialize(&ucb->dev); ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); - spin_lock_init(&ucb->lock); + raw_spin_lock_init(&ucb->irq_lock); spin_lock_init(&ucb->io_lock); - sema_init(&ucb->adc_sem, 1); + mutex_init(&ucb->adc_mutex); ucb->id = id; ucb->mcp = mcp; + + ret = device_add(&ucb->dev); + if (ret) + goto err_dev_add; + + ucb1x00_enable(ucb); ucb->irq = ucb1x00_detect_irq(ucb); + ucb1x00_disable(ucb); if (ucb->irq == NO_IRQ) { - printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + dev_err(&ucb->dev, "IRQ probe failed\n"); ret = -ENODEV; - goto err_free; + goto err_no_irq; } ucb->gpio.base = -1; - if (mcp->gpio_base != 0) { + irq_base = pdata ? pdata->irq_base : 0; + ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1); + if (ucb->irq_base < 0) { + dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n", + ucb->irq_base); + goto err_irq_alloc; + } + + for (i = 0; i < 16; i++) { + unsigned irq = ucb->irq_base + i; + + irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq); + irq_set_chip_data(irq, ucb); + set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST); + } + + irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, ucb1x00_irq); + + if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = mcp->gpio_base; + ucb->gpio.dev = &ucb->dev; + ucb->gpio.owner = THIS_MODULE; + ucb->gpio.base = pdata->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; ucb->gpio.direction_input = ucb1x00_gpio_direction_input; ucb->gpio.direction_output = ucb1x00_gpio_direction_output; + ucb->gpio.to_irq = ucb1x00_to_irq; ret = gpiochip_add(&ucb->gpio); if (ret) - goto err_free; + goto err_gpio_add; } else dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); - ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, - "UCB1x00", ucb); - if (ret) { - printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", - ucb->irq, ret); - goto err_gpio; - } - mcp_set_drvdata(mcp, ucb); - ret = device_register(&ucb->dev); - if (ret) - goto err_irq; - + if (pdata) + device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); - list_add(&ucb->node, &ucb1x00_devices); + list_add_tail(&ucb->node, &ucb1x00_devices); list_for_each_entry(drv, &ucb1x00_drivers, node) { ucb1x00_add_dev(ucb, drv); } mutex_unlock(&ucb1x00_mutex); - goto out; + return ret; - err_irq: - free_irq(ucb->irq, ucb); - err_gpio: - if (ucb->gpio.base != -1) - temp = gpiochip_remove(&ucb->gpio); - err_free: - kfree(ucb); - err_disable: - mcp_disable(mcp); + err_gpio_add: + irq_set_chained_handler(ucb->irq, NULL); + err_irq_alloc: + if (ucb->irq_base > 0) + irq_free_descs(ucb->irq_base, 16); + err_no_irq: + device_del(&ucb->dev); + err_dev_add: + put_device(&ucb->dev); out: + if (pdata && pdata->reset) + pdata->reset(UCB_RST_PROBE_FAIL); return ret; } static void ucb1x00_remove(struct mcp *mcp) { + struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct list_head *l, *n; int ret; @@ -643,8 +634,12 @@ static void ucb1x00_remove(struct mcp *mcp) dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); } - free_irq(ucb->irq, ucb); + irq_set_chained_handler(ucb->irq, NULL); + irq_free_descs(ucb->irq_base, 16); device_unregister(&ucb->dev); + + if (pdata && pdata->reset) + pdata->reset(UCB_RST_REMOVE); } int ucb1x00_register_driver(struct ucb1x00_driver *drv) @@ -653,7 +648,7 @@ int ucb1x00_register_driver(struct ucb1x00_driver *drv) INIT_LIST_HEAD(&drv->devs); mutex_lock(&ucb1x00_mutex); - list_add(&drv->node, &ucb1x00_drivers); + list_add_tail(&drv->node, &ucb1x00_drivers); list_for_each_entry(ucb, &ucb1x00_devices, node) { ucb1x00_add_dev(ucb, drv); } @@ -674,44 +669,86 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) mutex_unlock(&ucb1x00_mutex); } -static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state) +static int ucb1x00_suspend(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00_plat_data *pdata = dev->platform_data; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->suspend) - dev->drv->suspend(dev, state); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->suspend) + udev->drv->suspend(udev); } mutex_unlock(&ucb1x00_mutex); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_wake); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_wake); + ucb1x00_disable(ucb); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + enable_irq_wake(ucb->irq); + } else if (pdata && pdata->reset) + pdata->reset(UCB_RST_SUSPEND); + return 0; } -static int ucb1x00_resume(struct mcp *mcp) +static int ucb1x00_resume(struct device *dev) { - struct ucb1x00 *ucb = mcp_get_drvdata(mcp); - struct ucb1x00_dev *dev; + struct ucb1x00_plat_data *pdata = dev->platform_data; + struct ucb1x00 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; + + if (!ucb->irq_wake && pdata && pdata->reset) + pdata->reset(UCB_RST_RESUME); + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + + if (ucb->irq_wake) { + unsigned long flags; + + raw_spin_lock_irqsave(&ucb->irq_lock, flags); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & + ucb->irq_mask); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & + ucb->irq_mask); + raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); + + disable_irq_wake(ucb->irq); + } + ucb1x00_disable(ucb); + mutex_lock(&ucb1x00_mutex); - list_for_each_entry(dev, &ucb->devs, dev_node) { - if (dev->drv->resume) - dev->drv->resume(dev); + list_for_each_entry(udev, &ucb->devs, dev_node) { + if (udev->drv->resume) + udev->drv->resume(udev); } mutex_unlock(&ucb1x00_mutex); return 0; } +static const struct dev_pm_ops ucb1x00_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume) +}; + static struct mcp_driver ucb1x00_driver = { .drv = { .name = "ucb1x00", + .owner = THIS_MODULE, + .pm = &ucb1x00_pm_ops, }, .probe = ucb1x00_probe, .remove = ucb1x00_remove, - .suspend = ucb1x00_suspend, - .resume = ucb1x00_resume, }; static int __init ucb1x00_init(void) @@ -742,14 +779,10 @@ EXPORT_SYMBOL(ucb1x00_adc_enable); EXPORT_SYMBOL(ucb1x00_adc_read); EXPORT_SYMBOL(ucb1x00_adc_disable); -EXPORT_SYMBOL(ucb1x00_hook_irq); -EXPORT_SYMBOL(ucb1x00_free_irq); -EXPORT_SYMBOL(ucb1x00_enable_irq); -EXPORT_SYMBOL(ucb1x00_disable_irq); - EXPORT_SYMBOL(ucb1x00_register_driver); EXPORT_SYMBOL(ucb1x00_unregister_driver); +MODULE_ALIAS("mcp:ucb1x00"); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("UCB1x00 core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 63a3cbd..1e0e20c 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -20,8 +20,9 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/smp.h> +#include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/string.h> @@ -32,7 +33,6 @@ #include <linux/kthread.h> #include <linux/mfd/ucb1x00.h> -#include <mach/dma.h> #include <mach/collie.h> #include <asm/mach-types.h> @@ -42,6 +42,8 @@ struct ucb1x00_ts { struct input_dev *idev; struct ucb1x00 *ucb; + spinlock_t irq_lock; + unsigned irq_disabled; wait_queue_head_t irq_wait; struct task_struct *rtask; u16 x_res; @@ -238,7 +240,12 @@ static int ucb1x00_thread(void *_ts) if (ucb1x00_ts_pen_down(ts)) { set_current_state(TASK_INTERRUPTIBLE); - ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + spin_lock_irq(&ts->irq_lock); + if (ts->irq_disabled) { + ts->irq_disabled = 0; + enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); + } + spin_unlock_irq(&ts->irq_lock); ucb1x00_disable(ts->ucb); /* @@ -281,23 +288,37 @@ static int ucb1x00_thread(void *_ts) * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task. */ -static void ucb1x00_ts_irq(int idx, void *id) +static irqreturn_t ucb1x00_ts_irq(int irq, void *id) { struct ucb1x00_ts *ts = id; - ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + spin_lock(&ts->irq_lock); + ts->irq_disabled = 1; + disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); + spin_unlock(&ts->irq_lock); wake_up(&ts->irq_wait); + + return IRQ_HANDLED; } static int ucb1x00_ts_open(struct input_dev *idev) { struct ucb1x00_ts *ts = input_get_drvdata(idev); + unsigned long flags = 0; int ret = 0; BUG_ON(ts->rtask); + if (machine_is_collie()) + flags = IRQF_TRIGGER_RISING; + else + flags = IRQF_TRIGGER_FALLING; + + ts->irq_disabled = 0; + init_waitqueue_head(&ts->irq_wait); - ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, + flags, "ucb1x00-ts", ts); if (ret < 0) goto out; @@ -314,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev) if (!IS_ERR(ts->rtask)) { ret = 0; } else { - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ts->rtask = NULL; ret = -EFAULT; } @@ -334,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev) kthread_stop(ts->rtask); ucb1x00_enable(ts->ucb); - ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ucb1x00_disable(ts->ucb); } @@ -359,11 +380,13 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ts->ucb = dev->ucb; ts->idev = idev; ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; + spin_lock_init(&ts->irq_lock); idev->name = "Touchscreen panel"; idev->id.product = ts->ucb->id; idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; + idev->dev.parent = &ts->ucb->dev; idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 5028219..cbc3b78 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -23,106 +23,6 @@ #include <asm/sizes.h> #include <asm/mach/flash.h> -#if 0 -/* - * This is here for documentation purposes only - until these people - * submit their machine types. It will be gone January 2005. - */ -static struct mtd_partition consus_partitions[] = { - { - .name = "Consus boot firmware", - .offset = 0, - .size = 0x00040000, - .mask_flags = MTD_WRITABLE, /* force read-only */ - }, { - .name = "Consus kernel", - .offset = 0x00040000, - .size = 0x00100000, - .mask_flags = 0, - }, { - .name = "Consus disk", - .offset = 0x00140000, - /* The rest (up to 16M) for jffs. We could put 0 and - make it find the size automatically, but right now - i have 32 megs. jffs will use all 32 megs if given - the chance, and this leads to horrible problems - when you try to re-flash the image because blob - won't erase the whole partition. */ - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - }, { - /* this disk is a secondary disk, which can be used as - needed, for simplicity, make it the size of the other - consus partition, although realistically it could be - the remainder of the disk (depending on the file - system used) */ - .name = "Consus disk2", - .offset = 0x01000000, - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - } -}; - -/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ -static struct mtd_partition frodo_partitions[] = -{ - { - .name = "bootloader", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE - }, { - .name = "bootloader params", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "ramdisk", - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "file system", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND - } -}; - -static struct mtd_partition jornada56x_partitions[] = { - { - .name = "bootldr", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "rootfs", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; - -static void jornada56x_set_vpp(int vpp) -{ - if (vpp) - GPSR = GPIO_GPIO26; - else - GPCR = GPIO_GPIO26; - GPDR |= GPIO_GPIO26; -} - -/* - * Machine Phys Size set_vpp - * Consus : SA1100_CS0_PHYS SZ_32M - * Frodo : SA1100_CS0_PHYS SZ_32M - * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp - */ -#endif - struct sa_subdev_info { char name[16]; struct map_info map; @@ -373,21 +273,9 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static void sa1100_mtd_shutdown(struct platform_device *dev) -{ - struct sa_info *info = platform_get_drvdata(dev); - if (info && mtd_suspend(info->mtd) == 0) - mtd_resume(info->mtd); -} -#else -#define sa1100_mtd_shutdown NULL -#endif - static struct platform_driver sa1100_mtd_driver = { .probe = sa1100_mtd_probe, .remove = __exit_p(sa1100_mtd_remove), - .shutdown = sa1100_mtd_shutdown, .driver = { .name = "sa1100-mtd", .owner = THIS_MODULE, diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 64ad3ed..0dba050 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2281,7 +2281,7 @@ static int __devinit smc_drv_probe(struct platform_device *pdev) if (ret) goto out_release_io; #if defined(CONFIG_SA1100_ASSABET) - NCR_0 |= NCR_ENET_OSC_EN; + neponset_ncr_set(NCR_ENET_OSC_EN); #endif platform_set_drvdata(pdev, ndev); ret = smc_enable_device(pdev); diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index e535137..4680478 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -356,7 +356,7 @@ config VLSI_FIR config SA1100_FIR tristate "SA1100 Internal IR" - depends on ARCH_SA1100 && IRDA + depends on ARCH_SA1100 && IRDA && DMA_SA11X0 config VIA_FIR tristate "VIA VT8231/VT1211 SIR/MIR/FIR" diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index da27050..a0d1913 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -15,7 +15,7 @@ * This driver takes one kernel command line parameter, sa1100ir=, with * the following options: * max_rate:baudrate - set the maximum baud rate - * power_leve:level - set the transmitter power level + * power_level:level - set the transmitter power level * tx_lpm:0|1 - set transmit low power mode */ #include <linux/module.h> @@ -30,13 +30,13 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/sa11x0-dma.h> #include <net/irda/irda.h> #include <net/irda/wrapper.h> #include <net/irda/irda_device.h> -#include <asm/irq.h> -#include <mach/dma.h> #include <mach/hardware.h> #include <asm/mach/irda.h> @@ -44,8 +44,15 @@ static int power_level = 3; static int tx_lpm; static int max_rate = 4000000; +struct sa1100_buf { + struct device *dev; + struct sk_buff *skb; + struct scatterlist sg; + struct dma_chan *chan; + dma_cookie_t cookie; +}; + struct sa1100_irda { - unsigned char hscr0; unsigned char utcr4; unsigned char power; unsigned char open; @@ -53,12 +60,8 @@ struct sa1100_irda { int speed; int newspeed; - struct sk_buff *txskb; - struct sk_buff *rxskb; - dma_addr_t txbuf_dma; - dma_addr_t rxbuf_dma; - dma_regs_t *txdma; - dma_regs_t *rxdma; + struct sa1100_buf dma_rx; + struct sa1100_buf dma_tx; struct device *dev; struct irda_platform_data *pdata; @@ -67,23 +70,103 @@ struct sa1100_irda { iobuff_t tx_buff; iobuff_t rx_buff; + + int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *); + irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *); }; +static int sa1100_irda_set_speed(struct sa1100_irda *, int); + #define IS_FIR(si) ((si)->speed >= 4000000) #define HPSIR_MAX_RXLEN 2047 +static struct dma_slave_config sa1100_irda_sir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2UTDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 4, +}; + +static struct dma_slave_config sa1100_irda_fir_rx = { + .direction = DMA_FROM_DEVICE, + .src_addr = __PREG(Ser2HSDR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = 8, +}; + +static struct dma_slave_config sa1100_irda_fir_tx = { + .direction = DMA_TO_DEVICE, + .dst_addr = __PREG(Ser2HSDR), + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_maxburst = 8, +}; + +static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf) +{ + struct dma_chan *chan = buf->chan; + struct dma_tx_state state; + enum dma_status status; + + status = chan->device->device_tx_status(chan, buf->cookie, &state); + if (status != DMA_PAUSED) + return 0; + + return sg_dma_len(&buf->sg) - state.residue; +} + +static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf, + const char *name, struct dma_slave_config *cfg) +{ + dma_cap_mask_t m; + int ret; + + dma_cap_zero(m); + dma_cap_set(DMA_SLAVE, m); + + buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name); + if (!buf->chan) { + dev_err(dev, "unable to request DMA channel for %s\n", + name); + return -ENOENT; + } + + ret = dmaengine_slave_config(buf->chan, cfg); + if (ret) + dev_warn(dev, "DMA slave_config for %s returned %d\n", + name, ret); + + buf->dev = buf->chan->device->dev; + + return 0; +} + +static void sa1100_irda_dma_start(struct sa1100_buf *buf, + enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = buf->chan; + + desc = chan->device->device_prep_slave_sg(chan, &buf->sg, 1, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (desc) { + desc->callback = cb; + desc->callback_param = cb_p; + buf->cookie = dmaengine_submit(desc); + dma_async_issue_pending(chan); + } +} + /* * Allocate and map the receive buffer, unless it is already allocated. */ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) { - if (si->rxskb) + if (si->dma_rx.skb) return 0; - si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); - - if (!si->rxskb) { + si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + if (!si->dma_rx.skb) { printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); return -ENOMEM; } @@ -92,11 +175,14 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) * Align any IP headers that may be contained * within the frame. */ - skb_reserve(si->rxskb, 1); + skb_reserve(si->dma_rx.skb, 1); + + sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN); + if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) { + dev_kfree_skb_any(si->dma_rx.skb); + return -ENOMEM; + } - si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, - HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); return 0; } @@ -106,7 +192,7 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si) */ static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) { - if (!si->rxskb) { + if (!si->dma_rx.skb) { printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); return; } @@ -114,254 +200,87 @@ static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) /* * First empty receive FIFO */ - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + Ser2HSCR0 = HSCR0_HSSP; /* * Enable the DMA, receiver and receive interrupt. */ - sa1100_clear_dma(si->rxdma); - sa1100_start_dma(si->rxdma, si->rxbuf_dma, HPSIR_MAX_RXLEN); - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_RXE; + dmaengine_terminate_all(si->dma_rx.chan); + sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE; } -/* - * Set the IrDA communications speed. - */ -static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) +static void sa1100_irda_check_speed(struct sa1100_irda *si) { - unsigned long flags; - int brd, ret = -EINVAL; - - switch (speed) { - case 9600: case 19200: case 38400: - case 57600: case 115200: - brd = 3686400 / (16 * speed) - 1; - - /* - * Stop the receive DMA. - */ - if (IS_FIR(si)) - sa1100_stop_dma(si->rxdma); - - local_irq_save(flags); - - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - - Ser2UTCR1 = brd >> 8; - Ser2UTCR2 = brd; - - /* - * Clear status register - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - si->speed = speed; - - local_irq_restore(flags); - ret = 0; - break; - - case 4000000: - local_irq_save(flags); - - si->hscr0 = 0; - - Ser2HSSR0 = 0xff; - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; - Ser2UTCR3 = 0; - - si->speed = speed; - - if (si->pdata->set_speed) - si->pdata->set_speed(si->dev, speed); - - sa1100_irda_rx_alloc(si); - sa1100_irda_rx_dma_start(si); - - local_irq_restore(flags); - - break; - - default: - break; + if (si->newspeed) { + sa1100_irda_set_speed(si, si->newspeed); + si->newspeed = 0; } - - return ret; } /* - * Control the power state of the IrDA transmitter. - * State: - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - * - * Currently, only assabet is known to support this. + * HP-SIR format support. */ -static int -__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +static void sa1100_irda_sirtxdma_irq(void *id) { - int ret = 0; - if (si->pdata->set_power) - ret = si->pdata->set_power(si->dev, state); - return ret; -} - -static inline int -sa1100_set_power(struct sa1100_irda *si, unsigned int state) -{ - int ret; - - ret = __sa1100_irda_set_power(si, state); - if (ret == 0) - si->power = state; + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); - return ret; -} + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE); + dev_kfree_skb(si->dma_tx.skb); + si->dma_tx.skb = NULL; -static int sa1100_irda_startup(struct sa1100_irda *si) -{ - int ret; + dev->stats.tx_packets++; + dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg); - /* - * Ensure that the ports for this device are setup correctly. - */ - if (si->pdata->startup) { - ret = si->pdata->startup(si->dev); - if (ret) - return ret; - } - - /* - * Configure PPC for IRDA - we want to drive TXD2 low. - * We also want to drive this pin low during sleep. - */ - PPSR &= ~PPC_TXD2; - PSDR &= ~PPC_TXD2; - PPDR |= PPC_TXD2; - - /* - * Enable HP-SIR modulation, and ensure that the port is disabled. - */ - Ser2UTCR3 = 0; - Ser2HSCR0 = HSCR0_UART; - Ser2UTCR4 = si->utcr4; - Ser2UTCR0 = UTCR0_8BitData; - Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + /* We need to ensure that the transmitter has finished. */ + do + rmb(); + while (Ser2UTSR1 & UTSR1_TBY); /* - * Clear status register + * Ok, we've finished transmitting. Now enable the receiver. + * Sometimes we get a receive IRQ immediately after a transmit... */ Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; - ret = sa1100_irda_set_speed(si, si->speed = 9600); - if (ret) { - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; - - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); - } - - return ret; -} - -static void sa1100_irda_shutdown(struct sa1100_irda *si) -{ - /* - * Stop all DMA activity. - */ - sa1100_stop_dma(si->rxdma); - sa1100_stop_dma(si->txdma); - - /* Disable the port. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = 0; + sa1100_irda_check_speed(si); - if (si->pdata->shutdown) - si->pdata->shutdown(si->dev); + /* I'm hungry! */ + netif_wake_queue(dev); } -#ifdef CONFIG_PM -/* - * Suspend the IrDA interface. - */ -static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) +static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) { - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; - - si = netdev_priv(dev); - if (si->open) { - /* - * Stop the transmit queue - */ - netif_device_detach(dev); - disable_irq(dev->irq); - sa1100_irda_shutdown(si); - __sa1100_irda_set_power(si, 0); + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; } - return 0; -} - -/* - * Resume the IrDA interface. - */ -static int sa1100_irda_resume(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sa1100_irda *si; - - if (!dev) - return 0; + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev); - si = netdev_priv(dev); - if (si->open) { - /* - * If we missed a speed change, initialise at the new speed - * directly. It is debatable whether this is actually - * required, but in the interests of continuing from where - * we left off it is desirable. The converse argument is - * that we should re-negotiate at 9600 baud again. - */ - if (si->newspeed) { - si->speed = si->newspeed; - si->newspeed = 0; - } - - sa1100_irda_startup(si); - __sa1100_irda_set_power(si, si->power); - enable_irq(dev->irq); - - /* - * This automatically wakes up the queue - */ - netif_device_attach(dev); - } + /* + * The mean turn-around time is enforced by XBOF padding, + * so we don't have to do anything special here. + */ + Ser2UTCR3 = UTCR3_TXE; - return 0; + return NETDEV_TX_OK; } -#else -#define sa1100_irda_suspend NULL -#define sa1100_irda_resume NULL -#endif -/* - * HP-SIR format interrupt service routines. - */ -static void sa1100_irda_hpsir_irq(struct net_device *dev) +static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si) { - struct sa1100_irda *si = netdev_priv(dev); int status; status = Ser2UTSR0; @@ -414,51 +333,96 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) } - if (status & UTSR0_TFS && si->tx_buff.len) { - /* - * Transmitter FIFO is not full - */ - do { - Ser2UTDR = *si->tx_buff.data++; - si->tx_buff.len -= 1; - } while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len); + return IRQ_HANDLED; +} - if (si->tx_buff.len == 0) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += si->tx_buff.data - - si->tx_buff.head; +/* + * FIR format support. + */ +static void sa1100_irda_firtxdma_irq(void *id) +{ + struct net_device *dev = id; + struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; - /* - * We need to ensure that the transmitter has - * finished. - */ - do - rmb(); - while (Ser2UTSR1 & UTSR1_TBY); + /* + * Wait for the transmission to complete. Unfortunately, + * the hardware doesn't give us an interrupt to indicate + * "end of frame". + */ + do + rmb(); + while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); - /* - * Ok, we've finished transmitting. Now enable - * the receiver. Sometimes we get a receive IRQ - * immediately after a transmit... - */ - Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; - Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + /* + * Clear the transmit underrun bit. + */ + Ser2HSSR0 = HSSR0_TUR; - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } + /* + * Do we need to change speed? Note that we're lazy + * here - we don't free the old dma_rx.skb. We don't need + * to allocate a buffer either. + */ + sa1100_irda_check_speed(si); - /* I'm hungry! */ - netif_wake_queue(dev); - } + /* + * Start reception. This disables the transmitter for + * us. This will be using the existing RX buffer. + */ + sa1100_irda_rx_dma_start(si); + + /* Account and free the packet. */ + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev->stats.tx_packets ++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + si->dma_tx.skb = NULL; } + + /* + * Make sure that the TX queue is available for sending + * (for retries). TX has priority over RX at all times. + */ + netif_wake_queue(dev); +} + +static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev, + struct sa1100_irda *si) +{ + int mtt = irda_get_mtt(skb); + + si->dma_tx.skb = skb; + sg_set_buf(&si->dma_tx.sg, skb->data, skb->len); + if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) { + si->dma_tx.skb = NULL; + netif_wake_queue(dev); + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev); + + /* + * If we have a mean turn-around time, impose the specified + * specified delay. We could shorten this by timing from + * the point we received the packet. + */ + if (mtt) + udelay(mtt); + + Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE; + + return NETDEV_TX_OK; } static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) { - struct sk_buff *skb = si->rxskb; - dma_addr_t dma_addr; + struct sk_buff *skb = si->dma_rx.skb; unsigned int len, stat, data; if (!skb) { @@ -469,11 +433,10 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev /* * Get the current data position. */ - dma_addr = sa1100_get_dma_pos(si->rxdma); - len = dma_addr - si->rxbuf_dma; + len = sa1100_irda_dma_xferred(&si->dma_rx); if (len > HPSIR_MAX_RXLEN) len = HPSIR_MAX_RXLEN; - dma_unmap_single(si->dev, si->rxbuf_dma, len, DMA_FROM_DEVICE); + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); do { /* @@ -501,7 +464,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev } while (Ser2HSSR0 & HSSR0_EIF); if (stat & HSSR1_EOF) { - si->rxskb = NULL; + si->dma_rx.skb = NULL; skb_put(skb, len); skb->dev = dev; @@ -518,28 +481,23 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev netif_rx(skb); } else { /* - * Remap the buffer. + * Remap the buffer - it was previously mapped, and we + * hope that this succeeds. */ - si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, - HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); + dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE); } } /* - * FIR format interrupt service routine. We only have to - * handle RX events; transmit events go via the TX DMA handler. - * - * No matter what, we disable RX, process, and the restart RX. + * We only have to handle RX events here; transmit events go via the TX + * DMA handler. We disable RX, process, and the restart RX. */ -static void sa1100_irda_fir_irq(struct net_device *dev) +static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si) { - struct sa1100_irda *si = netdev_priv(dev); - /* * Stop RX DMA */ - sa1100_stop_dma(si->rxdma); + dmaengine_pause(si->dma_rx.chan); /* * Framing error - we throw away the packet completely. @@ -555,7 +513,7 @@ static void sa1100_irda_fir_irq(struct net_device *dev) /* * Clear out the DMA... */ - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + Ser2HSCR0 = HSCR0_HSSP; /* * Clear selected status bits now, so we @@ -577,74 +535,124 @@ static void sa1100_irda_fir_irq(struct net_device *dev) * No matter what happens, we must restart reception. */ sa1100_irda_rx_dma_start(si); -} -static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - if (IS_FIR(((struct sa1100_irda *)netdev_priv(dev)))) - sa1100_irda_fir_irq(dev); - else - sa1100_irda_hpsir_irq(dev); return IRQ_HANDLED; } /* - * TX DMA completion handler. + * Set the IrDA communications speed. */ -static void sa1100_irda_txdma_irq(void *id) +static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) { - struct net_device *dev = id; - struct sa1100_irda *si = netdev_priv(dev); - struct sk_buff *skb = si->txskb; + unsigned long flags; + int brd, ret = -EINVAL; - si->txskb = NULL; + switch (speed) { + case 9600: case 19200: case 38400: + case 57600: case 115200: + brd = 3686400 / (16 * speed) - 1; - /* - * Wait for the transmission to complete. Unfortunately, - * the hardware doesn't give us an interrupt to indicate - * "end of frame". - */ - do - rmb(); - while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); + /* Stop the receive DMA, and configure transmit. */ + if (IS_FIR(si)) { + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_sir_tx); + } - /* - * Clear the transmit underrun bit. - */ - Ser2HSSR0 = HSSR0_TUR; + local_irq_save(flags); - /* - * Do we need to change speed? Note that we're lazy - * here - we don't free the old rxskb. We don't need - * to allocate a buffer either. - */ - if (si->newspeed) { - sa1100_irda_set_speed(si, si->newspeed); - si->newspeed = 0; - } + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; - /* - * Start reception. This disables the transmitter for - * us. This will be using the existing RX buffer. - */ - sa1100_irda_rx_dma_start(si); + Ser2UTCR1 = brd >> 8; + Ser2UTCR2 = brd; - /* - * Account and free the packet. - */ - if (skb) { - dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE); - dev->stats.tx_packets ++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb_irq(skb); + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + si->speed = speed; + si->tx_start = sa1100_irda_sir_tx_start; + si->irq = sa1100_irda_sir_irq; + + local_irq_restore(flags); + ret = 0; + break; + + case 4000000: + if (!IS_FIR(si)) + dmaengine_slave_config(si->dma_tx.chan, + &sa1100_irda_fir_tx); + + local_irq_save(flags); + + Ser2HSSR0 = 0xff; + Ser2HSCR0 = HSCR0_HSSP; + Ser2UTCR3 = 0; + + si->speed = speed; + si->tx_start = sa1100_irda_fir_tx_start; + si->irq = sa1100_irda_fir_irq; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + sa1100_irda_rx_alloc(si); + sa1100_irda_rx_dma_start(si); + + local_irq_restore(flags); + + break; + + default: + break; } - /* - * Make sure that the TX queue is available for sending - * (for retries). TX has priority over RX at all times. - */ - netif_wake_queue(dev); + return ret; +} + +/* + * Control the power state of the IrDA transmitter. + * State: + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + * + * Currently, only assabet is known to support this. + */ +static int +__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret = 0; + if (si->pdata->set_power) + ret = si->pdata->set_power(si->dev, state); + return ret; +} + +static inline int +sa1100_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret; + + ret = __sa1100_irda_set_power(si, state); + if (ret == 0) + si->power = state; + + return ret; +} + +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct sa1100_irda *si = netdev_priv(dev); + + return si->irq(dev, si); } static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) @@ -660,62 +668,19 @@ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) if (speed != si->speed && speed != -1) si->newspeed = speed; - /* - * If this is an empty frame, we can bypass a lot. - */ + /* If this is an empty frame, we can bypass a lot. */ if (skb->len == 0) { - if (si->newspeed) { - si->newspeed = 0; - sa1100_irda_set_speed(si, speed); - } + sa1100_irda_check_speed(si); dev_kfree_skb(skb); return NETDEV_TX_OK; } - if (!IS_FIR(si)) { - netif_stop_queue(dev); - - si->tx_buff.data = si->tx_buff.head; - si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, - si->tx_buff.truesize); - - /* - * Set the transmit interrupt enable. This will fire - * off an interrupt immediately. Note that we disable - * the receiver so we won't get spurious characteres - * received. - */ - Ser2UTCR3 = UTCR3_TIE | UTCR3_TXE; - - dev_kfree_skb(skb); - } else { - int mtt = irda_get_mtt(skb); - - /* - * We must not be transmitting... - */ - BUG_ON(si->txskb); - - netif_stop_queue(dev); - - si->txskb = skb; - si->txbuf_dma = dma_map_single(si->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - sa1100_start_dma(si->txdma, si->txbuf_dma, skb->len); - - /* - * If we have a mean turn-around time, impose the specified - * specified delay. We could shorten this by timing from - * the point we received the packet. - */ - if (mtt) - udelay(mtt); + netif_stop_queue(dev); - Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_TXE; - } + /* We must not already have a skb to transmit... */ + BUG_ON(si->dma_tx.skb); - return NETDEV_TX_OK; + return si->tx_start(skb, dev, si); } static int @@ -762,6 +727,69 @@ sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) return ret; } +static int sa1100_irda_startup(struct sa1100_irda *si) +{ + int ret; + + /* + * Ensure that the ports for this device are setup correctly. + */ + if (si->pdata->startup) { + ret = si->pdata->startup(si->dev); + if (ret) + return ret; + } + + /* + * Configure PPC for IRDA - we want to drive TXD2 low. + * We also want to drive this pin low during sleep. + */ + PPSR &= ~PPC_TXD2; + PSDR &= ~PPC_TXD2; + PPDR |= PPC_TXD2; + + /* + * Enable HP-SIR modulation, and ensure that the port is disabled. + */ + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + Ser2UTCR4 = si->utcr4; + Ser2UTCR0 = UTCR0_8BitData; + Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + + ret = sa1100_irda_set_speed(si, si->speed = 9600); + if (ret) { + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + } + + return ret; +} + +static void sa1100_irda_shutdown(struct sa1100_irda *si) +{ + /* + * Stop all DMA activity. + */ + dmaengine_terminate_all(si->dma_rx.chan); + dmaengine_terminate_all(si->dma_tx.chan); + + /* Disable the port. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); +} + static int sa1100_irda_start(struct net_device *dev) { struct sa1100_irda *si = netdev_priv(dev); @@ -769,26 +797,17 @@ static int sa1100_irda_start(struct net_device *dev) si->speed = 9600; - err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); - if (err) - goto err_irq; - - err = sa1100_request_dma(DMA_Ser2HSSPRd, "IrDA receive", - NULL, NULL, &si->rxdma); + err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc", + &sa1100_irda_fir_rx); if (err) goto err_rx_dma; - err = sa1100_request_dma(DMA_Ser2HSSPWr, "IrDA transmit", - sa1100_irda_txdma_irq, dev, &si->txdma); + err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr", + &sa1100_irda_sir_tx); if (err) goto err_tx_dma; /* - * The interrupt must remain disabled for now. - */ - disable_irq(dev->irq); - - /* * Setup the serial port for the specified speed. */ err = sa1100_irda_startup(si); @@ -803,44 +822,60 @@ static int sa1100_irda_start(struct net_device *dev) if (!si->irlap) goto err_irlap; + err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + /* * Now enable the interrupt and start the queue */ si->open = 1; sa1100_set_power(si, power_level); /* low power mode */ - enable_irq(dev->irq); + netif_start_queue(dev); return 0; +err_irq: + irlap_close(si->irlap); err_irlap: si->open = 0; sa1100_irda_shutdown(si); err_startup: - sa1100_free_dma(si->txdma); + dma_release_channel(si->dma_tx.chan); err_tx_dma: - sa1100_free_dma(si->rxdma); + dma_release_channel(si->dma_rx.chan); err_rx_dma: - free_irq(dev->irq, dev); -err_irq: return err; } static int sa1100_irda_stop(struct net_device *dev) { struct sa1100_irda *si = netdev_priv(dev); + struct sk_buff *skb; + + netif_stop_queue(dev); - disable_irq(dev->irq); + si->open = 0; sa1100_irda_shutdown(si); /* - * If we have been doing DMA receive, make sure we + * If we have been doing any DMA activity, make sure we * tidy that up cleanly. */ - if (si->rxskb) { - dma_unmap_single(si->dev, si->rxbuf_dma, HPSIR_MAX_RXLEN, - DMA_FROM_DEVICE); - dev_kfree_skb(si->rxskb); - si->rxskb = NULL; + skb = si->dma_rx.skb; + if (skb) { + dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + si->dma_rx.skb = NULL; + } + + skb = si->dma_tx.skb; + if (skb) { + dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, + DMA_TO_DEVICE); + dev_kfree_skb(skb); + si->dma_tx.skb = NULL; } /* Stop IrLAP */ @@ -849,14 +884,11 @@ static int sa1100_irda_stop(struct net_device *dev) si->irlap = NULL; } - netif_stop_queue(dev); - si->open = 0; - /* * Free resources */ - sa1100_free_dma(si->txdma); - sa1100_free_dma(si->rxdma); + dma_release_channel(si->dma_tx.chan); + dma_release_channel(si->dma_rx.chan); free_irq(dev->irq, dev); sa1100_set_power(si, 0); @@ -888,11 +920,15 @@ static int sa1100_irda_probe(struct platform_device *pdev) struct net_device *dev; struct sa1100_irda *si; unsigned int baudrate_mask; - int err; + int err, irq; if (!pdev->dev.platform_data) return -EINVAL; + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -ENXIO; + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; if (err) goto err_mem_1; @@ -907,22 +943,27 @@ static int sa1100_irda_probe(struct platform_device *pdev) if (!dev) goto err_mem_4; + SET_NETDEV_DEV(dev, &pdev->dev); + si = netdev_priv(dev); si->dev = &pdev->dev; si->pdata = pdev->dev.platform_data; + sg_init_table(&si->dma_rx.sg, 1); + sg_init_table(&si->dma_tx.sg, 1); + /* * Initialise the HP-SIR buffers */ err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); if (err) goto err_mem_5; - err = sa1100_irda_init_iobuf(&si->tx_buff, 4000); + err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME); if (err) goto err_mem_5; dev->netdev_ops = &sa1100_irda_netdev_ops; - dev->irq = IRQ_Ser2ICP; + dev->irq = irq; irda_init_max_qos_capabilies(&si->qos); @@ -996,6 +1037,74 @@ static int sa1100_irda_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(dev->irq); + sa1100_irda_shutdown(si); + __sa1100_irda_set_power(si, 0); + } + + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int sa1100_irda_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct sa1100_irda *si; + + if (!dev) + return 0; + + si = netdev_priv(dev); + if (si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desirable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + sa1100_irda_startup(si); + __sa1100_irda_set_power(si, si->power); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + + return 0; +} +#else +#define sa1100_irda_suspend NULL +#define sa1100_irda_resume NULL +#endif + static struct platform_driver sa1100ir_driver = { .probe = sa1100_irda_probe, .remove = sa1100_irda_remove, diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c index c95639b..4300a7f 100644 --- a/drivers/pcmcia/sa1100_neponset.c +++ b/drivers/pcmcia/sa1100_neponset.c @@ -94,12 +94,7 @@ neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_sta ret = sa1111_pcmcia_configure_socket(skt, state); if (ret == 0) { - unsigned long flags; - - local_irq_save(flags); - NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set; - - local_irq_restore(flags); + neponset_ncr_frob(ncr_mask, ncr_set); sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); } diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 27f2fe3..33568e1 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -22,6 +22,40 @@ #include "sa1111_generic.h" +/* + * These are offsets from the above base. + */ +#define PCCR 0x0000 +#define PCSSR 0x0004 +#define PCSR 0x0008 + +#define PCSR_S0_READY (1<<0) +#define PCSR_S1_READY (1<<1) +#define PCSR_S0_DETECT (1<<2) +#define PCSR_S1_DETECT (1<<3) +#define PCSR_S0_VS1 (1<<4) +#define PCSR_S0_VS2 (1<<5) +#define PCSR_S1_VS1 (1<<6) +#define PCSR_S1_VS2 (1<<7) +#define PCSR_S0_WP (1<<8) +#define PCSR_S1_WP (1<<9) +#define PCSR_S0_BVD1 (1<<10) +#define PCSR_S0_BVD2 (1<<11) +#define PCSR_S1_BVD1 (1<<12) +#define PCSR_S1_BVD2 (1<<13) + +#define PCCR_S0_RST (1<<0) +#define PCCR_S1_RST (1<<1) +#define PCCR_S0_FLT (1<<2) +#define PCCR_S1_FLT (1<<3) +#define PCCR_S0_PWAITEN (1<<4) +#define PCCR_S1_PWAITEN (1<<5) +#define PCCR_S0_PSE (1<<6) +#define PCCR_S1_PSE (1<<7) + +#define PCSSR_S0_SLEEP (1<<0) +#define PCSSR_S1_SLEEP (1<<1) + #define IDX_IRQ_S0_READY_NINT (0) #define IDX_IRQ_S0_CD_VALID (1) #define IDX_IRQ_S0_BVD1_STSCHG (2) @@ -49,7 +83,7 @@ static void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { struct sa1111_pcmcia_socket *s = to_skt(skt); - unsigned long status = sa1111_readl(s->dev->mapbase + SA1111_PCSR); + unsigned long status = sa1111_readl(s->dev->mapbase + PCSR); switch (skt->nr) { case 0: @@ -105,10 +139,10 @@ int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT; local_irq_save(flags); - val = sa1111_readl(s->dev->mapbase + SA1111_PCCR); + val = sa1111_readl(s->dev->mapbase + PCCR); val &= ~pccr_skt_mask; val |= pccr_set_mask & pccr_skt_mask; - sa1111_writel(val, s->dev->mapbase + SA1111_PCCR); + sa1111_writel(val, s->dev->mapbase + PCCR); local_irq_restore(flags); return 0; @@ -163,12 +197,18 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, static int pcmcia_probe(struct sa1111_dev *dev) { void __iomem *base; + int ret; + + ret = sa1111_enable_device(dev); + if (ret) + return ret; dev_set_drvdata(&dev->dev, NULL); - if (!request_mem_region(dev->res.start, 512, - SA1111_DRIVER_NAME(dev))) + if (!request_mem_region(dev->res.start, 512, SA1111_DRIVER_NAME(dev))) { + sa1111_disable_device(dev); return -EBUSY; + } base = dev->mapbase; @@ -181,8 +221,8 @@ static int pcmcia_probe(struct sa1111_dev *dev) /* * Initialise the suspend state. */ - sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR); - sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR); + sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + PCSSR); + sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + PCCR); #ifdef CONFIG_SA1100_BADGE4 pcmcia_badge4_init(&dev->dev); @@ -212,6 +252,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) } release_mem_region(dev->res.start, 512); + sa1111_disable_device(dev); return 0; } diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index cb9a585..e8f1add 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -32,7 +32,7 @@ #include <linux/bitops.h> #include <mach/hardware.h> -#include <asm/irq.h> +#include <mach/irqs.h> #ifdef CONFIG_ARCH_PXA #include <mach/regs-rtc.h> diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index ef7a21a..2ca5959 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -38,6 +38,7 @@ #include <asm/irq.h> #include <mach/hardware.h> +#include <mach/irqs.h> #include <asm/mach/serial_sa1100.h> /* We've been assigned a range on the "Low-density serial ports" major */ diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 4bde4f9..e1004fb 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -16,29 +16,115 @@ #include <mach/hardware.h> #include <asm/mach-types.h> #include <mach/assabet.h> -#include <mach/badge4.h> #include <asm/hardware/sa1111.h> #ifndef CONFIG_SA1111 #error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined." #endif -extern int usb_disabled(void); +#define USB_STATUS 0x0118 +#define USB_RESET 0x011c +#define USB_IRQTEST 0x0120 + +#define USB_RESET_FORCEIFRESET (1 << 0) +#define USB_RESET_FORCEHCRESET (1 << 1) +#define USB_RESET_CLKGENRESET (1 << 2) +#define USB_RESET_SIMSCALEDOWN (1 << 3) +#define USB_RESET_USBINTTEST (1 << 4) +#define USB_RESET_SLEEPSTBYEN (1 << 5) +#define USB_RESET_PWRSENSELOW (1 << 6) +#define USB_RESET_PWRCTRLLOW (1 << 7) + +#define USB_STATUS_IRQHCIRMTWKUP (1 << 7) +#define USB_STATUS_IRQHCIBUFFACC (1 << 8) +#define USB_STATUS_NIRQHCIM (1 << 9) +#define USB_STATUS_NHCIMFCLR (1 << 10) +#define USB_STATUS_USBPWRSENSE (1 << 11) -/*-------------------------------------------------------------------------*/ +#if 0 +static void dump_hci_status(struct usb_hcd *hcd, const char *label) +{ + unsigned long status = sa1111_readl(hcd->regs + USB_STATUS); + + dbg("%s USB_STATUS = { %s%s%s%s%s}", label, + ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), + ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""), + ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "), + ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "), + ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "")); +} +#endif -static void sa1111_start_hc(struct sa1111_dev *dev) +static int ohci_sa1111_reset(struct usb_hcd *hcd) { - unsigned int usb_rst = 0; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + return ohci_init(ohci); +} - printk(KERN_DEBUG "%s: starting SA-1111 OHCI USB Controller\n", - __FILE__); +static int __devinit ohci_sa1111_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) { - badge4_set_5V(BADGE4_5V_USB, 1); + ret = ohci_run(ohci); + if (ret < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); } + return ret; +} + +static const struct hc_driver ohci_sa1111_hc_driver = { + .description = hcd_name, + .product_desc = "SA-1111 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ohci_sa1111_reset, + .start = ohci_sa1111_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, #endif + .start_port_reset = ohci_start_port_reset, +}; + +static int sa1111_start_hc(struct sa1111_dev *dev) +{ + unsigned int usb_rst = 0; + int ret; + + dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n"); if (machine_is_xp860() || machine_has_neponset() || @@ -51,220 +137,121 @@ static void sa1111_start_hc(struct sa1111_dev *dev) * host controller in reset. */ sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, - dev->mapbase + SA1111_USB_RESET); + dev->mapbase + USB_RESET); /* * Now, carefully enable the USB clock, and take * the USB host controller out of reset. */ - sa1111_enable_device(dev); - udelay(11); - sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET); + ret = sa1111_enable_device(dev); + if (ret == 0) { + udelay(11); + sa1111_writel(usb_rst, dev->mapbase + USB_RESET); + } + + return ret; } static void sa1111_stop_hc(struct sa1111_dev *dev) { unsigned int usb_rst; - printk(KERN_DEBUG "%s: stopping SA-1111 OHCI USB Controller\n", - __FILE__); + + dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n"); /* * Put the USB host controller into reset. */ - usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET); + usb_rst = sa1111_readl(dev->mapbase + USB_RESET); sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, - dev->mapbase + SA1111_USB_RESET); + dev->mapbase + USB_RESET); /* * Stop the USB clock. */ sa1111_disable_device(dev); - -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) { - /* Disable power to the USB bus */ - badge4_set_5V(BADGE4_5V_USB, 0); - } -#endif -} - - -/*-------------------------------------------------------------------------*/ - -#if 0 -static void dump_hci_status(struct usb_hcd *hcd, const char *label) -{ - unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS); - - dbg ("%s USB_STATUS = { %s%s%s%s%s}", label, - ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), - ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""), - ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "), - ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "), - ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "")); } -#endif - -/*-------------------------------------------------------------------------*/ - -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ - /** - * usb_hcd_sa1111_probe - initialize SA-1111-based HCDs - * Context: !in_interrupt() + * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs * * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - * - * Store this function in the HCD's struct pci_driver as probe(). + * then invokes the start() method for the HCD associated with it. */ -int usb_hcd_sa1111_probe (const struct hc_driver *driver, - struct sa1111_dev *dev) +static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) { struct usb_hcd *hcd; - int retval; + int ret; - hcd = usb_create_hcd (driver, &dev->dev, "sa1111"); + if (usb_disabled()) + return -ENODEV; + + hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111"); if (!hcd) return -ENOMEM; + hcd->rsrc_start = dev->res.start; hcd->rsrc_len = resource_size(&dev->res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dbg("request_mem_region failed"); - retval = -EBUSY; + ret = -EBUSY; goto err1; } + hcd->regs = dev->mapbase; - sa1111_start_hc(dev); - ohci_hcd_init(hcd_to_ohci(hcd)); + ret = sa1111_start_hc(dev); + if (ret) + goto err2; - retval = usb_add_hcd(hcd, dev->irq[1], 0); - if (retval == 0) - return retval; + ret = usb_add_hcd(hcd, dev->irq[1], 0); + if (ret == 0) + return ret; sa1111_stop_hc(dev); + err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); - return retval; + return ret; } - -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - /** - * usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs + * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_sa1111_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. * + * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking + * the HCD's stop() method. */ -void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev) +static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev) { + struct usb_hcd *hcd = sa1111_get_drvdata(dev); + usb_remove_hcd(hcd); sa1111_stop_hc(dev); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); -} - -/*-------------------------------------------------------------------------*/ -static int __devinit -ohci_sa1111_start (struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run (ohci)) < 0) { - err ("can't start %s", hcd->self.bus_name); - ohci_stop (hcd); - return ret; - } return 0; } -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ohci_sa1111_hc_driver = { - .description = hcd_name, - .product_desc = "SA-1111 OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .start = ohci_sa1111_start, - .stop = ohci_stop, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* - * scheduling support - */ - .get_frame_number = ohci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ - -static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev) -{ - int ret; - - if (usb_disabled()) - return -ENODEV; - - ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev); - return ret; -} - -static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev) +static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev) { struct usb_hcd *hcd = sa1111_get_drvdata(dev); - usb_hcd_sa1111_remove(hcd, dev); - return 0; + if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + hcd->driver->shutdown(hcd); + sa1111_stop_hc(dev); + } } static struct sa1111_driver ohci_hcd_sa1111_driver = { .drv = { .name = "sa1111-ohci", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_USB, - .probe = ohci_hcd_sa1111_drv_probe, - .remove = ohci_hcd_sa1111_drv_remove, + .probe = ohci_hcd_sa1111_probe, + .remove = ohci_hcd_sa1111_remove, + .shutdown = ohci_hcd_sa1111_shutdown, }; - diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index 98d55d0..b632584 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -173,282 +173,48 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/mutex.h> #include <linux/io.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> -#include <mach/assabet.h> #include <mach/shannon.h> /* - * debugging? - */ -#define DEBUG 0 -/* * Complain if VAR is out of range. */ #define DEBUG_VAR 1 -#undef ASSABET_PAL_VIDEO - #include "sa1100fb.h" -extern void (*sa1100fb_backlight_power)(int on); -extern void (*sa1100fb_lcd_power)(int on); - -static struct sa1100fb_rgb rgb_4 = { +static const struct sa1100fb_rgb rgb_4 = { .red = { .offset = 0, .length = 4, }, .green = { .offset = 0, .length = 4, }, .blue = { .offset = 0, .length = 4, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb rgb_8 = { +static const struct sa1100fb_rgb rgb_8 = { .red = { .offset = 0, .length = 8, }, .green = { .offset = 0, .length = 8, }, .blue = { .offset = 0, .length = 8, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb def_rgb_16 = { +static const struct sa1100fb_rgb def_rgb_16 = { .red = { .offset = 11, .length = 5, }, .green = { .offset = 5, .length = 6, }, .blue = { .offset = 0, .length = 5, }, .transp = { .offset = 0, .length = 0, }, }; -#ifdef CONFIG_SA1100_ASSABET -#ifndef ASSABET_PAL_VIDEO -/* - * The assabet uses a sharp LQ039Q2DS54 LCD module. It is actually - * takes an RGB666 signal, but we provide it with an RGB565 signal - * instead (def_rgb_16). - */ -static struct sa1100fb_mach_info lq039q2ds54_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 61, .upper_margin = 3, - .right_margin = 9, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#else -static struct sa1100fb_mach_info pal_info __initdata = { - .pixclock = 67797, .bpp = 16, - .xres = 640, .yres = 512, - - .hsync_len = 64, .vsync_len = 6, - .left_margin = 125, .upper_margin = 70, - .right_margin = 115, .lower_margin = 36, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#endif - -#ifdef CONFIG_SA1100_H3600 -static struct sa1100fb_mach_info h3600_info __initdata = { - .pixclock = 174757, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 3, .vsync_len = 3, - .left_margin = 12, .upper_margin = 10, - .right_margin = 17, .lower_margin = 1, - - .cmap_static = 1, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; - -static struct sa1100fb_rgb h3600_rgb_16 = { - .red = { .offset = 12, .length = 4, }, - .green = { .offset = 7, .length = 4, }, - .blue = { .offset = 1, .length = 4, }, - .transp = { .offset = 0, .length = 0, }, -}; -#endif - -#ifdef CONFIG_SA1100_H3100 -static struct sa1100fb_mach_info h3100_info __initdata = { - .pixclock = 406977, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 26, .vsync_len = 41, - .left_margin = 4, .upper_margin = 0, - .right_margin = 4, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .cmap_greyscale = 1, - .cmap_inverse = 1, - - .lccr0 = LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef CONFIG_SA1100_COLLIE -static struct sa1100fb_mach_info collie_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 11, .upper_margin = 2, - .right_margin = 30, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef LART_GREY_LCD -static struct sa1100fb_mach_info lart_grey_info __initdata = { - .pixclock = 150000, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 1, .vsync_len = 1, - .left_margin = 4, .upper_margin = 0, - .right_margin = 2, .lower_margin = 0, - - .cmap_greyscale = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_COLOR_LCD -static struct sa1100fb_mach_info lart_color_info __initdata = { - .pixclock = 150000, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 2, .vsync_len = 3, - .left_margin = 69, .upper_margin = 14, - .right_margin = 8, .lower_margin = 4, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_VIDEO_OUT -static struct sa1100fb_mach_info lart_video_info __initdata = { - .pixclock = 39721, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 95, .vsync_len = 2, - .left_margin = 40, .upper_margin = 32, - .right_margin = 24, .lower_margin = 11, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif - -#ifdef LART_KIT01_LCD -static struct sa1100fb_mach_info lart_kit01_info __initdata = { - .pixclock = 63291, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 64, .vsync_len = 3, - .left_margin = 122, .upper_margin = 45, - .right_margin = 10, .lower_margin = 10, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg -}; -#endif - -#ifdef CONFIG_SA1100_SHANNON -static struct sa1100fb_mach_info shannon_info __initdata = { - .pixclock = 152500, .bpp = 8, - .xres = 640, .yres = 480, - - .hsync_len = 4, .vsync_len = 3, - .left_margin = 2, .upper_margin = 0, - .right_margin = 1, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, - .lccr3 = LCCR3_ACBsDiv(512), -}; -#endif - -static struct sa1100fb_mach_info * __init -sa1100fb_get_machine_info(struct sa1100fb_info *fbi) -{ - struct sa1100fb_mach_info *inf = NULL; - - /* - * R G B T - * default {11,5}, { 5,6}, { 0,5}, { 0,0} - * h3600 {12,4}, { 7,4}, { 1,4}, { 0,0} - * freebird { 8,4}, { 4,4}, { 0,4}, {12,4} - */ -#ifdef CONFIG_SA1100_ASSABET - if (machine_is_assabet()) { -#ifndef ASSABET_PAL_VIDEO - inf = &lq039q2ds54_info; -#else - inf = &pal_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_H3100 - if (machine_is_h3100()) { - inf = &h3100_info; - } -#endif -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - inf = &h3600_info; - fbi->rgb[RGB_16] = &h3600_rgb_16; - } -#endif -#ifdef CONFIG_SA1100_COLLIE - if (machine_is_collie()) { - inf = &collie_info; - } -#endif -#ifdef CONFIG_SA1100_LART - if (machine_is_lart()) { -#ifdef LART_GREY_LCD - inf = &lart_grey_info; -#endif -#ifdef LART_COLOR_LCD - inf = &lart_color_info; -#endif -#ifdef LART_VIDEO_OUT - inf = &lart_video_info; -#endif -#ifdef LART_KIT01_LCD - inf = &lart_kit01_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_SHANNON - if (machine_is_shannon()) { - inf = &shannon_info; - } -#endif - return inf; -} - static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *); static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state); @@ -533,7 +299,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, * is what you poke into the framebuffer to produce the * colour you requested. */ - if (fbi->cmap_inverse) { + if (fbi->inf->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; @@ -607,14 +373,14 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xres = MIN_XRES; if (var->yres < MIN_YRES) var->yres = MIN_YRES; - if (var->xres > fbi->max_xres) - var->xres = fbi->max_xres; - if (var->yres > fbi->max_yres) - var->yres = fbi->max_yres; + if (var->xres > fbi->inf->xres) + var->xres = fbi->inf->xres; + if (var->yres > fbi->inf->yres) + var->yres = fbi->inf->yres; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = max(var->yres_virtual, var->yres); - DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel); + dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel); switch (var->bits_per_pixel) { case 4: rgbidx = RGB_4; @@ -638,16 +404,16 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue = fbi->rgb[rgbidx]->blue; var->transp = fbi->rgb[rgbidx]->transp; - DPRINTK("RGBT length = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n", var->red.length, var->green.length, var->blue.length, var->transp.length); - DPRINTK("RGBT offset = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n", var->red.offset, var->green.offset, var->blue.offset, var->transp.offset); #ifdef CONFIG_CPU_FREQ - printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz\n", + dev_dbg(fbi->dev, "dma period = %d ps, clock = %d kHz\n", sa1100fb_display_dma_period(var), cpufreq_get(smp_processor_id())); #endif @@ -655,22 +421,10 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -static inline void sa1100fb_set_truecolor(u_int is_true_color) +static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual) { - if (machine_is_assabet()) { -#if 1 // phase 4 or newer Assabet's - if (is_true_color) - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); -#else - // older Assabet's - if (is_true_color) - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); -#endif - } + if (fbi->inf->set_visual) + fbi->inf->set_visual(visual); } /* @@ -683,11 +437,11 @@ static int sa1100fb_set_par(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; unsigned long palette_mem_size; - DPRINTK("set_par\n"); + dev_dbg(fbi->dev, "set_par\n"); if (var->bits_per_pixel == 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; - else if (!fbi->cmap_static) + else if (!fbi->inf->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* @@ -704,7 +458,7 @@ static int sa1100fb_set_par(struct fb_info *info) palette_mem_size = fbi->palette_size * sizeof(u16); - DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); + dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size); fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; @@ -712,7 +466,7 @@ static int sa1100fb_set_par(struct fb_info *info) /* * Set (any) board control register to handle new color depth */ - sa1100fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); + sa1100fb_set_visual(fbi, fbi->fb.fix.visual); sa1100fb_activate_var(var, fbi); return 0; @@ -728,7 +482,7 @@ sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, /* * Make sure the user isn't doing something stupid. */ - if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->cmap_static)) + if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static)) return -EINVAL; return gen_set_cmap(cmap, kspc, con, info); @@ -775,7 +529,7 @@ static int sa1100fb_blank(int blank, struct fb_info *info) struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; int i; - DPRINTK("sa1100fb_blank: blank=%d\n", blank); + dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank); switch (blank) { case FB_BLANK_POWERDOWN: @@ -863,43 +617,43 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ u_int half_screen_size, yres, pcd; u_long flags; - DPRINTK("Configuring SA1100 LCD\n"); + dev_dbg(fbi->dev, "Configuring SA1100 LCD\n"); - DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n", + dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n", var->xres, var->hsync_len, var->left_margin, var->right_margin); - DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n", + dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n", var->yres, var->vsync_len, var->upper_margin, var->lower_margin); #if DEBUG_VAR if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", + dev_err(fbi->dev, "%s: invalid xres %d\n", fbi->fb.fix.id, var->xres); if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", + dev_err(fbi->dev, "%s: invalid hsync_len %d\n", fbi->fb.fix.id, var->hsync_len); if (var->left_margin < 1 || var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", + dev_err(fbi->dev, "%s: invalid left_margin %d\n", fbi->fb.fix.id, var->left_margin); if (var->right_margin < 1 || var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", + dev_err(fbi->dev, "%s: invalid right_margin %d\n", fbi->fb.fix.id, var->right_margin); if (var->yres < 1 || var->yres > 1024) - printk(KERN_ERR "%s: invalid yres %d\n", + dev_err(fbi->dev, "%s: invalid yres %d\n", fbi->fb.fix.id, var->yres); if (var->vsync_len < 1 || var->vsync_len > 64) - printk(KERN_ERR "%s: invalid vsync_len %d\n", + dev_err(fbi->dev, "%s: invalid vsync_len %d\n", fbi->fb.fix.id, var->vsync_len); if (var->upper_margin < 0 || var->upper_margin > 255) - printk(KERN_ERR "%s: invalid upper_margin %d\n", + dev_err(fbi->dev, "%s: invalid upper_margin %d\n", fbi->fb.fix.id, var->upper_margin); if (var->lower_margin < 0 || var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", + dev_err(fbi->dev, "%s: invalid lower_margin %d\n", fbi->fb.fix.id, var->lower_margin); #endif - new_regs.lccr0 = fbi->lccr0 | + new_regs.lccr0 = fbi->inf->lccr0 | LCCR0_LEN | LCCR0_LDM | LCCR0_BAM | LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0); @@ -914,7 +668,7 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * the YRES parameter. */ yres = var->yres; - if (fbi->lccr0 & LCCR0_Dual) + if (fbi->inf->lccr0 & LCCR0_Dual) yres /= 2; new_regs.lccr2 = @@ -924,14 +678,14 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ LCCR2_EndFrmDel(var->lower_margin); pcd = get_pcd(var->pixclock, cpufreq_get(0)); - new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->lccr3 | + new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 | (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); - DPRINTK("nlccr0 = 0x%08lx\n", new_regs.lccr0); - DPRINTK("nlccr1 = 0x%08lx\n", new_regs.lccr1); - DPRINTK("nlccr2 = 0x%08lx\n", new_regs.lccr2); - DPRINTK("nlccr3 = 0x%08lx\n", new_regs.lccr3); + dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0); + dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1); + dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2); + dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3); half_screen_size = var->bits_per_pixel; half_screen_size = half_screen_size * var->xres * var->yres / 16; @@ -951,9 +705,12 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * Only update the registers if the controller is enabled * and something has changed. */ - if ((LCCR0 != fbi->reg_lccr0) || (LCCR1 != fbi->reg_lccr1) || - (LCCR2 != fbi->reg_lccr2) || (LCCR3 != fbi->reg_lccr3) || - (DBAR1 != fbi->dbar1) || (DBAR2 != fbi->dbar2)) + if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 || + readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 || + readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 || + readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 || + readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 || + readl_relaxed(fbi->base + DBAR2) != fbi->dbar2) sa1100fb_schedule_work(fbi, C_REENABLE); return 0; @@ -967,18 +724,18 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ */ static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("backlight o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff"); - if (sa1100fb_backlight_power) - sa1100fb_backlight_power(on); + if (fbi->inf->backlight_power) + fbi->inf->backlight_power(on); } static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("LCD power o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff"); - if (sa1100fb_lcd_power) - sa1100fb_lcd_power(on); + if (fbi->inf->lcd_power) + fbi->inf->lcd_power(on); } static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) @@ -1008,14 +765,25 @@ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) } if (mask) { + unsigned long flags; + + /* + * SA-1100 requires the GPIO direction register set + * appropriately for the alternate function. Hence + * we set it here via bitmask rather than excessive + * fiddling via the GPIO subsystem - and even then + * we'll still have to deal with GAFR. + */ + local_irq_save(flags); GPDR |= mask; GAFR |= mask; + local_irq_restore(flags); } } static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) { - DPRINTK("Enabling LCD controller\n"); + dev_dbg(fbi->dev, "Enabling LCD controller\n"); /* * Make sure the mode bits are present in the first palette entry @@ -1024,43 +792,46 @@ static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var); /* Sequence from 11.7.10 */ - LCCR3 = fbi->reg_lccr3; - LCCR2 = fbi->reg_lccr2; - LCCR1 = fbi->reg_lccr1; - LCCR0 = fbi->reg_lccr0 & ~LCCR0_LEN; - DBAR1 = fbi->dbar1; - DBAR2 = fbi->dbar2; - LCCR0 |= LCCR0_LEN; - - if (machine_is_shannon()) { - GPDR |= SHANNON_GPIO_DISP_EN; - GPSR |= SHANNON_GPIO_DISP_EN; - } - - DPRINTK("DBAR1 = 0x%08x\n", DBAR1); - DPRINTK("DBAR2 = 0x%08x\n", DBAR2); - DPRINTK("LCCR0 = 0x%08x\n", LCCR0); - DPRINTK("LCCR1 = 0x%08x\n", LCCR1); - DPRINTK("LCCR2 = 0x%08x\n", LCCR2); - DPRINTK("LCCR3 = 0x%08x\n", LCCR3); + writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3); + writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2); + writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1); + writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0); + writel_relaxed(fbi->dbar1, fbi->base + DBAR1); + writel_relaxed(fbi->dbar2, fbi->base + DBAR2); + writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0); + + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 1); + + dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1)); + dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2)); + dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0)); + dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1)); + dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2)); + dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3)); } static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) { DECLARE_WAITQUEUE(wait, current); + u32 lccr0; - DPRINTK("Disabling LCD controller\n"); + dev_dbg(fbi->dev, "Disabling LCD controller\n"); - if (machine_is_shannon()) { - GPCR |= SHANNON_GPIO_DISP_EN; - } + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 0); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&fbi->ctrlr_wait, &wait); - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ - LCCR0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + /* Clear LCD Status Register */ + writel_relaxed(~0, fbi->base + LCSR); + + lccr0 = readl_relaxed(fbi->base + LCCR0); + lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ + writel_relaxed(lccr0, fbi->base + LCCR0); + lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + writel_relaxed(lccr0, fbi->base + LCCR0); schedule_timeout(20 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); @@ -1072,14 +843,15 @@ static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) static irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id) { struct sa1100fb_info *fbi = dev_id; - unsigned int lcsr = LCSR; + unsigned int lcsr = readl_relaxed(fbi->base + LCSR); if (lcsr & LCSR_LDD) { - LCCR0 |= LCCR0_LDM; + u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM; + writel_relaxed(lccr0, fbi->base + LCCR0); wake_up(&fbi->ctrlr_wait); } - LCSR = lcsr; + writel_relaxed(lcsr, fbi->base + LCSR); return IRQ_HANDLED; } @@ -1268,7 +1040,7 @@ sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val, switch (val) { case CPUFREQ_ADJUST: case CPUFREQ_INCOMPATIBLE: - printk(KERN_DEBUG "min dma period: %d ps, " + dev_dbg(fbi->dev, "min dma period: %d ps, " "new clock %d kHz\n", sa1100fb_min_dma_period(fbi), policy->max); /* todo: fill in min/max values */ @@ -1318,7 +1090,7 @@ static int sa1100fb_resume(struct platform_device *dev) * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ -static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) +static int __devinit sa1100fb_map_video_memory(struct sa1100fb_info *fbi) { /* * We reserve one page for the palette, plus the size @@ -1344,7 +1116,7 @@ static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) } /* Fake monspecs to fill in fbinfo structure */ -static struct fb_monspecs monspecs __initdata = { +static struct fb_monspecs monspecs __devinitdata = { .hfmin = 30000, .hfmax = 70000, .vfmin = 50, @@ -1352,10 +1124,11 @@ static struct fb_monspecs monspecs __initdata = { }; -static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) +static struct sa1100fb_info * __devinit sa1100fb_init_fbinfo(struct device *dev) { - struct sa1100fb_mach_info *inf; + struct sa1100fb_mach_info *inf = dev->platform_data; struct sa1100fb_info *fbi; + unsigned i; fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16, GFP_KERNEL); @@ -1390,8 +1163,6 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->rgb[RGB_8] = &rgb_8; fbi->rgb[RGB_16] = &def_rgb_16; - inf = sa1100fb_get_machine_info(fbi); - /* * People just don't seem to get this. We don't support * anything but correct entries now, so panic if someone @@ -1402,13 +1173,10 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) panic("sa1100fb error: invalid LCCR3 fields set or zero " "pixclock."); - fbi->max_xres = inf->xres; fbi->fb.var.xres = inf->xres; fbi->fb.var.xres_virtual = inf->xres; - fbi->max_yres = inf->yres; fbi->fb.var.yres = inf->yres; fbi->fb.var.yres_virtual = inf->yres; - fbi->max_bpp = inf->bpp; fbi->fb.var.bits_per_pixel = inf->bpp; fbi->fb.var.pixclock = inf->pixclock; fbi->fb.var.hsync_len = inf->hsync_len; @@ -1419,14 +1187,16 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->fb.var.lower_margin = inf->lower_margin; fbi->fb.var.sync = inf->sync; fbi->fb.var.grayscale = inf->cmap_greyscale; - fbi->cmap_inverse = inf->cmap_inverse; - fbi->cmap_static = inf->cmap_static; - fbi->lccr0 = inf->lccr0; - fbi->lccr3 = inf->lccr3; fbi->state = C_STARTUP; fbi->task_state = (u_char)-1; - fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * - fbi->max_bpp / 8; + fbi->fb.fix.smem_len = inf->xres * inf->yres * + inf->bpp / 8; + fbi->inf = inf; + + /* Copy the RGB bitfield overrides */ + for (i = 0; i < NR_RGB; i++) + if (inf->rgb[i]) + fbi->rgb[i] = inf->rgb[i]; init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, sa1100fb_task); @@ -1438,13 +1208,20 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) static int __devinit sa1100fb_probe(struct platform_device *pdev) { struct sa1100fb_info *fbi; + struct resource *res; int ret, irq; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform LCD data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (irq < 0) + if (irq < 0 || !res) return -EINVAL; - if (!request_mem_region(0xb0100000, 0x10000, "LCD")) + if (!request_mem_region(res->start, resource_size(res), "LCD")) return -EBUSY; fbi = sa1100fb_init_fbinfo(&pdev->dev); @@ -1452,6 +1229,10 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) if (!fbi) goto failed; + fbi->base = ioremap(res->start, resource_size(res)); + if (!fbi->base) + goto failed; + /* Initialize video memory */ ret = sa1100fb_map_video_memory(fbi); if (ret) @@ -1459,14 +1240,16 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi); if (ret) { - printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret); + dev_err(&pdev->dev, "request_irq failed: %d\n", ret); goto failed; } -#ifdef ASSABET_PAL_VIDEO - if (machine_is_assabet()) - ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); -#endif + if (machine_is_shannon()) { + ret = gpio_request_one(SHANNON_GPIO_DISP_EN, + GPIOF_OUT_INIT_LOW, "display enable"); + if (ret) + goto err_free_irq; + } /* * This makes sure that our colour bitfield @@ -1478,7 +1261,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = register_framebuffer(&fbi->fb); if (ret < 0) - goto err_free_irq; + goto err_reg_fb; #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = sa1100fb_freq_transition; @@ -1490,12 +1273,17 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) /* This driver cannot be unloaded at the moment */ return 0; + err_reg_fb: + if (machine_is_shannon()) + gpio_free(SHANNON_GPIO_DISP_EN); err_free_irq: free_irq(irq, fbi); failed: + if (fbi) + iounmap(fbi->base); platform_set_drvdata(pdev, NULL); kfree(fbi); - release_mem_region(0xb0100000, 0x10000); + release_mem_region(res->start, resource_size(res)); return ret; } @@ -1505,6 +1293,7 @@ static struct platform_driver sa1100fb_driver = { .resume = sa1100fb_resume, .driver = { .name = "sa11x0-fb", + .owner = THIS_MODULE, }, }; diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h index 1c3b459..fc5d429 100644 --- a/drivers/video/sa1100fb.h +++ b/drivers/video/sa1100fb.h @@ -10,44 +10,15 @@ * for more details. */ -/* - * These are the bitfields for each - * display depth that we support. - */ -struct sa1100fb_rgb { - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; -}; - -/* - * This structure describes the machine which we are running on. - */ -struct sa1100fb_mach_info { - u_long pixclock; - - u_short xres; - u_short yres; - - u_char bpp; - u_char hsync_len; - u_char left_margin; - u_char right_margin; - - u_char vsync_len; - u_char upper_margin; - u_char lower_margin; - u_char sync; - - u_int cmap_greyscale:1, - cmap_inverse:1, - cmap_static:1, - unused:29; - - u_int lccr0; - u_int lccr3; -}; +#define LCCR0 0x0000 /* LCD Control Reg. 0 */ +#define LCSR 0x0004 /* LCD Status Reg. */ +#define DBAR1 0x0010 /* LCD DMA Base Address Reg. channel 1 */ +#define DCAR1 0x0014 /* LCD DMA Current Address Reg. channel 1 */ +#define DBAR2 0x0018 /* LCD DMA Base Address Reg. channel 2 */ +#define DCAR2 0x001C /* LCD DMA Current Address Reg. channel 2 */ +#define LCCR1 0x0020 /* LCD Control Reg. 1 */ +#define LCCR2 0x0024 /* LCD Control Reg. 2 */ +#define LCCR3 0x0028 /* LCD Control Reg. 3 */ /* Shadows for LCD controller registers */ struct sa1100fb_lcd_reg { @@ -57,19 +28,11 @@ struct sa1100fb_lcd_reg { unsigned long lccr3; }; -#define RGB_4 (0) -#define RGB_8 (1) -#define RGB_16 (2) -#define NR_RGB 3 - struct sa1100fb_info { struct fb_info fb; struct device *dev; - struct sa1100fb_rgb *rgb[NR_RGB]; - - u_int max_bpp; - u_int max_xres; - u_int max_yres; + const struct sa1100fb_rgb *rgb[NR_RGB]; + void __iomem *base; /* * These are the addresses we mapped @@ -88,12 +51,6 @@ struct sa1100fb_info { dma_addr_t dbar1; dma_addr_t dbar2; - u_int lccr0; - u_int lccr3; - u_int cmap_inverse:1, - cmap_static:1, - unused:30; - u_int reg_lccr0; u_int reg_lccr1; u_int reg_lccr2; @@ -109,6 +66,8 @@ struct sa1100fb_info { struct notifier_block freq_transition; struct notifier_block freq_policy; #endif + + const struct sa1100fb_mach_info *inf; }; #define TO_INF(ptr,member) container_of(ptr,struct sa1100fb_info,member) @@ -130,15 +89,6 @@ struct sa1100fb_info { #define SA1100_NAME "SA1100" /* - * Debug macros - */ -#if DEBUG -# define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -/* * Minimum X and Y resolutions */ #define MIN_XRES 64 diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index f88c1cc..a9e8bd1 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -10,8 +10,6 @@ #ifndef MCP_H #define MCP_H -#include <mach/dma.h> - struct mcp_ops; struct mcp { @@ -21,12 +19,7 @@ struct mcp { int use_count; unsigned int sclk_rate; unsigned int rw_timeout; - dma_device_t dma_audio_rd; - dma_device_t dma_audio_wr; - dma_device_t dma_telco_rd; - dma_device_t dma_telco_wr; struct device attached_device; - int gpio_base; }; struct mcp_ops { @@ -47,15 +40,14 @@ void mcp_disable(struct mcp *); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_register(struct mcp *); -void mcp_host_unregister(struct mcp *); +int mcp_host_add(struct mcp *, void *); +void mcp_host_del(struct mcp *); +void mcp_host_free(struct mcp *); struct mcp_driver { struct device_driver drv; int (*probe)(struct mcp *); void (*remove)(struct mcp *); - int (*suspend)(struct mcp *, pm_message_t); - int (*resume)(struct mcp *); }; int mcp_driver_register(struct mcp_driver *); diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 4321f04..28af417 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -12,7 +12,7 @@ #include <linux/mfd/mcp.h> #include <linux/gpio.h> -#include <linux/semaphore.h> +#include <linux/mutex.h> #define UCB_IO_DATA 0x00 #define UCB_IO_DIR 0x01 @@ -104,17 +104,27 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +enum ucb1x00_reset { + UCB_RST_PROBE, + UCB_RST_RESUME, + UCB_RST_SUSPEND, + UCB_RST_REMOVE, + UCB_RST_PROBE_FAIL, +}; -struct ucb1x00_irq { - void *devid; - void (*fn)(int, void *); +struct ucb1x00_plat_data { + void (*reset)(enum ucb1x00_reset); + unsigned irq_base; + int gpio_base; + unsigned can_wakeup; }; struct ucb1x00 { - spinlock_t lock; + raw_spinlock_t irq_lock; struct mcp *mcp; unsigned int irq; - struct semaphore adc_sem; + int irq_base; + struct mutex adc_mutex; spinlock_t io_lock; u16 id; u16 io_dir; @@ -122,7 +132,8 @@ struct ucb1x00 { u16 adc_cr; u16 irq_fal_enbl; u16 irq_ris_enbl; - struct ucb1x00_irq irq_handler[16]; + u16 irq_mask; + u16 irq_wake; struct device dev; struct list_head node; struct list_head devs; @@ -144,7 +155,7 @@ struct ucb1x00_driver { struct list_head devs; int (*add)(struct ucb1x00_dev *dev); void (*remove)(struct ucb1x00_dev *dev); - int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state); + int (*suspend)(struct ucb1x00_dev *dev); int (*resume)(struct ucb1x00_dev *dev); }; @@ -245,15 +256,4 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); void ucb1x00_adc_enable(struct ucb1x00 *ucb); void ucb1x00_adc_disable(struct ucb1x00 *ucb); -/* - * Which edges of the IRQ do you want to control today? - */ -#define UCB_RISING (1 << 0) -#define UCB_FALLING (1 << 1) - -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); - #endif diff --git a/include/linux/sa11x0-dma.h b/include/linux/sa11x0-dma.h new file mode 100644 index 0000000..65839a5 --- /dev/null +++ b/include/linux/sa11x0-dma.h @@ -0,0 +1,24 @@ +/* + * SA11x0 DMA Engine support + * + * Copyright (C) 2012 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_SA11X0_DMA_H +#define __LINUX_SA11X0_DMA_H + +struct dma_chan; + +#if defined(CONFIG_DMA_SA11X0) || defined(CONFIG_DMA_SA11X0_MODULE) +bool sa11x0_dma_filter_fn(struct dma_chan *, void *); +#else +static inline bool sa11x0_dma_filter_fn(struct dma_chan *c, void *d) +{ + return false; +} +#endif + +#endif diff --git a/include/video/sa1100fb.h b/include/video/sa1100fb.h new file mode 100644 index 0000000..4ab4096 --- /dev/null +++ b/include/video/sa1100fb.h @@ -0,0 +1,63 @@ +/* + * StrongARM 1100 LCD Controller Frame Buffer Device + * + * Copyright (C) 1999 Eric A. Thomas + * Based on acornfb.c Copyright (C) Russell King. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ +#ifndef _VIDEO_SA1100FB_H +#define _VIDEO_SA1100FB_H + +#include <linux/fb.h> +#include <linux/types.h> + +#define RGB_4 0 +#define RGB_8 1 +#define RGB_16 2 +#define NR_RGB 3 + +/* These are the bitfields for each display depth that we support. */ +struct sa1100fb_rgb { + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +/* This structure describes the machine which we are running on. */ +struct sa1100fb_mach_info { + u_long pixclock; + + u_short xres; + u_short yres; + + u_char bpp; + u_char hsync_len; + u_char left_margin; + u_char right_margin; + + u_char vsync_len; + u_char upper_margin; + u_char lower_margin; + u_char sync; + + u_int cmap_greyscale:1, + cmap_inverse:1, + cmap_static:1, + unused:29; + + u_int lccr0; + u_int lccr3; + + /* Overrides for the default RGB maps */ + const struct sa1100fb_rgb *rgb[NR_RGB]; + + void (*backlight_power)(int); + void (*lcd_power)(int); + void (*set_visual)(u32); +}; + +#endif |