summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/Kconfig9
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/pata_palmld.c150
-rw-r--r--drivers/char/hw_random/Kconfig12
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/mxc-rnga.c247
-rw-r--r--drivers/i2c/busses/i2c-pxa.c22
-rw-r--r--drivers/input/serio/ambakmi.c2
-rw-r--r--drivers/leds/leds-h1940.c2
-rw-r--r--drivers/leds/leds-s3c24xx.c1
-rw-r--r--drivers/media/video/Kconfig4
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/mmci.c2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c6
-rw-r--r--drivers/mmc/host/s3cmci.c5
-rw-r--r--drivers/mtd/onenand/omap2.c1
-rw-r--r--drivers/net/arm/ixp4xx_eth.c26
-rw-r--r--drivers/net/smc91x.h5
-rw-r--r--drivers/net/wan/ixp4xx_hss.c11
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/pxa2xx_stargate2.c174
-rw-r--r--drivers/rtc/rtc-ep93xx.c149
-rw-r--r--drivers/rtc/rtc-pl030.c2
-rw-r--r--drivers/rtc/rtc-pl031.c3
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/amba-pl011.c38
-rw-r--r--drivers/serial/imx.c13
-rw-r--r--drivers/spi/Kconfig11
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/amba-pl022.c1866
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c1
-rw-r--r--drivers/usb/host/ohci-ep93xx.c13
-rw-r--r--drivers/video/Kconfig12
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/amba-clcd.c2
-rw-r--r--drivers/video/mx3fb.c4
-rw-r--r--drivers/video/omap/hwa742.c26
-rw-r--r--drivers/video/pxa168fb.c803
-rw-r--r--drivers/video/pxa168fb.h558
-rw-r--r--drivers/watchdog/Kconfig10
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/orion_wdt.c (renamed from drivers/watchdog/orion5x_wdt.c)120
43 files changed, 4145 insertions, 178 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 9120717c070..2aa1908e5ce 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -535,6 +535,15 @@ config PATA_OPTIDMA
If unsure, say N.
+config PATA_PALMLD
+ tristate "Palm LifeDrive PATA support"
+ depends on MACH_PALMLD
+ help
+ This option enables support for Palm LifeDrive's internal ATA
+ port via the new ATA layer.
+
+ If unsure, say N.
+
config PATA_PCMCIA
tristate "PCMCIA PATA support"
depends on PCMCIA
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 7f1ecf99528..1558059874f 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_PATA_MPC52xx) += pata_mpc52xx.o
obj-$(CONFIG_PATA_MARVELL) += pata_marvell.o
obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
obj-$(CONFIG_PATA_OLDPIIX) += pata_oldpiix.o
+obj-$(CONFIG_PATA_PALMLD) += pata_palmld.o
obj-$(CONFIG_PATA_PCMCIA) += pata_pcmcia.o
obj-$(CONFIG_PATA_PDC2027X) += pata_pdc2027x.o
obj-$(CONFIG_PATA_PDC_OLD) += pata_pdc202xx_old.o
diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c
new file mode 100644
index 00000000000..11fb4ccc74b
--- /dev/null
+++ b/drivers/ata/pata_palmld.c
@@ -0,0 +1,150 @@
+/*
+ * drivers/ata/pata_palmld.c
+ *
+ * Driver for IDE channel in Palm LifeDrive
+ *
+ * Based on research of:
+ * Alex Osborne <ato@meshy.org>
+ *
+ * Rewrite for mainline:
+ * Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Rewritten version based on pata_ixp4xx_cf.c:
+ * ixp4xx PATA/Compact Flash driver
+ * Copyright (C) 2006-07 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/libata.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <scsi/scsi_host.h>
+#include <mach/palmld.h>
+
+#define DRV_NAME "pata_palmld"
+
+static struct scsi_host_template palmld_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations palmld_port_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .cable_detect = ata_cable_40wire,
+};
+
+static __devinit int palmld_pata_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ void __iomem *mem;
+ int ret;
+
+ /* allocate host */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ /* remap drive's physical memory address */
+ mem = devm_ioremap(&pdev->dev, PALMLD_IDE_PHYS, 0x1000);
+ if (!mem)
+ return -ENOMEM;
+
+ /* request and activate power GPIO, IRQ GPIO */
+ ret = gpio_request(GPIO_NR_PALMLD_IDE_PWEN, "HDD PWR");
+ if (ret)
+ goto err1;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_IDE_PWEN, 1);
+ if (ret)
+ goto err2;
+
+ ret = gpio_request(GPIO_NR_PALMLD_IDE_RESET, "HDD RST");
+ if (ret)
+ goto err2;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_IDE_RESET, 0);
+ if (ret)
+ goto err3;
+
+ /* reset the drive */
+ gpio_set_value(GPIO_NR_PALMLD_IDE_RESET, 0);
+ msleep(30);
+ gpio_set_value(GPIO_NR_PALMLD_IDE_RESET, 1);
+ msleep(30);
+
+ /* setup the ata port */
+ ap = host->ports[0];
+ ap->ops = &palmld_port_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING;
+
+ /* memory mapping voodoo */
+ ap->ioaddr.cmd_addr = mem + 0x10;
+ ap->ioaddr.altstatus_addr = mem + 0xe;
+ ap->ioaddr.ctl_addr = mem + 0xe;
+
+ /* start the port */
+ ata_sff_std_ports(&ap->ioaddr);
+
+ /* activate host */
+ return ata_host_activate(host, 0, NULL, IRQF_TRIGGER_RISING,
+ &palmld_sht);
+
+err3:
+ gpio_free(GPIO_NR_PALMLD_IDE_RESET);
+err2:
+ gpio_free(GPIO_NR_PALMLD_IDE_PWEN);
+err1:
+ return ret;
+}
+
+static __devexit int palmld_pata_remove(struct platform_device *dev)
+{
+ struct ata_host *host = platform_get_drvdata(dev);
+
+ ata_host_detach(host);
+
+ /* power down the HDD */
+ gpio_set_value(GPIO_NR_PALMLD_IDE_PWEN, 0);
+
+ gpio_free(GPIO_NR_PALMLD_IDE_RESET);
+ gpio_free(GPIO_NR_PALMLD_IDE_PWEN);
+
+ return 0;
+}
+
+static struct platform_driver palmld_pata_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = palmld_pata_probe,
+ .remove = __devexit_p(palmld_pata_remove),
+};
+
+static int __init palmld_pata_init(void)
+{
+ return platform_driver_register(&palmld_pata_platform_driver);
+}
+
+static void __exit palmld_pata_exit(void)
+{
+ platform_driver_unregister(&palmld_pata_platform_driver);
+}
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("PalmLD PATA driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+module_init(palmld_pata_init);
+module_exit(palmld_pata_exit);
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 9c00440dcf8..f4b3f7293fe 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -148,3 +148,15 @@ config HW_RANDOM_VIRTIO
To compile this driver as a module, choose M here: the
module will be called virtio-rng. If unsure, say N.
+
+config HW_RANDOM_MXC_RNGA
+ tristate "Freescale i.MX RNGA Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGA
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxc-rnga.
+
+ If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index e81d21a5f28..fd1ecd2f673 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o
obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
+obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
new file mode 100644
index 00000000000..187c6be80f4
--- /dev/null
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -0,0 +1,247 @@
+/*
+ * RNG driver for Freescale RNGA
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Author: Alan Carvalho de Assis <acassis@gmail.com>
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * This driver is based on other RNG drivers.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+
+/* RNGA Registers */
+#define RNGA_CONTROL 0x00
+#define RNGA_STATUS 0x04
+#define RNGA_ENTROPY 0x08
+#define RNGA_OUTPUT_FIFO 0x0c
+#define RNGA_MODE 0x10
+#define RNGA_VERIFICATION_CONTROL 0x14
+#define RNGA_OSC_CONTROL_COUNTER 0x18
+#define RNGA_OSC1_COUNTER 0x1c
+#define RNGA_OSC2_COUNTER 0x20
+#define RNGA_OSC_COUNTER_STATUS 0x24
+
+/* RNGA Registers Range */
+#define RNG_ADDR_RANGE 0x28
+
+/* RNGA Control Register */
+#define RNGA_CONTROL_SLEEP 0x00000010
+#define RNGA_CONTROL_CLEAR_INT 0x00000008
+#define RNGA_CONTROL_MASK_INTS 0x00000004
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+#define RNGA_CONTROL_GO 0x00000001
+
+#define RNGA_STATUS_LEVEL_MASK 0x0000ff00
+
+/* RNGA Status Register */
+#define RNGA_STATUS_OSC_DEAD 0x80000000
+#define RNGA_STATUS_SLEEP 0x00000010
+#define RNGA_STATUS_ERROR_INT 0x00000008
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+
+static struct platform_device *rng_dev;
+
+static int mxc_rnga_data_present(struct hwrng *rng)
+{
+ int level;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* how many random numbers is in FIFO? [0-16] */
+ level = ((__raw_readl(rng_base + RNGA_STATUS) &
+ RNGA_STATUS_LEVEL_MASK) >> 8);
+
+ return level > 0 ? 1 : 0;
+}
+
+static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 ctrl;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
+
+ /* some error while reading this random number? */
+ err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
+
+ /* if error: clear error interrupt, but doesn't return random number */
+ if (err) {
+ dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
+ rng_base + RNGA_CONTROL);
+ return 0;
+ } else
+ return 4;
+}
+
+static int mxc_rnga_init(struct hwrng *rng)
+{
+ u32 ctrl, osc;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ /* wake up */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rng_base + RNGA_STATUS);
+ if (osc & RNGA_STATUS_OSC_DEAD) {
+ dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
+ return -ENODEV;
+ }
+
+ /* go running */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+
+ return 0;
+}
+
+static void mxc_rnga_cleanup(struct hwrng *rng)
+{
+ u32 ctrl;
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+
+ /* stop rnga */
+ __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+}
+
+static struct hwrng mxc_rnga = {
+ .name = "mxc-rnga",
+ .init = mxc_rnga_init,
+ .cleanup = mxc_rnga_cleanup,
+ .data_present = mxc_rnga_data_present,
+ .data_read = mxc_rnga_data_read
+};
+
+static int __init mxc_rnga_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rng_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(&pdev->dev, "rng");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get rng_clk!\n");
+ err = PTR_ERR(clk);
+ goto out;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENOENT;
+ goto err_region;
+ }
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (mem == NULL) {
+ err = -EBUSY;
+ goto err_region;
+ }
+
+ rng_base = ioremap(res->start, resource_size(res));
+ if (!rng_base) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ mxc_rnga.priv = (unsigned long)rng_base;
+
+ err = hwrng_register(&mxc_rnga);
+ if (err) {
+ dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
+ goto err_register;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "MXC RNGA Registered.\n");
+
+ return 0;
+
+err_register:
+ iounmap(rng_base);
+ rng_base = NULL;
+
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+
+err_region:
+ clk_disable(clk);
+ clk_put(clk);
+
+out:
+ return err;
+}
+
+static int __exit mxc_rnga_remove(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ void __iomem *rng_base = (void __iomem *)mxc_rnga.priv;
+ struct clk *clk = clk_get(&pdev->dev, "rng");
+
+ hwrng_unregister(&mxc_rnga);
+
+ iounmap(rng_base);
+
+ release_mem_region(res->start, resource_size(res));
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return 0;
+}
+
+static struct platform_driver mxc_rnga_driver = {
+ .driver = {
+ .name = "mxc_rnga",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(mxc_rnga_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&mxc_rnga_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index acc7143d965..035a6c7e59d 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -34,10 +34,24 @@
#include <linux/err.h>
#include <linux/clk.h>
-#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <mach/i2c.h>
+#include <plat/i2c.h>
+
+/*
+ * I2C register offsets will be shifted 0 or 1 bit left, depending on
+ * different SoCs
+ */
+#define REG_SHIFT_0 (0 << 0)
+#define REG_SHIFT_1 (1 << 0)
+#define REG_SHIFT(d) ((d) & 0x1)
+
+static const struct platform_device_id i2c_pxa_id_table[] = {
+ { "pxa2xx-i2c", REG_SHIFT_1 },
+ { "pxa3xx-pwri2c", REG_SHIFT_0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
/*
* I2C registers and bit definitions
@@ -985,6 +999,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
struct pxa_i2c *i2c;
struct resource *res;
struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
+ struct platform_device_id *id = platform_get_device_id(dev);
int ret;
int irq;
@@ -1028,7 +1043,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
ret = -EIO;
goto eremap;
}
- i2c->reg_shift = (cpu_is_pxa3xx() && (dev->id == 1)) ? 0 : 1;
+ i2c->reg_shift = REG_SHIFT(id->driver_data);
i2c->iobase = res->start;
i2c->iosize = res_len(res);
@@ -1150,6 +1165,7 @@ static struct platform_driver i2c_pxa_driver = {
.name = "pxa2xx-i2c",
.owner = THIS_MODULE,
},
+ .id_table = i2c_pxa_id_table,
};
static int __init i2c_adap_pxa_init(void)
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index a28c06d686e..89b394183a7 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -135,7 +135,7 @@ static int amba_kmi_probe(struct amba_device *dev, struct amba_id *id)
io->dev.parent = &dev->dev;
kmi->io = io;
- kmi->base = ioremap(dev->res.start, KMI_SIZE);
+ kmi->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!kmi->base) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c
index 1aa46a390a0..173d104d9ff 100644
--- a/drivers/leds/leds-h1940.c
+++ b/drivers/leds/leds-h1940.c
@@ -16,6 +16,8 @@
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
+#include <linux/gpio.h>
+
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <mach/h1940-latch.h>
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index aa2e7ae0cda..aa7acf3b922 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 9d48da2fb01..57835f5715f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -758,10 +758,14 @@ config VIDEO_MX1
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
+config MX3_VIDEO
+ bool
+
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
select VIDEOBUF_DMA_CONTIG
+ select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index b4cf691f3f6..3eb87bda14f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -155,7 +155,7 @@ config MMC_ATMELMCI_DMA
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
- depends on ARCH_IMX
+ depends on ARCH_MX1
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 7d4febdab28..e1aa8471ab1 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -546,7 +546,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->mclk = clk_get_rate(host->clk);
DBG(host, "eventual mclk rate: %u Hz\n", host->mclk);
}
- host->base = ioremap(dev->res.start, SZ_4K);
+ host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
goto clk_disable;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c40cb96255a..1cf9cfb3b64 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1073,7 +1073,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
- mmc->ocr_avail = mmc_slot(host).ocr_mask;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
if (pdata->slots[host->slot_id].wires >= 8)
@@ -1110,13 +1109,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err_irq;
}
+ /* initialize power supplies, gpios, etc */
if (pdata->init != NULL) {
if (pdata->init(&pdev->dev) != 0) {
- dev_dbg(mmc_dev(host->mmc),
- "Unable to configure MMC IRQs\n");
+ dev_dbg(mmc_dev(host->mmc), "late init error\n");
goto err_irq_cd_init;
}
}
+ mmc->ocr_avail = mmc_slot(host).ocr_mask;
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq)) {
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 2db166b7096..4eb4f37544a 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -17,6 +17,7 @@
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
#include <linux/cpufreq.h>
+#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/io.h>
@@ -789,7 +790,7 @@ static void s3cmci_dma_setup(struct s3cmci_host *host,
last_source = source;
- s3c2410_dma_devconfig(host->dma, source, 3,
+ s3c2410_dma_devconfig(host->dma, source,
host->mem->start + host->sdidata);
if (!setup_ok) {
@@ -1121,7 +1122,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
default:
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
- s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPIO_OUTPUT);
if (host->is2440)
mci_con |= S3C2440_SDICON_SDRESET;
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index f2e9de1414d..6391e3dc800 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -39,7 +39,6 @@
#include <mach/gpmc.h>
#include <mach/onenand.h>
#include <mach/gpio.h>
-#include <mach/pm.h>
#include <mach/dma.h>
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index a740053d3af..b6d188115ca 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -456,7 +456,8 @@ static inline void queue_put_desc(unsigned int queue, u32 phys,
debug_desc(phys, desc);
BUG_ON(phys & 0x1F);
qmgr_put_entry(queue, phys);
- BUG_ON(qmgr_stat_overflow(queue));
+ /* Don't check for queue overflow here, we've allocated sufficient
+ length and queues >= 32 don't support this check anyway. */
}
@@ -512,8 +513,8 @@ static int eth_poll(struct napi_struct *napi, int budget)
#endif
napi_complete(napi);
qmgr_enable_irq(rxq);
- if (!qmgr_stat_empty(rxq) &&
- napi_reschedule(napi)) {
+ if (!qmgr_stat_below_low_watermark(rxq) &&
+ napi_reschedule(napi)) { /* not empty again */
#if DEBUG_RX
printk(KERN_DEBUG "%s: eth_poll"
" napi_reschedule successed\n",
@@ -630,9 +631,9 @@ static void eth_txdone_irq(void *unused)
port->tx_buff_tab[n_desc] = NULL;
}
- start = qmgr_stat_empty(port->plat->txreadyq);
+ start = qmgr_stat_below_low_watermark(port->plat->txreadyq);
queue_put_desc(port->plat->txreadyq, phys, desc);
- if (start) {
+ if (start) { /* TX-ready queue was empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_txdone_irq xmit ready\n",
port->netdev->name);
@@ -708,13 +709,14 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
queue_put_desc(TX_QUEUE(port->id), tx_desc_phys(port, n), desc);
dev->trans_start = jiffies;
- if (qmgr_stat_empty(txreadyq)) {
+ if (qmgr_stat_below_low_watermark(txreadyq)) { /* empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_xmit queue full\n", dev->name);
#endif
netif_stop_queue(dev);
/* we could miss TX ready interrupt */
- if (!qmgr_stat_empty(txreadyq)) {
+ /* really empty in fact */
+ if (!qmgr_stat_below_low_watermark(txreadyq)) {
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_xmit ready again\n",
dev->name);
@@ -814,29 +816,29 @@ static int request_queues(struct port *port)
int err;
err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0,
- "%s:RX-free", port->netdev->name);
+ "%s:RX-free", port->netdev->name);
if (err)
return err;
err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0,
- "%s:RX", port->netdev->name);
+ "%s:RX", port->netdev->name);
if (err)
goto rel_rxfree;
err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0,
- "%s:TX", port->netdev->name);
+ "%s:TX", port->netdev->name);
if (err)
goto rel_rx;
err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0,
- "%s:TX-ready", port->netdev->name);
+ "%s:TX-ready", port->netdev->name);
if (err)
goto rel_tx;
/* TX-done queue handles skbs sent out by the NPEs */
if (!ports_open) {
err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0,
- "%s:TX-done", DRV_NAME);
+ "%s:TX-done", DRV_NAME);
if (err)
goto rel_txready;
}
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 329f890e290..f1f773b17fe 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -45,7 +45,8 @@
defined(CONFIG_MACH_ZYLONITE) ||\
defined(CONFIG_MACH_LITTLETON) ||\
defined(CONFIG_MACH_ZYLONITE2) ||\
- defined(CONFIG_ARCH_VIPER)
+ defined(CONFIG_ARCH_VIPER) ||\
+ defined(CONFIG_MACH_STARGATE2)
#include <asm/mach-types.h>
@@ -73,7 +74,7 @@
/* We actually can't write halfwords properly if not word aligned */
static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
{
- if (machine_is_mainstone() && reg & 2) {
+ if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) {
unsigned int v = val << 16;
v |= readl(ioaddr + (reg & ~2)) & 0xffff;
writel(v, ioaddr + (reg & ~2));
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 765a7f5d6aa..a6dc317083d 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -579,7 +579,8 @@ static inline void queue_put_desc(unsigned int queue, u32 phys,
debug_desc(phys, desc);
BUG_ON(phys & 0x1F);
qmgr_put_entry(queue, phys);
- BUG_ON(qmgr_stat_overflow(queue));
+ /* Don't check for queue overflow here, we've allocated sufficient
+ length and queues >= 32 don't support this check anyway. */
}
@@ -789,10 +790,10 @@ static void hss_hdlc_txdone_irq(void *pdev)
free_buffer_irq(port->tx_buff_tab[n_desc]);
port->tx_buff_tab[n_desc] = NULL;
- start = qmgr_stat_empty(port->plat->txreadyq);
+ start = qmgr_stat_below_low_watermark(port->plat->txreadyq);
queue_put_desc(port->plat->txreadyq,
tx_desc_phys(port, n_desc), desc);
- if (start) {
+ if (start) { /* TX-ready queue was empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_txdone_irq xmit"
" ready\n", dev->name);
@@ -867,13 +868,13 @@ static int hss_hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
queue_put_desc(queue_ids[port->id].tx, tx_desc_phys(port, n), desc);
dev->trans_start = jiffies;
- if (qmgr_stat_empty(txreadyq)) {
+ if (qmgr_stat_below_low_watermark(txreadyq)) { /* empty */
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_xmit queue full\n", dev->name);
#endif
netif_stop_queue(dev);
/* we could miss TX ready interrupt */
- if (!qmgr_stat_empty(txreadyq)) {
+ if (!qmgr_stat_below_low_watermark(txreadyq)) {
#if DEBUG_TX
printk(KERN_DEBUG "%s: hss_hdlc_xmit ready again\n",
dev->name);
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 27647354398..fbf965b31c1 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -217,7 +217,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER || ARCH_PXA_ESERIES)
+ || ARCH_VIPER || ARCH_PXA_ESERIES || MACH_STARGATE2)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index bbac4632722..047394d98ac 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -73,5 +73,6 @@ pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
+pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/pxa2xx_stargate2.c b/drivers/pcmcia/pxa2xx_stargate2.c
new file mode 100644
index 00000000000..490749ea677
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_stargate2.c
@@ -0,0 +1,174 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_stargate2.c
+ *
+ * Stargate 2 PCMCIA specific routines.
+ *
+ * Created: December 6, 2005
+ * Author: Ed C. Epp
+ * Copyright: Intel Corp 2005
+ * Jonathan Cameron <jic23@cam.ac.uk> 2009
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include "soc_common.h"
+
+#define SG2_S0_BUFF_CTL 120
+#define SG2_S0_POWER_CTL 108
+#define SG2_S0_GPIO_RESET 82
+#define SG2_S0_GPIO_DETECT 53
+#define SG2_S0_GPIO_READY 81
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, IRQ_GPIO(SG2_S0_GPIO_DETECT), "PCMCIA0 CD" },
+};
+
+static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ skt->irq = IRQ_GPIO(SG2_S0_GPIO_READY);
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ state->detect = !gpio_get_value(SG2_S0_GPIO_DETECT);
+ state->ready = !!gpio_get_value(SG2_S0_GPIO_READY);
+ state->bvd1 = 0; /* not available - battery detect on card */
+ state->bvd2 = 0; /* not available */
+ state->vs_3v = 1; /* not available - voltage detect for card */
+ state->vs_Xv = 0; /* not available */
+ state->wrprot = 0; /* not available - write protect */
+}
+
+static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ /* Enable card power */
+ switch (state->Vcc) {
+ case 0:
+ /* sets power ctl register high */
+ gpio_set_value(SG2_S0_POWER_CTL, 1);
+ break;
+ case 33:
+ case 50:
+ /* sets power control register low (clear) */
+ gpio_set_value(SG2_S0_POWER_CTL, 0);
+ msleep(100);
+ break;
+ default:
+ pr_err("%s(): bad Vcc %u\n",
+ __func__, state->Vcc);
+ return -1;
+ }
+
+ /* reset */
+ gpio_set_value(SG2_S0_GPIO_RESET, !!(state->flags & SS_RESET));
+
+ return 0;
+}
+
+static void sg2_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void sg2_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static struct pcmcia_low_level sg2_pcmcia_ops __initdata = {
+ .owner = THIS_MODULE,
+ .hw_init = sg2_pcmcia_hw_init,
+ .hw_shutdown = sg2_pcmcia_hw_shutdown,
+ .socket_state = sg2_pcmcia_socket_state,
+ .configure_socket = sg2_pcmcia_configure_socket,
+ .socket_init = sg2_pcmcia_socket_init,
+ .socket_suspend = sg2_pcmcia_socket_suspend,
+ .nr = 1,
+};
+
+static struct platform_device *sg2_pcmcia_device;
+
+static int __init sg2_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_stargate2())
+ return -ENODEV;
+
+ sg2_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!sg2_pcmcia_device)
+ return -ENOMEM;
+
+ ret = gpio_request(SG2_S0_BUFF_CTL, "SG2 CF buff ctl");
+ if (ret)
+ goto error_put_platform_device;
+ ret = gpio_request(SG2_S0_POWER_CTL, "SG2 CF power ctl");
+ if (ret)
+ goto error_free_gpio_buff_ctl;
+ ret = gpio_request(SG2_S0_GPIO_RESET, "SG2 CF reset");
+ if (ret)
+ goto error_free_gpio_power_ctl;
+ /* Set gpio directions */
+ gpio_direction_output(SG2_S0_BUFF_CTL, 0);
+ gpio_direction_output(SG2_S0_POWER_CTL, 1);
+ gpio_direction_output(SG2_S0_GPIO_RESET, 1);
+
+ ret = platform_device_add_data(sg2_pcmcia_device,
+ &sg2_pcmcia_ops,
+ sizeof(sg2_pcmcia_ops));
+ if (ret)
+ goto error_free_gpio_reset;
+
+ ret = platform_device_add(sg2_pcmcia_device);
+ if (ret)
+ goto error_free_gpio_reset;
+
+ return 0;
+error_free_gpio_reset:
+ gpio_free(SG2_S0_GPIO_RESET);
+error_free_gpio_power_ctl:
+ gpio_free(SG2_S0_POWER_CTL);
+error_free_gpio_buff_ctl:
+ gpio_free(SG2_S0_BUFF_CTL);
+error_put_platform_device:
+ platform_device_put(sg2_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit sg2_pcmcia_exit(void)
+{
+ platform_device_unregister(sg2_pcmcia_device);
+ gpio_free(SG2_S0_BUFF_CTL);
+ gpio_free(SG2_S0_POWER_CTL);
+ gpio_free(SG2_S0_GPIO_RESET);
+}
+
+fs_initcall(sg2_pcmcia_init);
+module_exit(sg2_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index f7a3283dd02..551332e4ed0 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -12,32 +12,56 @@
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
-#include <mach/hardware.h>
+#include <linux/io.h>
+
+#define EP93XX_RTC_DATA 0x000
+#define EP93XX_RTC_MATCH 0x004
+#define EP93XX_RTC_STATUS 0x008
+#define EP93XX_RTC_STATUS_INTR (1<<0)
+#define EP93XX_RTC_LOAD 0x00C
+#define EP93XX_RTC_CONTROL 0x010
+#define EP93XX_RTC_CONTROL_MIE (1<<0)
+#define EP93XX_RTC_SWCOMP 0x108
+#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000
+#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16
+#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff
+#define EP93XX_RTC_SWCOMP_INT_SHIFT 0
+
+#define DRV_VERSION "0.3"
-#define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x))
-#define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000)
-#define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C)
-#define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108)
-
-#define DRV_VERSION "0.2"
+/*
+ * struct device dev.platform_data is used to store our private data
+ * because struct rtc_device does not have a variable to hold it.
+ */
+struct ep93xx_rtc {
+ void __iomem *mmio_base;
+};
-static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload,
+static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
unsigned short *delete)
{
- unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+ unsigned long comp;
+
+ comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
if (preload)
- *preload = comp & 0xffff;
+ *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK)
+ >> EP93XX_RTC_SWCOMP_INT_SHIFT;
if (delete)
- *delete = (comp >> 16) & 0x1f;
+ *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK)
+ >> EP93XX_RTC_SWCOMP_DEL_SHIFT;
return 0;
}
static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- unsigned long time = __raw_readl(EP93XX_RTC_DATA);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+ unsigned long time;
+
+ time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
rtc_time_to_tm(time, tm);
return 0;
@@ -45,7 +69,9 @@ static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
{
- __raw_writel(secs + 1, EP93XX_RTC_LOAD);
+ struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
+
+ __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
return 0;
}
@@ -53,7 +79,7 @@ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned short preload, delete;
- ep93xx_get_swcomp(dev, &preload, &delete);
+ ep93xx_rtc_get_swcomp(dev, &preload, &delete);
seq_printf(seq, "preload\t\t: %d\n", preload);
seq_printf(seq, "delete\t\t: %d\n", delete);
@@ -67,54 +93,104 @@ static const struct rtc_class_ops ep93xx_rtc_ops = {
.proc = ep93xx_rtc_proc,
};
-static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev,
+static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short preload;
- ep93xx_get_swcomp(dev, &preload, NULL);
+ ep93xx_rtc_get_swcomp(dev, &preload, NULL);
return sprintf(buf, "%d\n", preload);
}
-static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL);
+static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL);
-static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev,
+static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short delete;
- ep93xx_get_swcomp(dev, NULL, &delete);
+ ep93xx_rtc_get_swcomp(dev, NULL, &delete);
return sprintf(buf, "%d\n", delete);
}
-static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL);
+static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
-static int __devinit ep93xx_rtc_probe(struct platform_device *dev)
+static int __init ep93xx_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc = rtc_device_register("ep93xx",
- &dev->dev, &ep93xx_rtc_ops, THIS_MODULE);
+ struct ep93xx_rtc *ep93xx_rtc;
+ struct resource *res;
+ struct rtc_device *rtc;
+ int err;
+
+ ep93xx_rtc = kzalloc(sizeof(struct ep93xx_rtc), GFP_KERNEL);
+ if (ep93xx_rtc == NULL)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENXIO;
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL)
+ return -EBUSY;
+
+ ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res));
+ if (ep93xx_rtc->mmio_base == NULL) {
+ err = -ENXIO;
+ goto fail;
+ }
+ pdev->dev.platform_data = ep93xx_rtc;
+
+ rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &ep93xx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
- return PTR_ERR(rtc);
+ err = PTR_ERR(rtc);
+ goto fail;
}
- platform_set_drvdata(dev, rtc);
+ platform_set_drvdata(pdev, rtc);
- device_create_file(&dev->dev, &dev_attr_comp_preload);
- device_create_file(&dev->dev, &dev_attr_comp_delete);
+ err = device_create_file(&pdev->dev, &dev_attr_comp_preload);
+ if (err)
+ goto fail;
+ err = device_create_file(&pdev->dev, &dev_attr_comp_delete);
+ if (err) {
+ device_remove_file(&pdev->dev, &dev_attr_comp_preload);
+ goto fail;
+ }
return 0;
+
+fail:
+ if (ep93xx_rtc->mmio_base) {
+ iounmap(ep93xx_rtc->mmio_base);
+ pdev->dev.platform_data = NULL;
+ }
+ release_mem_region(res->start, resource_size(res));
+ return err;
}
-static int __devexit ep93xx_rtc_remove(struct platform_device *dev)
+static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct ep93xx_rtc *ep93xx_rtc = pdev->dev.platform_data;
+ struct resource *res;
+
+ /* cleanup sysfs */
+ device_remove_file(&pdev->dev, &dev_attr_comp_delete);
+ device_remove_file(&pdev->dev, &dev_attr_comp_preload);
+
+ rtc_device_unregister(rtc);
+
+ iounmap(ep93xx_rtc->mmio_base);
+ pdev->dev.platform_data = NULL;
- if (rtc)
- rtc_device_unregister(rtc);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
- platform_set_drvdata(dev, NULL);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -122,23 +198,22 @@ static int __devexit ep93xx_rtc_remove(struct platform_device *dev)
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:ep93xx-rtc");
-static struct platform_driver ep93xx_rtc_platform_driver = {
+static struct platform_driver ep93xx_rtc_driver = {
.driver = {
.name = "ep93xx-rtc",
.owner = THIS_MODULE,
},
- .probe = ep93xx_rtc_probe,
- .remove = __devexit_p(ep93xx_rtc_remove),
+ .remove = __exit_p(ep93xx_rtc_remove),
};
static int __init ep93xx_rtc_init(void)
{
- return platform_driver_register(&ep93xx_rtc_platform_driver);
+ return platform_driver_probe(&ep93xx_rtc_driver, ep93xx_rtc_probe);
}
static void __exit ep93xx_rtc_exit(void)
{
- platform_driver_unregister(&ep93xx_rtc_platform_driver);
+ platform_driver_unregister(&ep93xx_rtc_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index aaf1f75fa29..457231bb102 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -117,7 +117,7 @@ static int pl030_probe(struct amba_device *dev, struct amba_id *id)
goto err_rtc;
}
- rtc->base = ioremap(dev->res.start, SZ_4K);
+ rtc->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!rtc->base) {
ret = -ENOMEM;
goto err_map;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 451fc13784d..f41873f98f6 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -142,8 +142,7 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
goto out;
}
- ldata->base = ioremap(adev->res.start,
- adev->res.end - adev->res.start + 1);
+ ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
if (!ldata->base) {
ret = -ENOMEM;
goto out_no_remap;
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index cdc049d4350..58a4879c7e4 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -686,7 +686,7 @@ static int pl010_probe(struct amba_device *dev, struct amba_id *id)
goto out;
}
- base = ioremap(dev->res.start, PAGE_SIZE);
+ base = ioremap(dev->res.start, resource_size(&dev->res));
if (!base) {
ret = -ENOMEM;
goto free;
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index 88fdac51b6c..bf82e28770a 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -70,6 +70,23 @@ struct uart_amba_port {
struct clk *clk;
unsigned int im; /* interrupt mask */
unsigned int old_status;
+ unsigned int ifls; /* vendor-specific */
+};
+
+/* There is by now at least one vendor with differing details, so handle it */
+struct vendor_data {
+ unsigned int ifls;
+ unsigned int fifosize;
+};
+
+static struct vendor_data vendor_arm = {
+ .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+ .fifosize = 16,
+};
+
+static struct vendor_data vendor_st = {
+ .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
+ .fifosize = 64,
};
static void pl011_stop_tx(struct uart_port *port)
@@ -360,8 +377,7 @@ static int pl011_startup(struct uart_port *port)
if (retval)
goto clk_dis;
- writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
- uap->port.membase + UART011_IFLS);
+ writew(uap->ifls, uap->port.membase + UART011_IFLS);
/*
* Provoke TX FIFO interrupt into asserting.
@@ -732,6 +748,7 @@ static struct uart_driver amba_reg = {
static int pl011_probe(struct amba_device *dev, struct amba_id *id)
{
struct uart_amba_port *uap;
+ struct vendor_data *vendor = id->data;
void __iomem *base;
int i, ret;
@@ -750,7 +767,7 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
goto out;
}
- base = ioremap(dev->res.start, PAGE_SIZE);
+ base = ioremap(dev->res.start, resource_size(&dev->res));
if (!base) {
ret = -ENOMEM;
goto free;
@@ -762,12 +779,13 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
goto unmap;
}
+ uap->ifls = vendor->ifls;
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
uap->port.iotype = UPIO_MEM;
uap->port.irq = dev->irq[0];
- uap->port.fifosize = 16;
+ uap->port.fifosize = vendor->fifosize;
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
@@ -812,6 +830,12 @@ static struct amba_id pl011_ids[] __initdata = {
{
.id = 0x00041011,
.mask = 0x000fffff,
+ .data = &vendor_arm,
+ },
+ {
+ .id = 0x00380802,
+ .mask = 0x00ffffff,
+ .data = &vendor_st,
},
{ 0, 0 },
};
@@ -845,7 +869,11 @@ static void __exit pl011_exit(void)
uart_unregister_driver(&amba_reg);
}
-module_init(pl011_init);
+/*
+ * While this can be a module, if builtin it's most likely the console
+ * So let's leave module_exit but move module_init to an earlier place
+ */
+arch_initcall(pl011_init);
module_exit(pl011_exit);
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 7b5d1de9cfe..285b414f305 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -71,7 +71,7 @@
#define ONEMS 0xb0 /* One Millisecond register */
#define UTS 0xb4 /* UART Test Register */
#endif
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
+#ifdef CONFIG_ARCH_MX1
#define BIPR1 0xb0 /* Incremental Preset Register 1 */
#define BIPR2 0xb4 /* Incremental Preset Register 2 */
#define BIPR3 0xb8 /* Incremental Preset Register 3 */
@@ -101,7 +101,7 @@
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
+#ifdef CONFIG_ARCH_MX1
#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
#endif
#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
@@ -132,7 +132,7 @@
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
-#ifdef CONFIG_ARCH_IMX
+#ifdef CONFIG_ARCH_MX1
#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
#endif
@@ -186,13 +186,6 @@
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
-#ifdef CONFIG_ARCH_IMX
-#define SERIAL_IMX_MAJOR 204
-#define MINOR_START 41
-#define DEV_NAME "ttySMX"
-#define MAX_INTERNAL_IRQ IMX_IRQS
-#endif
-
#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 95749477541..e8aae227b5e 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -118,7 +118,7 @@ config SPI_GPIO
config SPI_IMX
tristate "Freescale iMX SPI controller"
- depends on ARCH_IMX && EXPERIMENTAL
+ depends on ARCH_MX1 && EXPERIMENTAL
help
This enables using the Freescale iMX SPI controller in master
mode.
@@ -171,6 +171,15 @@ config SPI_ORION
help
This enables using the SPI master controller on the Orion chips.
+config SPI_PL022
+ tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
+ depends on ARM_AMBA && EXPERIMENTAL
+ default y if MACH_U300
+ help
+ This selects the ARM(R) AMBA(R) PrimeCell PL022 SSP
+ controller. If you have an embedded system with an AMBA(R)
+ bus and a PL022 controller, say Y or M here.
+
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
depends on ARCH_PXA && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5d0451936d8..ecfadb18048 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_ORION) += orion_spi.o
+obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
new file mode 100644
index 00000000000..da76797ce8b
--- /dev/null
+++ b/drivers/spi/amba-pl022.c
@@ -0,0 +1,1866 @@
+/*
+ * drivers/spi/amba-pl022.c
+ *
+ * A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
+ *
+ * Copyright (C) 2008-2009 ST-Ericsson AB
+ * Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
+ *
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * Initial version inspired by:
+ * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c
+ * Initial adoption to PL022 by:
+ * Sachin Verma <sachin.verma@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * TODO:
+ * - add timeout on polled transfers
+ * - add generic DMA framework support
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl022.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+/*
+ * This macro is used to define some register default values.
+ * reg is masked with mask, the OR:ed with an (again masked)
+ * val shifted sb steps to the left.
+ */
+#define SSP_WRITE_BITS(reg, val, mask, sb) \
+ ((reg) = (((reg) & ~(mask)) | (((val)<<(sb)) & (mask))))
+
+/*
+ * This macro is also used to define some default values.
+ * It will just shift val by sb steps to the left and mask
+ * the result with mask.
+ */
+#define GEN_MASK_BITS(val, mask, sb) \
+ (((val)<<(sb)) & (mask))
+
+#define DRIVE_TX 0
+#define DO_NOT_DRIVE_TX 1
+
+#define DO_NOT_QUEUE_DMA 0
+#define QUEUE_DMA 1
+
+#define RX_TRANSFER 1
+#define TX_TRANSFER 2
+
+/*
+ * Macros to access SSP Registers with their offsets
+ */
+#define SSP_CR0(r) (r + 0x000)
+#define SSP_CR1(r) (r + 0x004)
+#define SSP_DR(r) (r + 0x008)
+#define SSP_SR(r) (r + 0x00C)
+#define SSP_CPSR(r) (r + 0x010)
+#define SSP_IMSC(r) (r + 0x014)
+#define SSP_RIS(r) (r + 0x018)
+#define SSP_MIS(r) (r + 0x01C)
+#define SSP_ICR(r) (r + 0x020)
+#define SSP_DMACR(r) (r + 0x024)
+#define SSP_ITCR(r) (r + 0x080)
+#define SSP_ITIP(r) (r + 0x084)
+#define SSP_ITOP(r) (r + 0x088)
+#define SSP_TDR(r) (r + 0x08C)
+
+#define SSP_PID0(r) (r + 0xFE0)
+#define SSP_PID1(r) (r + 0xFE4)
+#define SSP_PID2(r) (r + 0xFE8)
+#define SSP_PID3(r) (r + 0xFEC)
+
+#define SSP_CID0(r) (r + 0xFF0)
+#define SSP_CID1(r) (r + 0xFF4)
+#define SSP_CID2(r) (r + 0xFF8)
+#define SSP_CID3(r) (r + 0xFFC)
+
+/*
+ * SSP Control Register 0 - SSP_CR0
+ */
+#define SSP_CR0_MASK_DSS (0x1FUL << 0)
+#define SSP_CR0_MASK_HALFDUP (0x1UL << 5)
+#define SSP_CR0_MASK_SPO (0x1UL << 6)
+#define SSP_CR0_MASK_SPH (0x1UL << 7)
+#define SSP_CR0_MASK_SCR (0xFFUL << 8)
+#define SSP_CR0_MASK_CSS (0x1FUL << 16)
+#define SSP_CR0_MASK_FRF (0x3UL << 21)
+
+/*
+ * SSP Control Register 0 - SSP_CR1
+ */
+#define SSP_CR1_MASK_LBM (0x1UL << 0)
+#define SSP_CR1_MASK_SSE (0x1UL << 1)
+#define SSP_CR1_MASK_MS (0x1UL << 2)
+#define SSP_CR1_MASK_SOD (0x1UL << 3)
+#define SSP_CR1_MASK_RENDN (0x1UL << 4)
+#define SSP_CR1_MASK_TENDN (0x1UL << 5)
+#define SSP_CR1_MASK_MWAIT (0x1UL << 6)
+#define SSP_CR1_MASK_RXIFLSEL (0x7UL << 7)
+#define SSP_CR1_MASK_TXIFLSEL (0x7UL << 10)
+
+/*
+ * SSP Data Register - SSP_DR
+ */
+#define SSP_DR_MASK_DATA 0xFFFFFFFF
+
+/*
+ * SSP Status Register - SSP_SR
+ */
+#define SSP_SR_MASK_TFE (0x1UL << 0) /* Transmit FIFO empty */
+#define SSP_SR_MASK_TNF (0x1UL << 1) /* Transmit FIFO not full */
+#define SSP_SR_MASK_RNE (0x1UL << 2) /* Receive FIFO not empty */
+#define SSP_SR_MASK_RFF (0x1UL << 3) /* Receive FIFO full */
+#define SSP_SR_MASK_BSY (0x1UL << 4) /* Busy Flag */
+
+/*
+ * SSP Clock Prescale Register - SSP_CPSR
+ */
+#define SSP_CPSR_MASK_CPSDVSR (0xFFUL << 0)
+
+/*
+ * SSP Interrupt Mask Set/Clear Register - SSP_IMSC
+ */
+#define SSP_IMSC_MASK_RORIM (0x1UL << 0) /* Receive Overrun Interrupt mask */
+#define SSP_IMSC_MASK_RTIM (0x1UL << 1) /* Receive timeout Interrupt mask */
+#define SSP_IMSC_MASK_RXIM (0x1UL << 2) /* Receive FIFO Interrupt mask */
+#define SSP_IMSC_MASK_TXIM (0x1UL << 3) /* Transmit FIFO Interrupt mask */
+
+/*
+ * SSP Raw Interrupt Status Register - SSP_RIS
+ */
+/* Receive Overrun Raw Interrupt status */
+#define SSP_RIS_MASK_RORRIS (0x1UL << 0)
+/* Receive Timeout Raw Interrupt status */
+#define SSP_RIS_MASK_RTRIS (0x1UL << 1)
+/* Receive FIFO Raw Interrupt status */
+#define SSP_RIS_MASK_RXRIS (0x1UL << 2)
+/* Transmit FIFO Raw Interrupt status */
+#define SSP_RIS_MASK_TXRIS (0x1UL << 3)
+
+/*
+ * SSP Masked Interrupt Status Register - SSP_MIS
+ */
+/* Receive Overrun Masked Interrupt status */
+#define SSP_MIS_MASK_RORMIS (0x1UL << 0)
+/* Receive Timeout Masked Interrupt status */
+#define SSP_MIS_MASK_RTMIS (0x1UL << 1)
+/* Receive FIFO Masked Interrupt status */
+#define SSP_MIS_MASK_RXMIS (0x1UL << 2)
+/* Transmit FIFO Masked Interrupt status */
+#define SSP_MIS_MASK_TXMIS (0x1UL << 3)
+
+/*
+ * SSP Interrupt Clear Register - SSP_ICR
+ */
+/* Receive Overrun Raw Clear Interrupt bit */
+#define SSP_ICR_MASK_RORIC (0x1UL << 0)
+/* Receive Timeout Clear Interrupt bit */
+#define SSP_ICR_MASK_RTIC (0x1UL << 1)
+
+/*
+ * SSP DMA Control Register - SSP_DMACR
+ */
+/* Receive DMA Enable bit */
+#define SSP_DMACR_MASK_RXDMAE (0x1UL << 0)
+/* Transmit DMA Enable bit */
+#define SSP_DMACR_MASK_TXDMAE (0x1UL << 1)
+
+/*
+ * SSP Integration Test control Register - SSP_ITCR
+ */
+#define SSP_ITCR_MASK_ITEN (0x1UL << 0)
+#define SSP_ITCR_MASK_TESTFIFO (0x1UL << 1)
+
+/*
+ * SSP Integration Test Input Register - SSP_ITIP
+ */
+#define ITIP_MASK_SSPRXD (0x1UL << 0)
+#define ITIP_MASK_SSPFSSIN (0x1UL << 1)
+#define ITIP_MASK_SSPCLKIN (0x1UL << 2)
+#define ITIP_MASK_RXDMAC (0x1UL << 3)
+#define ITIP_MASK_TXDMAC (0x1UL << 4)
+#define ITIP_MASK_SSPTXDIN (0x1UL << 5)
+
+/*
+ * SSP Integration Test output Register - SSP_ITOP
+ */
+#define ITOP_MASK_SSPTXD (0x1UL << 0)
+#define ITOP_MASK_SSPFSSOUT (0x1UL << 1)
+#define ITOP_MASK_SSPCLKOUT (0x1UL << 2)
+#define ITOP_MASK_SSPOEn (0x1UL << 3)
+#define ITOP_MASK_SSPCTLOEn (0x1UL << 4)
+#define ITOP_MASK_RORINTR (0x1UL << 5)
+#define ITOP_MASK_RTINTR (0x1UL << 6)
+#define ITOP_MASK_RXINTR (0x1UL << 7)
+#define ITOP_MASK_TXINTR (0x1UL << 8)
+#define ITOP_MASK_INTR (0x1UL << 9)
+#define ITOP_MASK_RXDMABREQ (0x1UL << 10)
+#define ITOP_MASK_RXDMASREQ (0x1UL << 11)
+#define ITOP_MASK_TXDMABREQ (0x1UL << 12)
+#define ITOP_MASK_TXDMASREQ (0x1UL << 13)
+
+/*
+ * SSP Test Data Register - SSP_TDR
+ */
+#define TDR_MASK_TESTDATA (0xFFFFFFFF)
+
+/*
+ * Message State
+ * we use the spi_message.state (void *) pointer to
+ * hold a single state value, that's why all this
+ * (void *) casting is done here.
+ */
+#define STATE_START ((void *) 0)
+#define STATE_RUNNING ((void *) 1)
+#define STATE_DONE ((void *) 2)
+#define STATE_ERROR ((void *) -1)
+
+/*
+ * Queue State
+ */
+#define QUEUE_RUNNING (0)
+#define QUEUE_STOPPED (1)
+/*
+ * SSP State - Whether Enabled or Disabled
+ */
+#define SSP_DISABLED (0)
+#define SSP_ENABLED (1)
+
+/*
+ * SSP DMA State - Whether DMA Enabled or Disabled
+ */
+#define SSP_DMA_DISABLED (0)
+#define SSP_DMA_ENABLED (1)
+
+/*
+ * SSP Clock Defaults
+ */
+#define NMDK_SSP_DEFAULT_CLKRATE 0x2
+#define NMDK_SSP_DEFAULT_PRESCALE 0x40
+
+/*
+ * SSP Clock Parameter ranges
+ */
+#define CPSDVR_MIN 0x02
+#define CPSDVR_MAX 0xFE
+#define SCR_MIN 0x00
+#define SCR_MAX 0xFF
+
+/*
+ * SSP Interrupt related Macros
+ */
+#define DEFAULT_SSP_REG_IMSC 0x0UL
+#define DISABLE_ALL_INTERRUPTS DEFAULT_SSP_REG_IMSC
+#define ENABLE_ALL_INTERRUPTS (~DEFAULT_SSP_REG_IMSC)
+
+#define CLEAR_ALL_INTERRUPTS 0x3
+
+
+/*
+ * The type of reading going on on this chip
+ */
+enum ssp_reading {
+ READING_NULL,
+ READING_U8,
+ READING_U16,
+ READING_U32
+};
+
+/**
+ * The type of writing going on on this chip
+ */
+enum ssp_writing {
+ WRITING_NULL,
+ WRITING_U8,
+ WRITING_U16,
+ WRITING_U32
+};
+
+/**
+ * struct vendor_data - vendor-specific config parameters
+ * for PL022 derivates
+ * @fifodepth: depth of FIFOs (both)
+ * @max_bpw: maximum number of bits per word
+ * @unidir: supports unidirection transfers
+ */
+struct vendor_data {
+ int fifodepth;
+ int max_bpw;
+ bool unidir;
+};
+
+/**
+ * struct pl022 - This is the private SSP driver data structure
+ * @adev: AMBA device model hookup
+ * @phybase: The physical memory where the SSP device resides
+ * @virtbase: The virtual memory where the SSP is mapped
+ * @master: SPI framework hookup
+ * @master_info: controller-specific data from machine setup
+ * @regs: SSP controller register's virtual address
+ * @pump_messages: Work struct for scheduling work to the workqueue
+ * @lock: spinlock to syncronise access to driver data
+ * @workqueue: a workqueue on which any spi_message request is queued
+ * @busy: workqueue is busy
+ * @run: workqueue is running
+ * @pump_transfers: Tasklet used in Interrupt Transfer mode
+ * @cur_msg: Pointer to current spi_message being processed
+ * @cur_transfer: Pointer to current spi_transfer
+ * @cur_chip: pointer to current clients chip(assigned from controller_state)
+ * @tx: current position in TX buffer to be read
+ * @tx_end: end position in TX buffer to be read
+ * @rx: current position in RX buffer to be written
+ * @rx_end: end position in RX buffer to be written
+ * @readingtype: the type of read currently going on
+ * @writingtype: the type or write currently going on
+ */
+struct pl022 {
+ struct amba_device *adev;
+ struct vendor_data *vendor;
+ resource_size_t phybase;
+ void __iomem *virtbase;
+ struct clk *clk;
+ struct spi_master *master;
+ struct pl022_ssp_controller *master_info;
+ /* Driver message queue */
+ struct workqueue_struct *workqueue;
+ struct work_struct pump_messages;
+ spinlock_t queue_lock;
+ struct list_head queue;
+ int busy;
+ int run;
+ /* Message transfer pump */
+ struct tasklet_struct pump_transfers;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct chip_data *cur_chip;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ enum ssp_reading read;
+ enum ssp_writing write;
+};
+
+/**
+ * struct chip_data - To maintain runtime state of SSP for each client chip
+ * @cr0: Value of control register CR0 of SSP
+ * @cr1: Value of control register CR1 of SSP
+ * @dmacr: Value of DMA control Register of SSP
+ * @cpsr: Value of Clock prescale register
+ * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
+ * @enable_dma: Whether to enable DMA or not
+ * @write: function ptr to be used to write when doing xfer for this chip
+ * @read: function ptr to be used to read when doing xfer for this chip
+ * @cs_control: chip select callback provided by chip
+ * @xfer_type: polling/interrupt/DMA
+ *
+ * Runtime state of the SSP controller, maintained per chip,
+ * This would be set according to the current message that would be served
+ */
+struct chip_data {
+ u16 cr0;
+ u16 cr1;
+ u16 dmacr;
+ u16 cpsr;
+ u8 n_bytes;
+ u8 enable_dma:1;
+ enum ssp_reading read;
+ enum ssp_writing write;
+ void (*cs_control) (u32 command);
+ int xfer_type;
+};
+
+/**
+ * null_cs_control - Dummy chip select function
+ * @command: select/delect the chip
+ *
+ * If no chip select function is provided by client this is used as dummy
+ * chip select
+ */
+static void null_cs_control(u32 command)
+{
+ pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
+}
+
+/**
+ * giveback - current spi_message is over, schedule next message and call
+ * callback of this message. Assumes that caller already
+ * set message->status; dma and pio irqs are blocked
+ * @pl022: SSP driver private data structure
+ */
+static void giveback(struct pl022 *pl022)
+{
+ struct spi_transfer *last_transfer;
+ unsigned long flags;
+ struct spi_message *msg;
+ void (*curr_cs_control) (u32 command);
+
+ /*
+ * This local reference to the chip select function
+ * is needed because we set curr_chip to NULL
+ * as a step toward termininating the message.
+ */
+ curr_cs_control = pl022->cur_chip->cs_control;
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ msg = pl022->cur_msg;
+ pl022->cur_msg = NULL;
+ pl022->cur_transfer = NULL;
+ pl022->cur_chip = NULL;
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ last_transfer = list_entry(msg->transfers.prev,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Delay if requested before any change in chip select */
+ if (last_transfer->delay_usecs)
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ udelay(last_transfer->delay_usecs);
+
+ /*
+ * Drop chip select UNLESS cs_change is true or we are returning
+ * a message with an error, or next message is for another chip
+ */
+ if (!last_transfer->cs_change)
+ curr_cs_control(SSP_CHIP_DESELECT);
+ else {
+ struct spi_message *next_msg;
+
+ /* Holding of cs was hinted, but we need to make sure
+ * the next message is for the same chip. Don't waste
+ * time with the following tests unless this was hinted.
+ *
+ * We cannot postpone this until pump_messages, because
+ * after calling msg->complete (below) the driver that
+ * sent the current message could be unloaded, which
+ * could invalidate the cs_control() callback...
+ */
+
+ /* get a pointer to the next message, if any */
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ if (list_empty(&pl022->queue))
+ next_msg = NULL;
+ else
+ next_msg = list_entry(pl022->queue.next,
+ struct spi_message, queue);
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ /* see if the next and current messages point
+ * to the same chip
+ */
+ if (next_msg && next_msg->spi != msg->spi)
+ next_msg = NULL;
+ if (!next_msg || msg->state == STATE_ERROR)
+ curr_cs_control(SSP_CHIP_DESELECT);
+ }
+ msg->state = NULL;
+ if (msg->complete)
+ msg->complete(msg->context);
+ /* This message is completed, so let's turn off the clock! */
+ clk_disable(pl022->clk);
+}
+
+/**
+ * flush - flush the FIFO to reach a clean state
+ * @pl022: SSP driver private data structure
+ */
+static int flush(struct pl022 *pl022)
+{
+ unsigned long limit = loops_per_jiffy << 1;
+
+ dev_dbg(&pl022->adev->dev, "flush\n");
+ do {
+ while (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ readw(SSP_DR(pl022->virtbase));
+ } while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_BSY) && limit--);
+ return limit;
+}
+
+/**
+ * restore_state - Load configuration of current chip
+ * @pl022: SSP driver private data structure
+ */
+static void restore_state(struct pl022 *pl022)
+{
+ struct chip_data *chip = pl022->cur_chip;
+
+ writew(chip->cr0, SSP_CR0(pl022->virtbase));
+ writew(chip->cr1, SSP_CR1(pl022->virtbase));
+ writew(chip->dmacr, SSP_DMACR(pl022->virtbase));
+ writew(chip->cpsr, SSP_CPSR(pl022->virtbase));
+ writew(DISABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+}
+
+/**
+ * load_ssp_default_config - Load default configuration for SSP
+ * @pl022: SSP driver private data structure
+ */
+
+/*
+ * Default SSP Register Values
+ */
+#define DEFAULT_SSP_REG_CR0 ( \
+ GEN_MASK_BITS(SSP_DATA_BITS_12, SSP_CR0_MASK_DSS, 0) | \
+ GEN_MASK_BITS(SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, SSP_CR0_MASK_HALFDUP, 5) | \
+ GEN_MASK_BITS(SSP_CLK_POL_IDLE_LOW, SSP_CR0_MASK_SPO, 6) | \
+ GEN_MASK_BITS(SSP_CLK_FALLING_EDGE, SSP_CR0_MASK_SPH, 7) | \
+ GEN_MASK_BITS(NMDK_SSP_DEFAULT_CLKRATE, SSP_CR0_MASK_SCR, 8) | \
+ GEN_MASK_BITS(SSP_BITS_8, SSP_CR0_MASK_CSS, 16) | \
+ GEN_MASK_BITS(SSP_INTERFACE_MOTOROLA_SPI, SSP_CR0_MASK_FRF, 21) \
+)
+
+#define DEFAULT_SSP_REG_CR1 ( \
+ GEN_MASK_BITS(LOOPBACK_DISABLED, SSP_CR1_MASK_LBM, 0) | \
+ GEN_MASK_BITS(SSP_DISABLED, SSP_CR1_MASK_SSE, 1) | \
+ GEN_MASK_BITS(SSP_MASTER, SSP_CR1_MASK_MS, 2) | \
+ GEN_MASK_BITS(DO_NOT_DRIVE_TX, SSP_CR1_MASK_SOD, 3) | \
+ GEN_MASK_BITS(SSP_RX_MSB, SSP_CR1_MASK_RENDN, 4) | \
+ GEN_MASK_BITS(SSP_TX_MSB, SSP_CR1_MASK_TENDN, 5) | \
+ GEN_MASK_BITS(SSP_MWIRE_WAIT_ZERO, SSP_CR1_MASK_MWAIT, 6) |\
+ GEN_MASK_BITS(SSP_RX_1_OR_MORE_ELEM, SSP_CR1_MASK_RXIFLSEL, 7) | \
+ GEN_MASK_BITS(SSP_TX_1_OR_MORE_EMPTY_LOC, SSP_CR1_MASK_TXIFLSEL, 10) \
+)
+
+#define DEFAULT_SSP_REG_CPSR ( \
+ GEN_MASK_BITS(NMDK_SSP_DEFAULT_PRESCALE, SSP_CPSR_MASK_CPSDVSR, 0) \
+)
+
+#define DEFAULT_SSP_REG_DMACR (\
+ GEN_MASK_BITS(SSP_DMA_DISABLED, SSP_DMACR_MASK_RXDMAE, 0) | \
+ GEN_MASK_BITS(SSP_DMA_DISABLED, SSP_DMACR_MASK_TXDMAE, 1) \
+)
+
+
+static void load_ssp_default_config(struct pl022 *pl022)
+{
+ writew(DEFAULT_SSP_REG_CR0, SSP_CR0(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_CR1, SSP_CR1(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_DMACR, SSP_DMACR(pl022->virtbase));
+ writew(DEFAULT_SSP_REG_CPSR, SSP_CPSR(pl022->virtbase));
+ writew(DISABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+}
+
+/**
+ * This will write to TX and read from RX according to the parameters
+ * set in pl022.
+ */
+static void readwriter(struct pl022 *pl022)
+{
+
+ /*
+ * The FIFO depth is different inbetween primecell variants.
+ * I believe filling in too much in the FIFO might cause
+ * errons in 8bit wide transfers on ARM variants (just 8 words
+ * FIFO, means only 8x8 = 64 bits in FIFO) at least.
+ *
+ * FIXME: currently we have no logic to account for this.
+ * perhaps there is even something broken in HW regarding
+ * 8bit transfers (it doesn't fail on 16bit) so this needs
+ * more investigation...
+ */
+ dev_dbg(&pl022->adev->dev,
+ "%s, rx: %p, rxend: %p, tx: %p, txend: %p\n",
+ __func__, pl022->rx, pl022->rx_end, pl022->tx, pl022->tx_end);
+
+ /* Read as much as you can */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ && (pl022->rx < pl022->rx_end)) {
+ switch (pl022->read) {
+ case READING_NULL:
+ readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U8:
+ *(u8 *) (pl022->rx) =
+ readw(SSP_DR(pl022->virtbase)) & 0xFFU;
+ break;
+ case READING_U16:
+ *(u16 *) (pl022->rx) =
+ (u16) readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U32:
+ *(u32 *) (pl022->rx) =
+ readl(SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->rx += (pl022->cur_chip->n_bytes);
+ }
+ /*
+ * Write as much as you can, while keeping an eye on the RX FIFO!
+ */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
+ && (pl022->tx < pl022->tx_end)) {
+ switch (pl022->write) {
+ case WRITING_NULL:
+ writew(0x0, SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U8:
+ writew(*(u8 *) (pl022->tx), SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U16:
+ writew((*(u16 *) (pl022->tx)), SSP_DR(pl022->virtbase));
+ break;
+ case WRITING_U32:
+ writel(*(u32 *) (pl022->tx), SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->tx += (pl022->cur_chip->n_bytes);
+ /*
+ * This inner reader takes care of things appearing in the RX
+ * FIFO as we're transmitting. This will happen a lot since the
+ * clock starts running when you put things into the TX FIFO,
+ * and then things are continously clocked into the RX FIFO.
+ */
+ while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
+ && (pl022->rx < pl022->rx_end)) {
+ switch (pl022->read) {
+ case READING_NULL:
+ readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U8:
+ *(u8 *) (pl022->rx) =
+ readw(SSP_DR(pl022->virtbase)) & 0xFFU;
+ break;
+ case READING_U16:
+ *(u16 *) (pl022->rx) =
+ (u16) readw(SSP_DR(pl022->virtbase));
+ break;
+ case READING_U32:
+ *(u32 *) (pl022->rx) =
+ readl(SSP_DR(pl022->virtbase));
+ break;
+ }
+ pl022->rx += (pl022->cur_chip->n_bytes);
+ }
+ }
+ /*
+ * When we exit here the TX FIFO should be full and the RX FIFO
+ * should be empty
+ */
+}
+
+
+/**
+ * next_transfer - Move to the Next transfer in the current spi message
+ * @pl022: SSP driver private data structure
+ *
+ * This function moves though the linked list of spi transfers in the
+ * current spi message and returns with the state of current spi
+ * message i.e whether its last transfer is done(STATE_DONE) or
+ * Next transfer is ready(STATE_RUNNING)
+ */
+static void *next_transfer(struct pl022 *pl022)
+{
+ struct spi_message *msg = pl022->cur_msg;
+ struct spi_transfer *trans = pl022->cur_transfer;
+
+ /* Move to next transfer */
+ if (trans->transfer_list.next != &msg->transfers) {
+ pl022->cur_transfer =
+ list_entry(trans->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ return STATE_RUNNING;
+ }
+ return STATE_DONE;
+}
+/**
+ * pl022_interrupt_handler - Interrupt handler for SSP controller
+ *
+ * This function handles interrupts generated for an interrupt based transfer.
+ * If a receive overrun (ROR) interrupt is there then we disable SSP, flag the
+ * current message's state as STATE_ERROR and schedule the tasklet
+ * pump_transfers which will do the postprocessing of the current message by
+ * calling giveback(). Otherwise it reads data from RX FIFO till there is no
+ * more data, and writes data in TX FIFO till it is not full. If we complete
+ * the transfer we move to the next transfer and schedule the tasklet.
+ */
+static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
+{
+ struct pl022 *pl022 = dev_id;
+ struct spi_message *msg = pl022->cur_msg;
+ u16 irq_status = 0;
+ u16 flag = 0;
+
+ if (unlikely(!msg)) {
+ dev_err(&pl022->adev->dev,
+ "bad message state in interrupt handler");
+ /* Never fail */
+ return IRQ_HANDLED;
+ }
+
+ /* Read the Interrupt Status Register */
+ irq_status = readw(SSP_MIS(pl022->virtbase));
+
+ if (unlikely(!irq_status))
+ return IRQ_NONE;
+
+ /* This handles the error code interrupts */
+ if (unlikely(irq_status & SSP_MIS_MASK_RORMIS)) {
+ /*
+ * Overrun interrupt - bail out since our Data has been
+ * corrupted
+ */
+ dev_err(&pl022->adev->dev,
+ "FIFO overrun\n");
+ if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
+ dev_err(&pl022->adev->dev,
+ "RXFIFO is full\n");
+ if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
+ dev_err(&pl022->adev->dev,
+ "TXFIFO is full\n");
+
+ /*
+ * Disable and clear interrupts, disable SSP,
+ * mark message with bad status so it can be
+ * retried.
+ */
+ writew(DISABLE_ALL_INTERRUPTS,
+ SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+ msg->state = STATE_ERROR;
+
+ /* Schedule message queue handler */
+ tasklet_schedule(&pl022->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ readwriter(pl022);
+
+ if ((pl022->tx == pl022->tx_end) && (flag == 0)) {
+ flag = 1;
+ /* Disable Transmit interrupt */
+ writew(readw(SSP_IMSC(pl022->virtbase)) &
+ (~SSP_IMSC_MASK_TXIM),
+ SSP_IMSC(pl022->virtbase));
+ }
+
+ /*
+ * Since all transactions must write as much as shall be read,
+ * we can conclude the entire transaction once RX is complete.
+ * At this point, all TX will always be finished.
+ */
+ if (pl022->rx >= pl022->rx_end) {
+ writew(DISABLE_ALL_INTERRUPTS,
+ SSP_IMSC(pl022->virtbase));
+ writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
+ if (unlikely(pl022->rx > pl022->rx_end)) {
+ dev_warn(&pl022->adev->dev, "read %u surplus "
+ "bytes (did you request an odd "
+ "number of bytes on a 16bit bus?)\n",
+ (u32) (pl022->rx - pl022->rx_end));
+ }
+ /* Update total bytes transfered */
+ msg->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->
+ cs_control(SSP_CHIP_DESELECT);
+ /* Move to next transfer */
+ msg->state = next_transfer(pl022);
+ tasklet_schedule(&pl022->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * This sets up the pointers to memory for the next message to
+ * send out on the SPI bus.
+ */
+static int set_up_next_transfer(struct pl022 *pl022,
+ struct spi_transfer *transfer)
+{
+ int residue;
+
+ /* Sanity check the message for this bus width */
+ residue = pl022->cur_transfer->len % pl022->cur_chip->n_bytes;
+ if (unlikely(residue != 0)) {
+ dev_err(&pl022->adev->dev,
+ "message of %u bytes to transmit but the current "
+ "chip bus has a data width of %u bytes!\n",
+ pl022->cur_transfer->len,
+ pl022->cur_chip->n_bytes);
+ dev_err(&pl022->adev->dev, "skipping this message\n");
+ return -EIO;
+ }
+ pl022->tx = (void *)transfer->tx_buf;
+ pl022->tx_end = pl022->tx + pl022->cur_transfer->len;
+ pl022->rx = (void *)transfer->rx_buf;
+ pl022->rx_end = pl022->rx + pl022->cur_transfer->len;
+ pl022->write =
+ pl022->tx ? pl022->cur_chip->write : WRITING_NULL;
+ pl022->read = pl022->rx ? pl022->cur_chip->read : READING_NULL;
+ return 0;
+}
+
+/**
+ * pump_transfers - Tasklet function which schedules next interrupt transfer
+ * when running in interrupt transfer mode.
+ * @data: SSP driver private data structure
+ *
+ */
+static void pump_transfers(unsigned long data)
+{
+ struct pl022 *pl022 = (struct pl022 *) data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+
+ /* Get current state information */
+ message = pl022->cur_msg;
+ transfer = pl022->cur_transfer;
+
+ /* Handle for abort */
+ if (message->state == STATE_ERROR) {
+ message->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+
+ /* Handle end of message */
+ if (message->state == STATE_DONE) {
+ message->status = 0;
+ giveback(pl022);
+ return;
+ }
+
+ /* Delay if requested at end of transfer before CS change */
+ if (message->state == STATE_RUNNING) {
+ previous = list_entry(transfer->transfer_list.prev,
+ struct spi_transfer,
+ transfer_list);
+ if (previous->delay_usecs)
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ udelay(previous->delay_usecs);
+
+ /* Drop chip select only if cs_change is requested */
+ if (previous->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ } else {
+ /* STATE_START */
+ message->state = STATE_RUNNING;
+ }
+
+ if (set_up_next_transfer(pl022, transfer)) {
+ message->state = STATE_ERROR;
+ message->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Flush the FIFOs and let's go! */
+ flush(pl022);
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+}
+
+/**
+ * NOT IMPLEMENTED
+ * configure_dma - It configures the DMA pipes for DMA transfers
+ * @data: SSP driver's private data structure
+ *
+ */
+static int configure_dma(void *data)
+{
+ struct pl022 *pl022 = data;
+ dev_dbg(&pl022->adev->dev, "configure DMA\n");
+ return -ENOTSUPP;
+}
+
+/**
+ * do_dma_transfer - It handles transfers of the current message
+ * if it is DMA xfer.
+ * NOT FULLY IMPLEMENTED
+ * @data: SSP driver's private data structure
+ */
+static void do_dma_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+
+ if (configure_dma(data)) {
+ dev_dbg(&pl022->adev->dev, "configuration of DMA Failed!\n");
+ goto err_config_dma;
+ }
+
+ /* TODO: Implememt DMA setup of pipes here */
+
+ /* Enable target chip, set up transfer */
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
+ /* Error path */
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Enable SSP */
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+
+ /* TODO: Enable the DMA transfer here */
+ return;
+
+ err_config_dma:
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+}
+
+static void do_interrupt_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+
+ /* Enable target chip */
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
+ /* Error path */
+ pl022->cur_msg->state = STATE_ERROR;
+ pl022->cur_msg->status = -EIO;
+ giveback(pl022);
+ return;
+ }
+ /* Enable SSP, turn on interrupts */
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+}
+
+static void do_polling_transfer(void *data)
+{
+ struct pl022 *pl022 = data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ struct chip_data *chip;
+
+ chip = pl022->cur_chip;
+ message = pl022->cur_msg;
+
+ while (message->state != STATE_DONE) {
+ /* Handle for abort */
+ if (message->state == STATE_ERROR)
+ break;
+ transfer = pl022->cur_transfer;
+
+ /* Delay if requested at end of transfer */
+ if (message->state == STATE_RUNNING) {
+ previous =
+ list_entry(transfer->transfer_list.prev,
+ struct spi_transfer, transfer_list);
+ if (previous->delay_usecs)
+ udelay(previous->delay_usecs);
+ if (previous->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ } else {
+ /* STATE_START */
+ message->state = STATE_RUNNING;
+ pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+ }
+
+ /* Configuration Changing Per Transfer */
+ if (set_up_next_transfer(pl022, transfer)) {
+ /* Error path */
+ message->state = STATE_ERROR;
+ break;
+ }
+ /* Flush FIFOs and enable SSP */
+ flush(pl022);
+ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
+ SSP_CR1(pl022->virtbase));
+
+ dev_dbg(&pl022->adev->dev, "POLLING TRANSFER ONGOING ... \n");
+ /* FIXME: insert a timeout so we don't hang here indefinately */
+ while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end)
+ readwriter(pl022);
+
+ /* Update total byte transfered */
+ message->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
+ /* Move to next transfer */
+ message->state = next_transfer(pl022);
+ }
+
+ /* Handle end of message */
+ if (message->state == STATE_DONE)
+ message->status = 0;
+ else
+ message->status = -EIO;
+
+ giveback(pl022);
+ return;
+}
+
+/**
+ * pump_messages - Workqueue function which processes spi message queue
+ * @data: pointer to private data of SSP driver
+ *
+ * This function checks if there is any spi message in the queue that
+ * needs processing and delegate control to appropriate function
+ * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * based on the kind of the transfer
+ *
+ */
+static void pump_messages(struct work_struct *work)
+{
+ struct pl022 *pl022 =
+ container_of(work, struct pl022, pump_messages);
+ unsigned long flags;
+
+ /* Lock queue and check for queue work */
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ if (list_empty(&pl022->queue) || pl022->run == QUEUE_STOPPED) {
+ pl022->busy = 0;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return;
+ }
+ /* Make sure we are not already running a message */
+ if (pl022->cur_msg) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return;
+ }
+ /* Extract head of queue */
+ pl022->cur_msg =
+ list_entry(pl022->queue.next, struct spi_message, queue);
+
+ list_del_init(&pl022->cur_msg->queue);
+ pl022->busy = 1;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ /* Initial message state */
+ pl022->cur_msg->state = STATE_START;
+ pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Setup the SPI using the per chip configuration */
+ pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
+ /*
+ * We enable the clock here, then the clock will be disabled when
+ * giveback() is called in each method (poll/interrupt/DMA)
+ */
+ clk_enable(pl022->clk);
+ restore_state(pl022);
+ flush(pl022);
+
+ if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
+ do_polling_transfer(pl022);
+ else if (pl022->cur_chip->xfer_type == INTERRUPT_TRANSFER)
+ do_interrupt_transfer(pl022);
+ else
+ do_dma_transfer(pl022);
+}
+
+
+static int __init init_queue(struct pl022 *pl022)
+{
+ INIT_LIST_HEAD(&pl022->queue);
+ spin_lock_init(&pl022->queue_lock);
+
+ pl022->run = QUEUE_STOPPED;
+ pl022->busy = 0;
+
+ tasklet_init(&pl022->pump_transfers,
+ pump_transfers, (unsigned long)pl022);
+
+ INIT_WORK(&pl022->pump_messages, pump_messages);
+ pl022->workqueue = create_singlethread_workqueue(
+ dev_name(pl022->master->dev.parent));
+ if (pl022->workqueue == NULL)
+ return -EBUSY;
+
+ return 0;
+}
+
+
+static int start_queue(struct pl022 *pl022)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ if (pl022->run == QUEUE_RUNNING || pl022->busy) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return -EBUSY;
+ }
+
+ pl022->run = QUEUE_RUNNING;
+ pl022->cur_msg = NULL;
+ pl022->cur_transfer = NULL;
+ pl022->cur_chip = NULL;
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+
+ return 0;
+}
+
+
+static int stop_queue(struct pl022 *pl022)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+ int status = 0;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ /* This is a bit lame, but is optimized for the common execution path.
+ * A wait_queue on the pl022->busy could be used, but then the common
+ * execution path (pump_messages) would be required to call wake_up or
+ * friends on every SPI message. Do this instead */
+ pl022->run = QUEUE_STOPPED;
+ while (!list_empty(&pl022->queue) && pl022->busy && limit--) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+ }
+
+ if (!list_empty(&pl022->queue) || pl022->busy)
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+
+ return status;
+}
+
+static int destroy_queue(struct pl022 *pl022)
+{
+ int status;
+
+ status = stop_queue(pl022);
+ /* we are unloading the module or failing to load (only two calls
+ * to this routine), and neither call can handle a return value.
+ * However, destroy_workqueue calls flush_workqueue, and that will
+ * block until all work is done. If the reason that stop_queue
+ * timed out is that the work will never finish, then it does no
+ * good to call destroy_workqueue, so return anyway. */
+ if (status != 0)
+ return status;
+
+ destroy_workqueue(pl022->workqueue);
+
+ return 0;
+}
+
+static int verify_controller_parameters(struct pl022 *pl022,
+ struct pl022_config_chip *chip_info)
+{
+ if ((chip_info->lbm != LOOPBACK_ENABLED)
+ && (chip_info->lbm != LOOPBACK_DISABLED)) {
+ dev_err(chip_info->dev,
+ "loopback Mode is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI)
+ || (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) {
+ dev_err(chip_info->dev,
+ "interface is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->iface == SSP_INTERFACE_UNIDIRECTIONAL) &&
+ (!pl022->vendor->unidir)) {
+ dev_err(chip_info->dev,
+ "unidirectional mode not supported in this "
+ "hardware version\n");
+ return -EINVAL;
+ }
+ if ((chip_info->hierarchy != SSP_MASTER)
+ && (chip_info->hierarchy != SSP_SLAVE)) {
+ dev_err(chip_info->dev,
+ "hierarchy is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN)
+ || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) {
+ dev_err(chip_info->dev,
+ "cpsdvsr is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->endian_rx != SSP_RX_MSB)
+ && (chip_info->endian_rx != SSP_RX_LSB)) {
+ dev_err(chip_info->dev,
+ "RX FIFO endianess is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->endian_tx != SSP_TX_MSB)
+ && (chip_info->endian_tx != SSP_TX_LSB)) {
+ dev_err(chip_info->dev,
+ "TX FIFO endianess is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->data_size < SSP_DATA_BITS_4)
+ || (chip_info->data_size > SSP_DATA_BITS_32)) {
+ dev_err(chip_info->dev,
+ "DATA Size is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->com_mode != INTERRUPT_TRANSFER)
+ && (chip_info->com_mode != DMA_TRANSFER)
+ && (chip_info->com_mode != POLLING_TRANSFER)) {
+ dev_err(chip_info->dev,
+ "Communication mode is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM)
+ || (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) {
+ dev_err(chip_info->dev,
+ "RX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC)
+ || (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) {
+ dev_err(chip_info->dev,
+ "TX FIFO Trigger Level is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) {
+ if ((chip_info->clk_phase != SSP_CLK_RISING_EDGE)
+ && (chip_info->clk_phase != SSP_CLK_FALLING_EDGE)) {
+ dev_err(chip_info->dev,
+ "Clock Phase is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->clk_pol != SSP_CLK_POL_IDLE_LOW)
+ && (chip_info->clk_pol != SSP_CLK_POL_IDLE_HIGH)) {
+ dev_err(chip_info->dev,
+ "Clock Polarity is configured incorrectly\n");
+ return -EINVAL;
+ }
+ }
+ if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) {
+ if ((chip_info->ctrl_len < SSP_BITS_4)
+ || (chip_info->ctrl_len > SSP_BITS_32)) {
+ dev_err(chip_info->dev,
+ "CTRL LEN is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->wait_state != SSP_MWIRE_WAIT_ZERO)
+ && (chip_info->wait_state != SSP_MWIRE_WAIT_ONE)) {
+ dev_err(chip_info->dev,
+ "Wait State is configured incorrectly\n");
+ return -EINVAL;
+ }
+ if ((chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
+ && (chip_info->duplex !=
+ SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) {
+ dev_err(chip_info->dev,
+ "DUPLEX is configured incorrectly\n");
+ return -EINVAL;
+ }
+ }
+ if (chip_info->cs_control == NULL) {
+ dev_warn(chip_info->dev,
+ "Chip Select Function is NULL for this chip\n");
+ chip_info->cs_control = null_cs_control;
+ }
+ return 0;
+}
+
+/**
+ * pl022_transfer - transfer function registered to SPI master framework
+ * @spi: spi device which is requesting transfer
+ * @msg: spi message which is to handled is queued to driver queue
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will queue the spi_message in the queue of driver if
+ * the queue is not stopped and return.
+ */
+static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct pl022 *pl022 = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pl022->queue_lock, flags);
+
+ if (pl022->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return -ESHUTDOWN;
+ }
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = STATE_START;
+
+ list_add_tail(&msg->queue, &pl022->queue);
+ if (pl022->run == QUEUE_RUNNING && !pl022->busy)
+ queue_work(pl022->workqueue, &pl022->pump_messages);
+
+ spin_unlock_irqrestore(&pl022->queue_lock, flags);
+ return 0;
+}
+
+static int calculate_effective_freq(struct pl022 *pl022,
+ int freq,
+ struct ssp_clock_params *clk_freq)
+{
+ /* Lets calculate the frequency parameters */
+ u16 cpsdvsr = 2;
+ u16 scr = 0;
+ bool freq_found = false;
+ u32 rate;
+ u32 max_tclk;
+ u32 min_tclk;
+
+ rate = clk_get_rate(pl022->clk);
+ /* cpsdvscr = 2 & scr 0 */
+ max_tclk = (rate / (CPSDVR_MIN * (1 + SCR_MIN)));
+ /* cpsdvsr = 254 & scr = 255 */
+ min_tclk = (rate / (CPSDVR_MAX * (1 + SCR_MAX)));
+
+ if ((freq <= max_tclk) && (freq >= min_tclk)) {
+ while (cpsdvsr <= CPSDVR_MAX && !freq_found) {
+ while (scr <= SCR_MAX && !freq_found) {
+ if ((rate /
+ (cpsdvsr * (1 + scr))) > freq)
+ scr += 1;
+ else {
+ /*
+ * This bool is made true when
+ * effective frequency >=
+ * target frequency is found
+ */
+ freq_found = true;
+ if ((rate /
+ (cpsdvsr * (1 + scr))) != freq) {
+ if (scr == SCR_MIN) {
+ cpsdvsr -= 2;
+ scr = SCR_MAX;
+ } else
+ scr -= 1;
+ }
+ }
+ }
+ if (!freq_found) {
+ cpsdvsr += 2;
+ scr = SCR_MIN;
+ }
+ }
+ if (cpsdvsr != 0) {
+ dev_dbg(&pl022->adev->dev,
+ "SSP Effective Frequency is %u\n",
+ (rate / (cpsdvsr * (1 + scr))));
+ clk_freq->cpsdvsr = (u8) (cpsdvsr & 0xFF);
+ clk_freq->scr = (u8) (scr & 0xFF);
+ dev_dbg(&pl022->adev->dev,
+ "SSP cpsdvsr = %d, scr = %d\n",
+ clk_freq->cpsdvsr, clk_freq->scr);
+ }
+ } else {
+ dev_err(&pl022->adev->dev,
+ "controller data is incorrect: out of range frequency");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * NOT IMPLEMENTED
+ * process_dma_info - Processes the DMA info provided by client drivers
+ * @chip_info: chip info provided by client device
+ * @chip: Runtime state maintained by the SSP controller for each spi device
+ *
+ * This function processes and stores DMA config provided by client driver
+ * into the runtime state maintained by the SSP controller driver
+ */
+static int process_dma_info(struct pl022_config_chip *chip_info,
+ struct chip_data *chip)
+{
+ dev_err(chip_info->dev,
+ "cannot process DMA info, DMA not implemented!\n");
+ return -ENOTSUPP;
+}
+
+/**
+ * pl022_setup - setup function registered to SPI master framework
+ * @spi: spi device which is requesting setup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. If it is the first time when setup is called by this device,
+ * this function will initialize the runtime state for this chip and save
+ * the same in the device structure. Else it will update the runtime info
+ * with the updated chip info. Nothing is really being written to the
+ * controller hardware here, that is not done until the actual transfer
+ * commence.
+ */
+
+/* FIXME: JUST GUESSING the spi->mode bits understood by this driver */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
+ | SPI_LSB_FIRST | SPI_LOOP)
+
+static int pl022_setup(struct spi_device *spi)
+{
+ struct pl022_config_chip *chip_info;
+ struct chip_data *chip;
+ int status = 0;
+ struct pl022 *pl022 = spi_master_get_devdata(spi->master);
+
+ if (spi->mode & ~MODEBITS) {
+ dev_dbg(&spi->dev, "unsupported mode bits %x\n",
+ spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ /* Get controller_state if one is supplied */
+ chip = spi_get_ctldata(spi);
+
+ if (chip == NULL) {
+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spi->dev,
+ "cannot allocate controller state\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&spi->dev,
+ "allocated memory for controller's runtime state\n");
+ }
+
+ /* Get controller data if one is supplied */
+ chip_info = spi->controller_data;
+
+ if (chip_info == NULL) {
+ /* spi_board_info.controller_data not is supplied */
+ dev_dbg(&spi->dev,
+ "using default controller_data settings\n");
+
+ chip_info =
+ kzalloc(sizeof(struct pl022_config_chip), GFP_KERNEL);
+
+ if (!chip_info) {
+ dev_err(&spi->dev,
+ "cannot allocate controller data\n");
+ status = -ENOMEM;
+ goto err_first_setup;
+ }
+
+ dev_dbg(&spi->dev, "allocated memory for controller data\n");
+
+ /* Pointer back to the SPI device */
+ chip_info->dev = &spi->dev;
+ /*
+ * Set controller data default values:
+ * Polling is supported by default
+ */
+ chip_info->lbm = LOOPBACK_DISABLED;
+ chip_info->com_mode = POLLING_TRANSFER;
+ chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI;
+ chip_info->hierarchy = SSP_SLAVE;
+ chip_info->slave_tx_disable = DO_NOT_DRIVE_TX;
+ chip_info->endian_tx = SSP_TX_LSB;
+ chip_info->endian_rx = SSP_RX_LSB;
+ chip_info->data_size = SSP_DATA_BITS_12;
+ chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
+ chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
+ chip_info->clk_phase = SSP_CLK_FALLING_EDGE;
+ chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW;
+ chip_info->ctrl_len = SSP_BITS_8;
+ chip_info->wait_state = SSP_MWIRE_WAIT_ZERO;
+ chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX;
+ chip_info->cs_control = null_cs_control;
+ } else {
+ dev_dbg(&spi->dev,
+ "using user supplied controller_data settings\n");
+ }
+
+ /*
+ * We can override with custom divisors, else we use the board
+ * frequency setting
+ */
+ if ((0 == chip_info->clk_freq.cpsdvsr)
+ && (0 == chip_info->clk_freq.scr)) {
+ status = calculate_effective_freq(pl022,
+ spi->max_speed_hz,
+ &chip_info->clk_freq);
+ if (status < 0)
+ goto err_config_params;
+ } else {
+ if ((chip_info->clk_freq.cpsdvsr % 2) != 0)
+ chip_info->clk_freq.cpsdvsr =
+ chip_info->clk_freq.cpsdvsr - 1;
+ }
+ status = verify_controller_parameters(pl022, chip_info);
+ if (status) {
+ dev_err(&spi->dev, "controller data is incorrect");
+ goto err_config_params;
+ }
+ /* Now set controller state based on controller data */
+ chip->xfer_type = chip_info->com_mode;
+ chip->cs_control = chip_info->cs_control;
+
+ if (chip_info->data_size <= 8) {
+ dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n");
+ chip->n_bytes = 1;
+ chip->read = READING_U8;
+ chip->write = WRITING_U8;
+ } else if (chip_info->data_size <= 16) {
+ dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n");
+ chip->n_bytes = 2;
+ chip->read = READING_U16;
+ chip->write = WRITING_U16;
+ } else {
+ if (pl022->vendor->max_bpw >= 32) {
+ dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n");
+ chip->n_bytes = 4;
+ chip->read = READING_U32;
+ chip->write = WRITING_U32;
+ } else {
+ dev_err(&spi->dev,
+ "illegal data size for this controller!\n");
+ dev_err(&spi->dev,
+ "a standard pl022 can only handle "
+ "1 <= n <= 16 bit words\n");
+ goto err_config_params;
+ }
+ }
+
+ /* Now Initialize all register settings required for this chip */
+ chip->cr0 = 0;
+ chip->cr1 = 0;
+ chip->dmacr = 0;
+ chip->cpsr = 0;
+ if ((chip_info->com_mode == DMA_TRANSFER)
+ && ((pl022->master_info)->enable_dma)) {
+ chip->enable_dma = 1;
+ dev_dbg(&spi->dev, "DMA mode set in controller state\n");
+ status = process_dma_info(chip_info, chip);
+ if (status < 0)
+ goto err_config_params;
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
+ SSP_DMACR_MASK_RXDMAE, 0);
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
+ SSP_DMACR_MASK_TXDMAE, 1);
+ } else {
+ chip->enable_dma = 0;
+ dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n");
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
+ SSP_DMACR_MASK_RXDMAE, 0);
+ SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
+ SSP_DMACR_MASK_TXDMAE, 1);
+ }
+
+ chip->cpsr = chip_info->clk_freq.cpsdvsr;
+
+ SSP_WRITE_BITS(chip->cr0, chip_info->data_size, SSP_CR0_MASK_DSS, 0);
+ SSP_WRITE_BITS(chip->cr0, chip_info->duplex, SSP_CR0_MASK_HALFDUP, 5);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_pol, SSP_CR0_MASK_SPO, 6);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_phase, SSP_CR0_MASK_SPH, 7);
+ SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8);
+ SSP_WRITE_BITS(chip->cr0, chip_info->ctrl_len, SSP_CR0_MASK_CSS, 16);
+ SSP_WRITE_BITS(chip->cr0, chip_info->iface, SSP_CR0_MASK_FRF, 21);
+ SSP_WRITE_BITS(chip->cr1, chip_info->lbm, SSP_CR1_MASK_LBM, 0);
+ SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
+ SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
+ SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
+ SSP_WRITE_BITS(chip->cr1, chip_info->endian_rx, SSP_CR1_MASK_RENDN, 4);
+ SSP_WRITE_BITS(chip->cr1, chip_info->endian_tx, SSP_CR1_MASK_TENDN, 5);
+ SSP_WRITE_BITS(chip->cr1, chip_info->wait_state, SSP_CR1_MASK_MWAIT, 6);
+ SSP_WRITE_BITS(chip->cr1, chip_info->rx_lev_trig, SSP_CR1_MASK_RXIFLSEL, 7);
+ SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig, SSP_CR1_MASK_TXIFLSEL, 10);
+
+ /* Save controller_state */
+ spi_set_ctldata(spi, chip);
+ return status;
+ err_config_params:
+ err_first_setup:
+ kfree(chip);
+ return status;
+}
+
+/**
+ * pl022_cleanup - cleanup function registered to SPI master framework
+ * @spi: spi device which is requesting cleanup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will free the runtime state of chip.
+ */
+static void pl022_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata(spi);
+
+ spi_set_ctldata(spi, NULL);
+ kfree(chip);
+}
+
+
+static int __init
+pl022_probe(struct amba_device *adev, struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
+ struct spi_master *master;
+ struct pl022 *pl022 = NULL; /*Data for this driver */
+ int status = 0;
+
+ dev_info(&adev->dev,
+ "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+ if (platform_info == NULL) {
+ dev_err(&adev->dev, "probe - no platform data supplied\n");
+ status = -ENODEV;
+ goto err_no_pdata;
+ }
+
+ /* Allocate master with space for data */
+ master = spi_alloc_master(dev, sizeof(struct pl022));
+ if (master == NULL) {
+ dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
+ status = -ENOMEM;
+ goto err_no_master;
+ }
+
+ pl022 = spi_master_get_devdata(master);
+ pl022->master = master;
+ pl022->master_info = platform_info;
+ pl022->adev = adev;
+ pl022->vendor = id->data;
+
+ /*
+ * Bus Number Which has been Assigned to this SSP controller
+ * on this board
+ */
+ master->bus_num = platform_info->bus_id;
+ master->num_chipselect = platform_info->num_chipselect;
+ master->cleanup = pl022_cleanup;
+ master->setup = pl022_setup;
+ master->transfer = pl022_transfer;
+
+ dev_dbg(&adev->dev, "BUSNO: %d\n", master->bus_num);
+
+ status = amba_request_regions(adev, NULL);
+ if (status)
+ goto err_no_ioregion;
+
+ pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
+ if (pl022->virtbase == NULL) {
+ status = -ENOMEM;
+ goto err_no_ioremap;
+ }
+ printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
+ adev->res.start, pl022->virtbase);
+
+ pl022->clk = clk_get(&adev->dev, NULL);
+ if (IS_ERR(pl022->clk)) {
+ status = PTR_ERR(pl022->clk);
+ dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+ goto err_no_clk;
+ }
+
+ /* Disable SSP */
+ clk_enable(pl022->clk);
+ writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
+ SSP_CR1(pl022->virtbase));
+ load_ssp_default_config(pl022);
+ clk_disable(pl022->clk);
+
+ status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
+ pl022);
+ if (status < 0) {
+ dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+ goto err_no_irq;
+ }
+ /* Initialize and start queue */
+ status = init_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev, "probe - problem initializing queue\n");
+ goto err_init_queue;
+ }
+ status = start_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev, "probe - problem starting queue\n");
+ goto err_start_queue;
+ }
+ /* Register with the SPI framework */
+ amba_set_drvdata(adev, pl022);
+ status = spi_register_master(master);
+ if (status != 0) {
+ dev_err(&adev->dev,
+ "probe - problem registering spi master\n");
+ goto err_spi_register;
+ }
+ dev_dbg(dev, "probe succeded\n");
+ return 0;
+
+ err_spi_register:
+ err_start_queue:
+ err_init_queue:
+ destroy_queue(pl022);
+ free_irq(adev->irq[0], pl022);
+ err_no_irq:
+ clk_put(pl022->clk);
+ err_no_clk:
+ iounmap(pl022->virtbase);
+ err_no_ioremap:
+ amba_release_regions(adev);
+ err_no_ioregion:
+ spi_master_put(master);
+ err_no_master:
+ err_no_pdata:
+ return status;
+}
+
+static int __exit
+pl022_remove(struct amba_device *adev)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+ if (!pl022)
+ return 0;
+
+ /* Remove the queue */
+ status = destroy_queue(pl022);
+ if (status != 0) {
+ dev_err(&adev->dev,
+ "queue remove failed (%d)\n", status);
+ return status;
+ }
+ load_ssp_default_config(pl022);
+ free_irq(adev->irq[0], pl022);
+ clk_disable(pl022->clk);
+ clk_put(pl022->clk);
+ iounmap(pl022->virtbase);
+ amba_release_regions(adev);
+ tasklet_disable(&pl022->pump_transfers);
+ spi_unregister_master(pl022->master);
+ spi_master_put(pl022->master);
+ amba_set_drvdata(adev, NULL);
+ dev_dbg(&adev->dev, "remove succeded\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pl022_suspend(struct amba_device *adev, pm_message_t state)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+
+ status = stop_queue(pl022);
+ if (status) {
+ dev_warn(&adev->dev, "suspend cannot stop queue\n");
+ return status;
+ }
+
+ clk_enable(pl022->clk);
+ load_ssp_default_config(pl022);
+ clk_disable(pl022->clk);
+ dev_dbg(&adev->dev, "suspended\n");
+ return 0;
+}
+
+static int pl022_resume(struct amba_device *adev)
+{
+ struct pl022 *pl022 = amba_get_drvdata(adev);
+ int status = 0;
+
+ /* Start the queue running */
+ status = start_queue(pl022);
+ if (status)
+ dev_err(&adev->dev, "problem starting queue (%d)\n", status);
+ else
+ dev_dbg(&adev->dev, "resumed\n");
+
+ return status;
+}
+#else
+#define pl022_suspend NULL
+#define pl022_resume NULL
+#endif /* CONFIG_PM */
+
+static struct vendor_data vendor_arm = {
+ .fifodepth = 8,
+ .max_bpw = 16,
+ .unidir = false,
+};
+
+
+static struct vendor_data vendor_st = {
+ .fifodepth = 32,
+ .max_bpw = 32,
+ .unidir = false,
+};
+
+static struct amba_id pl022_ids[] = {
+ {
+ /*
+ * ARM PL022 variant, this has a 16bit wide
+ * and 8 locations deep TX/RX FIFO
+ */
+ .id = 0x00041022,
+ .mask = 0x000fffff,
+ .data = &vendor_arm,
+ },
+ {
+ /*
+ * ST Micro derivative, this has 32bit wide
+ * and 32 locations deep TX/RX FIFO
+ */
+ .id = 0x00108022,
+ .mask = 0xffffffff,
+ .data = &vendor_st,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver pl022_driver = {
+ .drv = {
+ .name = "ssp-pl022",
+ },
+ .id_table = pl022_ids,
+ .probe = pl022_probe,
+ .remove = __exit_p(pl022_remove),
+ .suspend = pl022_suspend,
+ .resume = pl022_resume,
+};
+
+
+static int __init pl022_init(void)
+{
+ return amba_driver_register(&pl022_driver);
+}
+
+module_init(pl022_init);
+
+static void __exit pl022_exit(void)
+{
+ amba_driver_unregister(&pl022_driver);
+}
+
+module_exit(pl022_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("PL022 SSP Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
index f2447a5476b..bbf9371cd28 100644
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ b/drivers/spi/spi_s3c24xx_gpio.c
@@ -17,6 +17,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 7cf74f8c2db..b0dbf4157d2 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -47,7 +47,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
struct usb_hcd *hcd;
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
- pr_debug("resource[1] is not IORESOURCE_IRQ");
+ dbg("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
@@ -65,12 +65,18 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
- pr_debug("ioremap failed");
+ dbg("ioremap failed");
retval = -ENOMEM;
goto err2;
}
- usb_host_clock = clk_get(&pdev->dev, "usb_host");
+ usb_host_clock = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usb_host_clock)) {
+ dbg("clk_get failed");
+ retval = PTR_ERR(usb_host_clock);
+ goto err3;
+ }
+
ep93xx_start_hc(&pdev->dev);
ohci_hcd_init(hcd_to_ohci(hcd));
@@ -80,6 +86,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
return retval;
ep93xx_stop_hc(&pdev->dev);
+err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 74712cb8399..2b5a691064b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -397,7 +397,7 @@ config FB_SA1100
config FB_IMX
tristate "Motorola i.MX LCD support"
- depends on FB && (ARCH_IMX || ARCH_MX2)
+ depends on FB && (ARCH_MX1 || ARCH_MX2)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -1759,6 +1759,16 @@ config FB_68328
Say Y here if you want to support the built-in frame buffer of
the Motorola 68328 CPU family.
+config FB_PXA168
+ tristate "PXA168/910 LCD framebuffer support"
+ depends on FB && (CPU_PXA168 || CPU_PXA910)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Frame buffer driver for the built-in LCD controller in the Marvell
+ MMP processor.
+
config FB_PXA
tristate "PXA LCD framebuffer support"
depends on FB && ARCH_PXA
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index d8d0be5151e..01a819f4737 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
obj-$(CONFIG_FB_PXA) += pxafb.o
+obj-$(CONFIG_FB_PXA168) += pxa168fb.o
obj-$(CONFIG_FB_W100) += w100fb.o
obj-$(CONFIG_FB_TMIO) += tmiofb.o
obj-$(CONFIG_FB_AU1100) += au1100fb.o
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index d1f80bac54f..fb8163d181a 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -351,7 +351,7 @@ static int clcdfb_register(struct clcd_fb *fb)
}
fb->fb.fix.mmio_start = fb->dev->res.start;
- fb->fb.fix.mmio_len = 4096;
+ fb->fb.fix.mmio_len = resource_size(&fb->dev->res);
fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
if (!fb->regs) {
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index 9894de1c9b9..b7af5256e88 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -706,7 +706,7 @@ static void mx3fb_dma_done(void *arg)
dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq);
/* We only need one interrupt, it will be re-enabled as needed */
- disable_irq(ichannel->eof_irq);
+ disable_irq_nosync(ichannel->eof_irq);
complete(&mx3_fbi->flip_cmpl);
}
@@ -1366,7 +1366,7 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
mx3fb_blank(FB_BLANK_UNBLANK, fbi);
- dev_info(dev, "mx3fb: fb registered, using mode %s\n", fb_mode);
+ dev_info(dev, "registered, using mode %s\n", fb_mode);
ret = register_framebuffer(fbi);
if (ret < 0)
diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c
index 8aa6e47202b..5d4f34887a2 100644
--- a/drivers/video/omap/hwa742.c
+++ b/drivers/video/omap/hwa742.c
@@ -133,8 +133,7 @@ struct {
struct lcd_ctrl_extif *extif;
struct lcd_ctrl *int_ctrl;
- void (*power_up)(struct device *dev);
- void (*power_down)(struct device *dev);
+ struct clk *sys_ck;
} hwa742;
struct lcd_ctrl hwa742_ctrl;
@@ -915,14 +914,13 @@ static void hwa742_suspend(void)
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
/* Enable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1);
- if (hwa742.power_down != NULL)
- hwa742.power_down(hwa742.fbdev->dev);
+ clk_disable(hwa742.sys_ck);
}
static void hwa742_resume(void)
{
- if (hwa742.power_up != NULL)
- hwa742.power_up(hwa742.fbdev->dev);
+ clk_enable(hwa742.sys_ck);
+
/* Disable sleep mode */
hwa742_write_reg(HWA742_POWER_SAVE, 0);
while (1) {
@@ -955,14 +953,13 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
omapfb_conf = fbdev->dev->platform_data;
ctrl_conf = omapfb_conf->ctrl_platform_data;
- if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) {
+ if (ctrl_conf == NULL) {
dev_err(fbdev->dev, "HWA742: missing platform data\n");
r = -ENOENT;
goto err1;
}
- hwa742.power_down = ctrl_conf->power_down;
- hwa742.power_up = ctrl_conf->power_up;
+ hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck");
spin_lock_init(&hwa742.req_lock);
@@ -972,12 +969,11 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
if ((r = hwa742.extif->init(fbdev)) < 0)
goto err2;
- ext_clk = ctrl_conf->get_clock_rate(fbdev->dev);
+ ext_clk = clk_get_rate(hwa742.sys_ck);
if ((r = calc_extif_timings(ext_clk, &extif_mem_div)) < 0)
goto err3;
hwa742.extif->set_timings(&hwa742.reg_timings);
- if (hwa742.power_up != NULL)
- hwa742.power_up(fbdev->dev);
+ clk_enable(hwa742.sys_ck);
calc_hwa742_clk_rates(ext_clk, &sys_clk, &pix_clk);
if ((r = calc_extif_timings(sys_clk, &extif_mem_div)) < 0)
@@ -1040,8 +1036,7 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
return 0;
err4:
- if (hwa742.power_down != NULL)
- hwa742.power_down(fbdev->dev);
+ clk_disable(hwa742.sys_ck);
err3:
hwa742.extif->cleanup();
err2:
@@ -1055,8 +1050,7 @@ static void hwa742_cleanup(void)
hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
hwa742.extif->cleanup();
hwa742.int_ctrl->cleanup();
- if (hwa742.power_down != NULL)
- hwa742.power_down(hwa742.fbdev->dev);
+ clk_disable(hwa742.sys_ck);
}
struct lcd_ctrl hwa742_ctrl = {
diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c
new file mode 100644
index 00000000000..84d8327e47d
--- /dev/null
+++ b/drivers/video/pxa168fb.c
@@ -0,0 +1,803 @@
+/*
+ * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * All rights reserved.
+ *
+ * 2009-02-16 adapted from original version for PXA168/910
+ * Jun Nie <njun@marvell.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <video/pxa168fb.h>
+
+#include "pxa168fb.h"
+
+#define DEFAULT_REFRESH 60 /* Hz */
+
+static int determine_best_pix_fmt(struct fb_var_screeninfo *var)
+{
+ /*
+ * Pseudocolor mode?
+ */
+ if (var->bits_per_pixel == 8)
+ return PIX_FMT_PSEUDOCOLOR;
+
+ /*
+ * Check for 565/1555.
+ */
+ if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
+ var->green.length <= 6 && var->blue.length <= 5) {
+ if (var->transp.length == 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB565;
+ else
+ return PIX_FMT_BGR565;
+ }
+
+ if (var->transp.length == 1 && var->green.length <= 5) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB1555;
+ else
+ return PIX_FMT_BGR1555;
+ }
+
+ /* fall through */
+ }
+
+ /*
+ * Check for 888/A888.
+ */
+ if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
+ var->green.length <= 8 && var->blue.length <= 8) {
+ if (var->bits_per_pixel == 24 && var->transp.length == 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB888PACK;
+ else
+ return PIX_FMT_BGR888PACK;
+ }
+
+ if (var->bits_per_pixel == 32 && var->transp.length == 8) {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGBA888;
+ else
+ return PIX_FMT_BGRA888;
+ } else {
+ if (var->red.offset >= var->blue.offset)
+ return PIX_FMT_RGB888UNPACK;
+ else
+ return PIX_FMT_BGR888UNPACK;
+ }
+
+ /* fall through */
+ }
+
+ return -EINVAL;
+}
+
+static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
+{
+ switch (pix_fmt) {
+ case PIX_FMT_RGB565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 11; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 0; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_BGR565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 11; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_RGB1555:
+ var->bits_per_pixel = 16;
+ var->red.offset = 10; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 5;
+ var->blue.offset = 0; var->blue.length = 5;
+ var->transp.offset = 15; var->transp.length = 1;
+ break;
+ case PIX_FMT_BGR1555:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 5;
+ var->blue.offset = 10; var->blue.length = 5;
+ var->transp.offset = 15; var->transp.length = 1;
+ break;
+ case PIX_FMT_RGB888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_BGR888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIX_FMT_RGBA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIX_FMT_BGRA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIX_FMT_PSEUDOCOLOR:
+ var->bits_per_pixel = 8;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ }
+}
+
+static void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
+ struct fb_videomode *mode, int pix_fmt, int ystretch)
+{
+ struct fb_info *info = fbi->info;
+
+ set_pix_fmt(var, pix_fmt);
+
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = max(var->xres, var->xres_virtual);
+ if (ystretch)
+ var->yres_virtual = info->fix.smem_len /
+ (var->xres_virtual * (var->bits_per_pixel >> 3));
+ else
+ var->yres_virtual = max(var->yres, var->yres_virtual);
+ var->grayscale = 0;
+ var->accel_flags = FB_ACCEL_NONE;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->rotate = FB_ROTATE_UR;
+}
+
+static int pxa168fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ int pix_fmt;
+
+ /*
+ * Determine which pixel format we're going to use.
+ */
+ pix_fmt = determine_best_pix_fmt(var);
+ if (pix_fmt < 0)
+ return pix_fmt;
+ set_pix_fmt(var, pix_fmt);
+ fbi->pix_fmt = pix_fmt;
+
+ /*
+ * Basic geometry sanity checks.
+ */
+ if (var->xoffset + var->xres > var->xres_virtual)
+ return -EINVAL;
+ if (var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+ if (var->xres + var->right_margin +
+ var->hsync_len + var->left_margin > 2048)
+ return -EINVAL;
+ if (var->yres + var->lower_margin +
+ var->vsync_len + var->upper_margin > 2048)
+ return -EINVAL;
+
+ /*
+ * Check size of framebuffer.
+ */
+ if (var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3) > info->fix.smem_len)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * The hardware clock divider has an integer and a fractional
+ * stage:
+ *
+ * clk2 = clk_in / integer_divider
+ * clk_out = clk2 * (1 - (fractional_divider >> 12))
+ *
+ * Calculate integer and fractional divider for given clk_in
+ * and clk_out.
+ */
+static void set_clock_divider(struct pxa168fb_info *fbi,
+ const struct fb_videomode *m)
+{
+ int divider_int;
+ int needed_pixclk;
+ u64 div_result;
+ u32 x = 0;
+
+ /*
+ * Notice: The field pixclock is used by linux fb
+ * is in pixel second. E.g. struct fb_videomode &
+ * struct fb_var_screeninfo
+ */
+
+ /*
+ * Check input values.
+ */
+ if (!m || !m->pixclock || !m->refresh) {
+ dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
+ return;
+ }
+
+ /*
+ * Using PLL/AXI clock.
+ */
+ x = 0x80000000;
+
+ /*
+ * Calc divider according to refresh rate.
+ */
+ div_result = 1000000000000ll;
+ do_div(div_result, m->pixclock);
+ needed_pixclk = (u32)div_result;
+
+ divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
+
+ /* check whether divisor is too small. */
+ if (divider_int < 2) {
+ dev_warn(fbi->dev, "Warning: clock source is too slow."
+ "Try smaller resolution\n");
+ divider_int = 2;
+ }
+
+ /*
+ * Set setting to reg.
+ */
+ x |= divider_int;
+ writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
+}
+
+static void set_dma_control0(struct pxa168fb_info *fbi)
+{
+ u32 x;
+
+ /*
+ * Set bit to enable graphics DMA.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
+ x |= fbi->active ? 0x00000100 : 0;
+ fbi->active = 0;
+
+ /*
+ * If we are in a pseudo-color mode, we need to enable
+ * palette lookup.
+ */
+ if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
+ x |= 0x10000000;
+
+ /*
+ * Configure hardware pixel format.
+ */
+ x &= ~(0xF << 16);
+ x |= (fbi->pix_fmt >> 1) << 16;
+
+ /*
+ * Check red and blue pixel swap.
+ * 1. source data swap
+ * 2. panel output data swap
+ */
+ x &= ~(1 << 12);
+ x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
+
+ writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
+}
+
+static void set_dma_control1(struct pxa168fb_info *fbi, int sync)
+{
+ u32 x;
+
+ /*
+ * Configure default bits: vsync triggers DMA, gated clock
+ * enable, power save enable, configure alpha registers to
+ * display 100% graphics, and set pixel command.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
+ x |= 0x2032ff81;
+
+ /*
+ * We trigger DMA on the falling edge of vsync if vsync is
+ * active low, or on the rising edge if vsync is active high.
+ */
+ if (!(sync & FB_SYNC_VERT_HIGH_ACT))
+ x |= 0x08000000;
+
+ writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
+}
+
+static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int pixel_offset;
+ unsigned long addr;
+
+ pixel_offset = (yoffset * var->xres_virtual) + xoffset;
+
+ addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
+ writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
+}
+
+static void set_dumb_panel_control(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
+ u32 x;
+
+ /*
+ * Preserve enable flag.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
+
+ x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
+ x |= mi->gpio_output_data << 20;
+ x |= mi->gpio_output_mask << 12;
+ x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
+ x |= mi->invert_composite_blank ? 0x00000040 : 0;
+ x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
+ x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
+ x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
+ x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
+ x |= mi->invert_pixclock ? 0x00000002 : 0;
+
+ writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+}
+
+static void set_dumb_screen_dimensions(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *v = &info->var;
+ int x;
+ int y;
+
+ x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
+ y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
+
+ writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
+}
+
+static int pxa168fb_set_par(struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode mode;
+ u32 x;
+ struct pxa168fb_mach_info *mi;
+
+ mi = fbi->dev->platform_data;
+
+ /*
+ * Set additional mode info.
+ */
+ if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ info->fix.ypanstep = var->yres;
+
+ /*
+ * Disable panel output while we setup the display.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
+ writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+
+ /*
+ * Configure global panel parameters.
+ */
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_V_H_ACTIVE);
+
+ /*
+ * convet var to video mode
+ */
+ fb_var_to_videomode(&mode, &info->var);
+
+ /* Calculate clock divisor. */
+ set_clock_divider(fbi, &mode);
+
+ /* Configure dma ctrl regs. */
+ set_dma_control0(fbi);
+ set_dma_control1(fbi, info->var.sync);
+
+ /*
+ * Configure graphics DMA parameters.
+ */
+ x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
+ x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
+ writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
+ writel((var->yres << 16) | var->xres,
+ fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
+
+ /*
+ * Configure dumb panel ctrl regs & timings.
+ */
+ set_dumb_panel_control(info);
+ set_dumb_screen_dimensions(info);
+
+ writel((var->left_margin << 16) | var->right_margin,
+ fbi->reg_base + LCD_SPU_H_PORCH);
+ writel((var->upper_margin << 16) | var->lower_margin,
+ fbi->reg_base + LCD_SPU_V_PORCH);
+
+ /*
+ * Re-enable panel output.
+ */
+ x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
+ writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
+
+ return 0;
+}
+
+static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
+{
+ return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
+}
+
+static u32 to_rgb(u16 red, u16 green, u16 blue)
+{
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ return (red << 16) | (green << 8) | blue;
+}
+
+static int
+pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ unsigned int blue, unsigned int trans, struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+ u32 val;
+
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue , &info->var.blue);
+ fbi->pseudo_palette[regno] = val;
+ }
+
+ if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
+ val = to_rgb(red, green, blue);
+ writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
+ writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
+ }
+
+ return 0;
+}
+
+static int pxa168fb_blank(int blank, struct fb_info *info)
+{
+ struct pxa168fb_info *fbi = info->par;
+
+ fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
+ set_dumb_panel_control(info);
+
+ return 0;
+}
+
+static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ set_graphics_start(info, var->xoffset, var->yoffset);
+
+ return 0;
+}
+
+static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
+{
+ struct pxa168fb_info *fbi = dev_id;
+ u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
+
+ if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
+
+ writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
+ fbi->reg_base + SPU_IRQ_ISR);
+
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static struct fb_ops pxa168fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = pxa168fb_check_var,
+ .fb_set_par = pxa168fb_set_par,
+ .fb_setcolreg = pxa168fb_setcolreg,
+ .fb_blank = pxa168fb_blank,
+ .fb_pan_display = pxa168fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __init pxa168fb_init_mode(struct fb_info *info,
+ struct pxa168fb_mach_info *mi)
+{
+ struct pxa168fb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int ret = 0;
+ u32 total_w, total_h, refresh;
+ u64 div_result;
+ const struct fb_videomode *m;
+
+ /*
+ * Set default value
+ */
+ refresh = DEFAULT_REFRESH;
+
+ /* try to find best video mode. */
+ m = fb_find_best_mode(&info->var, &info->modelist);
+ if (m)
+ fb_videomode_to_var(&info->var, m);
+
+ /* Init settings. */
+ var->xres_virtual = var->xres;
+ var->yres_virtual = info->fix.smem_len /
+ (var->xres_virtual * (var->bits_per_pixel >> 3));
+ dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
+ var->xres, var->yres);
+
+ /* correct pixclock. */
+ total_w = var->xres + var->left_margin + var->right_margin +
+ var->hsync_len;
+ total_h = var->yres + var->upper_margin + var->lower_margin +
+ var->vsync_len;
+
+ div_result = 1000000000000ll;
+ do_div(div_result, total_w * total_h * refresh);
+ var->pixclock = (u32)div_result;
+
+ return ret;
+}
+
+static int __init pxa168fb_probe(struct platform_device *pdev)
+{
+ struct pxa168fb_mach_info *mi;
+ struct fb_info *info = 0;
+ struct pxa168fb_info *fbi = 0;
+ struct resource *res;
+ struct clk *clk;
+ int irq, ret;
+
+ mi = pdev->dev.platform_data;
+ if (mi == NULL) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ clk = clk_get(&pdev->dev, "LCDCLK");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get LCDCLK");
+ return PTR_ERR(clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no IO memory defined\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no IRQ defined\n");
+ return -ENOENT;
+ }
+
+ info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
+ if (info == NULL) {
+ clk_put(clk);
+ return -ENOMEM;
+ }
+
+ /* Initialize private data */
+ fbi = info->par;
+ fbi->info = info;
+ fbi->clk = clk;
+ fbi->dev = info->dev = &pdev->dev;
+ fbi->panel_rbswap = mi->panel_rbswap;
+ fbi->is_blanked = 0;
+ fbi->active = mi->active;
+
+ /*
+ * Initialise static fb parameters.
+ */
+ info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
+ FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
+ info->node = -1;
+ strlcpy(info->fix.id, mi->id, 16);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 0;
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+ info->fix.mmio_start = res->start;
+ info->fix.mmio_len = res->end - res->start + 1;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fbops = &pxa168fb_ops;
+ info->pseudo_palette = fbi->pseudo_palette;
+
+ /*
+ * Map LCD controller registers.
+ */
+ fbi->reg_base = ioremap_nocache(res->start, res->end - res->start);
+ if (fbi->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ /*
+ * Allocate framebuffer memory.
+ */
+ info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
+
+ info->screen_base = dma_alloc_writecombine(fbi->dev, info->fix.smem_len,
+ &fbi->fb_start_dma, GFP_KERNEL);
+ if (info->screen_base == NULL) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
+
+ /*
+ * Set video mode according to platform data.
+ */
+ set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
+
+ fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
+
+ /*
+ * init video mode data.
+ */
+ pxa168fb_init_mode(info, mi);
+
+ ret = pxa168fb_check_var(&info->var, info);
+ if (ret)
+ goto failed_free_fbmem;
+
+ /*
+ * Fill in sane defaults.
+ */
+ ret = pxa168fb_check_var(&info->var, info);
+ if (ret)
+ goto failed;
+
+ /*
+ * enable controller clock
+ */
+ clk_enable(fbi->clk);
+
+ pxa168fb_set_par(info);
+
+ /*
+ * Configure default register values.
+ */
+ writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
+ writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
+ writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
+ writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+ writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
+ writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
+ fbi->reg_base + LCD_SPU_SRAM_PARA1);
+
+ /*
+ * Allocate color map.
+ */
+ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
+ ret = -ENOMEM;
+ goto failed_free_clk;
+ }
+
+ /*
+ * Register irq handler.
+ */
+ ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED,
+ info->fix.id, fbi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to request IRQ\n");
+ ret = -ENXIO;
+ goto failed_free_cmap;
+ }
+
+ /*
+ * Enable GFX interrupt
+ */
+ writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
+
+ /*
+ * Register framebuffer.
+ */
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
+ ret = -ENXIO;
+ goto failed_free_irq;
+ }
+
+ platform_set_drvdata(pdev, fbi);
+ return 0;
+
+failed_free_irq:
+ free_irq(irq, fbi);
+failed_free_cmap:
+ fb_dealloc_cmap(&info->cmap);
+failed_free_clk:
+ clk_disable(fbi->clk);
+failed_free_fbmem:
+ dma_free_coherent(fbi->dev, info->fix.smem_len,
+ info->screen_base, fbi->fb_start_dma);
+failed:
+ kfree(info);
+ clk_put(clk);
+
+ dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
+ return ret;
+}
+
+static struct platform_driver pxa168fb_driver = {
+ .driver = {
+ .name = "pxa168-fb",
+ .owner = THIS_MODULE,
+ },
+ .probe = pxa168fb_probe,
+};
+
+static int __devinit pxa168fb_init(void)
+{
+ return platform_driver_register(&pxa168fb_driver);
+}
+module_init(pxa168fb_init);
+
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
+ "Green Wan <gwan@marvell.com>");
+MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/pxa168fb.h b/drivers/video/pxa168fb.h
new file mode 100644
index 00000000000..eee09279c52
--- /dev/null
+++ b/drivers/video/pxa168fb.h
@@ -0,0 +1,558 @@
+#ifndef __PXA168FB_H__
+#define __PXA168FB_H__
+
+/* ------------< LCD register >------------ */
+/* Video Frame 0&1 start address registers */
+#define LCD_SPU_DMA_START_ADDR_Y0 0x00C0
+#define LCD_SPU_DMA_START_ADDR_U0 0x00C4
+#define LCD_SPU_DMA_START_ADDR_V0 0x00C8
+#define LCD_CFG_DMA_START_ADDR_0 0x00CC /* Cmd address */
+#define LCD_SPU_DMA_START_ADDR_Y1 0x00D0
+#define LCD_SPU_DMA_START_ADDR_U1 0x00D4
+#define LCD_SPU_DMA_START_ADDR_V1 0x00D8
+#define LCD_CFG_DMA_START_ADDR_1 0x00DC /* Cmd address */
+
+/* YC & UV Pitch */
+#define LCD_SPU_DMA_PITCH_YC 0x00E0
+#define SPU_DMA_PITCH_C(c) ((c) << 16)
+#define SPU_DMA_PITCH_Y(y) (y)
+#define LCD_SPU_DMA_PITCH_UV 0x00E4
+#define SPU_DMA_PITCH_V(v) ((v) << 16)
+#define SPU_DMA_PITCH_U(u) (u)
+
+/* Video Starting Point on Screen Register */
+#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00E8
+#define CFG_DMA_OVSA_VLN(y) ((y) << 16) /* 0~0xfff */
+#define CFG_DMA_OVSA_HPXL(x) (x) /* 0~0xfff */
+
+/* Video Size Register */
+#define LCD_SPU_DMA_HPXL_VLN 0x00EC
+#define CFG_DMA_VLN(y) ((y) << 16)
+#define CFG_DMA_HPXL(x) (x)
+
+/* Video Size After zooming Register */
+#define LCD_SPU_DZM_HPXL_VLN 0x00F0
+#define CFG_DZM_VLN(y) ((y) << 16)
+#define CFG_DZM_HPXL(x) (x)
+
+/* Graphic Frame 0&1 Starting Address Register */
+#define LCD_CFG_GRA_START_ADDR0 0x00F4
+#define LCD_CFG_GRA_START_ADDR1 0x00F8
+
+/* Graphic Frame Pitch */
+#define LCD_CFG_GRA_PITCH 0x00FC
+
+/* Graphic Starting Point on Screen Register */
+#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100
+#define CFG_GRA_OVSA_VLN(y) ((y) << 16)
+#define CFG_GRA_OVSA_HPXL(x) (x)
+
+/* Graphic Size Register */
+#define LCD_SPU_GRA_HPXL_VLN 0x0104
+#define CFG_GRA_VLN(y) ((y) << 16)
+#define CFG_GRA_HPXL(x) (x)
+
+/* Graphic Size after Zooming Register */
+#define LCD_SPU_GZM_HPXL_VLN 0x0108
+#define CFG_GZM_VLN(y) ((y) << 16)
+#define CFG_GZM_HPXL(x) (x)
+
+/* HW Cursor Starting Point on Screen Register */
+#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010C
+#define CFG_HWC_OVSA_VLN(y) ((y) << 16)
+#define CFG_HWC_OVSA_HPXL(x) (x)
+
+/* HW Cursor Size */
+#define LCD_SPU_HWC_HPXL_VLN 0x0110
+#define CFG_HWC_VLN(y) ((y) << 16)
+#define CFG_HWC_HPXL(x) (x)
+
+/* Total Screen Size Register */
+#define LCD_SPUT_V_H_TOTAL 0x0114
+#define CFG_V_TOTAL(y) ((y) << 16)
+#define CFG_H_TOTAL(x) (x)
+
+/* Total Screen Active Size Register */
+#define LCD_SPU_V_H_ACTIVE 0x0118
+#define CFG_V_ACTIVE(y) ((y) << 16)
+#define CFG_H_ACTIVE(x) (x)
+
+/* Screen H&V Porch Register */
+#define LCD_SPU_H_PORCH 0x011C
+#define CFG_H_BACK_PORCH(b) ((b) << 16)
+#define CFG_H_FRONT_PORCH(f) (f)
+#define LCD_SPU_V_PORCH 0x0120
+#define CFG_V_BACK_PORCH(b) ((b) << 16)
+#define CFG_V_FRONT_PORCH(f) (f)
+
+/* Screen Blank Color Register */
+#define LCD_SPU_BLANKCOLOR 0x0124
+#define CFG_BLANKCOLOR_MASK 0x00FFFFFF
+#define CFG_BLANKCOLOR_R_MASK 0x000000FF
+#define CFG_BLANKCOLOR_G_MASK 0x0000FF00
+#define CFG_BLANKCOLOR_B_MASK 0x00FF0000
+
+/* HW Cursor Color 1&2 Register */
+#define LCD_SPU_ALPHA_COLOR1 0x0128
+#define CFG_HWC_COLOR1 0x00FFFFFF
+#define CFG_HWC_COLOR1_R(red) ((red) << 16)
+#define CFG_HWC_COLOR1_G(green) ((green) << 8)
+#define CFG_HWC_COLOR1_B(blue) (blue)
+#define CFG_HWC_COLOR1_R_MASK 0x000000FF
+#define CFG_HWC_COLOR1_G_MASK 0x0000FF00
+#define CFG_HWC_COLOR1_B_MASK 0x00FF0000
+#define LCD_SPU_ALPHA_COLOR2 0x012C
+#define CFG_HWC_COLOR2 0x00FFFFFF
+#define CFG_HWC_COLOR2_R_MASK 0x000000FF
+#define CFG_HWC_COLOR2_G_MASK 0x0000FF00
+#define CFG_HWC_COLOR2_B_MASK 0x00FF0000
+
+/* Video YUV Color Key Control */
+#define LCD_SPU_COLORKEY_Y 0x0130
+#define CFG_CKEY_Y2(y2) ((y2) << 24)
+#define CFG_CKEY_Y2_MASK 0xFF000000
+#define CFG_CKEY_Y1(y1) ((y1) << 16)
+#define CFG_CKEY_Y1_MASK 0x00FF0000
+#define CFG_CKEY_Y(y) ((y) << 8)
+#define CFG_CKEY_Y_MASK 0x0000FF00
+#define CFG_ALPHA_Y(y) (y)
+#define CFG_ALPHA_Y_MASK 0x000000FF
+#define LCD_SPU_COLORKEY_U 0x0134
+#define CFG_CKEY_U2(u2) ((u2) << 24)
+#define CFG_CKEY_U2_MASK 0xFF000000
+#define CFG_CKEY_U1(u1) ((u1) << 16)
+#define CFG_CKEY_U1_MASK 0x00FF0000
+#define CFG_CKEY_U(u) ((u) << 8)
+#define CFG_CKEY_U_MASK 0x0000FF00
+#define CFG_ALPHA_U(u) (u)
+#define CFG_ALPHA_U_MASK 0x000000FF
+#define LCD_SPU_COLORKEY_V 0x0138
+#define CFG_CKEY_V2(v2) ((v2) << 24)
+#define CFG_CKEY_V2_MASK 0xFF000000
+#define CFG_CKEY_V1(v1) ((v1) << 16)
+#define CFG_CKEY_V1_MASK 0x00FF0000
+#define CFG_CKEY_V(v) ((v) << 8)
+#define CFG_CKEY_V_MASK 0x0000FF00
+#define CFG_ALPHA_V(v) (v)
+#define CFG_ALPHA_V_MASK 0x000000FF
+
+/* SPI Read Data Register */
+#define LCD_SPU_SPI_RXDATA 0x0140
+
+/* Smart Panel Read Data Register */
+#define LCD_SPU_ISA_RSDATA 0x0144
+#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000FF
+#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000FF00
+#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00FF0000
+#define ISA_RXDATA_16BIT_4_DATA_MASK 0xFF000000
+#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00FFFFFF
+
+/* HWC SRAM Read Data Register */
+#define LCD_SPU_HWC_RDDAT 0x0158
+
+/* Gamma Table SRAM Read Data Register */
+#define LCD_SPU_GAMMA_RDDAT 0x015c
+#define CFG_GAMMA_RDDAT_MASK 0x000000FF
+
+/* Palette Table SRAM Read Data Register */
+#define LCD_SPU_PALETTE_RDDAT 0x0160
+#define CFG_PALETTE_RDDAT_MASK 0x00FFFFFF
+
+/* I/O Pads Input Read Only Register */
+#define LCD_SPU_IOPAD_IN 0x0178
+#define CFG_IOPAD_IN_MASK 0x0FFFFFFF
+
+/* Reserved Read Only Registers */
+#define LCD_CFG_RDREG5F 0x017C
+#define IRE_FRAME_CNT_MASK 0x000000C0
+#define IPE_FRAME_CNT_MASK 0x00000030
+#define GRA_FRAME_CNT_MASK 0x0000000C /* Graphic */
+#define DMA_FRAME_CNT_MASK 0x00000003 /* Video */
+
+/* SPI Control Register. */
+#define LCD_SPU_SPI_CTRL 0x0180
+#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */
+#define CFG_SCLKCNT_MASK 0xFF000000
+#define CFG_RXBITS(rx) ((rx) << 16) /* 0x1F~0x1 */
+#define CFG_RXBITS_MASK 0x00FF0000
+#define CFG_TXBITS(tx) ((tx) << 8) /* 0x1F~0x1 */
+#define CFG_TXBITS_MASK 0x0000FF00
+#define CFG_CLKINV(clk) ((clk) << 7)
+#define CFG_CLKINV_MASK 0x00000080
+#define CFG_KEEPXFER(transfer) ((transfer) << 6)
+#define CFG_KEEPXFER_MASK 0x00000040
+#define CFG_RXBITSTO0(rx) ((rx) << 5)
+#define CFG_RXBITSTO0_MASK 0x00000020
+#define CFG_TXBITSTO0(tx) ((tx) << 4)
+#define CFG_TXBITSTO0_MASK 0x00000010
+#define CFG_SPI_ENA(spi) ((spi) << 3)
+#define CFG_SPI_ENA_MASK 0x00000008
+#define CFG_SPI_SEL(spi) ((spi) << 2)
+#define CFG_SPI_SEL_MASK 0x00000004
+#define CFG_SPI_3W4WB(wire) ((wire) << 1)
+#define CFG_SPI_3W4WB_MASK 0x00000002
+#define CFG_SPI_START(start) (start)
+#define CFG_SPI_START_MASK 0x00000001
+
+/* SPI Tx Data Register */
+#define LCD_SPU_SPI_TXDATA 0x0184
+
+/*
+ 1. Smart Pannel 8-bit Bus Control Register.
+ 2. AHB Slave Path Data Port Register
+*/
+#define LCD_SPU_SMPN_CTRL 0x0188
+
+/* DMA Control 0 Register */
+#define LCD_SPU_DMA_CTRL0 0x0190
+#define CFG_NOBLENDING(nb) ((nb) << 31)
+#define CFG_NOBLENDING_MASK 0x80000000
+#define CFG_GAMMA_ENA(gn) ((gn) << 30)
+#define CFG_GAMMA_ENA_MASK 0x40000000
+#define CFG_CBSH_ENA(cn) ((cn) << 29)
+#define CFG_CBSH_ENA_MASK 0x20000000
+#define CFG_PALETTE_ENA(pn) ((pn) << 28)
+#define CFG_PALETTE_ENA_MASK 0x10000000
+#define CFG_ARBFAST_ENA(an) ((an) << 27)
+#define CFG_ARBFAST_ENA_MASK 0x08000000
+#define CFG_HWC_1BITMOD(mode) ((mode) << 26)
+#define CFG_HWC_1BITMOD_MASK 0x04000000
+#define CFG_HWC_1BITENA(mn) ((mn) << 25)
+#define CFG_HWC_1BITENA_MASK 0x02000000
+#define CFG_HWC_ENA(cn) ((cn) << 24)
+#define CFG_HWC_ENA_MASK 0x01000000
+#define CFG_DMAFORMAT(dmaformat) ((dmaformat) << 20)
+#define CFG_DMAFORMAT_MASK 0x00F00000
+#define CFG_GRAFORMAT(graformat) ((graformat) << 16)
+#define CFG_GRAFORMAT_MASK 0x000F0000
+/* for graphic part */
+#define CFG_GRA_FTOGGLE(toggle) ((toggle) << 15)
+#define CFG_GRA_FTOGGLE_MASK 0x00008000
+#define CFG_GRA_HSMOOTH(smooth) ((smooth) << 14)
+#define CFG_GRA_HSMOOTH_MASK 0x00004000
+#define CFG_GRA_TSTMODE(test) ((test) << 13)
+#define CFG_GRA_TSTMODE_MASK 0x00002000
+#define CFG_GRA_SWAPRB(swap) ((swap) << 12)
+#define CFG_GRA_SWAPRB_MASK 0x00001000
+#define CFG_GRA_SWAPUV(swap) ((swap) << 11)
+#define CFG_GRA_SWAPUV_MASK 0x00000800
+#define CFG_GRA_SWAPYU(swap) ((swap) << 10)
+#define CFG_GRA_SWAPYU_MASK 0x00000400
+#define CFG_YUV2RGB_GRA(cvrt) ((cvrt) << 9)
+#define CFG_YUV2RGB_GRA_MASK 0x00000200
+#define CFG_GRA_ENA(gra) ((gra) << 8)
+#define CFG_GRA_ENA_MASK 0x00000100
+/* for video part */
+#define CFG_DMA_FTOGGLE(toggle) ((toggle) << 7)
+#define CFG_DMA_FTOGGLE_MASK 0x00000080
+#define CFG_DMA_HSMOOTH(smooth) ((smooth) << 6)
+#define CFG_DMA_HSMOOTH_MASK 0x00000040
+#define CFG_DMA_TSTMODE(test) ((test) << 5)
+#define CFG_DMA_TSTMODE_MASK 0x00000020
+#define CFG_DMA_SWAPRB(swap) ((swap) << 4)
+#define CFG_DMA_SWAPRB_MASK 0x00000010
+#define CFG_DMA_SWAPUV(swap) ((swap) << 3)
+#define CFG_DMA_SWAPUV_MASK 0x00000008
+#define CFG_DMA_SWAPYU(swap) ((swap) << 2)
+#define CFG_DMA_SWAPYU_MASK 0x00000004
+#define CFG_DMA_SWAP_MASK 0x0000001C
+#define CFG_YUV2RGB_DMA(cvrt) ((cvrt) << 1)
+#define CFG_YUV2RGB_DMA_MASK 0x00000002
+#define CFG_DMA_ENA(video) (video)
+#define CFG_DMA_ENA_MASK 0x00000001
+
+/* DMA Control 1 Register */
+#define LCD_SPU_DMA_CTRL1 0x0194
+#define CFG_FRAME_TRIG(trig) ((trig) << 31)
+#define CFG_FRAME_TRIG_MASK 0x80000000
+#define CFG_VSYNC_TRIG(trig) ((trig) << 28)
+#define CFG_VSYNC_TRIG_MASK 0x70000000
+#define CFG_VSYNC_INV(inv) ((inv) << 27)
+#define CFG_VSYNC_INV_MASK 0x08000000
+#define CFG_COLOR_KEY_MODE(cmode) ((cmode) << 24)
+#define CFG_COLOR_KEY_MASK 0x07000000
+#define CFG_CARRY(carry) ((carry) << 23)
+#define CFG_CARRY_MASK 0x00800000
+#define CFG_LNBUF_ENA(lnbuf) ((lnbuf) << 22)
+#define CFG_LNBUF_ENA_MASK 0x00400000
+#define CFG_GATED_ENA(gated) ((gated) << 21)
+#define CFG_GATED_ENA_MASK 0x00200000
+#define CFG_PWRDN_ENA(power) ((power) << 20)
+#define CFG_PWRDN_ENA_MASK 0x00100000
+#define CFG_DSCALE(dscale) ((dscale) << 18)
+#define CFG_DSCALE_MASK 0x000C0000
+#define CFG_ALPHA_MODE(amode) ((amode) << 16)
+#define CFG_ALPHA_MODE_MASK 0x00030000
+#define CFG_ALPHA(alpha) ((alpha) << 8)
+#define CFG_ALPHA_MASK 0x0000FF00
+#define CFG_PXLCMD(pxlcmd) (pxlcmd)
+#define CFG_PXLCMD_MASK 0x000000FF
+
+/* SRAM Control Register */
+#define LCD_SPU_SRAM_CTRL 0x0198
+#define CFG_SRAM_INIT_WR_RD(mode) ((mode) << 14)
+#define CFG_SRAM_INIT_WR_RD_MASK 0x0000C000
+#define CFG_SRAM_ADDR_LCDID(id) ((id) << 8)
+#define CFG_SRAM_ADDR_LCDID_MASK 0x00000F00
+#define CFG_SRAM_ADDR(addr) (addr)
+#define CFG_SRAM_ADDR_MASK 0x000000FF
+
+/* SRAM Write Data Register */
+#define LCD_SPU_SRAM_WRDAT 0x019C
+
+/* SRAM RTC/WTC Control Register */
+#define LCD_SPU_SRAM_PARA0 0x01A0
+
+/* SRAM Power Down Control Register */
+#define LCD_SPU_SRAM_PARA1 0x01A4
+#define CFG_CSB_256x32(hwc) ((hwc) << 15) /* HWC */
+#define CFG_CSB_256x32_MASK 0x00008000
+#define CFG_CSB_256x24(palette) ((palette) << 14) /* Palette */
+#define CFG_CSB_256x24_MASK 0x00004000
+#define CFG_CSB_256x8(gamma) ((gamma) << 13) /* Gamma */
+#define CFG_CSB_256x8_MASK 0x00002000
+#define CFG_PDWN256x32(pdwn) ((pdwn) << 7) /* HWC */
+#define CFG_PDWN256x32_MASK 0x00000080
+#define CFG_PDWN256x24(pdwn) ((pdwn) << 6) /* Palette */
+#define CFG_PDWN256x24_MASK 0x00000040
+#define CFG_PDWN256x8(pdwn) ((pdwn) << 5) /* Gamma */
+#define CFG_PDWN256x8_MASK 0x00000020
+#define CFG_PDWN32x32(pdwn) ((pdwn) << 3)
+#define CFG_PDWN32x32_MASK 0x00000008
+#define CFG_PDWN16x66(pdwn) ((pdwn) << 2)
+#define CFG_PDWN16x66_MASK 0x00000004
+#define CFG_PDWN32x66(pdwn) ((pdwn) << 1)
+#define CFG_PDWN32x66_MASK 0x00000002
+#define CFG_PDWN64x66(pdwn) (pdwn)
+#define CFG_PDWN64x66_MASK 0x00000001
+
+/* Smart or Dumb Panel Clock Divider */
+#define LCD_CFG_SCLK_DIV 0x01A8
+#define SCLK_SOURCE_SELECT(src) ((src) << 31)
+#define SCLK_SOURCE_SELECT_MASK 0x80000000
+#define CLK_FRACDIV(frac) ((frac) << 16)
+#define CLK_FRACDIV_MASK 0x0FFF0000
+#define CLK_INT_DIV(div) (div)
+#define CLK_INT_DIV_MASK 0x0000FFFF
+
+/* Video Contrast Register */
+#define LCD_SPU_CONTRAST 0x01AC
+#define CFG_BRIGHTNESS(bright) ((bright) << 16)
+#define CFG_BRIGHTNESS_MASK 0xFFFF0000
+#define CFG_CONTRAST(contrast) (contrast)
+#define CFG_CONTRAST_MASK 0x0000FFFF
+
+/* Video Saturation Register */
+#define LCD_SPU_SATURATION 0x01B0
+#define CFG_C_MULTS(mult) ((mult) << 16)
+#define CFG_C_MULTS_MASK 0xFFFF0000
+#define CFG_SATURATION(sat) (sat)
+#define CFG_SATURATION_MASK 0x0000FFFF
+
+/* Video Hue Adjust Register */
+#define LCD_SPU_CBSH_HUE 0x01B4
+#define CFG_SIN0(sin0) ((sin0) << 16)
+#define CFG_SIN0_MASK 0xFFFF0000
+#define CFG_COS0(con0) (con0)
+#define CFG_COS0_MASK 0x0000FFFF
+
+/* Dump LCD Panel Control Register */
+#define LCD_SPU_DUMB_CTRL 0x01B8
+#define CFG_DUMBMODE(mode) ((mode) << 28)
+#define CFG_DUMBMODE_MASK 0xF0000000
+#define CFG_LCDGPIO_O(data) ((data) << 20)
+#define CFG_LCDGPIO_O_MASK 0x0FF00000
+#define CFG_LCDGPIO_ENA(gpio) ((gpio) << 12)
+#define CFG_LCDGPIO_ENA_MASK 0x000FF000
+#define CFG_BIAS_OUT(bias) ((bias) << 8)
+#define CFG_BIAS_OUT_MASK 0x00000100
+#define CFG_REVERSE_RGB(rRGB) ((rRGB) << 7)
+#define CFG_REVERSE_RGB_MASK 0x00000080
+#define CFG_INV_COMPBLANK(blank) ((blank) << 6)
+#define CFG_INV_COMPBLANK_MASK 0x00000040
+#define CFG_INV_COMPSYNC(sync) ((sync) << 5)
+#define CFG_INV_COMPSYNC_MASK 0x00000020
+#define CFG_INV_HENA(hena) ((hena) << 4)
+#define CFG_INV_HENA_MASK 0x00000010
+#define CFG_INV_VSYNC(vsync) ((vsync) << 3)
+#define CFG_INV_VSYNC_MASK 0x00000008
+#define CFG_INV_HSYNC(hsync) ((hsync) << 2)
+#define CFG_INV_HSYNC_MASK 0x00000004
+#define CFG_INV_PCLK(pclk) ((pclk) << 1)
+#define CFG_INV_PCLK_MASK 0x00000002
+#define CFG_DUMB_ENA(dumb) (dumb)
+#define CFG_DUMB_ENA_MASK 0x00000001
+
+/* LCD I/O Pads Control Register */
+#define SPU_IOPAD_CONTROL 0x01BC
+#define CFG_GRA_VM_ENA(vm) ((vm) << 15) /* gfx */
+#define CFG_GRA_VM_ENA_MASK 0x00008000
+#define CFG_DMA_VM_ENA(vm) ((vm) << 13) /* video */
+#define CFG_DMA_VM_ENA_MASK 0x00002000
+#define CFG_CMD_VM_ENA(vm) ((vm) << 13)
+#define CFG_CMD_VM_ENA_MASK 0x00000800
+#define CFG_CSC(csc) ((csc) << 8) /* csc */
+#define CFG_CSC_MASK 0x00000300
+#define CFG_AXICTRL(axi) ((axi) << 4)
+#define CFG_AXICTRL_MASK 0x000000F0
+#define CFG_IOPADMODE(iopad) (iopad)
+#define CFG_IOPADMODE_MASK 0x0000000F
+
+/* LCD Interrupt Control Register */
+#define SPU_IRQ_ENA 0x01C0
+#define DMA_FRAME_IRQ0_ENA(irq) ((irq) << 31)
+#define DMA_FRAME_IRQ0_ENA_MASK 0x80000000
+#define DMA_FRAME_IRQ1_ENA(irq) ((irq) << 30)
+#define DMA_FRAME_IRQ1_ENA_MASK 0x40000000
+#define DMA_FF_UNDERFLOW_ENA(ff) ((ff) << 29)
+#define DMA_FF_UNDERFLOW_ENA_MASK 0x20000000
+#define GRA_FRAME_IRQ0_ENA(irq) ((irq) << 27)
+#define GRA_FRAME_IRQ0_ENA_MASK 0x08000000
+#define GRA_FRAME_IRQ1_ENA(irq) ((irq) << 26)
+#define GRA_FRAME_IRQ1_ENA_MASK 0x04000000
+#define GRA_FF_UNDERFLOW_ENA(ff) ((ff) << 25)
+#define GRA_FF_UNDERFLOW_ENA_MASK 0x02000000
+#define VSYNC_IRQ_ENA(vsync_irq) ((vsync_irq) << 23)
+#define VSYNC_IRQ_ENA_MASK 0x00800000
+#define DUMB_FRAMEDONE_ENA(fdone) ((fdone) << 22)
+#define DUMB_FRAMEDONE_ENA_MASK 0x00400000
+#define TWC_FRAMEDONE_ENA(fdone) ((fdone) << 21)
+#define TWC_FRAMEDONE_ENA_MASK 0x00200000
+#define HWC_FRAMEDONE_ENA(fdone) ((fdone) << 20)
+#define HWC_FRAMEDONE_ENA_MASK 0x00100000
+#define SLV_IRQ_ENA(irq) ((irq) << 19)
+#define SLV_IRQ_ENA_MASK 0x00080000
+#define SPI_IRQ_ENA(irq) ((irq) << 18)
+#define SPI_IRQ_ENA_MASK 0x00040000
+#define PWRDN_IRQ_ENA(irq) ((irq) << 17)
+#define PWRDN_IRQ_ENA_MASK 0x00020000
+#define ERR_IRQ_ENA(irq) ((irq) << 16)
+#define ERR_IRQ_ENA_MASK 0x00010000
+#define CLEAN_SPU_IRQ_ISR(irq) (irq)
+#define CLEAN_SPU_IRQ_ISR_MASK 0x0000FFFF
+
+/* LCD Interrupt Status Register */
+#define SPU_IRQ_ISR 0x01C4
+#define DMA_FRAME_IRQ0(irq) ((irq) << 31)
+#define DMA_FRAME_IRQ0_MASK 0x80000000
+#define DMA_FRAME_IRQ1(irq) ((irq) << 30)
+#define DMA_FRAME_IRQ1_MASK 0x40000000
+#define DMA_FF_UNDERFLOW(ff) ((ff) << 29)
+#define DMA_FF_UNDERFLOW_MASK 0x20000000
+#define GRA_FRAME_IRQ0(irq) ((irq) << 27)
+#define GRA_FRAME_IRQ0_MASK 0x08000000
+#define GRA_FRAME_IRQ1(irq) ((irq) << 26)
+#define GRA_FRAME_IRQ1_MASK 0x04000000
+#define GRA_FF_UNDERFLOW(ff) ((ff) << 25)
+#define GRA_FF_UNDERFLOW_MASK 0x02000000
+#define VSYNC_IRQ(vsync_irq) ((vsync_irq) << 23)
+#define VSYNC_IRQ_MASK 0x00800000
+#define DUMB_FRAMEDONE(fdone) ((fdone) << 22)
+#define DUMB_FRAMEDONE_MASK 0x00400000
+#define TWC_FRAMEDONE(fdone) ((fdone) << 21)
+#define TWC_FRAMEDONE_MASK 0x00200000
+#define HWC_FRAMEDONE(fdone) ((fdone) << 20)
+#define HWC_FRAMEDONE_MASK 0x00100000
+#define SLV_IRQ(irq) ((irq) << 19)
+#define SLV_IRQ_MASK 0x00080000
+#define SPI_IRQ(irq) ((irq) << 18)
+#define SPI_IRQ_MASK 0x00040000
+#define PWRDN_IRQ(irq) ((irq) << 17)
+#define PWRDN_IRQ_MASK 0x00020000
+#define ERR_IRQ(irq) ((irq) << 16)
+#define ERR_IRQ_MASK 0x00010000
+/* read-only */
+#define DMA_FRAME_IRQ0_LEVEL_MASK 0x00008000
+#define DMA_FRAME_IRQ1_LEVEL_MASK 0x00004000
+#define DMA_FRAME_CNT_ISR_MASK 0x00003000
+#define GRA_FRAME_IRQ0_LEVEL_MASK 0x00000800
+#define GRA_FRAME_IRQ1_LEVEL_MASK 0x00000400
+#define GRA_FRAME_CNT_ISR_MASK 0x00000300
+#define VSYNC_IRQ_LEVEL_MASK 0x00000080
+#define DUMB_FRAMEDONE_LEVEL_MASK 0x00000040
+#define TWC_FRAMEDONE_LEVEL_MASK 0x00000020
+#define HWC_FRAMEDONE_LEVEL_MASK 0x00000010
+#define SLV_FF_EMPTY_MASK 0x00000008
+#define DMA_FF_ALLEMPTY_MASK 0x00000004
+#define GRA_FF_ALLEMPTY_MASK 0x00000002
+#define PWRDN_IRQ_LEVEL_MASK 0x00000001
+
+
+/*
+ * defined Video Memory Color format for DMA control 0 register
+ * DMA0 bit[23:20]
+ */
+#define VMODE_RGB565 0x0
+#define VMODE_RGB1555 0x1
+#define VMODE_RGB888PACKED 0x2
+#define VMODE_RGB888UNPACKED 0x3
+#define VMODE_RGBA888 0x4
+#define VMODE_YUV422PACKED 0x5
+#define VMODE_YUV422PLANAR 0x6
+#define VMODE_YUV420PLANAR 0x7
+#define VMODE_SMPNCMD 0x8
+#define VMODE_PALETTE4BIT 0x9
+#define VMODE_PALETTE8BIT 0xa
+#define VMODE_RESERVED 0xb
+
+/*
+ * defined Graphic Memory Color format for DMA control 0 register
+ * DMA0 bit[19:16]
+ */
+#define GMODE_RGB565 0x0
+#define GMODE_RGB1555 0x1
+#define GMODE_RGB888PACKED 0x2
+#define GMODE_RGB888UNPACKED 0x3
+#define GMODE_RGBA888 0x4
+#define GMODE_YUV422PACKED 0x5
+#define GMODE_YUV422PLANAR 0x6
+#define GMODE_YUV420PLANAR 0x7
+#define GMODE_SMPNCMD 0x8
+#define GMODE_PALETTE4BIT 0x9
+#define GMODE_PALETTE8BIT 0xa
+#define GMODE_RESERVED 0xb
+
+/*
+ * define for DMA control 1 register
+ */
+#define DMA1_FRAME_TRIG 31 /* bit location */
+#define DMA1_VSYNC_MODE 28
+#define DMA1_VSYNC_INV 27
+#define DMA1_CKEY 24
+#define DMA1_CARRY 23
+#define DMA1_LNBUF_ENA 22
+#define DMA1_GATED_ENA 21
+#define DMA1_PWRDN_ENA 20
+#define DMA1_DSCALE 18
+#define DMA1_ALPHA_MODE 16
+#define DMA1_ALPHA 08
+#define DMA1_PXLCMD 00
+
+/*
+ * defined for Configure Dumb Mode
+ * DUMB LCD Panel bit[31:28]
+ */
+#define DUMB16_RGB565_0 0x0
+#define DUMB16_RGB565_1 0x1
+#define DUMB18_RGB666_0 0x2
+#define DUMB18_RGB666_1 0x3
+#define DUMB12_RGB444_0 0x4
+#define DUMB12_RGB444_1 0x5
+#define DUMB24_RGB888_0 0x6
+#define DUMB_BLANK 0x7
+
+/*
+ * defined for Configure I/O Pin Allocation Mode
+ * LCD LCD I/O Pads control register bit[3:0]
+ */
+#define IOPAD_DUMB24 0x0
+#define IOPAD_DUMB18SPI 0x1
+#define IOPAD_DUMB18GPIO 0x2
+#define IOPAD_DUMB16SPI 0x3
+#define IOPAD_DUMB16GPIO 0x4
+#define IOPAD_DUMB12 0x5
+#define IOPAD_SMART18SPI 0x6
+#define IOPAD_SMART16SPI 0x7
+#define IOPAD_SMART8BOTH 0x8
+
+#endif /* __PXA168FB_H__ */
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5eb8f21da82..5744cac4864 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -231,14 +231,14 @@ config DAVINCI_WATCHDOG
NOTE: once enabled, this timer cannot be disabled.
Say N if you are unsure.
-config ORION5X_WATCHDOG
- tristate "Orion5x watchdog"
- depends on ARCH_ORION5X
+config ORION_WATCHDOG
+ tristate "Orion watchdog"
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD
help
Say Y here if to include support for the watchdog timer
- in the Orion5x ARM SoCs.
+ in the Marvell Orion5x and Kirkwood ARM SoCs.
To compile this driver as a module, choose M here: the
- module will be called orion5x_wdt.
+ module will be called orion_wdt.
# AVR32 Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 7f8c56b14f5..c3afa14d5be 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -40,7 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
-obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
+obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion_wdt.c
index 2cde568e4fb..2d9fb96a9ee 100644
--- a/drivers/watchdog/orion5x_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -1,7 +1,7 @@
/*
- * drivers/watchdog/orion5x_wdt.c
+ * drivers/watchdog/orion_wdt.c
*
- * Watchdog driver for Orion5x processors
+ * Watchdog driver for Orion/Kirkwood processors
*
* Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
*
@@ -23,7 +23,7 @@
#include <linux/io.h>
#include <linux/spinlock.h>
#include <mach/bridge-regs.h>
-#include <plat/orion5x_wdt.h>
+#include <plat/orion_wdt.h>
/*
* Watchdog timer block registers.
@@ -43,7 +43,7 @@ static unsigned int wdt_tclk;
static unsigned long wdt_status;
static spinlock_t wdt_lock;
-static void orion5x_wdt_ping(void)
+static void orion_wdt_ping(void)
{
spin_lock(&wdt_lock);
@@ -53,7 +53,7 @@ static void orion5x_wdt_ping(void)
spin_unlock(&wdt_lock);
}
-static void orion5x_wdt_enable(void)
+static void orion_wdt_enable(void)
{
u32 reg;
@@ -73,23 +73,23 @@ static void orion5x_wdt_enable(void)
writel(reg, TIMER_CTRL);
/* Enable reset on watchdog */
- reg = readl(CPU_RESET_MASK);
- reg |= WDT_RESET;
- writel(reg, CPU_RESET_MASK);
+ reg = readl(RSTOUTn_MASK);
+ reg |= WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
spin_unlock(&wdt_lock);
}
-static void orion5x_wdt_disable(void)
+static void orion_wdt_disable(void)
{
u32 reg;
spin_lock(&wdt_lock);
/* Disable reset on watchdog */
- reg = readl(CPU_RESET_MASK);
- reg &= ~WDT_RESET;
- writel(reg, CPU_RESET_MASK);
+ reg = readl(RSTOUTn_MASK);
+ reg &= ~WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
/* Disable watchdog timer */
reg = readl(TIMER_CTRL);
@@ -99,7 +99,7 @@ static void orion5x_wdt_disable(void)
spin_unlock(&wdt_lock);
}
-static int orion5x_wdt_get_timeleft(int *time_left)
+static int orion_wdt_get_timeleft(int *time_left)
{
spin_lock(&wdt_lock);
*time_left = readl(WDT_VAL) / wdt_tclk;
@@ -107,16 +107,16 @@ static int orion5x_wdt_get_timeleft(int *time_left)
return 0;
}
-static int orion5x_wdt_open(struct inode *inode, struct file *file)
+static int orion_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- orion5x_wdt_enable();
+ orion_wdt_enable();
return nonseekable_open(inode, file);
}
-static ssize_t orion5x_wdt_write(struct file *file, const char *data,
+static ssize_t orion_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
if (len) {
@@ -133,18 +133,18 @@ static ssize_t orion5x_wdt_write(struct file *file, const char *data,
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
- orion5x_wdt_ping();
+ orion_wdt_ping();
}
return len;
}
-static int orion5x_wdt_settimeout(int new_time)
+static int orion_wdt_settimeout(int new_time)
{
if ((new_time <= 0) || (new_time > wdt_max_duration))
return -EINVAL;
/* Set new watchdog time to be used when
- * orion5x_wdt_enable() or orion5x_wdt_ping() is called. */
+ * orion_wdt_enable() or orion_wdt_ping() is called. */
heartbeat = new_time;
return 0;
}
@@ -152,10 +152,10 @@ static int orion5x_wdt_settimeout(int new_time)
static const struct watchdog_info ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
- .identity = "Orion5x Watchdog",
+ .identity = "Orion Watchdog",
};
-static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
+static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = -ENOTTY;
@@ -173,7 +173,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_KEEPALIVE:
- orion5x_wdt_ping();
+ orion_wdt_ping();
ret = 0;
break;
@@ -182,11 +182,11 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
if (ret)
break;
- if (orion5x_wdt_settimeout(time)) {
+ if (orion_wdt_settimeout(time)) {
ret = -EINVAL;
break;
}
- orion5x_wdt_ping();
+ orion_wdt_ping();
/* Fall through */
case WDIOC_GETTIMEOUT:
@@ -194,7 +194,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_GETTIMELEFT:
- if (orion5x_wdt_get_timeleft(&time)) {
+ if (orion_wdt_get_timeleft(&time)) {
ret = -EINVAL;
break;
}
@@ -204,10 +204,10 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
return ret;
}
-static int orion5x_wdt_release(struct inode *inode, struct file *file)
+static int orion_wdt_release(struct inode *inode, struct file *file)
{
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- orion5x_wdt_disable();
+ orion_wdt_disable();
else
printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
"timer will not stop\n");
@@ -218,98 +218,98 @@ static int orion5x_wdt_release(struct inode *inode, struct file *file)
}
-static const struct file_operations orion5x_wdt_fops = {
+static const struct file_operations orion_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .write = orion5x_wdt_write,
- .unlocked_ioctl = orion5x_wdt_ioctl,
- .open = orion5x_wdt_open,
- .release = orion5x_wdt_release,
+ .write = orion_wdt_write,
+ .unlocked_ioctl = orion_wdt_ioctl,
+ .open = orion_wdt_open,
+ .release = orion_wdt_release,
};
-static struct miscdevice orion5x_wdt_miscdev = {
+static struct miscdevice orion_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
- .fops = &orion5x_wdt_fops,
+ .fops = &orion_wdt_fops,
};
-static int __devinit orion5x_wdt_probe(struct platform_device *pdev)
+static int __devinit orion_wdt_probe(struct platform_device *pdev)
{
- struct orion5x_wdt_platform_data *pdata = pdev->dev.platform_data;
+ struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
int ret;
if (pdata) {
wdt_tclk = pdata->tclk;
} else {
- printk(KERN_ERR "Orion5x Watchdog misses platform data\n");
+ printk(KERN_ERR "Orion Watchdog misses platform data\n");
return -ENODEV;
}
- if (orion5x_wdt_miscdev.parent)
+ if (orion_wdt_miscdev.parent)
return -EBUSY;
- orion5x_wdt_miscdev.parent = &pdev->dev;
+ orion_wdt_miscdev.parent = &pdev->dev;
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
- if (orion5x_wdt_settimeout(heartbeat))
+ if (orion_wdt_settimeout(heartbeat))
heartbeat = wdt_max_duration;
- ret = misc_register(&orion5x_wdt_miscdev);
+ ret = misc_register(&orion_wdt_miscdev);
if (ret)
return ret;
- printk(KERN_INFO "Orion5x Watchdog Timer: Initial timeout %d sec%s\n",
+ printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n",
heartbeat, nowayout ? ", nowayout" : "");
return 0;
}
-static int __devexit orion5x_wdt_remove(struct platform_device *pdev)
+static int __devexit orion_wdt_remove(struct platform_device *pdev)
{
int ret;
if (test_bit(WDT_IN_USE, &wdt_status)) {
- orion5x_wdt_disable();
+ orion_wdt_disable();
clear_bit(WDT_IN_USE, &wdt_status);
}
- ret = misc_deregister(&orion5x_wdt_miscdev);
+ ret = misc_deregister(&orion_wdt_miscdev);
if (!ret)
- orion5x_wdt_miscdev.parent = NULL;
+ orion_wdt_miscdev.parent = NULL;
return ret;
}
-static void orion5x_wdt_shutdown(struct platform_device *pdev)
+static void orion_wdt_shutdown(struct platform_device *pdev)
{
if (test_bit(WDT_IN_USE, &wdt_status))
- orion5x_wdt_disable();
+ orion_wdt_disable();
}
-static struct platform_driver orion5x_wdt_driver = {
- .probe = orion5x_wdt_probe,
- .remove = __devexit_p(orion5x_wdt_remove),
- .shutdown = orion5x_wdt_shutdown,
+static struct platform_driver orion_wdt_driver = {
+ .probe = orion_wdt_probe,
+ .remove = __devexit_p(orion_wdt_remove),
+ .shutdown = orion_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
- .name = "orion5x_wdt",
+ .name = "orion_wdt",
},
};
-static int __init orion5x_wdt_init(void)
+static int __init orion_wdt_init(void)
{
spin_lock_init(&wdt_lock);
- return platform_driver_register(&orion5x_wdt_driver);
+ return platform_driver_register(&orion_wdt_driver);
}
-static void __exit orion5x_wdt_exit(void)
+static void __exit orion_wdt_exit(void)
{
- platform_driver_unregister(&orion5x_wdt_driver);
+ platform_driver_unregister(&orion_wdt_driver);
}
-module_init(orion5x_wdt_init);
-module_exit(orion5x_wdt_exit);
+module_init(orion_wdt_init);
+module_exit(orion_wdt_exit);
MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
-MODULE_DESCRIPTION("Orion5x Processor Watchdog");
+MODULE_DESCRIPTION("Orion Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");