📄 sa1111.c
字号:
/* * linux/arch/arm/mach-sa1100/sa1111.c * * SA1111 support * * Original code by John Dorsey * * 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 all generic SA1111 support. * * All initialization functions provided here are intended to be called * from machine specific code with proper arguments when required. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/platform_device.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/dma-mapping.h>#include <asm/hardware.h>#include <asm/mach-types.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/mach/irq.h>#include <asm/sizes.h>#include <asm/hardware/sa1111.h>#ifdef CONFIG_ARCH_PXA#include <asm/arch/pxa-regs.h>#endifextern void __init sa1110_mb_enable(void);/* * We keep the following data for the overall SA1111. Note that the * struct device and struct resource are "fake"; they should be supplied * by the bus above us. However, in the interests of getting all SA1111 * drivers converted over to the device model, we provide this as an * anchor point for all the other drivers. */struct sa1111 { struct device *dev; unsigned long phys; int irq; spinlock_t lock; void __iomem *base;};/* * We _really_ need to eliminate this. Its only users * are the PWM and DMA checking code. */static struct sa1111 *g_sa1111;struct sa1111_dev_info { unsigned long offset; unsigned long skpcr_mask; unsigned int devid; unsigned int irq[6];};static struct sa1111_dev_info sa1111_devices[] = { { .offset = SA1111_USB, .skpcr_mask = SKPCR_UCLKEN, .devid = SA1111_DEVID_USB, .irq = { IRQ_USBPWR, IRQ_HCIM, IRQ_HCIBUFFACC, IRQ_HCIRMTWKP, IRQ_NHCIMFCIR, IRQ_USB_PORT_RESUME }, }, { .offset = 0x0600, .skpcr_mask = SKPCR_I2SCLKEN | SKPCR_L3CLKEN, .devid = SA1111_DEVID_SAC, .irq = { AUDXMTDMADONEA, AUDXMTDMADONEB, AUDRCVDMADONEA, AUDRCVDMADONEB }, }, { .offset = 0x0800, .skpcr_mask = SKPCR_SCLKEN, .devid = SA1111_DEVID_SSP, }, { .offset = SA1111_KBD, .skpcr_mask = SKPCR_PTCLKEN, .devid = SA1111_DEVID_PS2, .irq = { IRQ_TPRXINT, IRQ_TPTXINT }, }, { .offset = SA1111_MSE, .skpcr_mask = SKPCR_PMCLKEN, .devid = SA1111_DEVID_PS2, .irq = { IRQ_MSRXINT, IRQ_MSTXINT }, }, { .offset = 0x1800, .skpcr_mask = 0, .devid = SA1111_DEVID_PCMCIA, .irq = { IRQ_S0_READY_NINT, IRQ_S0_CD_VALID, IRQ_S0_BVD1_STSCHG, IRQ_S1_READY_NINT, IRQ_S1_CD_VALID, IRQ_S1_BVD1_STSCHG, }, },};void __init sa1111_adjust_zones(int node, unsigned long *size, unsigned long *holes){ unsigned int sz = SZ_1M >> PAGE_SHIFT; if (node != 0) sz = 0; size[1] = size[0] - sz; size[0] = sz;}/* * SA1111 interrupt support. Since clearing an IRQ while there are * active IRQs causes the interrupt output to pulse, the upper levels * will call us again if there are more interrupts to process. */static voidsa1111_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs){ unsigned int stat0, stat1, i; void __iomem *base = desc->data; stat0 = sa1111_readl(base + SA1111_INTSTATCLR0); stat1 = sa1111_readl(base + SA1111_INTSTATCLR1); sa1111_writel(stat0, base + SA1111_INTSTATCLR0); desc->chip->ack(irq); sa1111_writel(stat1, base + SA1111_INTSTATCLR1); if (stat0 == 0 && stat1 == 0) { do_bad_IRQ(irq, desc, regs); return; } for (i = IRQ_SA1111_START; stat0; i++, stat0 >>= 1) if (stat0 & 1) do_edge_IRQ(i, irq_desc + i, regs); for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1) if (stat1 & 1) do_edge_IRQ(i, irq_desc + i, regs); /* For level-based interrupts */ desc->chip->unmask(irq);}#define SA1111_IRQMASK_LO(x) (1 << (x - IRQ_SA1111_START))#define SA1111_IRQMASK_HI(x) (1 << (x - IRQ_SA1111_START - 32))static void sa1111_ack_irq(unsigned int irq){}static void sa1111_mask_lowirq(unsigned int irq){ void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ie0; ie0 = sa1111_readl(mapbase + SA1111_INTEN0); ie0 &= ~SA1111_IRQMASK_LO(irq); writel(ie0, mapbase + SA1111_INTEN0);}static void sa1111_unmask_lowirq(unsigned int irq){ void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ie0; ie0 = sa1111_readl(mapbase + SA1111_INTEN0); ie0 |= SA1111_IRQMASK_LO(irq); sa1111_writel(ie0, mapbase + SA1111_INTEN0);}/* * Attempt to re-trigger the interrupt. The SA1111 contains a register * (INTSET) which claims to do this. However, in practice no amount of * manipulation of INTEN and INTSET guarantees that the interrupt will * be triggered. In fact, its very difficult, if not impossible to get * INTSET to re-trigger the interrupt. */static int sa1111_retrigger_lowirq(unsigned int irq){ unsigned int mask = SA1111_IRQMASK_LO(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ip0; int i; ip0 = sa1111_readl(mapbase + SA1111_INTPOL0); for (i = 0; i < 8; i++) { sa1111_writel(ip0 ^ mask, mapbase + SA1111_INTPOL0); sa1111_writel(ip0, mapbase + SA1111_INTPOL0); if (sa1111_readl(mapbase + SA1111_INTSTATCLR1) & mask) break; } if (i == 8) printk(KERN_ERR "Danger Will Robinson: failed to " "re-trigger IRQ%d\n", irq); return i == 8 ? -1 : 0;}static int sa1111_type_lowirq(unsigned int irq, unsigned int flags){ unsigned int mask = SA1111_IRQMASK_LO(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ip0; if (flags == IRQT_PROBE) return 0; if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0) return -EINVAL; ip0 = sa1111_readl(mapbase + SA1111_INTPOL0); if (flags & __IRQT_RISEDGE) ip0 &= ~mask; else ip0 |= mask; sa1111_writel(ip0, mapbase + SA1111_INTPOL0); sa1111_writel(ip0, mapbase + SA1111_WAKEPOL0); return 0;}static int sa1111_wake_lowirq(unsigned int irq, unsigned int on){ unsigned int mask = SA1111_IRQMASK_LO(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long we0; we0 = sa1111_readl(mapbase + SA1111_WAKEEN0); if (on) we0 |= mask; else we0 &= ~mask; sa1111_writel(we0, mapbase + SA1111_WAKEEN0); return 0;}static struct irqchip sa1111_low_chip = { .ack = sa1111_ack_irq, .mask = sa1111_mask_lowirq, .unmask = sa1111_unmask_lowirq, .retrigger = sa1111_retrigger_lowirq, .set_type = sa1111_type_lowirq, .set_wake = sa1111_wake_lowirq,};static void sa1111_mask_highirq(unsigned int irq){ void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ie1; ie1 = sa1111_readl(mapbase + SA1111_INTEN1); ie1 &= ~SA1111_IRQMASK_HI(irq); sa1111_writel(ie1, mapbase + SA1111_INTEN1);}static void sa1111_unmask_highirq(unsigned int irq){ void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ie1; ie1 = sa1111_readl(mapbase + SA1111_INTEN1); ie1 |= SA1111_IRQMASK_HI(irq); sa1111_writel(ie1, mapbase + SA1111_INTEN1);}/* * Attempt to re-trigger the interrupt. The SA1111 contains a register * (INTSET) which claims to do this. However, in practice no amount of * manipulation of INTEN and INTSET guarantees that the interrupt will * be triggered. In fact, its very difficult, if not impossible to get * INTSET to re-trigger the interrupt. */static int sa1111_retrigger_highirq(unsigned int irq){ unsigned int mask = SA1111_IRQMASK_HI(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ip1; int i; ip1 = sa1111_readl(mapbase + SA1111_INTPOL1); for (i = 0; i < 8; i++) { sa1111_writel(ip1 ^ mask, mapbase + SA1111_INTPOL1); sa1111_writel(ip1, mapbase + SA1111_INTPOL1); if (sa1111_readl(mapbase + SA1111_INTSTATCLR1) & mask) break; } if (i == 8) printk(KERN_ERR "Danger Will Robinson: failed to " "re-trigger IRQ%d\n", irq); return i == 8 ? -1 : 0;}static int sa1111_type_highirq(unsigned int irq, unsigned int flags){ unsigned int mask = SA1111_IRQMASK_HI(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long ip1; if (flags == IRQT_PROBE) return 0; if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0) return -EINVAL; ip1 = sa1111_readl(mapbase + SA1111_INTPOL1); if (flags & __IRQT_RISEDGE) ip1 &= ~mask; else ip1 |= mask; sa1111_writel(ip1, mapbase + SA1111_INTPOL1); sa1111_writel(ip1, mapbase + SA1111_WAKEPOL1); return 0;}static int sa1111_wake_highirq(unsigned int irq, unsigned int on){ unsigned int mask = SA1111_IRQMASK_HI(irq); void __iomem *mapbase = get_irq_chipdata(irq); unsigned long we1; we1 = sa1111_readl(mapbase + SA1111_WAKEEN1); if (on) we1 |= mask; else we1 &= ~mask; sa1111_writel(we1, mapbase + SA1111_WAKEEN1); return 0;}static struct irqchip sa1111_high_chip = { .ack = sa1111_ack_irq, .mask = sa1111_mask_highirq, .unmask = sa1111_unmask_highirq, .retrigger = sa1111_retrigger_highirq, .set_type = sa1111_type_highirq, .set_wake = sa1111_wake_highirq,};static void sa1111_setup_irq(struct sa1111 *sachip){ void __iomem *irqbase = sachip->base + SA1111_INTC; unsigned int irq; /* * We're guaranteed that this region hasn't been taken. */ request_mem_region(sachip->phys + SA1111_INTC, 512, "irq"); /* disable all IRQs */ sa1111_writel(0, irqbase + SA1111_INTEN0); sa1111_writel(0, irqbase + SA1111_INTEN1); sa1111_writel(0, irqbase + SA1111_WAKEEN0); sa1111_writel(0, irqbase + SA1111_WAKEEN1); /* * detect on rising edge. Note: Feb 2001 Errata for SA1111 * specifies that S0ReadyInt and S1ReadyInt should be '1'. */ sa1111_writel(0, irqbase + SA1111_INTPOL0); sa1111_writel(SA1111_IRQMASK_HI(IRQ_S0_READY_NINT) | SA1111_IRQMASK_HI(IRQ_S1_READY_NINT), irqbase + SA1111_INTPOL1); /* clear all IRQs */ sa1111_writel(~0, irqbase + SA1111_INTSTATCLR0); sa1111_writel(~0, irqbase + SA1111_INTSTATCLR1); for (irq = IRQ_GPAIN0; irq <= SSPROR; irq++) { set_irq_chip(irq, &sa1111_low_chip); set_irq_chipdata(irq, irqbase); set_irq_handler(irq, do_edge_IRQ); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } for (irq = AUDXMTDMADONEA; irq <= IRQ_S1_BVD1_STSCHG; irq++) { set_irq_chip(irq, &sa1111_high_chip); set_irq_chipdata(irq, irqbase); set_irq_handler(irq, do_edge_IRQ); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } /* * Register SA1111 interrupt */ set_irq_type(sachip->irq, IRQT_RISING); set_irq_data(sachip->irq, irqbase); set_irq_chained_handler(sachip->irq, sa1111_irq_handler);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -