|
@@ -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 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);
|
|
|
}
|
|
|
|