diff options
Diffstat (limited to 'arch/arm/common/sa1111.c')
| -rw-r--r-- | arch/arm/common/sa1111.c | 281 | 
1 files changed, 185 insertions, 96 deletions
diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index 61691cdbdcf..9173d112ea0 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);  }  |