⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sa1111.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -