summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-09 19:03:16 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-09 19:03:16 -0800
commit3a43aaa31790c36b69ebf8a6396f37fade86b531 (patch)
tree7c7f8da6219d546f2b44534cb7be1fb5591d6ac4 /drivers
parentaed886ce777590eac87f7ce2897d9f8357754331 (diff)
parent6a5a0b9139b19dd1a107870269a35bc9cf18d2dc (diff)
downloadlwn-3a43aaa31790c36b69ebf8a6396f37fade86b531.tar.gz
lwn-3a43aaa31790c36b69ebf8a6396f37fade86b531.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6: (137 commits) sh: include empty zero page in romImage sh: Make associative cache writes fatal on all SH-4A parts. sh: Drop associative writes for SH-4 cache flushes. sh: Partial revert of copy/clear_user_highpage() optimizations. sh: Add default uImage rule for se7724, ap325rxa, and migor. sh: allow runtime pm without suspend/resume callbacks sh: mach-ecovec24: Remove un-defined settings for VPU sh: mach-ecovec24: LCDC drive ability become high sh: fix sh7724 VEU3F resource size serial: sh-sci: Fix too early port disabling. sh: pfc: pr_info() -> pr_debug() cleanups. sh: pfc: Convert from ctrl_xxx() to __raw_xxx() I/O routines. sh: Improve kfr2r09 serial port setup code sh: Break out SuperH PFC code sh: Move KEYSC header file sh: convert /proc/cpu/aligmnent, /proc/cpu/kernel_alignment to seq_file sh: Add CPG save/restore code for sh7724 R-standby sh: Add SDHI power control support to Ecovec mfd: Add power control platform data to SDHI driver sh: mach-ecovec24: modify address map ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cdrom/gdrom.c10
-rw-r--r--drivers/input/keyboard/sh_keysc.c2
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/sh_mobile_sdhi.c156
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/rtc/rtc-ds1302.c2
-rw-r--r--drivers/serial/Kconfig2
-rw-r--r--drivers/serial/sh-sci.c59
-rw-r--r--drivers/serial/sh-sci.h2
-rw-r--r--drivers/sh/Makefile1
-rw-r--r--drivers/sh/intc.c123
-rw-r--r--drivers/sh/maple/maple.c4
-rw-r--r--drivers/sh/pfc.c577
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c32
15 files changed, 892 insertions, 89 deletions
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index a762283d2a21..e789e6c9a422 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -214,7 +214,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
gdrom_getsense(NULL);
return;
}
- outsw(PHYSADDR(GDROM_DATA_REG), cmd, 6);
+ outsw(GDROM_DATA_REG, cmd, 6);
}
@@ -298,7 +298,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session)
err = -EINVAL;
goto cleanup_readtoc;
}
- insw(PHYSADDR(GDROM_DATA_REG), toc, tocsize/2);
+ insw(GDROM_DATA_REG, toc, tocsize/2);
if (gd.status & 0x01)
err = -EINVAL;
@@ -449,7 +449,7 @@ static int gdrom_getsense(short *bufstring)
GDROM_DEFAULT_TIMEOUT);
if (gd.pending)
goto cleanup_sense;
- insw(PHYSADDR(GDROM_DATA_REG), &sense, sense_command->buflen/2);
+ insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
if (sense[1] & 40) {
printk(KERN_INFO "GDROM: Drive not ready - command aborted\n");
goto cleanup_sense;
@@ -586,7 +586,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
spin_unlock(&gdrom_lock);
block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET;
block_cnt = blk_rq_sectors(req)/GD_TO_BLK;
- ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG);
+ ctrl_outl(virt_to_phys(req->buffer), GDROM_DMA_STARTADDR_REG);
ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG);
ctrl_outl(1, GDROM_DMA_DIRECTION_REG);
ctrl_outl(1, GDROM_DMA_ENABLE_REG);
@@ -615,7 +615,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
cpu_relax();
gd.pending = 1;
gd.transfer = 1;
- outsw(PHYSADDR(GDROM_DATA_REG), &read_command->cmd, 6);
+ outsw(GDROM_DATA_REG, &read_command->cmd, 6);
timeout = jiffies + HZ / 2;
/* Wait for any pending DMA to finish */
while (ctrl_inb(GDROM_DMA_STATUS_REG) &&
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 887af79b7bff..076111fc72d2 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -18,9 +18,9 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
+#include <linux/input/sh_keysc.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <asm/sh_keysc.h>
#define KYCR1_OFFS 0x00
#define KYCR2_OFFS 0x04
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 08f2d07bf56a..a296e717e86e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -35,6 +35,14 @@ config MFD_ASIC3
This driver supports the ASIC3 multifunction chip found on many
PDAs (mainly iPAQ and HTC based ones)
+config MFD_SH_MOBILE_SDHI
+ bool "Support for SuperH Mobile SDHI"
+ depends on SUPERH
+ select MFD_CORE
+ ---help---
+ This driver supports the SDHI hardware block found in many
+ SuperH Mobile SoCs.
+
config MFD_DM355EVM_MSP
bool "DaVinci DM355 EVM microcontroller"
depends on I2C && MACH_DAVINCI_DM355_EVM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index af0fc903cec8..11350c1d9301 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o
+obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c
new file mode 100644
index 000000000000..03efae8041ab
--- /dev/null
+++ b/drivers/mfd/sh_mobile_sdhi.c
@@ -0,0 +1,156 @@
+/*
+ * SuperH Mobile SDHI
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * 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.
+ *
+ * Based on "Compaq ASIC3 support":
+ *
+ * Copyright 2001 Compaq Computer Corporation.
+ * Copyright 2004-2005 Phil Blundell
+ * Copyright 2007-2008 OpenedHand Ltd.
+ *
+ * Authors: Phil Blundell <pb@handhelds.org>,
+ * Samuel Ortiz <sameo@openedhand.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
+
+struct sh_mobile_sdhi {
+ struct clk *clk;
+ struct tmio_mmc_data mmc_data;
+ struct mfd_cell cell_mmc;
+};
+
+static struct resource sh_mobile_sdhi_resources[] = {
+ {
+ .start = 0x000,
+ .end = 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell sh_mobile_sdhi_cell = {
+ .name = "tmio-mmc",
+ .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources),
+ .resources = sh_mobile_sdhi_resources,
+};
+
+static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
+{
+ struct platform_device *pdev = to_platform_device(tmio->dev.parent);
+ struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
+
+ if (p && p->set_pwr)
+ p->set_pwr(pdev, state);
+}
+
+static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_sdhi *priv;
+ struct resource *mem;
+ char clk_name[8];
+ int ret, irq;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ dev_err(&pdev->dev, "missing MEM resource\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ dev_err(&pdev->dev, "missing IRQ resource\n");
+
+ if (!mem || (irq < 0))
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
+ priv->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(priv->clk);
+ kfree(priv);
+ return ret;
+ }
+
+ clk_enable(priv->clk);
+
+ /* FIXME: silly const unsigned int hclk */
+ *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk);
+ priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr;
+
+ memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc));
+ priv->cell_mmc.driver_data = &priv->mmc_data;
+ priv->cell_mmc.platform_data = &priv->cell_mmc;
+ priv->cell_mmc.data_size = sizeof(priv->cell_mmc);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id,
+ &priv->cell_mmc, 1, mem, irq);
+ if (ret) {
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int sh_mobile_sdhi_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver sh_mobile_sdhi_driver = {
+ .driver = {
+ .name = "sh_mobile_sdhi",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_mobile_sdhi_probe,
+ .remove = __devexit_p(sh_mobile_sdhi_remove),
+};
+
+static int __init sh_mobile_sdhi_init(void)
+{
+ return platform_driver_register(&sh_mobile_sdhi_driver);
+}
+
+static void __exit sh_mobile_sdhi_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_sdhi_driver);
+}
+
+module_init(sh_mobile_sdhi_init);
+module_exit(sh_mobile_sdhi_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 432ae8358c86..e04b751680d0 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -329,7 +329,7 @@ config MMC_SDRICOH_CS
config MMC_TMIO
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
- depends on MFD_TMIO || MFD_ASIC3
+ depends on MFD_TMIO || MFD_ASIC3 || SUPERH
help
This provides support for the SD/MMC cell found in TC6393XB,
T7L66XB and also HTC ASIC3
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index d490628b64da..1e73c8f42e38 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -201,7 +201,7 @@ static struct platform_driver ds1302_platform_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .remove = __exit_p(ds1302_rtc_remove),
+ .remove = __devexit_p(ds1302_rtc_remove),
};
static int __init ds1302_rtc_init(void)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 50943ff78f4b..9ff47db0b2ce 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -996,7 +996,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on SUPERH || H8300
+ depends on HAVE_CLK && (SUPERH || H8300)
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 6498bd1fb6dd..ff38dbdb5c6e 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -50,7 +50,6 @@
#include <linux/list.h>
#ifdef CONFIG_SUPERH
-#include <asm/clock.h>
#include <asm/sh_bios.h>
#endif
@@ -79,22 +78,18 @@ struct sci_port {
struct timer_list break_timer;
int break_flag;
-#ifdef CONFIG_HAVE_CLK
/* Interface clock */
struct clk *iclk;
/* Data clock */
struct clk *dclk;
-#endif
+
struct list_head node;
};
struct sh_sci_priv {
spinlock_t lock;
struct list_head ports;
-
-#ifdef CONFIG_HAVE_CLK
struct notifier_block clk_nb;
-#endif
};
/* Function prototypes */
@@ -156,32 +151,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
-#if defined(__H8300S__)
-enum { sci_disable, sci_enable };
-
-static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
-{
- volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL;
- int ch = (port->mapbase - SMR0) >> 3;
- unsigned char mask = 1 << (ch+1);
-
- if (ctrl == sci_disable)
- *mstpcrl |= mask;
- else
- *mstpcrl &= ~mask;
-}
-
-static void h8300_sci_enable(struct uart_port *port)
-{
- h8300_sci_config(port, sci_enable);
-}
-
-static void h8300_sci_disable(struct uart_port *port)
-{
- h8300_sci_config(port, sci_disable);
-}
-#endif
-
#if defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
@@ -733,7 +702,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
return ret;
}
-#ifdef CONFIG_HAVE_CLK
/*
* Here we define a transistion notifier so that we can update all of our
* ports' baud rate when the peripheral clock changes.
@@ -751,7 +719,6 @@ static int sci_notifier(struct notifier_block *self,
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(sci_port, &priv->ports, node)
sci_port->port.uartclk = clk_get_rate(sci_port->dclk);
-
spin_unlock_irqrestore(&priv->lock, flags);
}
@@ -778,7 +745,6 @@ static void sci_clk_disable(struct uart_port *port)
clk_disable(sci_port->dclk);
}
-#endif
static int sci_request_irq(struct sci_port *port)
{
@@ -833,8 +799,8 @@ static void sci_free_irq(struct sci_port *port)
static unsigned int sci_tx_empty(struct uart_port *port)
{
- /* Can't detect */
- return TIOCSER_TEMT;
+ unsigned short status = sci_in(port, SCxSR);
+ return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0;
}
static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1077,21 +1043,10 @@ static void __devinit sci_init_single(struct platform_device *dev,
sci_port->port.iotype = UPIO_MEM;
sci_port->port.line = index;
sci_port->port.fifosize = 1;
-
-#if defined(__H8300H__) || defined(__H8300S__)
-#ifdef __H8300S__
- sci_port->enable = h8300_sci_enable;
- sci_port->disable = h8300_sci_disable;
-#endif
- sci_port->port.uartclk = CONFIG_CPU_CLOCK;
-#elif defined(CONFIG_HAVE_CLK)
sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL;
sci_port->dclk = clk_get(&dev->dev, "peripheral_clk");
sci_port->enable = sci_clk_enable;
sci_port->disable = sci_clk_disable;
-#else
-#error "Need a valid uartclk"
-#endif
sci_port->break_timer.data = (unsigned long)sci_port;
sci_port->break_timer.function = sci_break_timer;
@@ -1106,7 +1061,6 @@ static void __devinit sci_init_single(struct platform_device *dev,
sci_port->type = sci_port->port.type = p->type;
memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs));
-
}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
@@ -1239,14 +1193,11 @@ static int sci_remove(struct platform_device *dev)
struct sci_port *p;
unsigned long flags;
-#ifdef CONFIG_HAVE_CLK
cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(p, &priv->ports, node)
uart_remove_one_port(&sci_uart_driver, &p->port);
-
spin_unlock_irqrestore(&priv->lock, flags);
kfree(priv);
@@ -1307,10 +1258,8 @@ static int __devinit sci_probe(struct platform_device *dev)
spin_lock_init(&priv->lock);
platform_set_drvdata(dev, priv);
-#ifdef CONFIG_HAVE_CLK
priv->clk_nb.notifier_call = sci_notifier;
cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
if (dev->id != -1) {
ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]);
@@ -1370,7 +1319,7 @@ static struct dev_pm_ops sci_dev_pm_ops = {
static struct platform_driver sci_driver = {
.probe = sci_probe,
- .remove = __devexit_p(sci_remove),
+ .remove = sci_remove,
.driver = {
.name = "sh-sci",
.owner = THIS_MODULE,
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 3e2fcf93b42e..a32094eeb42b 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -1,5 +1,5 @@
#include <linux/serial_core.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/gpio.h>
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 6a025cefe6dc..4956bf1f2134 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -3,4 +3,5 @@
#
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+obj-$(CONFIG_GENERIC_GPIO) += pfc.o
obj-y += intc.o
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 559b5fe9dc0f..a7e5c2e9986c 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -2,6 +2,7 @@
* Shared interrupt handling code for IPR and INTC2 types of IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009 Paul Mundt
*
* Based on intc2.c and ipr.c
*
@@ -24,6 +25,7 @@
#include <linux/sysdev.h>
#include <linux/list.h>
#include <linux/topology.h>
+#include <linux/bitmap.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -59,6 +61,20 @@ struct intc_desc_int {
static LIST_HEAD(intc_list);
+/*
+ * The intc_irq_map provides a global map of bound IRQ vectors for a
+ * given platform. Allocation of IRQs are either static through the CPU
+ * vector map, or dynamic in the case of board mux vectors or MSI.
+ *
+ * As this is a central point for all IRQ controllers on the system,
+ * each of the available sources are mapped out here. This combined with
+ * sparseirq makes it quite trivial to keep the vector map tightly packed
+ * when dynamically creating IRQs, as well as tying in to otherwise
+ * unused irq_desc positions in the sparse array.
+ */
+static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
+static DEFINE_SPINLOCK(vector_lock);
+
#ifdef CONFIG_SMP
#define IS_SMP(x) x.smp
#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
@@ -70,9 +86,7 @@ static LIST_HEAD(intc_list);
#endif
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static unsigned long ack_handle[NR_IRQS];
-#endif
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
{
@@ -250,7 +264,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on)
return 0; /* allow wakeup, but setup hardware in intc_suspend() */
}
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static void intc_mask_ack(unsigned int irq)
{
struct intc_desc_int *d = get_intc_desc(irq);
@@ -282,7 +295,6 @@ static void intc_mask_ack(unsigned int irq)
}
}
}
-#endif
static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
unsigned int nr_hp,
@@ -501,7 +513,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc,
return 0;
}
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static unsigned int __init intc_ack_data(struct intc_desc *desc,
struct intc_desc_int *d,
intc_enum enum_id)
@@ -533,7 +544,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc,
return 0;
}
-#endif
static unsigned int __init intc_sense_data(struct intc_desc *desc,
struct intc_desc_int *d,
@@ -572,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc,
struct intc_handle_int *hp;
unsigned int data[2], primary;
+ /*
+ * Register the IRQ position with the global IRQ map
+ */
+ set_bit(irq, intc_irq_map);
+
/* Prefer single interrupt source bitmap over other combinations:
* 1. bitmap, single interrupt source
* 2. priority, single interrupt source
@@ -641,10 +656,8 @@ static void __init intc_register_irq(struct intc_desc *desc,
/* irq should be disabled by default */
d->chip.mask(irq);
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
if (desc->ack_regs)
ack_handle[irq] = intc_ack_data(desc, d, enum_id);
-#endif
}
static unsigned int __init save_reg(struct intc_desc_int *d,
@@ -681,10 +694,8 @@ void __init register_intc_controller(struct intc_desc *desc)
d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0;
d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0;
d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0;
-
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
-#endif
+
d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
#ifdef CONFIG_SMP
d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
@@ -727,14 +738,12 @@ void __init register_intc_controller(struct intc_desc *desc)
d->chip.set_type = intc_set_sense;
d->chip.set_wake = intc_set_wake;
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
if (desc->ack_regs) {
for (i = 0; i < desc->nr_ack_regs; i++)
k += save_reg(d, k, desc->ack_regs[i].set_reg, 0);
d->chip.mask_ack = intc_mask_ack;
}
-#endif
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
@@ -856,5 +865,91 @@ static int __init register_intc_sysdevs(void)
return error;
}
-
device_initcall(register_intc_sysdevs);
+
+/*
+ * Dynamic IRQ allocation and deallocation
+ */
+static unsigned int create_irq_on_node(unsigned int irq_want, int node)
+{
+ unsigned int irq = 0, new;
+ unsigned long flags;
+ struct irq_desc *desc;
+
+ spin_lock_irqsave(&vector_lock, flags);
+
+ /*
+ * First try the wanted IRQ, then scan.
+ */
+ if (test_and_set_bit(irq_want, intc_irq_map)) {
+ new = find_first_zero_bit(intc_irq_map, nr_irqs);
+ if (unlikely(new == nr_irqs))
+ goto out_unlock;
+
+ desc = irq_to_desc_alloc_node(new, node);
+ if (unlikely(!desc)) {
+ pr_info("can't get irq_desc for %d\n", new);
+ goto out_unlock;
+ }
+
+ desc = move_irq_desc(desc, node);
+ __set_bit(new, intc_irq_map);
+ irq = new;
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&vector_lock, flags);
+
+ if (irq > 0)
+ dynamic_irq_init(irq);
+
+ return irq;
+}
+
+int create_irq(void)
+{
+ int nid = cpu_to_node(smp_processor_id());
+ int irq;
+
+ irq = create_irq_on_node(NR_IRQS_LEGACY, nid);
+ if (irq == 0)
+ irq = -1;
+
+ return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ dynamic_irq_cleanup(irq);
+
+ spin_lock_irqsave(&vector_lock, flags);
+ __clear_bit(irq, intc_irq_map);
+ spin_unlock_irqrestore(&vector_lock, flags);
+}
+
+int reserve_irq_vector(unsigned int irq)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vector_lock, flags);
+ if (test_and_set_bit(irq, intc_irq_map))
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&vector_lock, flags);
+
+ return ret;
+}
+
+void reserve_irq_legacy(void)
+{
+ unsigned long flags;
+ int i, j;
+
+ spin_lock_irqsave(&vector_lock, flags);
+ j = find_first_bit(intc_irq_map, nr_irqs);
+ for (i = 0; i < j; i++)
+ __set_bit(i, intc_irq_map);
+ spin_unlock_irqrestore(&vector_lock, flags);
+}
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 93c20e135ee1..4e8f57d4131f 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -106,7 +106,7 @@ static void maple_dma_reset(void)
* max delay is 11
*/
ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED);
- ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
+ ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR);
ctrl_outl(1, MAPLE_ENABLE);
}
@@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq)
maple_lastptr = maple_sendptr;
*maple_sendptr++ = (port << 16) | len | 0x80000000;
- *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf);
+ *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf);
*maple_sendptr++ =
mq->command | (to << 8) | (from << 16) | (len << 24);
while (len-- > 0)
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
new file mode 100644
index 000000000000..841ed5030c8f
--- /dev/null
+++ b/drivers/sh/pfc.c
@@ -0,0 +1,577 @@
+/*
+ * Pinmuxed GPIO support for SuperH.
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+
+static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r)
+{
+ if (enum_id < r->begin)
+ return 0;
+
+ if (enum_id > r->end)
+ return 0;
+
+ return 1;
+}
+
+static unsigned long gpio_read_raw_reg(unsigned long reg,
+ unsigned long reg_width)
+{
+ switch (reg_width) {
+ case 8:
+ return __raw_readb(reg);
+ case 16:
+ return __raw_readw(reg);
+ case 32:
+ return __raw_readl(reg);
+ }
+
+ BUG();
+ return 0;
+}
+
+static void gpio_write_raw_reg(unsigned long reg,
+ unsigned long reg_width,
+ unsigned long data)
+{
+ switch (reg_width) {
+ case 8:
+ __raw_writeb(data, reg);
+ return;
+ case 16:
+ __raw_writew(data, reg);
+ return;
+ case 32:
+ __raw_writel(data, reg);
+ return;
+ }
+
+ BUG();
+}
+
+static void gpio_write_bit(struct pinmux_data_reg *dr,
+ unsigned long in_pos, unsigned long value)
+{
+ unsigned long pos;
+
+ pos = dr->reg_width - (in_pos + 1);
+
+ pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld\n",
+ dr->reg, !!value, pos, dr->reg_width);
+
+ if (value)
+ set_bit(pos, &dr->reg_shadow);
+ else
+ clear_bit(pos, &dr->reg_shadow);
+
+ gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow);
+}
+
+static int gpio_read_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos)
+{
+ unsigned long data, mask, pos;
+
+ data = 0;
+ mask = (1 << field_width) - 1;
+ pos = reg_width - ((in_pos + 1) * field_width);
+
+ pr_debug("read_reg: addr = %lx, pos = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ reg, pos, reg_width, field_width);
+
+ data = gpio_read_raw_reg(reg, reg_width);
+ return (data >> pos) & mask;
+}
+
+static void gpio_write_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos,
+ unsigned long value)
+{
+ unsigned long mask, pos;
+
+ mask = (1 << field_width) - 1;
+ pos = reg_width - ((in_pos + 1) * field_width);
+
+ pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ reg, value, pos, reg_width, field_width);
+
+ mask = ~(mask << pos);
+ value = value << pos;
+
+ switch (reg_width) {
+ case 8:
+ __raw_writeb((__raw_readb(reg) & mask) | value, reg);
+ break;
+ case 16:
+ __raw_writew((__raw_readw(reg) & mask) | value, reg);
+ break;
+ case 32:
+ __raw_writel((__raw_readl(reg) & mask) | value, reg);
+ break;
+ }
+}
+
+static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ struct pinmux_data_reg *data_reg;
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = 0;
+ while (1) {
+ data_reg = gpioc->data_regs + k;
+
+ if (!data_reg->reg_width)
+ break;
+
+ for (n = 0; n < data_reg->reg_width; n++) {
+ if (data_reg->enum_ids[n] == gpiop->enum_id) {
+ gpiop->flags &= ~PINMUX_FLAG_DREG;
+ gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT);
+ gpiop->flags &= ~PINMUX_FLAG_DBIT;
+ gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT);
+ return 0;
+ }
+ }
+ k++;
+ }
+
+ BUG();
+
+ return -1;
+}
+
+static void setup_data_regs(struct pinmux_info *gpioc)
+{
+ struct pinmux_data_reg *drp;
+ int k;
+
+ for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++)
+ setup_data_reg(gpioc, k);
+
+ k = 0;
+ while (1) {
+ drp = gpioc->data_regs + k;
+
+ if (!drp->reg_width)
+ break;
+
+ drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width);
+ k++;
+ }
+}
+
+static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
+ struct pinmux_data_reg **drp, int *bitp)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
+ n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
+ *drp = gpioc->data_regs + k;
+ *bitp = n;
+ return 0;
+}
+
+static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
+ struct pinmux_cfg_reg **crp, int *indexp,
+ unsigned long **cntp)
+{
+ struct pinmux_cfg_reg *config_reg;
+ unsigned long r_width, f_width;
+ int k, n;
+
+ k = 0;
+ while (1) {
+ config_reg = gpioc->cfg_regs + k;
+
+ r_width = config_reg->reg_width;
+ f_width = config_reg->field_width;
+
+ if (!r_width)
+ break;
+ for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) {
+ if (config_reg->enum_ids[n] == enum_id) {
+ *crp = config_reg;
+ *indexp = n;
+ *cntp = &config_reg->cnt[n / (1 << f_width)];
+ return 0;
+ }
+ }
+ k++;
+ }
+
+ return -1;
+}
+
+static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
+ int pos, pinmux_enum_t *enum_idp)
+{
+ pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id;
+ pinmux_enum_t *data = gpioc->gpio_data;
+ int k;
+
+ if (!enum_in_range(enum_id, &gpioc->data)) {
+ if (!enum_in_range(enum_id, &gpioc->mark)) {
+ pr_err("non data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+ }
+ }
+
+ if (pos) {
+ *enum_idp = data[pos + 1];
+ return pos + 1;
+ }
+
+ for (k = 0; k < gpioc->gpio_data_size; k++) {
+ if (data[k] == enum_id) {
+ *enum_idp = data[k + 1];
+ return k + 1;
+ }
+ }
+
+ pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+}
+
+static void write_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ int index)
+{
+ unsigned long ncomb, pos, value;
+
+ ncomb = 1 << crp->field_width;
+ pos = index / ncomb;
+ value = index % ncomb;
+
+ gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value);
+}
+
+static int check_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ int index)
+{
+ unsigned long ncomb, pos, value;
+
+ ncomb = 1 << crp->field_width;
+ pos = index / ncomb;
+ value = index % ncomb;
+
+ if (gpio_read_reg(crp->reg, crp->reg_width,
+ crp->field_width, pos) == value)
+ return 0;
+
+ return -1;
+}
+
+enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE };
+
+static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
+ int pinmux_type, int cfg_mode)
+{
+ struct pinmux_cfg_reg *cr = NULL;
+ pinmux_enum_t enum_id;
+ struct pinmux_range *range;
+ int in_range, pos, index;
+ unsigned long *cntp;
+
+ switch (pinmux_type) {
+
+ case PINMUX_TYPE_FUNCTION:
+ range = NULL;
+ break;
+
+ case PINMUX_TYPE_OUTPUT:
+ range = &gpioc->output;
+ break;
+
+ case PINMUX_TYPE_INPUT:
+ range = &gpioc->input;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLUP:
+ range = &gpioc->input_pu;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ range = &gpioc->input_pd;
+ break;
+
+ default:
+ goto out_err;
+ }
+
+ pos = 0;
+ enum_id = 0;
+ index = 0;
+ while (1) {
+ pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id);
+ if (pos <= 0)
+ goto out_err;
+
+ if (!enum_id)
+ break;
+
+ in_range = enum_in_range(enum_id, &gpioc->function);
+ if (!in_range && range) {
+ in_range = enum_in_range(enum_id, range);
+
+ if (in_range && enum_id == range->force)
+ continue;
+ }
+
+ if (!in_range)
+ continue;
+
+ if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0)
+ goto out_err;
+
+ switch (cfg_mode) {
+ case GPIO_CFG_DRYRUN:
+ if (!*cntp || !check_config_reg(gpioc, cr, index))
+ continue;
+ break;
+
+ case GPIO_CFG_REQ:
+ write_config_reg(gpioc, cr, index);
+ *cntp = *cntp + 1;
+ break;
+
+ case GPIO_CFG_FREE:
+ *cntp = *cntp - 1;
+ break;
+ }
+ }
+
+ return 0;
+ out_err:
+ return -1;
+}
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pinmux_info, chip);
+}
+
+static int sh_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ struct pinmux_data_reg *dummy;
+ unsigned long flags;
+ int i, ret, pinmux_type;
+
+ ret = -EINVAL;
+
+ if (!gpioc)
+ goto err_out;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
+ goto err_unlock;
+
+ /* setup pin function here if no data is associated with pin */
+
+ if (get_data_reg(gpioc, offset, &dummy, &i) != 0)
+ pinmux_type = PINMUX_TYPE_FUNCTION;
+ else
+ pinmux_type = PINMUX_TYPE_GPIO;
+
+ if (pinmux_type == PINMUX_TYPE_FUNCTION) {
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_unlock;
+
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+ }
+
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= pinmux_type;
+
+ ret = 0;
+ err_unlock:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ err_out:
+ return ret;
+}
+
+static void sh_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int pinmux_type;
+
+ if (!gpioc)
+ return;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE;
+ pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE);
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static int pinmux_direction(struct pinmux_info *gpioc,
+ unsigned gpio, int new_pinmux_type)
+{
+ int pinmux_type;
+ int ret = -EINVAL;
+
+ if (!gpioc)
+ goto err_out;
+
+ pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
+
+ switch (pinmux_type) {
+ case PINMUX_TYPE_GPIO:
+ break;
+ case PINMUX_TYPE_OUTPUT:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_INPUT_PULLUP:
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE);
+ break;
+ default:
+ goto err_out;
+ }
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_out;
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+
+ gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[gpio].flags |= new_pinmux_type;
+
+ ret = 0;
+ err_out:
+ return ret;
+}
+
+static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+
+static void sh_gpio_set_value(struct pinmux_info *gpioc,
+ unsigned gpio, int value)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ BUG();
+ else
+ gpio_write_bit(dr, bit, value);
+}
+
+static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int ret;
+
+ sh_gpio_set_value(gpioc, offset, value);
+ spin_lock_irqsave(&gpio_lock, flags);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+
+static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) {
+ BUG();
+ return 0;
+ }
+
+ return gpio_read_reg(dr->reg, dr->reg_width, 1, bit);
+}
+
+static int sh_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return sh_gpio_get_value(chip_to_pinmux(chip), offset);
+}
+
+static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
+}
+
+int register_pinmux(struct pinmux_info *pip)
+{
+ struct gpio_chip *chip = &pip->chip;
+
+ pr_info("sh pinmux: %s handling gpio %d -> %d\n",
+ pip->name, pip->first_gpio, pip->last_gpio);
+
+ setup_data_regs(pip);
+
+ chip->request = sh_gpio_request;
+ chip->free = sh_gpio_free;
+ chip->direction_input = sh_gpio_direction_input;
+ chip->get = sh_gpio_get;
+ chip->direction_output = sh_gpio_direction_output;
+ chip->set = sh_gpio_set;
+
+ WARN_ON(pip->first_gpio != 0); /* needs testing */
+
+ chip->label = pip->name;
+ chip->owner = THIS_MODULE;
+ chip->base = pip->first_gpio;
+ chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1;
+
+ return gpiochip_add(chip);
+}
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 3ad5157f9899..b4b5de930cf5 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- unsigned int nr_pages;
/* enable clocks before accessing hardware */
sh_mobile_lcdc_clk_on(ch->lcdc);
- nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
- dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
-
- /* trigger panel update */
- lcdc_write_chan(ch, LDSM2R, 1);
-
- dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ /*
+ * It's possible to get here without anything on the pagelist via
+ * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+ * invocation. In the former case, the acceleration routines are
+ * stepped in to when using the framebuffer console causing the
+ * workqueue to be scheduled without any dirty pages on the list.
+ *
+ * Despite this, a panel update is still needed given that the
+ * acceleration routines have their own methods for writing in
+ * that still need to be updated.
+ *
+ * The fsync() and empty pagelist case could be optimized for,
+ * but we don't bother, as any application exhibiting such
+ * behaviour is fundamentally broken anyways.
+ */
+ if (!list_empty(pagelist)) {
+ unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
+
+ /* trigger panel update */
+ dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ lcdc_write_chan(ch, LDSM2R, 1);
+ dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ } else
+ lcdc_write_chan(ch, LDSM2R, 1);
}
static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)