From f21e0e81d81b649ad309cedc7226f1bed72982e0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 May 2011 08:12:40 +0800 Subject: regulator: Do bulk enables of regulators in parallel In order to reduce the impact of ramp times rather than enabling the regulators for a device in series use async tasks to run the actual enables. This means that the delays which the enables implement can all run in parallel, though it does mean that the order in which the supplies come on may be unstable. For super bonus fun points if any of the regulators are shared between multiple supplies on the same device (as is rather likely) then this will test our locking. Note that in this case we only delay once for each physical regulator so the threads shouldn't block each other while delaying. It'd be even nicer if we could coalesce writes to a shared enable registers in PMICs but that's definitely future work, and it may also be useful and is certainly more achievable to optimise out the parallelism if none of the regulators implement ramp delays. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d3e38790906..7b38af90a01 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -2264,6 +2265,13 @@ err: } EXPORT_SYMBOL_GPL(regulator_bulk_get); +static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) +{ + struct regulator_bulk_data *bulk = data; + + bulk->ret = regulator_enable(bulk->consumer); +} + /** * regulator_bulk_enable - enable multiple regulator consumers * @@ -2279,21 +2287,33 @@ EXPORT_SYMBOL_GPL(regulator_bulk_get); int regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers) { + LIST_HEAD(async_domain); int i; - int ret; + int ret = 0; + + for (i = 0; i < num_consumers; i++) + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); + + async_synchronize_full_domain(&async_domain); + /* If any consumer failed we need to unwind any that succeeded */ for (i = 0; i < num_consumers; i++) { - ret = regulator_enable(consumers[i].consumer); - if (ret != 0) + if (consumers[i].ret != 0) { + ret = consumers[i].ret; goto err; + } } return 0; err: - pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret); - for (--i; i >= 0; --i) - regulator_disable(consumers[i].consumer); + for (i = 0; i < num_consumers; i++) + if (consumers[i].ret == 0) + regulator_disable(consumers[i].consumer); + else + pr_err("Failed to enable %s: %d\n", + consumers[i].supply, consumers[i].ret); return ret; } -- cgit v1.2.3-70-g09d2 From 2ae3636b79aee1a69b2e84eff68bb123090796d3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 May 2011 23:14:40 +0800 Subject: regulator: Use _cansleep() for WM8994 regulator GPIOs The WM8994 regulator driver is perfectly happy if the GPIO used to enable the regulator sleeps so call the appropriate GPIO API. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/wm8994-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 35b2958d510..1a6a690f24d 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -43,7 +43,7 @@ static int wm8994_ldo_enable(struct regulator_dev *rdev) if (!ldo->enable) return 0; - gpio_set_value(ldo->enable, 1); + gpio_set_value_cansleep(ldo->enable, 1); ldo->is_enabled = true; return 0; @@ -57,7 +57,7 @@ static int wm8994_ldo_disable(struct regulator_dev *rdev) if (!ldo->enable) return -EINVAL; - gpio_set_value(ldo->enable, 0); + gpio_set_value_cansleep(ldo->enable, 0); ldo->is_enabled = false; return 0; -- cgit v1.2.3-70-g09d2 From 7736f11dbadce33d3f12bf0e8114d0f1da5e8622 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 27 May 2011 12:25:27 -0700 Subject: regulator: twl-regulator: fix n_voltages for twl6030 variable LDOs The n_voltages initializer for the TWL6030_ADJUSTABLE_LDO macro is off by one, causing the the highest supported voltage to be unreachable. Setting the machine constraints to only allow the highest voltage causes errors: machine_constraints_voltage: VAUX3_6030: unsupportable voltage constraints twl_reg twl_reg.39: can't register VAUX3_6030, -22 twl_reg: probe of twl_reg.39 failed with error -22 This patch fixes the off by one error. Tested by setting VAUX3_6030 to 3.3V. Signed-off-by: Colin Cross Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/twl-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 87fe0f75a56..503c2bc64c8 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -864,7 +864,7 @@ static struct regulator_ops twlsmps_ops = { .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ - .n_voltages = (max_mVolts - min_mVolts)/100, \ + .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ -- cgit v1.2.3-70-g09d2 From c3d4913cd4cd469cbf29d411293e937729e83f3a Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Tue, 31 May 2011 10:34:45 +0900 Subject: pch_dma: fix DMA issue(ch8-ch11) ISSUE: In case PCH_DMA with I2S communications with ch8~ch11, sometimes I2S data is not send correctly. CAUSE: The following patch I submitted before was not enough modification for supporting DMA ch8~ch11. The modification for status register of ch8~11 was not enough. pch_dma: Support I2S for ML7213 IOH author Tomoya MORINAGA Mon, 9 May 2011 07:09:38 +0000 (16:09 +0900) committer Vinod Koul Mon, 9 May 2011 11:42:23 +0000 (16:42 +0530) commit 194f5f2706c7472f9c6bb2d17fa788993606581f tree c9d4903ea02b18939a4f390956a48be1a3734517 parent 60092d0bde4c8741198da4a69b693d3709385bf1 This patch fixes the issue. We can confirm PCH_DMA with I2S communications with ch8~ch11 works well. Signed-off-by: Tomoya MORINAGA Signed-off-by: Vinod Koul --- drivers/dma/pch_dma.c | 69 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index ff5b38f9d45..65c32f893a5 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -45,7 +45,8 @@ #define DMA_STATUS_MASK_BITS 0x3 #define DMA_STATUS_SHIFT_BITS 16 #define DMA_STATUS_IRQ(x) (0x1 << (x)) -#define DMA_STATUS_ERR(x) (0x1 << ((x) + 8)) +#define DMA_STATUS0_ERR(x) (0x1 << ((x) + 8)) +#define DMA_STATUS2_ERR(x) (0x1 << (x)) #define DMA_DESC_WIDTH_SHIFT_BITS 12 #define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS) @@ -133,6 +134,7 @@ struct pch_dma { #define PCH_DMA_CTL3 0x0C #define PCH_DMA_STS0 0x10 #define PCH_DMA_STS1 0x14 +#define PCH_DMA_STS2 0x18 #define dma_readl(pd, name) \ readl((pd)->membase + PCH_DMA_##name) @@ -183,13 +185,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable) { struct pch_dma *pd = to_pd(chan->device); u32 val; + int pos; + + if (chan->chan_id < 8) + pos = chan->chan_id; + else + pos = chan->chan_id + 8; val = dma_readl(pd, CTL2); if (enable) - val |= 0x1 << chan->chan_id; + val |= 0x1 << pos; else - val &= ~(0x1 << chan->chan_id); + val &= ~(0x1 << pos); dma_writel(pd, CTL2, val); @@ -262,7 +270,7 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode) chan->chan_id, val); } -static u32 pdc_get_status(struct pch_dma_chan *pd_chan) +static u32 pdc_get_status0(struct pch_dma_chan *pd_chan) { struct pch_dma *pd = to_pd(pd_chan->chan.device); u32 val; @@ -272,9 +280,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan) DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id)); } +static u32 pdc_get_status2(struct pch_dma_chan *pd_chan) +{ + struct pch_dma *pd = to_pd(pd_chan->chan.device); + u32 val; + + val = dma_readl(pd, STS2); + return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS + + DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8))); +} + static bool pdc_is_idle(struct pch_dma_chan *pd_chan) { - if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE) + u32 sts; + + if (pd_chan->chan.chan_id < 8) + sts = pdc_get_status0(pd_chan); + else + sts = pdc_get_status2(pd_chan); + + + if (sts == DMA_STATUS_IDLE) return true; else return false; @@ -693,30 +719,45 @@ static irqreturn_t pd_irq(int irq, void *devid) struct pch_dma *pd = (struct pch_dma *)devid; struct pch_dma_chan *pd_chan; u32 sts0; + u32 sts2; int i; - int ret = IRQ_NONE; + int ret0 = IRQ_NONE; + int ret2 = IRQ_NONE; sts0 = dma_readl(pd, STS0); + sts2 = dma_readl(pd, STS2); dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0); for (i = 0; i < pd->dma.chancnt; i++) { pd_chan = &pd->channels[i]; - if (sts0 & DMA_STATUS_IRQ(i)) { - if (sts0 & DMA_STATUS_ERR(i)) - set_bit(0, &pd_chan->err_status); + if (i < 8) { + if (sts0 & DMA_STATUS_IRQ(i)) { + if (sts0 & DMA_STATUS0_ERR(i)) + set_bit(0, &pd_chan->err_status); - tasklet_schedule(&pd_chan->tasklet); - ret = IRQ_HANDLED; - } + tasklet_schedule(&pd_chan->tasklet); + ret0 = IRQ_HANDLED; + } + } else { + if (sts2 & DMA_STATUS_IRQ(i - 8)) { + if (sts2 & DMA_STATUS2_ERR(i)) + set_bit(0, &pd_chan->err_status); + tasklet_schedule(&pd_chan->tasklet); + ret2 = IRQ_HANDLED; + } + } } /* clear interrupt bits in status register */ - dma_writel(pd, STS0, sts0); + if (ret0) + dma_writel(pd, STS0, sts0); + if (ret2) + dma_writel(pd, STS2, sts2); - return ret; + return ret0 | ret2; } #ifdef CONFIG_PM -- cgit v1.2.3-70-g09d2 From ab7cfb5548d22604fafeaaa95950be2f97869f1e Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 1 Jun 2011 14:47:42 +0900 Subject: serial: sh-sci: Kill off bitrotted H8/300 support. h8300 has never been updated upstream to support the conversion to the driver model (which happened mid-2.5), and it doesn't seem likely that it ever will. Kill off the remaining bitrotted support to reduce the maintenance burden going forward. Signed-off-by: Paul Mundt --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/sh-sci.c | 30 +------------ drivers/tty/serial/sh-sci.h | 103 +++++--------------------------------------- 3 files changed, 14 insertions(+), 121 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 636144cea93..1c0cd2d26d3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -974,7 +974,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE) + depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE) select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ebd8629c108..280c02af0ea 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -54,10 +54,6 @@ #include #endif -#ifdef CONFIG_H8300 -#include -#endif - #include "sh-sci.h" struct sci_port { @@ -164,23 +160,7 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(__H8300H__) || defined(__H8300S__) -static void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - int ch = (port->mapbase - SMR0) >> 3; - - /* set DDR regs */ - H8300_GPIO_DDR(h8300_sci_pins[ch].port, - h8300_sci_pins[ch].rx, - H8300_GPIO_INPUT); - H8300_GPIO_DDR(h8300_sci_pins[ch].port, - h8300_sci_pins[ch].tx, - H8300_GPIO_OUTPUT); - - /* tx mark output*/ - H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) +#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) { if (port->mapbase == 0xA4400000) { @@ -1863,14 +1843,8 @@ static int __devinit serial_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - ret = uart_set_options(port, co, baud, parity, bits, flow); -#if defined(__H8300H__) || defined(__H8300S__) - /* disable rx interrupt */ - if (ret == 0) - sci_stop_rx(port); -#endif /* TODO: disable clock */ - return ret; + return uart_set_options(port, co, baud, parity, bits, flow); } static struct console serial_console = { diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index b04d937c911..4dc249ecc59 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,13 +2,6 @@ #include #include -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include -#endif -#if defined(CONFIG_H8S2678) -#include -#endif - #if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ defined(CONFIG_CPU_SUBTYPE_SH7707) || \ defined(CONFIG_CPU_SUBTYPE_SH7708) || \ @@ -72,10 +65,6 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* overrun error bit */ -#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) -#elif defined(CONFIG_H8S2678) -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) #elif defined(CONFIG_CPU_SUBTYPE_SH7757) # define SCSPTR0 0xfe4b0020 # define SCIF_ORER 0x0001 @@ -223,17 +212,6 @@ } \ } -#ifdef CONFIG_H8300 -/* h8300 don't have SCIF */ -#define CPU_SCIF_FNS(name) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - return 0; \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - } -#else #define CPU_SCIF_FNS(name, scif_offset, scif_size) \ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ @@ -243,7 +221,6 @@ { \ SCI_OUT(scif_size, scif_offset, value); \ } -#endif #define CPU_SCI_FNS(name, sci_offset, sci_size) \ static inline unsigned int sci_##name##_in(struct uart_port* port) \ @@ -262,8 +239,7 @@ defined(CONFIG_ARCH_SH7372) #if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) @@ -282,19 +258,11 @@ CPU_SCIF_FNS(name, scif_offset, scif_size) #else #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) #endif -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name) #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ defined(CONFIG_CPU_SUBTYPE_SH7724) #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ @@ -303,8 +271,7 @@ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) #else #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) @@ -353,14 +320,14 @@ SCIF_FNS(SCFCR, 0x18, 16) SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCLSR, 0x24, 16) #else -/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ -/* name off sz off sz off sz off sz off sz*/ -SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) -SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) -SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) -SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) -SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) -SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) +/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 */ +/* name off sz off sz off sz off sz */ +SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16) +SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8) +SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16) +SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8) +SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16) +SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8) SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ @@ -390,48 +357,6 @@ SCIF_FNS(SCLSR, 0, 0, 0x24, 16) #define sci_in(port, reg) sci_##reg##_in(port) #define sci_out(port, reg, value) sci_##reg##_out(port, value) -/* H8/300 series SCI pins assignment */ -#if defined(__H8300H__) || defined(__H8300S__) -static const struct __attribute__((packed)) { - int port; /* GPIO port no */ - unsigned short rx,tx; /* GPIO bit no */ -} h8300_sci_pins[] = { -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) - { /* SCI0 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_PB, - .rx = H8300_GPIO_B7, - .tx = H8300_GPIO_B6, - } -#elif defined(CONFIG_H8S2678) - { /* SCI0 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_P5, - .rx = H8300_GPIO_B1, - .tx = H8300_GPIO_B0, - } -#endif -}; -#endif - #if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ defined(CONFIG_CPU_SUBTYPE_SH7707) || \ defined(CONFIG_CPU_SUBTYPE_SH7708) || \ @@ -454,12 +379,6 @@ static inline int sci_rxd_in(struct uart_port *port) return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ return 1; } -#elif defined(__H8300H__) || defined(__H8300S__) -static inline int sci_rxd_in(struct uart_port *port) -{ - int ch = (port->mapbase - SMR0) >> 3; - return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; -} #else /* default case for non-SCI processors */ static inline int sci_rxd_in(struct uart_port *port) { -- cgit v1.2.3-70-g09d2 From 5fa29a17fabfe204fa9f20edd5fc81ab2364eb4b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sun, 29 May 2011 13:10:02 +0300 Subject: dmaengine: add ep93xx DMA support The ep93xx DMA controller has 10 independent memory to peripheral (M2P) channels, and 2 dedicated memory to memory (M2M) channels. M2M channels can also be used by SPI and IDE to perform DMA transfers to/from their memory mapped FIFOs. This driver supports both M2P and M2M channels with DMA_SLAVE, DMA_CYCLIC and DMA_MEMCPY (M2M only) capabilities. Signed-off-by: Mika Westerberg Signed-off-by: Ryan Mallon Acked-by: H Hartley Sweeten Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- arch/arm/mach-ep93xx/include/mach/dma.h | 87 ++ drivers/dma/Kconfig | 7 + drivers/dma/Makefile | 1 + drivers/dma/ep93xx_dma.c | 1355 +++++++++++++++++++++++++++++++ 4 files changed, 1450 insertions(+) create mode 100644 drivers/dma/ep93xx_dma.c (limited to 'drivers') diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h index 5e31b2b25da..6e7049a796a 100644 --- a/arch/arm/mach-ep93xx/include/mach/dma.h +++ b/arch/arm/mach-ep93xx/include/mach/dma.h @@ -15,6 +15,8 @@ #include #include +#include +#include /** * struct ep93xx_dma_buffer - Information about a buffer to be transferred @@ -146,4 +148,89 @@ void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p, */ void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p); +/* + * M2P channels. + * + * Note that these values are also directly used for setting the PPALLOC + * register. + */ +#define EP93XX_DMA_I2S1 0 +#define EP93XX_DMA_I2S2 1 +#define EP93XX_DMA_AAC1 2 +#define EP93XX_DMA_AAC2 3 +#define EP93XX_DMA_AAC3 4 +#define EP93XX_DMA_I2S3 5 +#define EP93XX_DMA_UART1 6 +#define EP93XX_DMA_UART2 7 +#define EP93XX_DMA_UART3 8 +#define EP93XX_DMA_IRDA 9 +/* M2M channels */ +#define EP93XX_DMA_SSP 10 +#define EP93XX_DMA_IDE 11 + +/** + * struct ep93xx_dma_data - configuration data for the EP93xx dmaengine + * @port: peripheral which is requesting the channel + * @direction: TX/RX channel + * @name: optional name for the channel, this is displayed in /proc/interrupts + * + * This information is passed as private channel parameter in a filter + * function. Note that this is only needed for slave/cyclic channels. For + * memcpy channels %NULL data should be passed. + */ +struct ep93xx_dma_data { + int port; + enum dma_data_direction direction; + const char *name; +}; + +/** + * struct ep93xx_dma_chan_data - platform specific data for a DMA channel + * @name: name of the channel, used for getting the right clock for the channel + * @base: mapped registers + * @irq: interrupt number used by this channel + */ +struct ep93xx_dma_chan_data { + const char *name; + void __iomem *base; + int irq; +}; + +/** + * struct ep93xx_dma_platform_data - platform data for the dmaengine driver + * @channels: array of channels which are passed to the driver + * @num_channels: number of channels in the array + * + * This structure is passed to the DMA engine driver via platform data. For + * M2P channels, contract is that even channels are for TX and odd for RX. + * There is no requirement for the M2M channels. + */ +struct ep93xx_dma_platform_data { + struct ep93xx_dma_chan_data *channels; + size_t num_channels; +}; + +static inline bool ep93xx_dma_chan_is_m2p(struct dma_chan *chan) +{ + return !strcmp(dev_name(chan->device->dev), "ep93xx-dma-m2p"); +} + +/** + * ep93xx_dma_chan_direction - returns direction the channel can be used + * @chan: channel + * + * This function can be used in filter functions to find out whether the + * channel supports given DMA direction. Only M2P channels have such + * limitation, for M2M channels the direction is configurable. + */ +static inline enum dma_data_direction +ep93xx_dma_chan_direction(struct dma_chan *chan) +{ + if (!ep93xx_dma_chan_is_m2p(chan)) + return DMA_NONE; + + /* even channels are for TX, odd for RX */ + return (chan->chan_id % 2 == 0) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; +} + #endif /* __ASM_ARCH_DMA_H */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 25cf327cd1c..2e3b3d38c46 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -237,6 +237,13 @@ config MXS_DMA Support the MXS DMA engine. This engine including APBH-DMA and APBX-DMA is integrated into Freescale i.MX23/28 chips. +config EP93XX_DMA + bool "Cirrus Logic EP93xx DMA support" + depends on ARCH_EP93XX + select DMA_ENGINE + help + Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 836095ab3c5..30cf3b1f0c5 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o +obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c new file mode 100644 index 00000000000..0766c1e53b1 --- /dev/null +++ b/drivers/dma/ep93xx_dma.c @@ -0,0 +1,1355 @@ +/* + * Driver for the Cirrus Logic EP93xx DMA Controller + * + * Copyright (C) 2011 Mika Westerberg + * + * DMA M2P implementation is based on the original + * arch/arm/mach-ep93xx/dma-m2p.c which has following copyrights: + * + * Copyright (C) 2006 Lennert Buytenhek + * Copyright (C) 2006 Applied Data Systems + * Copyright (C) 2009 Ryan Mallon + * + * This driver is based on dw_dmac and amba-pl08x drivers. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* M2P registers */ +#define M2P_CONTROL 0x0000 +#define M2P_CONTROL_STALLINT BIT(0) +#define M2P_CONTROL_NFBINT BIT(1) +#define M2P_CONTROL_CH_ERROR_INT BIT(3) +#define M2P_CONTROL_ENABLE BIT(4) +#define M2P_CONTROL_ICE BIT(6) + +#define M2P_INTERRUPT 0x0004 +#define M2P_INTERRUPT_STALL BIT(0) +#define M2P_INTERRUPT_NFB BIT(1) +#define M2P_INTERRUPT_ERROR BIT(3) + +#define M2P_PPALLOC 0x0008 +#define M2P_STATUS 0x000c + +#define M2P_MAXCNT0 0x0020 +#define M2P_BASE0 0x0024 +#define M2P_MAXCNT1 0x0030 +#define M2P_BASE1 0x0034 + +#define M2P_STATE_IDLE 0 +#define M2P_STATE_STALL 1 +#define M2P_STATE_ON 2 +#define M2P_STATE_NEXT 3 + +/* M2M registers */ +#define M2M_CONTROL 0x0000 +#define M2M_CONTROL_DONEINT BIT(2) +#define M2M_CONTROL_ENABLE BIT(3) +#define M2M_CONTROL_START BIT(4) +#define M2M_CONTROL_DAH BIT(11) +#define M2M_CONTROL_SAH BIT(12) +#define M2M_CONTROL_PW_SHIFT 9 +#define M2M_CONTROL_PW_8 (0 << M2M_CONTROL_PW_SHIFT) +#define M2M_CONTROL_PW_16 (1 << M2M_CONTROL_PW_SHIFT) +#define M2M_CONTROL_PW_32 (2 << M2M_CONTROL_PW_SHIFT) +#define M2M_CONTROL_PW_MASK (3 << M2M_CONTROL_PW_SHIFT) +#define M2M_CONTROL_TM_SHIFT 13 +#define M2M_CONTROL_TM_TX (1 << M2M_CONTROL_TM_SHIFT) +#define M2M_CONTROL_TM_RX (2 << M2M_CONTROL_TM_SHIFT) +#define M2M_CONTROL_RSS_SHIFT 22 +#define M2M_CONTROL_RSS_SSPRX (1 << M2M_CONTROL_RSS_SHIFT) +#define M2M_CONTROL_RSS_SSPTX (2 << M2M_CONTROL_RSS_SHIFT) +#define M2M_CONTROL_RSS_IDE (3 << M2M_CONTROL_RSS_SHIFT) +#define M2M_CONTROL_NO_HDSK BIT(24) +#define M2M_CONTROL_PWSC_SHIFT 25 + +#define M2M_INTERRUPT 0x0004 +#define M2M_INTERRUPT_DONEINT BIT(1) + +#define M2M_BCR0 0x0010 +#define M2M_BCR1 0x0014 +#define M2M_SAR_BASE0 0x0018 +#define M2M_SAR_BASE1 0x001c +#define M2M_DAR_BASE0 0x002c +#define M2M_DAR_BASE1 0x0030 + +#define DMA_MAX_CHAN_BYTES 0xffff +#define DMA_MAX_CHAN_DESCRIPTORS 32 + +struct ep93xx_dma_engine; + +/** + * struct ep93xx_dma_desc - EP93xx specific transaction descriptor + * @src_addr: source address of the transaction + * @dst_addr: destination address of the transaction + * @size: size of the transaction (in bytes) + * @complete: this descriptor is completed + * @txd: dmaengine API descriptor + * @tx_list: list of linked descriptors + * @node: link used for putting this into a channel queue + */ +struct ep93xx_dma_desc { + u32 src_addr; + u32 dst_addr; + size_t size; + bool complete; + struct dma_async_tx_descriptor txd; + struct list_head tx_list; + struct list_head node; +}; + +/** + * struct ep93xx_dma_chan - an EP93xx DMA M2P/M2M channel + * @chan: dmaengine API channel + * @edma: pointer to to the engine device + * @regs: memory mapped registers + * @irq: interrupt number of the channel + * @clk: clock used by this channel + * @tasklet: channel specific tasklet used for callbacks + * @lock: lock protecting the fields following + * @flags: flags for the channel + * @buffer: which buffer to use next (0/1) + * @last_completed: last completed cookie value + * @active: flattened chain of descriptors currently being processed + * @queue: pending descriptors which are handled next + * @free_list: list of free descriptors which can be used + * @runtime_addr: physical address currently used as dest/src (M2M only). This + * is set via %DMA_SLAVE_CONFIG before slave operation is + * prepared + * @runtime_ctrl: M2M runtime values for the control register. + * + * As EP93xx DMA controller doesn't support real chained DMA descriptors we + * will have slightly different scheme here: @active points to a head of + * flattened DMA descriptor chain. + * + * @queue holds pending transactions. These are linked through the first + * descriptor in the chain. When a descriptor is moved to the @active queue, + * the first and chained descriptors are flattened into a single list. + * + * @chan.private holds pointer to &struct ep93xx_dma_data which contains + * necessary channel configuration information. For memcpy channels this must + * be %NULL. + */ +struct ep93xx_dma_chan { + struct dma_chan chan; + const struct ep93xx_dma_engine *edma; + void __iomem *regs; + int irq; + struct clk *clk; + struct tasklet_struct tasklet; + /* protects the fields following */ + spinlock_t lock; + unsigned long flags; +/* Channel is configured for cyclic transfers */ +#define EP93XX_DMA_IS_CYCLIC 0 + + int buffer; + dma_cookie_t last_completed; + struct list_head active; + struct list_head queue; + struct list_head free_list; + u32 runtime_addr; + u32 runtime_ctrl; +}; + +/** + * struct ep93xx_dma_engine - the EP93xx DMA engine instance + * @dma_dev: holds the dmaengine device + * @m2m: is this an M2M or M2P device + * @hw_setup: method which sets the channel up for operation + * @hw_shutdown: shuts the channel down and flushes whatever is left + * @hw_submit: pushes active descriptor(s) to the hardware + * @hw_interrupt: handle the interrupt + * @num_channels: number of channels for this instance + * @channels: array of channels + * + * There is one instance of this struct for the M2P channels and one for the + * M2M channels. hw_xxx() methods are used to perform operations which are + * different on M2M and M2P channels. These methods are called with channel + * lock held and interrupts disabled so they cannot sleep. + */ +struct ep93xx_dma_engine { + struct dma_device dma_dev; + bool m2m; + int (*hw_setup)(struct ep93xx_dma_chan *); + void (*hw_shutdown)(struct ep93xx_dma_chan *); + void (*hw_submit)(struct ep93xx_dma_chan *); + int (*hw_interrupt)(struct ep93xx_dma_chan *); +#define INTERRUPT_UNKNOWN 0 +#define INTERRUPT_DONE 1 +#define INTERRUPT_NEXT_BUFFER 2 + + size_t num_channels; + struct ep93xx_dma_chan channels[]; +}; + +static inline struct device *chan2dev(struct ep93xx_dma_chan *edmac) +{ + return &edmac->chan.dev->device; +} + +static struct ep93xx_dma_chan *to_ep93xx_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct ep93xx_dma_chan, chan); +} + +/** + * ep93xx_dma_set_active - set new active descriptor chain + * @edmac: channel + * @desc: head of the new active descriptor chain + * + * Sets @desc to be the head of the new active descriptor chain. This is the + * chain which is processed next. The active list must be empty before calling + * this function. + * + * Called with @edmac->lock held and interrupts disabled. + */ +static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac, + struct ep93xx_dma_desc *desc) +{ + BUG_ON(!list_empty(&edmac->active)); + + list_add_tail(&desc->node, &edmac->active); + + /* Flatten the @desc->tx_list chain into @edmac->active list */ + while (!list_empty(&desc->tx_list)) { + struct ep93xx_dma_desc *d = list_first_entry(&desc->tx_list, + struct ep93xx_dma_desc, node); + + /* + * We copy the callback parameters from the first descriptor + * to all the chained descriptors. This way we can call the + * callback without having to find out the first descriptor in + * the chain. Useful for cyclic transfers. + */ + d->txd.callback = desc->txd.callback; + d->txd.callback_param = desc->txd.callback_param; + + list_move_tail(&d->node, &edmac->active); + } +} + +/* Called with @edmac->lock held and interrupts disabled */ +static struct ep93xx_dma_desc * +ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) +{ + return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node); +} + +/** + * ep93xx_dma_advance_active - advances to the next active descriptor + * @edmac: channel + * + * Function advances active descriptor to the next in the @edmac->active and + * returns %true if we still have descriptors in the chain to process. + * Otherwise returns %false. + * + * When the channel is in cyclic mode always returns %true. + * + * Called with @edmac->lock held and interrupts disabled. + */ +static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac) +{ + list_rotate_left(&edmac->active); + + if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) + return true; + + /* + * If txd.cookie is set it means that we are back in the first + * descriptor in the chain and hence done with it. + */ + return !ep93xx_dma_get_active(edmac)->txd.cookie; +} + +/* + * M2P DMA implementation + */ + +static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control) +{ + writel(control, edmac->regs + M2P_CONTROL); + /* + * EP93xx User's Guide states that we must perform a dummy read after + * write to the control register. + */ + readl(edmac->regs + M2P_CONTROL); +} + +static int m2p_hw_setup(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_data *data = edmac->chan.private; + u32 control; + + writel(data->port & 0xf, edmac->regs + M2P_PPALLOC); + + control = M2P_CONTROL_CH_ERROR_INT | M2P_CONTROL_ICE + | M2P_CONTROL_ENABLE; + m2p_set_control(edmac, control); + + return 0; +} + +static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac) +{ + return (readl(edmac->regs + M2P_STATUS) >> 4) & 0x3; +} + +static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac) +{ + u32 control; + + control = readl(edmac->regs + M2P_CONTROL); + control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT); + m2p_set_control(edmac, control); + + while (m2p_channel_state(edmac) >= M2P_STATE_ON) + cpu_relax(); + + m2p_set_control(edmac, 0); + + while (m2p_channel_state(edmac) == M2P_STATE_STALL) + cpu_relax(); +} + +static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); + u32 bus_addr; + + if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_TO_DEVICE) + bus_addr = desc->src_addr; + else + bus_addr = desc->dst_addr; + + if (edmac->buffer == 0) { + writel(desc->size, edmac->regs + M2P_MAXCNT0); + writel(bus_addr, edmac->regs + M2P_BASE0); + } else { + writel(desc->size, edmac->regs + M2P_MAXCNT1); + writel(bus_addr, edmac->regs + M2P_BASE1); + } + + edmac->buffer ^= 1; +} + +static void m2p_hw_submit(struct ep93xx_dma_chan *edmac) +{ + u32 control = readl(edmac->regs + M2P_CONTROL); + + m2p_fill_desc(edmac); + control |= M2P_CONTROL_STALLINT; + + if (ep93xx_dma_advance_active(edmac)) { + m2p_fill_desc(edmac); + control |= M2P_CONTROL_NFBINT; + } + + m2p_set_control(edmac, control); +} + +static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac) +{ + u32 irq_status = readl(edmac->regs + M2P_INTERRUPT); + u32 control; + + if (irq_status & M2P_INTERRUPT_ERROR) { + struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); + + /* Clear the error interrupt */ + writel(1, edmac->regs + M2P_INTERRUPT); + + /* + * It seems that there is no easy way of reporting errors back + * to client so we just report the error here and continue as + * usual. + * + * Revisit this when there is a mechanism to report back the + * errors. + */ + dev_err(chan2dev(edmac), + "DMA transfer failed! Details:\n" + "\tcookie : %d\n" + "\tsrc_addr : 0x%08x\n" + "\tdst_addr : 0x%08x\n" + "\tsize : %zu\n", + desc->txd.cookie, desc->src_addr, desc->dst_addr, + desc->size); + } + + switch (irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) { + case M2P_INTERRUPT_STALL: + /* Disable interrupts */ + control = readl(edmac->regs + M2P_CONTROL); + control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT); + m2p_set_control(edmac, control); + + return INTERRUPT_DONE; + + case M2P_INTERRUPT_NFB: + if (ep93xx_dma_advance_active(edmac)) + m2p_fill_desc(edmac); + + return INTERRUPT_NEXT_BUFFER; + } + + return INTERRUPT_UNKNOWN; +} + +/* + * M2M DMA implementation + * + * For the M2M transfers we don't use NFB at all. This is because it simply + * doesn't work well with memcpy transfers. When you submit both buffers it is + * extremely unlikely that you get an NFB interrupt, but it instead reports + * DONE interrupt and both buffers are already transferred which means that we + * weren't able to update the next buffer. + * + * So for now we "simulate" NFB by just submitting buffer after buffer + * without double buffering. + */ + +static int m2m_hw_setup(struct ep93xx_dma_chan *edmac) +{ + const struct ep93xx_dma_data *data = edmac->chan.private; + u32 control = 0; + + if (!data) { + /* This is memcpy channel, nothing to configure */ + writel(control, edmac->regs + M2M_CONTROL); + return 0; + } + + switch (data->port) { + case EP93XX_DMA_SSP: + /* + * This was found via experimenting - anything less than 5 + * causes the channel to perform only a partial transfer which + * leads to problems since we don't get DONE interrupt then. + */ + control = (5 << M2M_CONTROL_PWSC_SHIFT); + control |= M2M_CONTROL_NO_HDSK; + + if (data->direction == DMA_TO_DEVICE) { + control |= M2M_CONTROL_DAH; + control |= M2M_CONTROL_TM_TX; + control |= M2M_CONTROL_RSS_SSPTX; + } else { + control |= M2M_CONTROL_SAH; + control |= M2M_CONTROL_TM_RX; + control |= M2M_CONTROL_RSS_SSPRX; + } + break; + + case EP93XX_DMA_IDE: + /* + * This IDE part is totally untested. Values below are taken + * from the EP93xx Users's Guide and might not be correct. + */ + control |= M2M_CONTROL_NO_HDSK; + control |= M2M_CONTROL_RSS_IDE; + control |= M2M_CONTROL_PW_16; + + if (data->direction == DMA_TO_DEVICE) { + /* Worst case from the UG */ + control = (3 << M2M_CONTROL_PWSC_SHIFT); + control |= M2M_CONTROL_DAH; + control |= M2M_CONTROL_TM_TX; + } else { + control = (2 << M2M_CONTROL_PWSC_SHIFT); + control |= M2M_CONTROL_SAH; + control |= M2M_CONTROL_TM_RX; + } + break; + + default: + return -EINVAL; + } + + writel(control, edmac->regs + M2M_CONTROL); + return 0; +} + +static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac) +{ + /* Just disable the channel */ + writel(0, edmac->regs + M2M_CONTROL); +} + +static void m2m_fill_desc(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); + + if (edmac->buffer == 0) { + writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0); + writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE0); + writel(desc->size, edmac->regs + M2M_BCR0); + } else { + writel(desc->src_addr, edmac->regs + M2M_SAR_BASE1); + writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE1); + writel(desc->size, edmac->regs + M2M_BCR1); + } + + edmac->buffer ^= 1; +} + +static void m2m_hw_submit(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_data *data = edmac->chan.private; + u32 control = readl(edmac->regs + M2M_CONTROL); + + /* + * Since we allow clients to configure PW (peripheral width) we always + * clear PW bits here and then set them according what is given in + * the runtime configuration. + */ + control &= ~M2M_CONTROL_PW_MASK; + control |= edmac->runtime_ctrl; + + m2m_fill_desc(edmac); + control |= M2M_CONTROL_DONEINT; + + /* + * Now we can finally enable the channel. For M2M channel this must be + * done _after_ the BCRx registers are programmed. + */ + control |= M2M_CONTROL_ENABLE; + writel(control, edmac->regs + M2M_CONTROL); + + if (!data) { + /* + * For memcpy channels the software trigger must be asserted + * in order to start the memcpy operation. + */ + control |= M2M_CONTROL_START; + writel(control, edmac->regs + M2M_CONTROL); + } +} + +static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac) +{ + u32 control; + + if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT)) + return INTERRUPT_UNKNOWN; + + /* Clear the DONE bit */ + writel(0, edmac->regs + M2M_INTERRUPT); + + /* Disable interrupts and the channel */ + control = readl(edmac->regs + M2M_CONTROL); + control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE); + writel(control, edmac->regs + M2M_CONTROL); + + /* + * Since we only get DONE interrupt we have to find out ourselves + * whether there still is something to process. So we try to advance + * the chain an see whether it succeeds. + */ + if (ep93xx_dma_advance_active(edmac)) { + edmac->edma->hw_submit(edmac); + return INTERRUPT_NEXT_BUFFER; + } + + return INTERRUPT_DONE; +} + +/* + * DMA engine API implementation + */ + +static struct ep93xx_dma_desc * +ep93xx_dma_desc_get(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_desc *desc, *_desc; + struct ep93xx_dma_desc *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&edmac->lock, flags); + list_for_each_entry_safe(desc, _desc, &edmac->free_list, node) { + if (async_tx_test_ack(&desc->txd)) { + list_del_init(&desc->node); + + /* Re-initialize the descriptor */ + desc->src_addr = 0; + desc->dst_addr = 0; + desc->size = 0; + desc->complete = false; + desc->txd.cookie = 0; + desc->txd.callback = NULL; + desc->txd.callback_param = NULL; + + ret = desc; + break; + } + } + spin_unlock_irqrestore(&edmac->lock, flags); + return ret; +} + +static void ep93xx_dma_desc_put(struct ep93xx_dma_chan *edmac, + struct ep93xx_dma_desc *desc) +{ + if (desc) { + unsigned long flags; + + spin_lock_irqsave(&edmac->lock, flags); + list_splice_init(&desc->tx_list, &edmac->free_list); + list_add(&desc->node, &edmac->free_list); + spin_unlock_irqrestore(&edmac->lock, flags); + } +} + +/** + * ep93xx_dma_advance_work - start processing the next pending transaction + * @edmac: channel + * + * If we have pending transactions queued and we are currently idling, this + * function takes the next queued transaction from the @edmac->queue and + * pushes it to the hardware for execution. + */ +static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_desc *new; + unsigned long flags; + + spin_lock_irqsave(&edmac->lock, flags); + if (!list_empty(&edmac->active) || list_empty(&edmac->queue)) { + spin_unlock_irqrestore(&edmac->lock, flags); + return; + } + + /* Take the next descriptor from the pending queue */ + new = list_first_entry(&edmac->queue, struct ep93xx_dma_desc, node); + list_del_init(&new->node); + + ep93xx_dma_set_active(edmac, new); + + /* Push it to the hardware */ + edmac->edma->hw_submit(edmac); + spin_unlock_irqrestore(&edmac->lock, flags); +} + +static void ep93xx_dma_unmap_buffers(struct ep93xx_dma_desc *desc) +{ + struct device *dev = desc->txd.chan->device->dev; + + if (!(desc->txd.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (desc->txd.flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(dev, desc->src_addr, desc->size, + DMA_TO_DEVICE); + else + dma_unmap_page(dev, desc->src_addr, desc->size, + DMA_TO_DEVICE); + } + if (!(desc->txd.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (desc->txd.flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(dev, desc->dst_addr, desc->size, + DMA_FROM_DEVICE); + else + dma_unmap_page(dev, desc->dst_addr, desc->size, + DMA_FROM_DEVICE); + } +} + +static void ep93xx_dma_tasklet(unsigned long data) +{ + struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data; + struct ep93xx_dma_desc *desc, *d; + dma_async_tx_callback callback; + void *callback_param; + LIST_HEAD(list); + + spin_lock_irq(&edmac->lock); + desc = ep93xx_dma_get_active(edmac); + if (desc->complete) { + edmac->last_completed = desc->txd.cookie; + list_splice_init(&edmac->active, &list); + } + spin_unlock_irq(&edmac->lock); + + /* Pick up the next descriptor from the queue */ + ep93xx_dma_advance_work(edmac); + + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + + /* Now we can release all the chained descriptors */ + list_for_each_entry_safe(desc, d, &list, node) { + /* + * For the memcpy channels the API requires us to unmap the + * buffers unless requested otherwise. + */ + if (!edmac->chan.private) + ep93xx_dma_unmap_buffers(desc); + + ep93xx_dma_desc_put(edmac, desc); + } + + if (callback) + callback(callback_param); +} + +static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id) +{ + struct ep93xx_dma_chan *edmac = dev_id; + irqreturn_t ret = IRQ_HANDLED; + + spin_lock(&edmac->lock); + + switch (edmac->edma->hw_interrupt(edmac)) { + case INTERRUPT_DONE: + ep93xx_dma_get_active(edmac)->complete = true; + tasklet_schedule(&edmac->tasklet); + break; + + case INTERRUPT_NEXT_BUFFER: + if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) + tasklet_schedule(&edmac->tasklet); + break; + + default: + dev_warn(chan2dev(edmac), "unknown interrupt!\n"); + ret = IRQ_NONE; + break; + } + + spin_unlock(&edmac->lock); + return ret; +} + +/** + * ep93xx_dma_tx_submit - set the prepared descriptor(s) to be executed + * @tx: descriptor to be executed + * + * Function will execute given descriptor on the hardware or if the hardware + * is busy, queue the descriptor to be executed later on. Returns cookie which + * can be used to poll the status of the descriptor. + */ +static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(tx->chan); + struct ep93xx_dma_desc *desc; + dma_cookie_t cookie; + unsigned long flags; + + spin_lock_irqsave(&edmac->lock, flags); + + cookie = edmac->chan.cookie; + + if (++cookie < 0) + cookie = 1; + + desc = container_of(tx, struct ep93xx_dma_desc, txd); + + edmac->chan.cookie = cookie; + desc->txd.cookie = cookie; + + /* + * If nothing is currently prosessed, we push this descriptor + * directly to the hardware. Otherwise we put the descriptor + * to the pending queue. + */ + if (list_empty(&edmac->active)) { + ep93xx_dma_set_active(edmac, desc); + edmac->edma->hw_submit(edmac); + } else { + list_add_tail(&desc->node, &edmac->queue); + } + + spin_unlock_irqrestore(&edmac->lock, flags); + return cookie; +} + +/** + * ep93xx_dma_alloc_chan_resources - allocate resources for the channel + * @chan: channel to allocate resources + * + * Function allocates necessary resources for the given DMA channel and + * returns number of allocated descriptors for the channel. Negative errno + * is returned in case of failure. + */ +static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct ep93xx_dma_data *data = chan->private; + const char *name = dma_chan_name(chan); + int ret, i; + + /* Sanity check the channel parameters */ + if (!edmac->edma->m2m) { + if (!data) + return -EINVAL; + if (data->port < EP93XX_DMA_I2S1 || + data->port > EP93XX_DMA_IRDA) + return -EINVAL; + if (data->direction != ep93xx_dma_chan_direction(chan)) + return -EINVAL; + } else { + if (data) { + switch (data->port) { + case EP93XX_DMA_SSP: + case EP93XX_DMA_IDE: + if (data->direction != DMA_TO_DEVICE && + data->direction != DMA_FROM_DEVICE) + return -EINVAL; + break; + default: + return -EINVAL; + } + } + } + + if (data && data->name) + name = data->name; + + ret = clk_enable(edmac->clk); + if (ret) + return ret; + + ret = request_irq(edmac->irq, ep93xx_dma_interrupt, 0, name, edmac); + if (ret) + goto fail_clk_disable; + + spin_lock_irq(&edmac->lock); + edmac->last_completed = 1; + edmac->chan.cookie = 1; + ret = edmac->edma->hw_setup(edmac); + spin_unlock_irq(&edmac->lock); + + if (ret) + goto fail_free_irq; + + for (i = 0; i < DMA_MAX_CHAN_DESCRIPTORS; i++) { + struct ep93xx_dma_desc *desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) { + dev_warn(chan2dev(edmac), "not enough descriptors\n"); + break; + } + + INIT_LIST_HEAD(&desc->tx_list); + + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.flags = DMA_CTRL_ACK; + desc->txd.tx_submit = ep93xx_dma_tx_submit; + + ep93xx_dma_desc_put(edmac, desc); + } + + return i; + +fail_free_irq: + free_irq(edmac->irq, edmac); +fail_clk_disable: + clk_disable(edmac->clk); + + return ret; +} + +/** + * ep93xx_dma_free_chan_resources - release resources for the channel + * @chan: channel + * + * Function releases all the resources allocated for the given channel. + * The channel must be idle when this is called. + */ +static void ep93xx_dma_free_chan_resources(struct dma_chan *chan) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct ep93xx_dma_desc *desc, *d; + unsigned long flags; + LIST_HEAD(list); + + BUG_ON(!list_empty(&edmac->active)); + BUG_ON(!list_empty(&edmac->queue)); + + spin_lock_irqsave(&edmac->lock, flags); + edmac->edma->hw_shutdown(edmac); + edmac->runtime_addr = 0; + edmac->runtime_ctrl = 0; + edmac->buffer = 0; + list_splice_init(&edmac->free_list, &list); + spin_unlock_irqrestore(&edmac->lock, flags); + + list_for_each_entry_safe(desc, d, &list, node) + kfree(desc); + + clk_disable(edmac->clk); + free_irq(edmac->irq, edmac); +} + +/** + * ep93xx_dma_prep_dma_memcpy - prepare a memcpy DMA operation + * @chan: channel + * @dest: destination bus address + * @src: source bus address + * @len: size of the transaction + * @flags: flags for the descriptor + * + * Returns a valid DMA descriptor or %NULL in case of failure. + */ +struct dma_async_tx_descriptor * +ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, + dma_addr_t src, size_t len, unsigned long flags) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct ep93xx_dma_desc *desc, *first; + size_t bytes, offset; + + first = NULL; + for (offset = 0; offset < len; offset += bytes) { + desc = ep93xx_dma_desc_get(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), "couln't get descriptor\n"); + goto fail; + } + + bytes = min_t(size_t, len - offset, DMA_MAX_CHAN_BYTES); + + desc->src_addr = src + offset; + desc->dst_addr = dest + offset; + desc->size = bytes; + + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->tx_list); + } + + first->txd.cookie = -EBUSY; + first->txd.flags = flags; + + return &first->txd; +fail: + ep93xx_dma_desc_put(edmac, first); + return NULL; +} + +/** + * ep93xx_dma_prep_slave_sg - prepare a slave DMA operation + * @chan: channel + * @sgl: list of buffers to transfer + * @sg_len: number of entries in @sgl + * @dir: direction of tha DMA transfer + * @flags: flags for the descriptor + * + * Returns a valid DMA descriptor or %NULL in case of failure. + */ +static struct dma_async_tx_descriptor * +ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction dir, + unsigned long flags) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct ep93xx_dma_desc *desc, *first; + struct scatterlist *sg; + int i; + + if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) { + dev_warn(chan2dev(edmac), + "channel was configured with different direction\n"); + return NULL; + } + + if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) { + dev_warn(chan2dev(edmac), + "channel is already used for cyclic transfers\n"); + return NULL; + } + + first = NULL; + for_each_sg(sgl, sg, sg_len, i) { + size_t sg_len = sg_dma_len(sg); + + if (sg_len > DMA_MAX_CHAN_BYTES) { + dev_warn(chan2dev(edmac), "too big transfer size %d\n", + sg_len); + goto fail; + } + + desc = ep93xx_dma_desc_get(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), "couln't get descriptor\n"); + goto fail; + } + + if (dir == DMA_TO_DEVICE) { + desc->src_addr = sg_dma_address(sg); + desc->dst_addr = edmac->runtime_addr; + } else { + desc->src_addr = edmac->runtime_addr; + desc->dst_addr = sg_dma_address(sg); + } + desc->size = sg_len; + + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->tx_list); + } + + first->txd.cookie = -EBUSY; + first->txd.flags = flags; + + return &first->txd; + +fail: + ep93xx_dma_desc_put(edmac, first); + return NULL; +} + +/** + * ep93xx_dma_prep_dma_cyclic - prepare a cyclic DMA operation + * @chan: channel + * @dma_addr: DMA mapped address of the buffer + * @buf_len: length of the buffer (in bytes) + * @period_len: lenght of a single period + * @dir: direction of the operation + * + * Prepares a descriptor for cyclic DMA operation. This means that once the + * descriptor is submitted, we will be submitting in a @period_len sized + * buffers and calling callback once the period has been elapsed. Transfer + * terminates only when client calls dmaengine_terminate_all() for this + * channel. + * + * Returns a valid DMA descriptor or %NULL in case of failure. + */ +static struct dma_async_tx_descriptor * +ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, + size_t buf_len, size_t period_len, + enum dma_data_direction dir) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct ep93xx_dma_desc *desc, *first; + size_t offset = 0; + + if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) { + dev_warn(chan2dev(edmac), + "channel was configured with different direction\n"); + return NULL; + } + + if (test_and_set_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) { + dev_warn(chan2dev(edmac), + "channel is already used for cyclic transfers\n"); + return NULL; + } + + if (period_len > DMA_MAX_CHAN_BYTES) { + dev_warn(chan2dev(edmac), "too big period length %d\n", + period_len); + return NULL; + } + + /* Split the buffer into period size chunks */ + first = NULL; + for (offset = 0; offset < buf_len; offset += period_len) { + desc = ep93xx_dma_desc_get(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), "couln't get descriptor\n"); + goto fail; + } + + if (dir == DMA_TO_DEVICE) { + desc->src_addr = dma_addr + offset; + desc->dst_addr = edmac->runtime_addr; + } else { + desc->src_addr = edmac->runtime_addr; + desc->dst_addr = dma_addr + offset; + } + + desc->size = period_len; + + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->tx_list); + } + + first->txd.cookie = -EBUSY; + + return &first->txd; + +fail: + ep93xx_dma_desc_put(edmac, first); + return NULL; +} + +/** + * ep93xx_dma_terminate_all - terminate all transactions + * @edmac: channel + * + * Stops all DMA transactions. All descriptors are put back to the + * @edmac->free_list and callbacks are _not_ called. + */ +static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac) +{ + struct ep93xx_dma_desc *desc, *_d; + unsigned long flags; + LIST_HEAD(list); + + spin_lock_irqsave(&edmac->lock, flags); + /* First we disable and flush the DMA channel */ + edmac->edma->hw_shutdown(edmac); + clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags); + list_splice_init(&edmac->active, &list); + list_splice_init(&edmac->queue, &list); + /* + * We then re-enable the channel. This way we can continue submitting + * the descriptors by just calling ->hw_submit() again. + */ + edmac->edma->hw_setup(edmac); + spin_unlock_irqrestore(&edmac->lock, flags); + + list_for_each_entry_safe(desc, _d, &list, node) + ep93xx_dma_desc_put(edmac, desc); + + return 0; +} + +static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac, + struct dma_slave_config *config) +{ + enum dma_slave_buswidth width; + unsigned long flags; + u32 addr, ctrl; + + if (!edmac->edma->m2m) + return -EINVAL; + + switch (config->direction) { + case DMA_FROM_DEVICE: + width = config->src_addr_width; + addr = config->src_addr; + break; + + case DMA_TO_DEVICE: + width = config->dst_addr_width; + addr = config->dst_addr; + break; + + default: + return -EINVAL; + } + + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + ctrl = 0; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + ctrl = M2M_CONTROL_PW_16; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + ctrl = M2M_CONTROL_PW_32; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&edmac->lock, flags); + edmac->runtime_addr = addr; + edmac->runtime_ctrl = ctrl; + spin_unlock_irqrestore(&edmac->lock, flags); + + return 0; +} + +/** + * ep93xx_dma_control - manipulate all pending operations on a channel + * @chan: channel + * @cmd: control command to perform + * @arg: optional argument + * + * Controls the channel. Function returns %0 in case of success or negative + * error in case of failure. + */ +static int ep93xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + struct dma_slave_config *config; + + switch (cmd) { + case DMA_TERMINATE_ALL: + return ep93xx_dma_terminate_all(edmac); + + case DMA_SLAVE_CONFIG: + config = (struct dma_slave_config *)arg; + return ep93xx_dma_slave_config(edmac, config); + + default: + break; + } + + return -ENOSYS; +} + +/** + * ep93xx_dma_tx_status - check if a transaction is completed + * @chan: channel + * @cookie: transaction specific cookie + * @state: state of the transaction is stored here if given + * + * This function can be used to query state of a given transaction. + */ +static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *state) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + dma_cookie_t last_used, last_completed; + enum dma_status ret; + unsigned long flags; + + spin_lock_irqsave(&edmac->lock, flags); + last_used = chan->cookie; + last_completed = edmac->last_completed; + spin_unlock_irqrestore(&edmac->lock, flags); + + ret = dma_async_is_complete(cookie, last_completed, last_used); + dma_set_tx_state(state, last_completed, last_used, 0); + + return ret; +} + +/** + * ep93xx_dma_issue_pending - push pending transactions to the hardware + * @chan: channel + * + * When this function is called, all pending transactions are pushed to the + * hardware and executed. + */ +static void ep93xx_dma_issue_pending(struct dma_chan *chan) +{ + ep93xx_dma_advance_work(to_ep93xx_dma_chan(chan)); +} + +static int __init ep93xx_dma_probe(struct platform_device *pdev) +{ + struct ep93xx_dma_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct ep93xx_dma_engine *edma; + struct dma_device *dma_dev; + size_t edma_size; + int ret, i; + + edma_size = pdata->num_channels * sizeof(struct ep93xx_dma_chan); + edma = kzalloc(sizeof(*edma) + edma_size, GFP_KERNEL); + if (!edma) + return -ENOMEM; + + dma_dev = &edma->dma_dev; + edma->m2m = platform_get_device_id(pdev)->driver_data; + edma->num_channels = pdata->num_channels; + + INIT_LIST_HEAD(&dma_dev->channels); + for (i = 0; i < pdata->num_channels; i++) { + const struct ep93xx_dma_chan_data *cdata = &pdata->channels[i]; + struct ep93xx_dma_chan *edmac = &edma->channels[i]; + + edmac->chan.device = dma_dev; + edmac->regs = cdata->base; + edmac->irq = cdata->irq; + edmac->edma = edma; + + edmac->clk = clk_get(NULL, cdata->name); + if (IS_ERR(edmac->clk)) { + dev_warn(&pdev->dev, "failed to get clock for %s\n", + cdata->name); + continue; + } + + spin_lock_init(&edmac->lock); + INIT_LIST_HEAD(&edmac->active); + INIT_LIST_HEAD(&edmac->queue); + INIT_LIST_HEAD(&edmac->free_list); + tasklet_init(&edmac->tasklet, ep93xx_dma_tasklet, + (unsigned long)edmac); + + list_add_tail(&edmac->chan.device_node, + &dma_dev->channels); + } + + dma_cap_zero(dma_dev->cap_mask); + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); + dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask); + + dma_dev->dev = &pdev->dev; + dma_dev->device_alloc_chan_resources = ep93xx_dma_alloc_chan_resources; + dma_dev->device_free_chan_resources = ep93xx_dma_free_chan_resources; + dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg; + dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic; + dma_dev->device_control = ep93xx_dma_control; + dma_dev->device_issue_pending = ep93xx_dma_issue_pending; + dma_dev->device_tx_status = ep93xx_dma_tx_status; + + dma_set_max_seg_size(dma_dev->dev, DMA_MAX_CHAN_BYTES); + + if (edma->m2m) { + dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); + dma_dev->device_prep_dma_memcpy = ep93xx_dma_prep_dma_memcpy; + + edma->hw_setup = m2m_hw_setup; + edma->hw_shutdown = m2m_hw_shutdown; + edma->hw_submit = m2m_hw_submit; + edma->hw_interrupt = m2m_hw_interrupt; + } else { + dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask); + + edma->hw_setup = m2p_hw_setup; + edma->hw_shutdown = m2p_hw_shutdown; + edma->hw_submit = m2p_hw_submit; + edma->hw_interrupt = m2p_hw_interrupt; + } + + ret = dma_async_device_register(dma_dev); + if (unlikely(ret)) { + for (i = 0; i < edma->num_channels; i++) { + struct ep93xx_dma_chan *edmac = &edma->channels[i]; + if (!IS_ERR_OR_NULL(edmac->clk)) + clk_put(edmac->clk); + } + kfree(edma); + } else { + dev_info(dma_dev->dev, "EP93xx M2%s DMA ready\n", + edma->m2m ? "M" : "P"); + } + + return ret; +} + +static struct platform_device_id ep93xx_dma_driver_ids[] = { + { "ep93xx-dma-m2p", 0 }, + { "ep93xx-dma-m2m", 1 }, + { }, +}; + +static struct platform_driver ep93xx_dma_driver = { + .driver = { + .name = "ep93xx-dma", + }, + .id_table = ep93xx_dma_driver_ids, +}; + +static int __init ep93xx_dma_module_init(void) +{ + return platform_driver_probe(&ep93xx_dma_driver, ep93xx_dma_probe); +} +subsys_initcall(ep93xx_dma_module_init); + +MODULE_AUTHOR("Mika Westerberg "); +MODULE_DESCRIPTION("EP93xx DMA driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From d41071575b0b20b780bb0e8e7e70c62c1b07a883 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sun, 29 May 2011 13:10:06 +0300 Subject: spi/ep93xx: add DMA support This patch adds DMA support for the EP93xx SPI driver. By default the DMA is not enabled but it can be enabled by setting ep93xx_spi_info.use_dma to true in board configuration file. Note that the SPI driver still uses PIO for small transfers (<= 8 bytes) for performance reasons. Signed-off-by: Mika Westerberg Acked-by: H Hartley Sweeten Cc: Grant Likely Acked-by: Grant Likely Signed-off-by: Vinod Koul --- Documentation/spi/ep93xx_spi | 10 + arch/arm/mach-ep93xx/core.c | 6 +- arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h | 2 + drivers/spi/ep93xx_spi.c | 303 ++++++++++++++++++++++++- 4 files changed, 308 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi index 6325f5b4863..d8eb01c15db 100644 --- a/Documentation/spi/ep93xx_spi +++ b/Documentation/spi/ep93xx_spi @@ -88,6 +88,16 @@ static void __init ts72xx_init_machine(void) ARRAY_SIZE(ts72xx_spi_devices)); } +The driver can use DMA for the transfers also. In this case ts72xx_spi_info +becomes: + +static struct ep93xx_spi_info ts72xx_spi_info = { + .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), + .use_dma = true; +}; + +Note that CONFIG_EP93XX_DMA should be enabled as well. + Thanks to ========= Martin Guy, H. Hartley Sweeten and others who helped me during development of diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 82079545adc..cc9f1d4b104 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -488,11 +488,15 @@ static struct resource ep93xx_spi_resources[] = { }, }; +static u64 ep93xx_spi_dma_mask = DMA_BIT_MASK(32); + static struct platform_device ep93xx_spi_device = { .name = "ep93xx-spi", .id = 0, .dev = { - .platform_data = &ep93xx_spi_master_data, + .platform_data = &ep93xx_spi_master_data, + .coherent_dma_mask = DMA_BIT_MASK(32), + .dma_mask = &ep93xx_spi_dma_mask, }, .num_resources = ARRAY_SIZE(ep93xx_spi_resources), .resource = ep93xx_spi_resources, diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h index 0a37961b345..9bb63ac13f0 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h @@ -7,9 +7,11 @@ struct spi_device; * struct ep93xx_spi_info - EP93xx specific SPI descriptor * @num_chipselect: number of chip selects on this board, must be * at least one + * @use_dma: use DMA for the transfers */ struct ep93xx_spi_info { int num_chipselect; + bool use_dma; }; /** diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c index d3570071e98..1cf645479bf 100644 --- a/drivers/spi/ep93xx_spi.c +++ b/drivers/spi/ep93xx_spi.c @@ -1,7 +1,7 @@ /* * Driver for Cirrus Logic EP93xx SPI controller. * - * Copyright (c) 2010 Mika Westerberg + * Copyright (C) 2010-2011 Mika Westerberg * * Explicit FIFO handling code was inspired by amba-pl022 driver. * @@ -21,13 +21,16 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #define SSPCR0 0x0000 @@ -71,6 +74,7 @@ * @pdev: pointer to platform device * @clk: clock for the controller * @regs_base: pointer to ioremap()'d registers + * @sspdr_phys: physical address of the SSPDR register * @irq: IRQ number used by the driver * @min_rate: minimum clock rate (in Hz) supported by the controller * @max_rate: maximum clock rate (in Hz) supported by the controller @@ -84,6 +88,14 @@ * @rx: current byte in transfer to receive * @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one * frame decreases this level and sending one frame increases it. + * @dma_rx: RX DMA channel + * @dma_tx: TX DMA channel + * @dma_rx_data: RX parameters passed to the DMA engine + * @dma_tx_data: TX parameters passed to the DMA engine + * @rx_sgt: sg table for RX transfers + * @tx_sgt: sg table for TX transfers + * @zeropage: dummy page used as RX buffer when only TX buffer is passed in by + * the client * * This structure holds EP93xx SPI controller specific information. When * @running is %true, driver accepts transfer requests from protocol drivers. @@ -100,6 +112,7 @@ struct ep93xx_spi { const struct platform_device *pdev; struct clk *clk; void __iomem *regs_base; + unsigned long sspdr_phys; int irq; unsigned long min_rate; unsigned long max_rate; @@ -112,6 +125,13 @@ struct ep93xx_spi { size_t tx; size_t rx; size_t fifo_level; + struct dma_chan *dma_rx; + struct dma_chan *dma_tx; + struct ep93xx_dma_data dma_rx_data; + struct ep93xx_dma_data dma_tx_data; + struct sg_table rx_sgt; + struct sg_table tx_sgt; + void *zeropage; }; /** @@ -496,14 +516,195 @@ static int ep93xx_spi_read_write(struct ep93xx_spi *espi) espi->fifo_level++; } - if (espi->rx == t->len) { - msg->actual_length += t->len; + if (espi->rx == t->len) return 0; - } return -EINPROGRESS; } +static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi) +{ + /* + * Now everything is set up for the current transfer. We prime the TX + * FIFO, enable interrupts, and wait for the transfer to complete. + */ + if (ep93xx_spi_read_write(espi)) { + ep93xx_spi_enable_interrupts(espi); + wait_for_completion(&espi->wait); + } +} + +/** + * ep93xx_spi_dma_prepare() - prepares a DMA transfer + * @espi: ep93xx SPI controller struct + * @dir: DMA transfer direction + * + * Function configures the DMA, maps the buffer and prepares the DMA + * descriptor. Returns a valid DMA descriptor in case of success and ERR_PTR + * in case of failure. + */ +static struct dma_async_tx_descriptor * +ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) +{ + struct spi_transfer *t = espi->current_msg->state; + struct dma_async_tx_descriptor *txd; + enum dma_slave_buswidth buswidth; + struct dma_slave_config conf; + struct scatterlist *sg; + struct sg_table *sgt; + struct dma_chan *chan; + const void *buf, *pbuf; + size_t len = t->len; + int i, ret, nents; + + if (bits_per_word(espi) > 8) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + + memset(&conf, 0, sizeof(conf)); + conf.direction = dir; + + if (dir == DMA_FROM_DEVICE) { + chan = espi->dma_rx; + buf = t->rx_buf; + sgt = &espi->rx_sgt; + + conf.src_addr = espi->sspdr_phys; + conf.src_addr_width = buswidth; + } else { + chan = espi->dma_tx; + buf = t->tx_buf; + sgt = &espi->tx_sgt; + + conf.dst_addr = espi->sspdr_phys; + conf.dst_addr_width = buswidth; + } + + ret = dmaengine_slave_config(chan, &conf); + if (ret) + return ERR_PTR(ret); + + /* + * We need to split the transfer into PAGE_SIZE'd chunks. This is + * because we are using @espi->zeropage to provide a zero RX buffer + * for the TX transfers and we have only allocated one page for that. + * + * For performance reasons we allocate a new sg_table only when + * needed. Otherwise we will re-use the current one. Eventually the + * last sg_table is released in ep93xx_spi_release_dma(). + */ + + nents = DIV_ROUND_UP(len, PAGE_SIZE); + if (nents != sgt->nents) { + sg_free_table(sgt); + + ret = sg_alloc_table(sgt, nents, GFP_KERNEL); + if (ret) + return ERR_PTR(ret); + } + + pbuf = buf; + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + size_t bytes = min_t(size_t, len, PAGE_SIZE); + + if (buf) { + sg_set_page(sg, virt_to_page(pbuf), bytes, + offset_in_page(pbuf)); + } else { + sg_set_page(sg, virt_to_page(espi->zeropage), + bytes, 0); + } + + pbuf += bytes; + len -= bytes; + } + + if (WARN_ON(len)) { + dev_warn(&espi->pdev->dev, "len = %d expected 0!", len); + return ERR_PTR(-EINVAL); + } + + nents = dma_map_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); + if (!nents) + return ERR_PTR(-ENOMEM); + + txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents, + dir, DMA_CTRL_ACK); + if (!txd) { + dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); + return ERR_PTR(-ENOMEM); + } + return txd; +} + +/** + * ep93xx_spi_dma_finish() - finishes with a DMA transfer + * @espi: ep93xx SPI controller struct + * @dir: DMA transfer direction + * + * Function finishes with the DMA transfer. After this, the DMA buffer is + * unmapped. + */ +static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi, + enum dma_data_direction dir) +{ + struct dma_chan *chan; + struct sg_table *sgt; + + if (dir == DMA_FROM_DEVICE) { + chan = espi->dma_rx; + sgt = &espi->rx_sgt; + } else { + chan = espi->dma_tx; + sgt = &espi->tx_sgt; + } + + dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); +} + +static void ep93xx_spi_dma_callback(void *callback_param) +{ + complete(callback_param); +} + +static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi) +{ + struct spi_message *msg = espi->current_msg; + struct dma_async_tx_descriptor *rxd, *txd; + + rxd = ep93xx_spi_dma_prepare(espi, DMA_FROM_DEVICE); + if (IS_ERR(rxd)) { + dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd)); + msg->status = PTR_ERR(rxd); + return; + } + + txd = ep93xx_spi_dma_prepare(espi, DMA_TO_DEVICE); + if (IS_ERR(txd)) { + ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE); + dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd)); + msg->status = PTR_ERR(txd); + return; + } + + /* We are ready when RX is done */ + rxd->callback = ep93xx_spi_dma_callback; + rxd->callback_param = &espi->wait; + + /* Now submit both descriptors and wait while they finish */ + dmaengine_submit(rxd); + dmaengine_submit(txd); + + dma_async_issue_pending(espi->dma_rx); + dma_async_issue_pending(espi->dma_tx); + + wait_for_completion(&espi->wait); + + ep93xx_spi_dma_finish(espi, DMA_TO_DEVICE); + ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE); +} + /** * ep93xx_spi_process_transfer() - processes one SPI transfer * @espi: ep93xx SPI controller struct @@ -556,13 +757,14 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, espi->tx = 0; /* - * Now everything is set up for the current transfer. We prime the TX - * FIFO, enable interrupts, and wait for the transfer to complete. + * There is no point of setting up DMA for the transfers which will + * fit into the FIFO and can be transferred with a single interrupt. + * So in these cases we will be using PIO and don't bother for DMA. */ - if (ep93xx_spi_read_write(espi)) { - ep93xx_spi_enable_interrupts(espi); - wait_for_completion(&espi->wait); - } + if (espi->dma_rx && t->len > SPI_FIFO_SIZE) + ep93xx_spi_dma_transfer(espi); + else + ep93xx_spi_pio_transfer(espi); /* * In case of error during transmit, we bail out from processing @@ -571,6 +773,8 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, if (msg->status) return; + msg->actual_length += t->len; + /* * After this transfer is finished, perform any possible * post-transfer actions requested by the protocol driver. @@ -752,6 +956,75 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static bool ep93xx_spi_dma_filter(struct dma_chan *chan, void *filter_param) +{ + if (ep93xx_dma_chan_is_m2p(chan)) + return false; + + chan->private = filter_param; + return true; +} + +static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi) +{ + dma_cap_mask_t mask; + int ret; + + espi->zeropage = (void *)get_zeroed_page(GFP_KERNEL); + if (!espi->zeropage) + return -ENOMEM; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + espi->dma_rx_data.port = EP93XX_DMA_SSP; + espi->dma_rx_data.direction = DMA_FROM_DEVICE; + espi->dma_rx_data.name = "ep93xx-spi-rx"; + + espi->dma_rx = dma_request_channel(mask, ep93xx_spi_dma_filter, + &espi->dma_rx_data); + if (!espi->dma_rx) { + ret = -ENODEV; + goto fail_free_page; + } + + espi->dma_tx_data.port = EP93XX_DMA_SSP; + espi->dma_tx_data.direction = DMA_TO_DEVICE; + espi->dma_tx_data.name = "ep93xx-spi-tx"; + + espi->dma_tx = dma_request_channel(mask, ep93xx_spi_dma_filter, + &espi->dma_tx_data); + if (!espi->dma_tx) { + ret = -ENODEV; + goto fail_release_rx; + } + + return 0; + +fail_release_rx: + dma_release_channel(espi->dma_rx); + espi->dma_rx = NULL; +fail_free_page: + free_page((unsigned long)espi->zeropage); + + return ret; +} + +static void ep93xx_spi_release_dma(struct ep93xx_spi *espi) +{ + if (espi->dma_rx) { + dma_release_channel(espi->dma_rx); + sg_free_table(&espi->rx_sgt); + } + if (espi->dma_tx) { + dma_release_channel(espi->dma_tx); + sg_free_table(&espi->tx_sgt); + } + + if (espi->zeropage) + free_page((unsigned long)espi->zeropage); +} + static int __init ep93xx_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -818,6 +1091,7 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev) goto fail_put_clock; } + espi->sspdr_phys = res->start + SSPDR; espi->regs_base = ioremap(res->start, resource_size(res)); if (!espi->regs_base) { dev_err(&pdev->dev, "failed to map resources\n"); @@ -832,10 +1106,13 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev) goto fail_unmap_regs; } + if (info->use_dma && ep93xx_spi_setup_dma(espi)) + dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n"); + espi->wq = create_singlethread_workqueue("ep93xx_spid"); if (!espi->wq) { dev_err(&pdev->dev, "unable to create workqueue\n"); - goto fail_free_irq; + goto fail_free_dma; } INIT_WORK(&espi->msg_work, ep93xx_spi_work); INIT_LIST_HEAD(&espi->msg_queue); @@ -857,7 +1134,8 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev) fail_free_queue: destroy_workqueue(espi->wq); -fail_free_irq: +fail_free_dma: + ep93xx_spi_release_dma(espi); free_irq(espi->irq, espi); fail_unmap_regs: iounmap(espi->regs_base); @@ -901,6 +1179,7 @@ static int __exit ep93xx_spi_remove(struct platform_device *pdev) } spin_unlock_irq(&espi->lock); + ep93xx_spi_release_dma(espi); free_irq(espi->irq, espi); iounmap(espi->regs_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3-70-g09d2 From 7dab35c0c01c5d960d7b551a607270adccfadb42 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 1 Jun 2011 15:10:30 -0700 Subject: dma: ipu_idmac.c: use resource_size in ioremap Signed-off-by: H Hartley Sweeten Cc: Dan Williams Cc: Vinod Koul Cc: Guennadi Liakhovetski Cc: Anatolij Gustschin Signed-off-by: Vinod Koul --- drivers/dma/ipu/ipu_idmac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index c1a125e7d1d..25447a8ca28 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1705,16 +1705,14 @@ static int __init ipu_probe(struct platform_device *pdev) ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); /* Remap IPU common registers */ - ipu_data.reg_ipu = ioremap(mem_ipu->start, - mem_ipu->end - mem_ipu->start + 1); + ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); if (!ipu_data.reg_ipu) { ret = -ENOMEM; goto err_ioremap_ipu; } /* Remap Image Converter and Image DMA Controller registers */ - ipu_data.reg_ic = ioremap(mem_ic->start, - mem_ic->end - mem_ic->start + 1); + ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic)); if (!ipu_data.reg_ic) { ret = -ENOMEM; goto err_ioremap_ic; -- cgit v1.2.3-70-g09d2 From 114df7d66efd5c23561782f38e97c48fb30d4f5d Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 1 Jun 2011 15:16:09 -0700 Subject: dma: at_hdmac.c: use resource_size Signed-off-by: H Hartley Sweeten Cc: Dan Williams Cc: Vinod Koul Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 36144f88d71..6a483eac7b3 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1216,7 +1216,7 @@ static int __init at_dma_probe(struct platform_device *pdev) atdma->dma_common.cap_mask = pdata->cap_mask; atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; - size = io->end - io->start + 1; + size = resource_size(io); if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { err = -EBUSY; goto err_kfree; @@ -1362,7 +1362,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) atdma->regs = NULL; io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(io->start, io->end - io->start + 1); + release_mem_region(io->start, resource_size(io)); kfree(atdma); -- cgit v1.2.3-70-g09d2 From a01cdc10689f5d252530d14474528ea785ecfde4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 17:06:25 +0900 Subject: serial: sh-sci: Tidy up ioread/write wrappers, kill off unused SCI helper. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.h | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 4dc249ecc59..923ebd908e7 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -181,32 +181,25 @@ #define SCI_MAJOR 204 #define SCI_MINOR_START 8 -#define SCI_IN(size, offset) \ - if ((size) == 8) { \ - return ioread8(port->membase + (offset)); \ - } else { \ - return ioread16(port->membase + (offset)); \ - } -#define SCI_OUT(size, offset, value) \ - if ((size) == 8) { \ - iowrite8(value, port->membase + (offset)); \ - } else if ((size) == 16) { \ - iowrite16(value, port->membase + (offset)); \ - } +#define SCI_IN(size, offset) \ + ioread##size(port->membase + (offset)) + +#define SCI_OUT(size, offset, value) \ + iowrite##size(value, port->membase + (offset)) #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_IN(scif_size, scif_offset) \ + return SCI_IN(scif_size, scif_offset); \ } else { /* PORT_SCI or PORT_SCIFA */ \ - SCI_IN(sci_size, sci_offset); \ + return SCI_IN(sci_size, sci_offset); \ } \ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_OUT(scif_size, scif_offset, value) \ + SCI_OUT(scif_size, scif_offset, value); \ } else { /* PORT_SCI or PORT_SCIFA */ \ SCI_OUT(sci_size, sci_offset, value); \ } \ @@ -215,23 +208,13 @@ #define CPU_SCIF_FNS(name, scif_offset, scif_size) \ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ - SCI_IN(scif_size, scif_offset); \ + return SCI_IN(scif_size, scif_offset); \ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ SCI_OUT(scif_size, scif_offset, value); \ } -#define CPU_SCI_FNS(name, sci_offset, sci_size) \ - static inline unsigned int sci_##name##_in(struct uart_port* port) \ - { \ - SCI_IN(sci_size, sci_offset); \ - } \ - static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \ - { \ - SCI_OUT(sci_size, sci_offset, value); \ - } - #if defined(CONFIG_CPU_SH3) || \ defined(CONFIG_ARCH_SH73A0) || \ defined(CONFIG_ARCH_SH7367) || \ -- cgit v1.2.3-70-g09d2 From b03034016184b7e9fd19f2a24ffb131953fdcc41 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 17:13:20 +0900 Subject: serial: sh-sci: Kill off some more unused definitions. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 923ebd908e7..ed1c09c0454 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -9,8 +9,6 @@ # define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ # define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7705) -# define SCIF0 0xA4400000 -# define SCIF2 0xA4410000 # define SCPCR 0xA4000116 # define SCPDR 0xA4000136 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ @@ -48,8 +46,6 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7343) # define SCSPTR0 0xffe00010 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7722) -# define PADR 0xA4050120 -# define PSDR 0xA405013e # define PWDR 0xA4050166 # define PSCR 0xA405011E # define SCIF_ORER 0x0001 /* overrun error bit */ -- cgit v1.2.3-70-g09d2 From debf9507166eede1e676d27d3298cdfb27399cb4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 18:19:37 +0900 Subject: serial: sh-sci: Generalize overrun handling. This consolidates all of the broken out overrun handling and ensures that we have sensible defaults per-port type, in addition to making sure that overruns are flagged appropriately in the error mask for parts that haven't explicitly disabled support for it. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 51 ++++++++++++++++++++++++++++++++++++++------ drivers/tty/serial/sh-sci.h | 52 ++------------------------------------------- include/linux/serial_sci.h | 30 ++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 280c02af0ea..bb27885ea2e 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -563,13 +563,19 @@ static int sci_handle_errors(struct uart_port *port) int copied = 0; unsigned short status = sci_in(port, SCxSR); struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); - if (status & SCxSR_ORER(port)) { - /* overrun error */ - if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) - copied++; + /* + * Handle overruns, if supported. + */ + if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) { + if (status & (1 << s->cfg->overrun_bit)) { + /* overrun error */ + if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) + copied++; - dev_notice(port->dev, "overrun error"); + dev_notice(port->dev, "overrun error"); + } } if (status & SCxSR_FER(port)) { @@ -617,12 +623,19 @@ static int sci_handle_errors(struct uart_port *port) static int sci_handle_fifo_overrun(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); int copied = 0; + /* + * XXX: Technically not limited to non-SCIFs, it's simply the + * SCLSR check that is for the moment SCIF-specific. This + * probably wants to be revisited for SCIFA/B as well as for + * factoring in SCI overrun detection. + */ if (port->type != PORT_SCIF) return 0; - if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { + if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { sci_out(port, SCLSR, 0); tty_insert_flip_char(tty, 0, TTY_OVERRUN); @@ -1755,6 +1768,32 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_port->break_timer.function = sci_break_timer; init_timer(&sci_port->break_timer); + /* + * Establish some sensible defaults for the error detection. + */ + if (!p->error_mask) + p->error_mask = (p->type == PORT_SCI) ? + SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; + + /* + * Establish sensible defaults for the overrun detection, unless + * the part has explicitly disabled support for it. + */ + if (p->overrun_bit != SCIx_NOT_SUPPORTED) { + if (p->type == PORT_SCI) + p->overrun_bit = 5; + else if (p->scbrr_algo_id == SCBRR_ALGO_4) + p->overrun_bit = 9; + else + p->overrun_bit = 0; + + /* + * Make the error mask inclusive of overrun detection, if + * supported. + */ + p->error_mask |= (1 << p->overrun_bit); + } + sci_port->cfg = p; port->mapbase = p->mapbase; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index ed1c09c0454..caab353a98b 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -19,11 +19,9 @@ defined(CONFIG_ARCH_SH7372) # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL -# define SCIF_ORER 0x0200 /* overrun error bit */ #elif defined(CONFIG_SH_RTS7751R2D) # define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ @@ -32,15 +30,12 @@ defined(CONFIG_CPU_SUBTYPE_SH7751R) # define SCSPTR1 0xffe0001c /* 8 bit SCI */ # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7760) # define SCSPTR0 0xfe600024 /* 16 bit SCIF */ # define SCSPTR1 0xfe610024 /* 16 bit SCIF */ # define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) # define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ # define PACR 0xa4050100 # define PBCR 0xa4050102 #elif defined(CONFIG_CPU_SUBTYPE_SH7343) @@ -48,35 +43,24 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7722) # define PWDR 0xA4050166 # define PSCR 0xA405011E -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7366) # define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ # define SCSPTR0 SCPDR0 -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7723) # define SCSPTR0 0xa4050160 -# define SCIF_ORER 0x0001 /* overrun error bit */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7757) # define SCSPTR0 0xfe4b0020 -# define SCIF_ORER 0x0001 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7770) # define SCSPTR0 0xff923020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7780) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ defined(CONFIG_CPU_SUBTYPE_SH7203) || \ defined(CONFIG_CPU_SUBTYPE_SH7206) || \ @@ -84,36 +68,12 @@ # define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7619) # define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SHX3) # define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #else # error CPU subtype not defined #endif -/* SCxSR SCI */ -#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ - -#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) - -/* SCxSR SCIF */ -#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ - #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ @@ -121,35 +81,27 @@ defined(CONFIG_ARCH_SH7367) || \ defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) -# define SCIF_ORER 0x0200 -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) # define SCIF_RFDC_MASK 0x007f # define SCIF_TXROOM_MAX 64 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) # define SCIF_RFDC_MASK 0x007f # define SCIF_TXROOM_MAX 64 /* SH7763 SCIF2 support */ # define SCIF2_RFDC_MASK 0x001f # define SCIF2_TXROOM_MAX 16 #else -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) # define SCIF_RFDC_MASK 0x001f # define SCIF_TXROOM_MAX 16 #endif -#ifndef SCIF_ORER -#define SCIF_ORER 0x0000 -#endif - #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) -#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) #define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) #define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) #define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) + +#define SCxSR_ERRORS(port) (to_sci_port(port)->cfg->error_mask) #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index a2afc9fbe18..5fac3bccfd8 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -8,6 +8,8 @@ * Generic header for SuperH SCI(F) (used by sh/sh64/h8300 and related parts) */ +#define SCIx_NOT_SUPPORTED (-1) + enum { SCBRR_ALGO_1, /* ((clk + 16 * bps) / (16 * bps) - 1) */ SCBRR_ALGO_2, /* ((clk + 16 * bps) / (32 * bps) - 1) */ @@ -25,6 +27,28 @@ enum { #define SCSCR_CKE1 (1 << 1) #define SCSCR_CKE0 (1 << 0) +/* SCxSR SCI */ +#define SCI_TDRE 0x80 +#define SCI_RDRF 0x40 +#define SCI_ORER 0x20 +#define SCI_FER 0x10 +#define SCI_PER 0x08 +#define SCI_TEND 0x04 + +#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) + +/* SCxSR SCIF */ +#define SCIF_ER 0x0080 +#define SCIF_TEND 0x0040 +#define SCIF_TDFE 0x0020 +#define SCIF_BRK 0x0010 +#define SCIF_FER 0x0008 +#define SCIF_PER 0x0004 +#define SCIF_RDF 0x0002 +#define SCIF_DR 0x0001 + +#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) + /* Offsets into the sci_port->irqs array */ enum { SCIx_ERI_IRQ, @@ -56,6 +80,12 @@ struct plat_sci_port { unsigned int scbrr_algo_id; /* SCBRR calculation algo */ unsigned int scscr; /* SCSCR initialization */ + /* + * Platform overrides if necessary, defaults otherwise. + */ + int overrun_bit; + unsigned int error_mask; + struct device *dma_dev; unsigned int dma_slave_tx; -- cgit v1.2.3-70-g09d2 From 514820eb982eb85677ed2ecef9710e90e24fbdab Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 18:51:32 +0900 Subject: serial: sh-sci: Consolidate RXD pin handling. Non-SCI parts do not have the special port reg necessary for cases where the RX and SCI pins are muxed and need to be manually polled, so these like always fall back on the normal FIFO processing paths. SH7760 is in a class in and of itself with regards to mapping its SIM card interface via the SCI port class despite not having any of the RXD lines wired up and so implicitly behaving more like a SCIF in this regard. Out of the other CPUs, some support the port check via the same block while others do it through an external SuperI/O, so it's not even possible to perform the check relative to the ioremapped cookie offset, so the separate read semantics are preserved here, too. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh3/setup-sh770x.c | 1 + arch/sh/kernel/cpu/sh4/setup-sh7750.c | 1 + drivers/tty/serial/sh-sci.c | 13 +++++++++++++ drivers/tty/serial/sh-sci.h | 29 ----------------------------- include/linux/serial_sci.h | 2 ++ 5 files changed, 17 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 4551ad647c2..6d549792f79 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -108,6 +108,7 @@ static struct platform_device rtc_device = { static struct plat_sci_port scif0_platform_data = { .mapbase = 0xfffffe80, + .port_reg = 0xa4000136, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index e53b4b38bd1..8ea26e79118 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -38,6 +38,7 @@ static struct platform_device rtc_device = { static struct plat_sci_port sci_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xffe0001C .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bb27885ea2e..3248ddaa889 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -363,6 +363,19 @@ static int sci_rxfill(struct uart_port *port) return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } +/* + * SCI helper for checking the state of the muxed port/RXD pins. + */ +static inline int sci_rxd_in(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (s->cfg->port_reg <= 0) + return 1; + + return !!__raw_readb(s->cfg->port_reg); +} + /* ********************************************************************** * * the interrupt related routines * * ********************************************************************** */ diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index caab353a98b..1c20f7f9ba4 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -287,32 +287,3 @@ SCIF_FNS(SCLSR, 0, 0, 0x24, 16) #endif #define sci_in(port, reg) sci_##reg##_in(port) #define sci_out(port, reg, value) sci_##reg##_out(port, value) - -#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfffffe80) - return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#else /* default case for non-SCI processors */ -static inline int sci_rxd_in(struct uart_port *port) -{ - return 1; -} -#endif diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 5fac3bccfd8..ecefec7c0b6 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -86,6 +86,8 @@ struct plat_sci_port { int overrun_bit; unsigned int error_mask; + int port_reg; + struct device *dma_dev; unsigned int dma_slave_tx; -- cgit v1.2.3-70-g09d2 From e13198894bf6308c097e5678ee315e12b2e1b7a8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 19:13:06 +0900 Subject: serial: sh-sci: More unused define purging. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.h | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 1c20f7f9ba4..5834f33d20f 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,15 +2,13 @@ #include #include -#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7706) || \ defined(CONFIG_CPU_SUBTYPE_SH7707) || \ defined(CONFIG_CPU_SUBTYPE_SH7708) || \ defined(CONFIG_CPU_SUBTYPE_SH7709) # define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ # define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) -# define SCPCR 0xA4000116 -# define SCPDR 0xA4000136 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH73A0) || \ @@ -19,20 +17,16 @@ defined(CONFIG_ARCH_SH7372) # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL -#elif defined(CONFIG_SH_RTS7751R2D) -# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ defined(CONFIG_CPU_SUBTYPE_SH7091) || \ defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) -# define SCSPTR1 0xffe0001c /* 8 bit SCI */ + defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ + defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7760) # define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ # define SCSPTR2 0xfe620024 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) # define SCSPTR0 0xA4400000 /* 16 bit SCIF */ @@ -48,16 +42,13 @@ # define SCSPTR0 SCPDR0 #elif defined(CONFIG_CPU_SUBTYPE_SH7723) # define SCSPTR0 0xa4050160 -#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) -# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7757) # define SCSPTR0 0xfe4b0020 -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7770) # define SCSPTR0 0xff923020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7780) -# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -- cgit v1.2.3-70-g09d2 From c08957a2cf3c4a14e68d72c845d3c52cf3d826e1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Jun 2011 23:36:18 +0100 Subject: regulator: Properly register dummy regulator driver Recent changes in the driver core appear to mean that the data structures for the driver core are not fully initialised unless the driver is bound. Make sure the driver core knows the dummy driver is in use by binding it to a driver. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/dummy.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index c7410bde7b5..f6ef6694ab9 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -36,6 +36,29 @@ static struct regulator_desc dummy_desc = { .ops = &dummy_ops, }; +static int __devinit dummy_regulator_probe(struct platform_device *pdev) +{ + int ret; + + dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, + &dummy_initdata, NULL); + if (IS_ERR(dummy_regulator_rdev)) { + ret = PTR_ERR(dummy_regulator_rdev); + pr_err("Failed to register regulator: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver dummy_regulator_driver = { + .probe = dummy_regulator_probe, + .driver = { + .name = "reg-dummy", + .owner = THIS_MODULE, + }, +}; + static struct platform_device *dummy_pdev; void __init regulator_dummy_init(void) @@ -55,12 +78,9 @@ void __init regulator_dummy_init(void) return; } - dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, - &dummy_initdata, NULL); - if (IS_ERR(dummy_regulator_rdev)) { - ret = PTR_ERR(dummy_regulator_rdev); - pr_err("Failed to register regulator: %d\n", ret); + ret = platform_driver_register(&dummy_regulator_driver); + if (ret != 0) { + pr_err("Failed to register dummy regulator driver: %d\n", ret); platform_device_unregister(dummy_pdev); - return; } } -- cgit v1.2.3-70-g09d2 From f5726ae33c382366ea1b23240d5620dcf675d81d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 Jun 2011 16:22:20 +0100 Subject: regulator: Increase the limit on sysfs file names With verbose filenames we can easily hit 32 characters. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7b38af90a01..75312bd6aac 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1033,7 +1033,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev) } } -#define REG_STR_SIZE 32 +#define REG_STR_SIZE 64 static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, -- cgit v1.2.3-70-g09d2 From e0eaedefda8e14ed3f445f382c568c5d69e4223f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 Jun 2011 16:22:21 +0100 Subject: regulator: Include the device name in the microamps_requested_ file We may have multiple devices requesting a supply with the same name so include the device name in the generated filename for microamps_requested to avoid duplicate files. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75312bd6aac..e3b67ee48b2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1053,8 +1053,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, if (dev) { /* create a 'requested_microamps_name' sysfs entry */ - size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s", - supply_name); + size = scnprintf(buf, REG_STR_SIZE, + "microamps_requested_%s-%s", + dev_name(dev), supply_name); if (size >= REG_STR_SIZE) goto overflow_err; -- cgit v1.2.3-70-g09d2 From 3801b86aa482d26a8ae460f67fca29e016491a86 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 Jun 2011 16:22:22 +0100 Subject: regulator: Refactor supply implementation to work as regular consumers Currently the regulator supply implementation is somewhat complex and fragile as it doesn't look like standard consumers but is instead a parallel implementation. This causes issues with locking and reference counting. Move the implementation over to using standard consumers to address this. Rather than only notifying the supply on the first enable/disable we do so every time the regulator is enabled or disabled, simplifying locking as we don't need to hold a lock on the consumer we are about to enable. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 101 ++++++++++++++------------------------- include/linux/regulator/driver.h | 4 +- 2 files changed, 37 insertions(+), 68 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e3b67ee48b2..f0cc3983ffe 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -82,8 +82,7 @@ struct regulator { }; static int _regulator_is_enabled(struct regulator_dev *rdev); -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr); +static int _regulator_disable(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); @@ -91,6 +90,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name); static const char *rdev_get_name(struct regulator_dev *rdev) { @@ -930,21 +932,20 @@ out: * core if it's child is enabled. */ static int set_supply(struct regulator_dev *rdev, - struct regulator_dev *supply_rdev) + struct regulator_dev *supply_rdev) { int err; - err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, - "supply"); - if (err) { - rdev_err(rdev, "could not add device link %s err %d\n", - supply_rdev->dev.kobj.name, err); - goto out; + rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev)); + + rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY"); + if (IS_ERR(rdev->supply)) { + err = PTR_ERR(rdev->supply); + rdev->supply = NULL; + return err; } - rdev->supply = supply_rdev; - list_add(&rdev->slist, &supply_rdev->supply_list); -out: - return err; + + return 0; } /** @@ -1303,19 +1304,6 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret, delay; - if (rdev->use_count == 0) { - /* do we need to enable the supply regulator first */ - if (rdev->supply) { - mutex_lock(&rdev->supply->mutex); - ret = _regulator_enable(rdev->supply); - mutex_unlock(&rdev->supply->mutex); - if (ret < 0) { - rdev_err(rdev, "failed to enable: %d\n", ret); - return ret; - } - } - } - /* check voltage and requested load before enabling */ if (rdev->constraints && (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) @@ -1390,19 +1378,27 @@ int regulator_enable(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; int ret = 0; + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret != 0) + return ret; + } + mutex_lock(&rdev->mutex); ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); + + if (ret != 0) + regulator_disable(rdev->supply); + return ret; } EXPORT_SYMBOL_GPL(regulator_enable); /* locks held by regulator_disable() */ -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; - *supply_rdev_ptr = NULL; if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) @@ -1429,9 +1425,6 @@ static int _regulator_disable(struct regulator_dev *rdev, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - rdev->use_count = 0; } else if (rdev->use_count > 1) { @@ -1442,6 +1435,7 @@ static int _regulator_disable(struct regulator_dev *rdev, rdev->use_count--; } + return ret; } @@ -1460,29 +1454,21 @@ static int _regulator_disable(struct regulator_dev *rdev, int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret = 0; mutex_lock(&rdev->mutex); - ret = _regulator_disable(rdev, &supply_rdev); + ret = _regulator_disable(rdev); mutex_unlock(&rdev->mutex); - /* decrease our supplies ref count and disable if required */ - while (supply_rdev != NULL) { - rdev = supply_rdev; - - mutex_lock(&rdev->mutex); - _regulator_disable(rdev, &supply_rdev); - mutex_unlock(&rdev->mutex); - } + if (ret == 0 && rdev->supply) + regulator_disable(rdev->supply); return ret; } EXPORT_SYMBOL_GPL(regulator_disable); /* locks held by regulator_force_disable() */ -static int _regulator_force_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; @@ -1499,10 +1485,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev, REGULATOR_EVENT_DISABLE, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - - rdev->use_count = 0; return ret; } @@ -1518,16 +1500,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev, int regulator_force_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret; mutex_lock(&rdev->mutex); regulator->uA_load = 0; - ret = _regulator_force_disable(rdev, &supply_rdev); + ret = _regulator_force_disable(regulator->rdev); mutex_unlock(&rdev->mutex); - if (supply_rdev) - regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); + if (rdev->supply) + while (rdev->open_count--) + regulator_disable(rdev->supply); return ret; } @@ -2138,7 +2120,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) /* get input voltage */ input_uV = 0; if (rdev->supply) - input_uV = _regulator_get_voltage(rdev->supply); + input_uV = regulator_get_voltage(rdev->supply); if (input_uV <= 0) input_uV = rdev->constraints->input_uV; if (input_uV <= 0) { @@ -2208,17 +2190,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier); static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - struct regulator_dev *_rdev; - /* call rdev chain first */ blocking_notifier_call_chain(&rdev->notifier, event, NULL); - - /* now notify regulator we supply */ - list_for_each_entry(_rdev, &rdev->supply_list, slist) { - mutex_lock(&_rdev->mutex); - _notifier_call_chain(_rdev, event, data); - mutex_unlock(&_rdev->mutex); - } } /** @@ -2610,9 +2583,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); - INIT_LIST_HEAD(&rdev->supply_list); INIT_LIST_HEAD(&rdev->list); - INIT_LIST_HEAD(&rdev->slist); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); /* preform any regulator specific init */ @@ -2724,7 +2695,7 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) - sysfs_remove_link(&rdev->dev.kobj, "supply"); + regulator_put(rdev->supply); device_unregister(&rdev->dev); kfree(rdev->constraints); mutex_unlock(®ulator_list_mutex); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 6c433b89c80..1a80bc77517 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -188,18 +188,16 @@ struct regulator_dev { /* lists we belong to */ struct list_head list; /* list of all regulators */ - struct list_head slist; /* list of supplied regulators */ /* lists we own */ struct list_head consumer_list; /* consumers we supply */ - struct list_head supply_list; /* regulators we supply */ struct blocking_notifier_head notifier; struct mutex mutex; /* consumer lock */ struct module *owner; struct device dev; struct regulation_constraints *constraints; - struct regulator_dev *supply; /* for tree */ + struct regulator *supply; /* for tree */ void *reg_data; /* regulator_dev data */ -- cgit v1.2.3-70-g09d2 From 7d51a0dbe51282f3ed13cadf6e7f13a974374be2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 Jun 2011 16:06:37 +0100 Subject: regulator: Add rdev_crit() macro No actual users but provide the macro so there's less surprise when it's not there. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f0cc3983ffe..cc3dfd66f39 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -34,6 +34,8 @@ #include "dummy.h" +#define rdev_crit(rdev, fmt, ...) \ + pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) #define rdev_err(rdev, fmt, ...) \ pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) #define rdev_warn(rdev, fmt, ...) \ -- cgit v1.2.3-70-g09d2 From dbcb4a1a3f16702918caa4d4ab7062965050a780 Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Fri, 10 Jun 2011 13:07:48 -0400 Subject: arch/tile: add hypervisor-based character driver for SPI flash ROM The first version of this patch proposed an arch/tile/drivers/ directory, but the consensus was that this was probably a poor choice for a place to group Tilera-specific drivers, and that in any case grouping by platform was discouraged, and grouping by function was preferred. This version of the patch addresses various issues raised in the community, primarily the absence of sysfs integration. The sysfs integration now handles passing information on sector size, page size, and total partition size to userspace as well. In addition, we now use a single "struct cdev" to manage all the partition minor devices, and dynamically discover the correct number of partitions from the hypervisor rather than using a module_param with a default value. This driver has no particular "peer" drivers it can be grouped with. It is sort of like an MTD driver for SPI ROM, but it doesn't group well with the other MTD devices since it relies on hypervisor virtualization to handle many of the irritating aspects of flash ROM management: sector awareness, background read for sub-sector writes, bit examination to determine whether a sector erase needs to be issued, etc. It is in fact more like an EEPROM driver, but the hypervisor virtualization does require a "flush" command if you wish to commit a sector write prior to writing to a different sector, and this is sufficiently different from generic I2C/SPI EEPROMs that as a result it doesn't group well with them either. The simple character device is already in use by a range of Tilera SPI ROM management tools, as well as by customers. In addition, using the simple character device actually simplifies the userspace tools, since they don't need to manage sector erase, background read, etc. This both simplifies the code (since we can uniformly manage plain files and the SPI ROM) as well as makes the user code portable to non-Linux platforms that don't offer the same MTD ioctls. Signed-off-by: Chris Metcalf Reviewed-by: Arnd Bergmann --- arch/tile/include/hv/drv_srom_intf.h | 41 +++ drivers/char/Kconfig | 11 + drivers/char/Makefile | 2 + drivers/char/tile-srom.c | 481 +++++++++++++++++++++++++++++++++++ 4 files changed, 535 insertions(+) create mode 100644 arch/tile/include/hv/drv_srom_intf.h create mode 100644 drivers/char/tile-srom.c (limited to 'drivers') diff --git a/arch/tile/include/hv/drv_srom_intf.h b/arch/tile/include/hv/drv_srom_intf.h new file mode 100644 index 00000000000..6395faa6d9e --- /dev/null +++ b/arch/tile/include/hv/drv_srom_intf.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/** + * @file drv_srom_intf.h + * Interface definitions for the SPI Flash ROM driver. + */ + +#ifndef _SYS_HV_INCLUDE_DRV_SROM_INTF_H +#define _SYS_HV_INCLUDE_DRV_SROM_INTF_H + +/** Read this offset to get the total device size. */ +#define SROM_TOTAL_SIZE_OFF 0xF0000000 + +/** Read this offset to get the device sector size. */ +#define SROM_SECTOR_SIZE_OFF 0xF0000004 + +/** Read this offset to get the device page size. */ +#define SROM_PAGE_SIZE_OFF 0xF0000008 + +/** Write this offset to flush any pending writes. */ +#define SROM_FLUSH_OFF 0xF1000000 + +/** Write this offset, plus the byte offset of the start of a sector, to + * erase a sector. Any write data is ignored, but there must be at least + * one byte of write data. Only applies when the driver is in MTD mode. + */ +#define SROM_ERASE_OFF 0xF2000000 + +#endif /* _SYS_HV_INCLUDE_DRV_SROM_INTF_H */ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 49502bc5360..423fd56bf61 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -616,5 +616,16 @@ config MSM_SMD_PKT Enables userspace clients to read and write to some packet SMD ports via device interface for MSM chipset. +config TILE_SROM + bool "Character-device access via hypervisor to the Tilera SPI ROM" + depends on TILE + default y + ---help--- + This device provides character-level read-write access + to the SROM, typically via the "0", "1", and "2" devices + in /dev/srom/. The Tilera hypervisor makes the flash + device appear much like a simple EEPROM, and knows + how to partition a single ROM for multiple purposes. + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7a00672bd85..32762ba769c 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -63,3 +63,5 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o + +obj-$(CONFIG_TILE_SROM) += tile-srom.o diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c new file mode 100644 index 00000000000..cf3ee008dca --- /dev/null +++ b/drivers/char/tile-srom.c @@ -0,0 +1,481 @@ +/* + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * SPI Flash ROM driver + * + * This source code is derived from code provided in "Linux Device + * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and + * Greg Kroah-Hartman, published by O'Reilly Media, Inc. + */ + +#include +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Size of our hypervisor I/O requests. We break up large transfers + * so that we don't spend large uninterrupted spans of time in the + * hypervisor. Erasing an SROM sector takes a significant fraction of + * a second, so if we allowed the user to, say, do one I/O to write the + * entire ROM, we'd get soft lockup timeouts, or worse. + */ +#define SROM_CHUNK_SIZE ((size_t)4096) + +/* + * When hypervisor is busy (e.g. erasing), poll the status periodically. + */ + +/* + * Interval to poll the state in msec + */ +#define SROM_WAIT_TRY_INTERVAL 20 + +/* + * Maximum times to poll the state + */ +#define SROM_MAX_WAIT_TRY_TIMES 1000 + +struct srom_dev { + int hv_devhdl; /* Handle for hypervisor device */ + u32 total_size; /* Size of this device */ + u32 sector_size; /* Size of a sector */ + u32 page_size; /* Size of a page */ + struct mutex lock; /* Allow only one accessor at a time */ +}; + +static int srom_major; /* Dynamic major by default */ +module_param(srom_major, int, 0); +MODULE_AUTHOR("Tilera Corporation"); +MODULE_LICENSE("GPL"); + +static int srom_devs; /* Number of SROM partitions */ +static struct cdev srom_cdev; +static struct class *srom_class; +static struct srom_dev *srom_devices; + +/* + * Handle calling the hypervisor and managing EAGAIN/EBUSY. + */ + +static ssize_t _srom_read(int hv_devhdl, void *buf, + loff_t off, size_t count) +{ + int retval, retries = SROM_MAX_WAIT_TRY_TIMES; + for (;;) { + retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf, + count, off); + if (retval >= 0) + return retval; + if (retval == HV_EAGAIN) + continue; + if (retval == HV_EBUSY && --retries > 0) { + msleep(SROM_WAIT_TRY_INTERVAL); + continue; + } + pr_err("_srom_read: error %d\n", retval); + return -EIO; + } +} + +static ssize_t _srom_write(int hv_devhdl, const void *buf, + loff_t off, size_t count) +{ + int retval, retries = SROM_MAX_WAIT_TRY_TIMES; + for (;;) { + retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf, + count, off); + if (retval >= 0) + return retval; + if (retval == HV_EAGAIN) + continue; + if (retval == HV_EBUSY && --retries > 0) { + msleep(SROM_WAIT_TRY_INTERVAL); + continue; + } + pr_err("_srom_write: error %d\n", retval); + return -EIO; + } +} + +/** + * srom_open() - Device open routine. + * @inode: Inode for this device. + * @filp: File for this specific open of the device. + * + * Returns zero, or an error code. + */ +static int srom_open(struct inode *inode, struct file *filp) +{ + filp->private_data = &srom_devices[iminor(inode)]; + return 0; +} + + +/** + * srom_release() - Device release routine. + * @inode: Inode for this device. + * @filp: File for this specific open of the device. + * + * Returns zero, or an error code. + */ +static int srom_release(struct inode *inode, struct file *filp) +{ + struct srom_dev *srom = filp->private_data; + char dummy; + + /* Make sure we've flushed anything written to the ROM. */ + mutex_lock(&srom->lock); + if (srom->hv_devhdl >= 0) + _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1); + mutex_unlock(&srom->lock); + + filp->private_data = NULL; + + return 0; +} + + +/** + * srom_read() - Read data from the device. + * @filp: File for this specific open of the device. + * @buf: User's data buffer. + * @count: Number of bytes requested. + * @f_pos: File position. + * + * Returns number of bytes read, or an error code. + */ +static ssize_t srom_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = 0; + void *kernbuf; + struct srom_dev *srom = filp->private_data; + + kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (mutex_lock_interruptible(&srom->lock)) { + retval = -ERESTARTSYS; + kfree(kernbuf); + return retval; + } + + while (count) { + int hv_retval; + int bytes_this_pass = min(count, SROM_CHUNK_SIZE); + + hv_retval = _srom_read(srom->hv_devhdl, kernbuf, + *f_pos, bytes_this_pass); + if (hv_retval > 0) { + if (copy_to_user(buf, kernbuf, hv_retval) != 0) { + retval = -EFAULT; + break; + } + } else if (hv_retval <= 0) { + if (retval == 0) + retval = hv_retval; + break; + } + + retval += hv_retval; + *f_pos += hv_retval; + buf += hv_retval; + count -= hv_retval; + } + + mutex_unlock(&srom->lock); + kfree(kernbuf); + + return retval; +} + +/** + * srom_write() - Write data to the device. + * @filp: File for this specific open of the device. + * @buf: User's data buffer. + * @count: Number of bytes requested. + * @f_pos: File position. + * + * Returns number of bytes written, or an error code. + */ +static ssize_t srom_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = 0; + void *kernbuf; + struct srom_dev *srom = filp->private_data; + + kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (mutex_lock_interruptible(&srom->lock)) { + retval = -ERESTARTSYS; + kfree(kernbuf); + return retval; + } + + while (count) { + int hv_retval; + int bytes_this_pass = min(count, SROM_CHUNK_SIZE); + + if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) { + retval = -EFAULT; + break; + } + + hv_retval = _srom_write(srom->hv_devhdl, kernbuf, + *f_pos, bytes_this_pass); + if (hv_retval <= 0) { + if (retval == 0) + retval = hv_retval; + break; + } + + retval += hv_retval; + *f_pos += hv_retval; + buf += hv_retval; + count -= hv_retval; + } + + mutex_unlock(&srom->lock); + kfree(kernbuf); + + return retval; +} + +/* Provide our own implementation so we can use srom->total_size. */ +loff_t srom_llseek(struct file *filp, loff_t offset, int origin) +{ + struct srom_dev *srom = filp->private_data; + + if (mutex_lock_interruptible(&srom->lock)) + return -ERESTARTSYS; + + switch (origin) { + case SEEK_END: + offset += srom->total_size; + break; + case SEEK_CUR: + offset += filp->f_pos; + break; + } + + if (offset < 0 || offset > srom->total_size) { + offset = -EINVAL; + } else { + filp->f_pos = offset; + filp->f_version = 0; + } + + mutex_unlock(&srom->lock); + + return offset; +} + +static ssize_t total_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct srom_dev *srom = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", srom->total_size); +} + +static ssize_t sector_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct srom_dev *srom = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", srom->sector_size); +} + +static ssize_t page_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct srom_dev *srom = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", srom->page_size); +} + +static struct device_attribute srom_dev_attrs[] = { + __ATTR(total_size, S_IRUGO, total_show, NULL), + __ATTR(sector_size, S_IRUGO, sector_show, NULL), + __ATTR(page_size, S_IRUGO, page_show, NULL), + __ATTR_NULL +}; + +static char *srom_devnode(struct device *dev, mode_t *mode) +{ + *mode = S_IRUGO | S_IWUSR; + return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev)); +} + +/* + * The fops + */ +static const struct file_operations srom_fops = { + .owner = THIS_MODULE, + .llseek = srom_llseek, + .read = srom_read, + .write = srom_write, + .open = srom_open, + .release = srom_release, +}; + +/** + * srom_setup_minor() - Initialize per-minor information. + * @srom: Per-device SROM state. + * @index: Device to set up. + */ +static int srom_setup_minor(struct srom_dev *srom, int index) +{ + struct device *dev; + int devhdl = srom->hv_devhdl; + + mutex_init(&srom->lock); + + if (_srom_read(devhdl, &srom->total_size, + SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0) + return -EIO; + if (_srom_read(devhdl, &srom->sector_size, + SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0) + return -EIO; + if (_srom_read(devhdl, &srom->page_size, + SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0) + return -EIO; + + dev = device_create(srom_class, &platform_bus, + MKDEV(srom_major, index), srom, "%d", index); + return IS_ERR(dev) ? PTR_ERR(dev) : 0; +} + +/** srom_init() - Initialize the driver's module. */ +static int srom_init(void) +{ + int result, i; + dev_t dev = MKDEV(srom_major, 0); + + /* + * Start with a plausible number of partitions; the krealloc() call + * below will yield about log(srom_devs) additional allocations. + */ + srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); + + /* Discover the number of srom partitions. */ + for (i = 0; ; i++) { + int devhdl; + char buf[20]; + struct srom_dev *new_srom_devices = + krealloc(srom_devices, (i+1) * sizeof(struct srom_dev), + GFP_KERNEL | __GFP_ZERO); + if (!new_srom_devices) { + result = -ENOMEM; + goto fail_mem; + } + srom_devices = new_srom_devices; + sprintf(buf, "srom/0/%d", i); + devhdl = hv_dev_open((HV_VirtAddr)buf, 0); + if (devhdl < 0) { + if (devhdl != HV_ENODEV) + pr_notice("srom/%d: hv_dev_open failed: %d.\n", + i, devhdl); + break; + } + srom_devices[i].hv_devhdl = devhdl; + } + srom_devs = i; + + /* Bail out early if we have no partitions at all. */ + if (srom_devs == 0) { + result = -ENODEV; + goto fail_mem; + } + + /* Register our major, and accept a dynamic number. */ + if (srom_major) + result = register_chrdev_region(dev, srom_devs, "srom"); + else { + result = alloc_chrdev_region(&dev, 0, srom_devs, "srom"); + srom_major = MAJOR(dev); + } + if (result < 0) + goto fail_mem; + + /* Register a character device. */ + cdev_init(&srom_cdev, &srom_fops); + srom_cdev.owner = THIS_MODULE; + srom_cdev.ops = &srom_fops; + result = cdev_add(&srom_cdev, dev, srom_devs); + if (result < 0) + goto fail_chrdev; + + /* Create a sysfs class. */ + srom_class = class_create(THIS_MODULE, "srom"); + if (IS_ERR(srom_class)) { + result = PTR_ERR(srom_class); + goto fail_cdev; + } + srom_class->dev_attrs = srom_dev_attrs; + srom_class->devnode = srom_devnode; + + /* Do per-partition initialization */ + for (i = 0; i < srom_devs; i++) { + result = srom_setup_minor(srom_devices + i, i); + if (result < 0) + goto fail_class; + } + + return 0; + +fail_class: + for (i = 0; i < srom_devs; i++) + device_destroy(srom_class, MKDEV(srom_major, i)); + class_destroy(srom_class); +fail_cdev: + cdev_del(&srom_cdev); +fail_chrdev: + unregister_chrdev_region(dev, srom_devs); +fail_mem: + kfree(srom_devices); + return result; +} + +/** srom_cleanup() - Clean up the driver's module. */ +static void srom_cleanup(void) +{ + int i; + for (i = 0; i < srom_devs; i++) + device_destroy(srom_class, MKDEV(srom_major, i)); + class_destroy(srom_class); + cdev_del(&srom_cdev); + unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs); + kfree(srom_devices); +} + +module_init(srom_init); +module_exit(srom_cleanup); -- cgit v1.2.3-70-g09d2 From 61a6976bf19a6cf5dfcf37c3536665b316f22d49 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 14 Jun 2011 12:40:19 +0900 Subject: serial: sh-sci: Abstract register maps. This takes a bit of a sledgehammer to the horribly CPU subtype ifdef-ridden header and abstracts all of the different register layouts in to distinct types which in turn can be overriden on a per-port basis, or permitted to default to the map matching the port type at probe time. In the process this ultimately fixes up inumerable bugs with mismatches on various CPU types (particularly the legacy ones that were obviously broken years ago and no one noticed) and provides a more tightly coupled and consolidated platform for extending and implementing generic features. Signed-off-by: Paul Mundt --- arch/sh/Makefile | 1 + arch/sh/include/cpu-sh3/cpu/serial.h | 10 + arch/sh/include/cpu-sh4a/cpu/serial.h | 7 + arch/sh/kernel/cpu/sh3/Makefile | 18 +- arch/sh/kernel/cpu/sh3/serial-sh770x.c | 33 +++ arch/sh/kernel/cpu/sh3/serial-sh7710.c | 20 ++ arch/sh/kernel/cpu/sh3/serial-sh7720.c | 36 ++++ arch/sh/kernel/cpu/sh3/setup-sh7705.c | 5 + arch/sh/kernel/cpu/sh3/setup-sh770x.c | 8 + arch/sh/kernel/cpu/sh3/setup-sh7720.c | 5 + arch/sh/kernel/cpu/sh4/setup-sh7750.c | 3 +- arch/sh/kernel/cpu/sh4/setup-sh7760.c | 4 + arch/sh/kernel/cpu/sh4a/Makefile | 2 +- arch/sh/kernel/cpu/sh4a/serial-sh7722.c | 23 ++ arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 6 + arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 9 + arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 9 + arch/sh/kernel/cpu/sh4a/setup-sh7763.c | 3 + arch/sh/kernel/cpu/sh4a/setup-sh7780.c | 3 +- arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 8 +- arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 8 +- drivers/tty/serial/sh-sci.c | 364 ++++++++++++++++++++++++-------- drivers/tty/serial/sh-sci.h | 222 ------------------- include/linux/serial_sci.h | 36 ++++ 25 files changed, 516 insertions(+), 328 deletions(-) create mode 100644 arch/sh/include/cpu-sh3/cpu/serial.h create mode 100644 arch/sh/include/cpu-sh4a/cpu/serial.h create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh770x.c create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh7710.c create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh7720.c create mode 100644 arch/sh/kernel/cpu/sh4a/serial-sh7722.c (limited to 'drivers') diff --git a/arch/sh/Makefile b/arch/sh/Makefile index e3d8170ad00..99385d0b3f3 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -173,6 +173,7 @@ core-$(CONFIG_HD6446X_SERIES) += arch/sh/cchips/hd6446x/ cpuincdir-$(CONFIG_CPU_SH2A) += cpu-sh2a cpuincdir-$(CONFIG_CPU_SH2) += cpu-sh2 cpuincdir-$(CONFIG_CPU_SH3) += cpu-sh3 +cpuincdir-$(CONFIG_CPU_SH4A) += cpu-sh4a cpuincdir-$(CONFIG_CPU_SH4) += cpu-sh4 cpuincdir-$(CONFIG_CPU_SH5) += cpu-sh5 cpuincdir-y += cpu-common # Must be last diff --git a/arch/sh/include/cpu-sh3/cpu/serial.h b/arch/sh/include/cpu-sh3/cpu/serial.h new file mode 100644 index 00000000000..7766329bc10 --- /dev/null +++ b/arch/sh/include/cpu-sh3/cpu/serial.h @@ -0,0 +1,10 @@ +#ifndef __CPU_SH3_SERIAL_H +#define __CPU_SH3_SERIAL_H + +#include + +extern struct plat_sci_port_ops sh770x_sci_port_ops; +extern struct plat_sci_port_ops sh7710_sci_port_ops; +extern struct plat_sci_port_ops sh7720_sci_port_ops; + +#endif /* __CPU_SH3_SERIAL_H */ diff --git a/arch/sh/include/cpu-sh4a/cpu/serial.h b/arch/sh/include/cpu-sh4a/cpu/serial.h new file mode 100644 index 00000000000..ff1bc275d21 --- /dev/null +++ b/arch/sh/include/cpu-sh4a/cpu/serial.h @@ -0,0 +1,7 @@ +#ifndef __CPU_SH4A_SERIAL_H +#define __CPU_SH4A_SERIAL_H + +/* arch/sh/kernel/cpu/sh4a/serial-sh7722.c */ +extern struct plat_sci_port_ops sh7722_sci_port_ops; + +#endif /* __CPU_SH4A_SERIAL_H */ diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index ecab274141a..6f13f33a35f 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -7,15 +7,15 @@ obj-y := ex.o probe.o entry.o setup-sh3.o obj-$(CONFIG_HIBERNATION) += swsusp.o # CPU subtype setup -obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o -obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o -obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o -obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o -obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o +obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o serial-sh7720.o +obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o serial-sh7720.o # Primary on-chip clocks (common) clock-$(CONFIG_CPU_SH3) := clock-sh3.o diff --git a/arch/sh/kernel/cpu/sh3/serial-sh770x.c b/arch/sh/kernel/cpu/sh3/serial-sh770x.c new file mode 100644 index 00000000000..4f7242c676b --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh770x.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +#define SCPCR 0xA4000116 +#define SCPDR 0xA4000136 + +static void sh770x_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + __raw_writew(data & 0x0fcf, SCPCR); + + if (!(cflag & CRTSCTS)) { + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + + data = __raw_readb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + __raw_writeb(data & 0xbf, SCPDR); + } +} + +struct plat_sci_port_ops sh770x_sci_port_ops = { + .init_pins = sh770x_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7710.c b/arch/sh/kernel/cpu/sh3/serial-sh7710.c new file mode 100644 index 00000000000..42190ef6aeb --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7710.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#define PACR 0xa4050100 +#define PBCR 0xa4050102 + +static void sh7710_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (port->mapbase == 0xA4400000) { + __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); + __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); + } else if (port->mapbase == 0xA4410000) + __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); +} + +struct plat_sci_port_ops sh7710_sci_port_ops = { + .init_pins = sh7710_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7720.c b/arch/sh/kernel/cpu/sh3/serial-sh7720.c new file mode 100644 index 00000000000..8234e1e7abd --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7720.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +static void sh7720_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (cflag & CRTSCTS) { + /* enable RTS/CTS */ + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 9-2; enable all scif pins but sck */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xfc03), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 9-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xfc03), PORT_PVCR); + } + } else { + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 5-2; enable only tx and rx */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xffc3), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 5-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xffc3), PORT_PVCR); + } + } +} + +struct plat_sci_port_ops sh7720_sci_port_ops = { + .init_pins = sh7720_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c index cd2e702feb7..2309618c015 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -15,6 +15,7 @@ #include #include #include +#include enum { UNUSED = 0, @@ -75,6 +76,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 56, 56, 56 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif0_device = { @@ -92,6 +95,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 52, 52, 52 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 6d549792f79..3f3d5fe5892 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -19,6 +19,7 @@ #include #include #include +#include enum { UNUSED = 0, @@ -114,6 +115,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 23, 23, 23, 0 }, + .ops = &sh770x_sci_port_ops, + .regshift = 1, }; static struct platform_device scif0_device = { @@ -133,6 +136,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 56, 56, 56, 56 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH3_SCIF_REGTYPE, }; static struct platform_device scif1_device = { @@ -147,11 +152,14 @@ static struct platform_device scif1_device = { defined(CONFIG_CPU_SUBTYPE_SH7709) static struct plat_sci_port scif2_platform_data = { .mapbase = 0xa4000140, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_IRDA, .irqs = { 52, 52, 52, 52 }, + .ops = &sh770x_sci_port_ops, + .regshift = 1, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c index 365b94a6fcb..94920345c14 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct resource rtc_resources[] = { [0] = { @@ -55,6 +56,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif0_device = { @@ -72,6 +75,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index 8ea26e79118..c10db5b96e5 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -1,5 +1,5 @@ /* - * SH7750/SH7751 Setup + * SH7091/SH7750/SH7750S/SH7750R/SH7751/SH7751R Setup * * Copyright (C) 2006 Paul Mundt * Copyright (C) 2006 Jamie Lenehan @@ -44,6 +44,7 @@ static struct plat_sci_port sci_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 23, 23, 23, 0 }, + .regshift = 2, }; static struct platform_device sci_device = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c index 78bbf232e39..c0b4c774700 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -133,6 +133,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 52, 53, 55, 54 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -150,6 +151,7 @@ static struct plat_sci_port scif1_platform_data = { .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .irqs = { 72, 73, 75, 74 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -167,6 +169,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 76, 77, 79, 78 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -184,6 +187,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 80, 81, 82, 0 }, + .regshift = 2, }; static struct platform_device scif3_device = { diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile index cc122b1d303..c57fb287011 100644 --- a/arch/sh/kernel/cpu/sh4a/Makefile +++ b/arch/sh/kernel/cpu/sh4a/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o intc-shx3.o obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o -obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o +obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o serial-sh7722.o obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o obj-$(CONFIG_CPU_SUBTYPE_SH7724) += setup-sh7724.o obj-$(CONFIG_CPU_SUBTYPE_SH7366) += setup-sh7366.o diff --git a/arch/sh/kernel/cpu/sh4a/serial-sh7722.c b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c new file mode 100644 index 00000000000..59bc3a72702 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#define PSCR 0xA405011E + +static void sh7722_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (port->mapbase == 0xffe00000) { + data = __raw_readw(PSCR); + data &= ~0x03cf; + if (!(cflag & CRTSCTS)) + data |= 0x0340; + + __raw_writew(data, PSCR); + } +} + +struct plat_sci_port_ops sh7722_sci_port_ops = { + .init_pins = sh7722_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c index 82616af64d6..87773869a2f 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -20,6 +20,7 @@ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xa405013e, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index 5813d802361..863249dbf05 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -185,6 +185,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -202,6 +204,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -219,6 +223,8 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index 072382280f9..3c2810d8f72 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -23,11 +23,13 @@ /* Serial */ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xa4050160, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -40,11 +42,13 @@ static struct platform_device scif0_device = { static struct plat_sci_port scif1_platform_data = { .mapbase = 0xffe10000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -57,11 +61,13 @@ static struct platform_device scif1_device = { static struct plat_sci_port scif2_platform_data = { .mapbase = 0xffe20000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { @@ -75,6 +81,7 @@ static struct platform_device scif2_device = { static struct plat_sci_port scif3_platform_data = { .mapbase = 0xa4e30000, .flags = UPF_BOOT_AUTOCONF, + .port_reg = SCIx_NOT_SUPPORTED, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, .type = PORT_SCIFA, @@ -91,6 +98,7 @@ static struct platform_device scif3_device = { static struct plat_sci_port scif4_platform_data = { .mapbase = 0xa4e40000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -108,6 +116,7 @@ static struct platform_device scif4_device = { static struct plat_sci_port scif5_platform_data = { .mapbase = 0xa4e50000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index 0333fe9e388..8c892887ebd 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -256,11 +256,13 @@ static struct platform_device dma1_device = { /* Serial */ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -273,11 +275,13 @@ static struct platform_device scif0_device = { static struct plat_sci_port scif1_platform_data = { .mapbase = 0xffe10000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -290,11 +294,13 @@ static struct platform_device scif1_device = { static struct plat_sci_port scif2_platform_data = { .mapbase = 0xffe20000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { @@ -307,6 +313,7 @@ static struct platform_device scif2_device = { static struct plat_sci_port scif3_platform_data = { .mapbase = 0xa4e30000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -324,6 +331,7 @@ static struct platform_device scif3_device = { static struct plat_sci_port scif4_platform_data = { .mapbase = 0xa4e40000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -341,6 +349,7 @@ static struct platform_device scif4_device = { static struct plat_sci_port scif5_platform_data = { .mapbase = 0xa4e50000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c index 593eca6509b..00113515f23 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c @@ -23,6 +23,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -40,6 +41,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 76, 76, 76, 76 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -57,6 +59,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 104, 104, 104, 104 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c index 08add7fa684..3d4d2075c19 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -14,7 +14,6 @@ #include #include #include - #include static struct plat_sci_port scif0_platform_data = { @@ -24,6 +23,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -41,6 +41,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 76, 76, 76, 76 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index 18d8fc136fb..b29e6340414 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -15,9 +15,7 @@ #include #include #include - #include - #include static struct plat_sci_port scif0_platform_data = { @@ -27,6 +25,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -44,6 +43,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 44, 44, 44, 44 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -61,6 +61,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 60, 60, 60, 60 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -78,6 +79,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 61, 61, 61, 61 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif3_device = { @@ -95,6 +97,7 @@ static struct plat_sci_port scif4_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 62, 62, 62, 62 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif4_device = { @@ -112,6 +115,7 @@ static struct plat_sci_port scif5_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 63, 63, 63, 63 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif5_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index beba32beb6d..dd5e709f982 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -1,7 +1,7 @@ /* * SH7786 Setup * - * Copyright (C) 2009 - 2010 Renesas Solutions Corp. + * Copyright (C) 2009 - 2011 Renesas Solutions Corp. * Kuninori Morimoto * Paul Mundt * @@ -33,6 +33,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 41, 43, 42 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -53,6 +54,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 44, 44, 44, 44 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -70,6 +72,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 50, 50, 50, 50 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -87,6 +90,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 51, 51, 51, 51 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif3_device = { @@ -104,6 +108,7 @@ static struct plat_sci_port scif4_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 52, 52, 52, 52 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif4_device = { @@ -121,6 +126,7 @@ static struct plat_sci_port scif5_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 53, 53, 53, 53 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif5_device = { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3248ddaa889..14e1bae5039 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -117,6 +117,255 @@ to_sci_port(struct uart_port *uart) return container_of(uart, struct sci_port, port); } +struct plat_sci_reg { + u8 offset, size; +}; + +/* Helper for invalidating specific entries of an inherited map. */ +#define sci_reg_invalid { .offset = 0, .size = 0 } + +static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { + [SCIx_PROBE_REGTYPE] = { + [0 ... SCIx_NR_REGS - 1] = sci_reg_invalid, + }, + + /* + * Common SCI definitions, dependent on the port's regshift + * value. + */ + [SCIx_SCI_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x01, 8 }, + [SCSCR] = { 0x02, 8 }, + [SCxTDR] = { 0x03, 8 }, + [SCxSR] = { 0x04, 8 }, + [SCxRDR] = { 0x05, 8 }, + [SCFCR] = sci_reg_invalid, + [SCFDR] = sci_reg_invalid, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common definitions for legacy IrDA ports, dependent on + * regshift value. + */ + [SCIx_IRDA_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x01, 8 }, + [SCSCR] = { 0x02, 8 }, + [SCxTDR] = { 0x03, 8 }, + [SCxSR] = { 0x04, 8 }, + [SCxRDR] = { 0x05, 8 }, + [SCFCR] = { 0x06, 8 }, + [SCFDR] = { 0x07, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SCIFA definitions. + */ + [SCIx_SCIFA_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SCIFB definitions. + */ + [SCIx_SCIFB_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x40, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x60, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SH-3 SCIF definitions. + */ + [SCIx_SH3_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 8 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 8 }, + [SCFDR] = { 0x0e, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SH-4(A) SCIF(B) definitions. + */ + [SCIx_SH4_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + }, + + /* + * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR + * register. + */ + [SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = { 0x24, 16 }, + }, + + /* + * Common SH-4(A) SCIF(B) definitions for ports with FIFO data + * count registers. + */ + [SCIx_SH4_SCIF_FIFODATA_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = { 0x1c, 16 }, /* aliased to SCFDR */ + [SCRFDR] = { 0x20, 16 }, + [SCSPTR] = { 0x24, 16 }, + [SCLSR] = { 0x28, 16 }, + }, + + /* + * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR + * registers. + */ + [SCIx_SH7705_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, +}; + +/* + * The "offset" here is rather misleading, in that it refers to an enum + * value relative to the port mapping rather than the fixed offset + * itself, which needs to be manually retrieved from the platform's + * register map for the given port. + */ +static unsigned int sci_serial_in(struct uart_port *p, int offset) +{ + struct sci_port *s = to_sci_port(p); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + + if (reg->size == 8) + return ioread8(p->membase + (reg->offset << p->regshift)); + else if (reg->size == 16) + return ioread16(p->membase + (reg->offset << p->regshift)); + else + WARN(1, "Invalid register access\n"); + + return 0; +} + +static void sci_serial_out(struct uart_port *p, int offset, int value) +{ + struct sci_port *s = to_sci_port(p); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + + if (reg->size == 8) + iowrite8(value, p->membase + (reg->offset << p->regshift)); + else if (reg->size == 16) + iowrite16(value, p->membase + (reg->offset << p->regshift)); + else + WARN(1, "Invalid register access\n"); +} + +#define sci_in(up, offset) (up->serial_in(up, offset)) +#define sci_out(up, offset, value) (up->serial_out(up, offset, value)) + +static int sci_probe_regmap(struct plat_sci_port *cfg) +{ + switch (cfg->type) { + case PORT_SCI: + cfg->regtype = SCIx_SCI_REGTYPE; + break; + case PORT_IRDA: + cfg->regtype = SCIx_IRDA_REGTYPE; + break; + case PORT_SCIFA: + cfg->regtype = SCIx_SCIFA_REGTYPE; + break; + case PORT_SCIFB: + cfg->regtype = SCIx_SCIFB_REGTYPE; + break; + case PORT_SCIF: + /* + * The SH-4 is a bit of a misnomer here, although that's + * where this particular port layout originated. This + * configuration (or some slight variation thereof) + * remains the dominant model for all SCIFs. + */ + cfg->regtype = SCIx_SH4_SCIF_REGTYPE; + break; + default: + printk(KERN_ERR "Can't probe register map for given port\n"); + return -EINVAL; + } + + return 0; +} + #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #ifdef CONFIG_CONSOLE_POLL @@ -160,103 +409,29 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (port->mapbase == 0xA4400000) { - __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); - __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); - } else if (port->mapbase == 0xA4410000) - __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - - if (cflag & CRTSCTS) { - /* enable RTS/CTS */ - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 9-2; enable all scif pins but sck */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xfc03), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 9-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xfc03), PORT_PVCR); - } - } else { - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 5-2; enable only tx and rx */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xffc3), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 5-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xffc3), PORT_PVCR); - } - } -} -#elif defined(CONFIG_CPU_SH3) -/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +static void sci_init_pins(struct uart_port *port, unsigned int cflag) { - unsigned short data; - - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ - __raw_writew(data & 0x0fcf, SCPCR); - - if (!(cflag & CRTSCTS)) { - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP4MD1,0, - Set SCP6MD1,0 = {01} (output) */ - __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + struct sci_port *s = to_sci_port(port); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; - data = __raw_readb(SCPDR); - /* Set /RTS2 (bit6) = 0 */ - __raw_writeb(data & 0xbf, SCPDR); + /* + * Use port-specific handler if provided. + */ + if (s->cfg->ops && s->cfg->ops->init_pins) { + s->cfg->ops->init_pins(port, cflag); + return; } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - if (port->mapbase == 0xffe00000) { - data = __raw_readw(PSCR); - data &= ~0x03cf; - if (!(cflag & CRTSCTS)) - data |= 0x0340; + /* + * For the generic path SCSPTR is necessary. Bail out if that's + * unavailable, too. + */ + if (!reg->size) + return; - __raw_writew(data, PSCR); - } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) || \ - defined(CONFIG_CPU_SUBTYPE_SHX3) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ -} -#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ + sci_out(port, SCSPTR, 0x0080); /* Set RTS = 1 */ } -#else -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - /* Nothing to do */ -} -#endif #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ @@ -1752,6 +1927,9 @@ static int __devinit sci_init_single(struct platform_device *dev, break; } + if (p->regtype == SCIx_PROBE_REGTYPE) + BUG_ON(sci_probe_regmap(p) != 0); + if (dev) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); if (IS_ERR(sci_port->iclk)) { @@ -1812,9 +1990,10 @@ static int __devinit sci_init_single(struct platform_device *dev, port->mapbase = p->mapbase; port->type = p->type; port->flags = p->flags; + port->regshift = p->regshift; /* - * The UART port needs an IRQ value, so we peg this to the TX IRQ + * The UART port needs an IRQ value, so we peg this to the RX IRQ * for the multi-IRQ ports, which is where we are primarily * concerned with the shutdown path synchronization. * @@ -1822,6 +2001,9 @@ static int __devinit sci_init_single(struct platform_device *dev, */ port->irq = p->irqs[SCIx_RXI_IRQ]; + port->serial_in = sci_serial_in; + port->serial_out = sci_serial_out; + if (p->dma_dev) dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n", p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 5834f33d20f..26de640a9d0 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,69 +2,6 @@ #include #include -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ -# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define PORT_PTCR 0xA405011EUL -# define PORT_PVCR 0xA4050122UL -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH4_202) -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define PACR 0xa4050100 -# define PBCR 0xa4050102 -#elif defined(CONFIG_CPU_SUBTYPE_SH7343) -# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -# define PWDR 0xA4050166 -# define PSCR 0xA405011E -#elif defined(CONFIG_CPU_SUBTYPE_SH7366) -# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ -# define SCSPTR0 SCPDR0 -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) -# define SCSPTR0 0xa4050160 -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) -# define SCSPTR0 0xfe4b0020 -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) -# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7770) -# define SCSPTR0 0xff923020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ - defined(CONFIG_CPU_SUBTYPE_SH7203) || \ - defined(CONFIG_CPU_SUBTYPE_SH7206) || \ - defined(CONFIG_CPU_SUBTYPE_SH7263) -# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SHX3) -# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -#else -# error CPU subtype not defined -#endif - #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ @@ -119,162 +56,3 @@ #define SCI_MAJOR 204 #define SCI_MINOR_START 8 - -#define SCI_IN(size, offset) \ - ioread##size(port->membase + (offset)) - -#define SCI_OUT(size, offset, value) \ - iowrite##size(value, port->membase + (offset)) - -#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - return SCI_IN(scif_size, scif_offset); \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - return SCI_IN(sci_size, sci_offset); \ - } \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_OUT(scif_size, scif_offset, value); \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - SCI_OUT(sci_size, sci_offset, value); \ - } \ - } - -#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - return SCI_IN(scif_size, scif_offset); \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - SCI_OUT(scif_size, scif_offset, value); \ - } - -#if defined(CONFIG_CPU_SH3) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH7367) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#elif defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_ARCH_SH73A0) -#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) -#endif -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) - #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) - #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH7367) - -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCxTDR, 0x20, 8) -SCIF_FNS(SCxRDR, 0x24, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_ARCH_SH73A0) -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCTDSR, 0x0c, 16) -SCIF_FNS(SCFER, 0x10, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCTFDR, 0x38, 16) -SCIF_FNS(SCRFDR, 0x3c, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) -SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) -SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) -SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) -SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) -SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) -SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) -SCIx_FNS(SCSPTR, 0, 0, 0, 0) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCLSR, 0x24, 16) -#else -/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 */ -/* name off sz off sz off sz off sz */ -SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16) -SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8) -SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16) -SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8) -SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16) -SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8) -SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#else -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7722) -SCIF_FNS(SCSPTR, 0, 0, 0, 0) -#else -SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) -#endif -SCIF_FNS(SCLSR, 0, 0, 0x24, 16) -#endif -#endif -#define sci_in(port, reg) sci_##reg##_in(port) -#define sci_out(port, reg, value) sci_##reg##_out(port, value) diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index ecefec7c0b6..4ca130a90ea 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -58,6 +58,22 @@ enum { SCIx_NR_IRQS, }; +enum { + SCIx_PROBE_REGTYPE, + + SCIx_SCI_REGTYPE, + SCIx_IRDA_REGTYPE, + SCIx_SCIFA_REGTYPE, + SCIx_SCIFB_REGTYPE, + SCIx_SH3_SCIF_REGTYPE, + SCIx_SH4_SCIF_REGTYPE, + SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, + SCIx_SH4_SCIF_FIFODATA_REGTYPE, + SCIx_SH7705_SCIF_REGTYPE, + + SCIx_NR_REGTYPES, +}; + #define SCIx_IRQ_MUXED(irq) \ { \ [SCIx_ERI_IRQ] = (irq), \ @@ -66,8 +82,24 @@ enum { [SCIx_BRI_IRQ] = (irq), \ } +/* + * SCI register subset common for all port types. + * Not all registers will exist on all parts. + */ +enum { + SCSMR, SCBRR, SCSCR, SCxSR, + SCFCR, SCFDR, SCxTDR, SCxRDR, + SCLSR, SCTFDR, SCRFDR, SCSPTR, + + SCIx_NR_REGS, +}; + struct device; +struct plat_sci_port_ops { + void (*init_pins)(struct uart_port *, unsigned int cflag); +}; + /* * Platform device specific platform_data struct */ @@ -87,6 +119,10 @@ struct plat_sci_port { unsigned int error_mask; int port_reg; + unsigned char regshift; + unsigned char regtype; + + struct plat_sci_port_ops *ops; struct device *dma_dev; -- cgit v1.2.3-70-g09d2 From 72b294cf76dcd6d37891387049ddbe3c25043cb8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 14 Jun 2011 17:38:19 +0900 Subject: serial: sh-sci: FIFO sizing helper consolidation. This consolidates all of the TX/RX fill/room nonsense in to a single set of fairly heavyweight definitions. The implementation goes in descending order of complexity, testing the register map for capabilities until we run out of options and do it the legacy SCI way. Masks are derived directly from the per-port FIFO size, meaning that platforms with FIFO sizes not matching the standard port types will still need to manually fix them up. This also fixes up a number of issues such as tx_empty being completely bogus for SCI and IrDA ports, some ports using masks smaller or greater than their FIFO size, and so forth. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 126 +++++++++----------------------------------- drivers/tty/serial/sh-sci.h | 20 ------- 2 files changed, 24 insertions(+), 122 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 14e1bae5039..60027d51bb5 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -297,6 +297,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { }, }; +#define sci_getreg(up, offset) (sci_regmap[to_sci_port(up)->cfg->regtype] + offset) + /* * The "offset" here is rather misleading, in that it refers to an enum * value relative to the port mapping rather than the fixed offset @@ -305,8 +307,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { */ static unsigned int sci_serial_in(struct uart_port *p, int offset) { - struct sci_port *s = to_sci_port(p); - struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + struct plat_sci_reg *reg = sci_getreg(p, offset); if (reg->size == 8) return ioread8(p->membase + (reg->offset << p->regshift)); @@ -320,8 +321,7 @@ static unsigned int sci_serial_in(struct uart_port *p, int offset) static void sci_serial_out(struct uart_port *p, int offset, int value) { - struct sci_port *s = to_sci_port(p); - struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + struct plat_sci_reg *reg = sci_getreg(p, offset); if (reg->size == 8) iowrite8(value, p->membase + (reg->offset << p->regshift)); @@ -433,108 +433,38 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) sci_out(port, SCSPTR, 0x0080); /* Set RTS = 1 */ } -#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -static int scif_txfill(struct uart_port *port) -{ - return sci_in(port, SCTFDR) & 0xff; -} - -static int scif_txroom(struct uart_port *port) +static int sci_txfill(struct uart_port *port) { - return SCIF_TXROOM_MAX - scif_txfill(port); -} + struct plat_sci_reg *reg; -static int scif_rxfill(struct uart_port *port) -{ - return sci_in(port, SCRFDR) & 0xff; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -static int scif_txfill(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000 || - port->mapbase == 0xffe08000) - /* SCIF0/1*/ + reg = sci_getreg(port, SCTFDR); + if (reg->size) return sci_in(port, SCTFDR) & 0xff; - else - /* SCIF2 */ - return sci_in(port, SCFDR) >> 8; -} -static int scif_txroom(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000 || - port->mapbase == 0xffe08000) - /* SCIF0/1*/ - return SCIF_TXROOM_MAX - scif_txfill(port); - else - /* SCIF2 */ - return SCIF2_TXROOM_MAX - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - if ((port->mapbase == 0xffe00000) || - (port->mapbase == 0xffe08000)) { - /* SCIF0/1*/ - return sci_in(port, SCRFDR) & 0xff; - } else { - /* SCIF2 */ - return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; - } -} -#elif defined(CONFIG_ARCH_SH7372) -static int scif_txfill(struct uart_port *port) -{ - if (port->type == PORT_SCIFA) + reg = sci_getreg(port, SCFDR); + if (reg->size) return sci_in(port, SCFDR) >> 8; - else - return sci_in(port, SCTFDR); -} - -static int scif_txroom(struct uart_port *port) -{ - return port->fifosize - scif_txfill(port); -} -static int scif_rxfill(struct uart_port *port) -{ - if (port->type == PORT_SCIFA) - return sci_in(port, SCFDR) & SCIF_RFDC_MASK; - else - return sci_in(port, SCRFDR); -} -#else -static int scif_txfill(struct uart_port *port) -{ - return sci_in(port, SCFDR) >> 8; -} - -static int scif_txroom(struct uart_port *port) -{ - return SCIF_TXROOM_MAX - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - return sci_in(port, SCFDR) & SCIF_RFDC_MASK; -} -#endif - -static int sci_txfill(struct uart_port *port) -{ return !(sci_in(port, SCxSR) & SCI_TDRE); } static int sci_txroom(struct uart_port *port) { - return !sci_txfill(port); + return port->fifosize - sci_txfill(port); } static int sci_rxfill(struct uart_port *port) { + struct plat_sci_reg *reg; + + reg = sci_getreg(port, SCRFDR); + if (reg->size) + return sci_in(port, SCRFDR) & 0xff; + + reg = sci_getreg(port, SCFDR); + if (reg->size) + return sci_in(port, SCFDR) & ((port->fifosize << 1) - 1); + return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } @@ -574,10 +504,7 @@ static void sci_transmit_chars(struct uart_port *port) return; } - if (port->type == PORT_SCI) - count = sci_txroom(port); - else - count = scif_txroom(port); + count = sci_txroom(port); do { unsigned char c; @@ -632,13 +559,8 @@ static void sci_receive_chars(struct uart_port *port) return; while (1) { - if (port->type == PORT_SCI) - count = sci_rxfill(port); - else - count = scif_rxfill(port); - /* Don't copy more bytes than there is room for in the buffer */ - count = tty_buffer_request_room(tty, count); + count = tty_buffer_request_room(tty, sci_rxfill(port)); /* If for any reason we can't copy more data, we're done! */ if (count == 0) @@ -1096,7 +1018,7 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = sci_in(port, SCxSR); - unsigned short in_tx_fifo = scif_txfill(port); + unsigned short in_tx_fifo = sci_txfill(port); return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; } diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 26de640a9d0..e9bed038aa1 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,26 +2,6 @@ #include #include -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define SCIF_RFDC_MASK 0x007f -# define SCIF_TXROOM_MAX 64 -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -# define SCIF_RFDC_MASK 0x007f -# define SCIF_TXROOM_MAX 64 -/* SH7763 SCIF2 support */ -# define SCIF2_RFDC_MASK 0x001f -# define SCIF2_TXROOM_MAX 16 -#else -# define SCIF_RFDC_MASK 0x001f -# define SCIF_TXROOM_MAX 16 -#endif - #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) -- cgit v1.2.3-70-g09d2 From 4b8c59a3d83e9cf2b65b16999a0c704fc72de056 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 14 Jun 2011 17:53:34 +0900 Subject: serial: sh-sci: Support generic SCLSR overrun detection. For all ports with a valid SCLSR register we can use the generic FIFO overrun detection logic. Test the validity of the SCLSR register rather than depending explicitly on port type, which can be ambiguous for the SCIFA/B types. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 60027d51bb5..8e55e0a2733 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -734,15 +734,11 @@ static int sci_handle_fifo_overrun(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; struct sci_port *s = to_sci_port(port); + struct plat_sci_reg *reg; int copied = 0; - /* - * XXX: Technically not limited to non-SCIFs, it's simply the - * SCLSR check that is for the moment SCIF-specific. This - * probably wants to be revisited for SCIFA/B as well as for - * factoring in SCI overrun detection. - */ - if (port->type != PORT_SCIF) + reg = sci_getreg(port, SCLSR); + if (!reg->size) return 0; if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { -- cgit v1.2.3-70-g09d2 From e2f5e5a71dfe6bf155590de0fdd6d748ac79bf76 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 10 Jun 2011 15:15:05 -0700 Subject: dma/ep93xx_dma.c: local symbols should be static The symbol 'ep93xx_dma_prep_dma_memcpy' is only used in this driver and should be marked static. Signed-off-by: H Hartley Sweeten Cc: Mika Westerberg Cc: Dan Williams Cc: Vinod Koul Acked-by: Mika Westerberg Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 0766c1e53b1..5d7a49bd7c2 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -902,7 +902,7 @@ static void ep93xx_dma_free_chan_resources(struct dma_chan *chan) * * Returns a valid DMA descriptor or %NULL in case of failure. */ -struct dma_async_tx_descriptor * +static struct dma_async_tx_descriptor * ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { -- cgit v1.2.3-70-g09d2 From c4e0dd7835d12d9765a372b586a5020ac29cc706 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 16 Jun 2011 05:08:09 +0000 Subject: dmaengine: shdma: add to_sh_dev define This patch adds "to_sh_dev" macro, and clean up codes. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 13 ++++--------- drivers/dma/shdma.h | 2 ++ 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 2a638f9f09a..d2fb16d31bb 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -130,8 +130,7 @@ static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) | ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift); @@ -144,8 +143,7 @@ static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int i; @@ -209,8 +207,7 @@ static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; u16 __iomem *addr = shdev->dmars; @@ -296,9 +293,7 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) static const struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param) { - struct dma_device *dma_dev = sh_chan->common.device; - struct sh_dmae_device *shdev = container_of(dma_dev, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int i; diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 5ae9fc51218..6c73b654a5c 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -52,5 +52,7 @@ struct sh_dmae_device { #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common) #define to_sh_desc(lh) container_of(lh, struct sh_desc, node) #define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx) +#define to_sh_dev(chan) container_of(chan->common.device,\ + struct sh_dmae_device, common) #endif /* __DMA_SHDMA_H */ -- cgit v1.2.3-70-g09d2 From 1d2c0980262e70f5643df34493ffd7e608282c16 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 16 Jun 2011 05:08:18 +0000 Subject: dmaengine: shdma: tidyup spin_unlock_bh on sh_chan_xfer_ld_queue It is not readable that there is any spin_unlock_bh on same function. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index d2fb16d31bb..3d22eb82289 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -766,10 +766,8 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) spin_lock_bh(&sh_chan->desc_lock); /* DMA work check */ - if (dmae_is_busy(sh_chan)) { - spin_unlock_bh(&sh_chan->desc_lock); - return; - } + if (dmae_is_busy(sh_chan)) + goto sh_chan_xfer_ld_queue_end; /* Find the first not transferred descriptor */ list_for_each_entry(desc, &sh_chan->ld_queue, node) @@ -783,6 +781,7 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) break; } +sh_chan_xfer_ld_queue_end: spin_unlock_bh(&sh_chan->desc_lock); } -- cgit v1.2.3-70-g09d2 From 090b91805a97f58a7deff0f2b40aad1cc2f1b7e0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 16 Jun 2011 05:08:28 +0000 Subject: dmaengine: shdma: fixup parameter definition on dmae_set_dmars chan_pdata->dmars_bit is unsigned int Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 3d22eb82289..41a21b32296 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -211,7 +211,7 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) struct sh_dmae_pdata *pdata = shdev->pdata; const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; u16 __iomem *addr = shdev->dmars; - int shift = chan_pdata->dmars_bit; + unsigned int shift = chan_pdata->dmars_bit; if (dmae_is_busy(sh_chan)) return -EBUSY; -- cgit v1.2.3-70-g09d2 From 5899a723b336da241b492583d7e55f1055f8f3b3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 17 Jun 2011 08:20:40 +0000 Subject: dmaengine: shdma: add chcr_write/read function CHCR register position is not same in all DMAC. This patch adds new "chcr_offset" to decide it. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 35 +++++++++++++++++++++++++++-------- drivers/dma/shdma.h | 1 + include/linux/sh_dma.h | 1 + 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 41a21b32296..40900c1cee9 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -78,6 +78,20 @@ static void dmaor_write(struct sh_dmae_device *shdev, u16 data) __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32)); } +static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + __raw_writel(data, sh_dc->base + shdev->chcr_offset / sizeof(u32)); +} + +static u32 chcr_read(struct sh_dmae_chan *sh_dc) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + return __raw_readl(sh_dc->base + shdev->chcr_offset / sizeof(u32)); +} + /* * Reset DMA controller * @@ -120,7 +134,7 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev) static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + u32 chcr = chcr_read(sh_chan); if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE) return true; /* working */ @@ -167,18 +181,18 @@ static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw) static void dmae_start(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + u32 chcr = chcr_read(sh_chan); chcr |= CHCR_DE | CHCR_IE; - sh_dmae_writel(sh_chan, chcr & ~CHCR_TE, CHCR); + chcr_write(sh_chan, chcr & ~CHCR_TE); } static void dmae_halt(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + u32 chcr = chcr_read(sh_chan); chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); - sh_dmae_writel(sh_chan, chcr, CHCR); + chcr_write(sh_chan, chcr); } static void dmae_init(struct sh_dmae_chan *sh_chan) @@ -190,7 +204,7 @@ static void dmae_init(struct sh_dmae_chan *sh_chan) u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan, LOG2_DEFAULT_XFER_SIZE); sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr); - sh_dmae_writel(sh_chan, chcr, CHCR); + chcr_write(sh_chan, chcr); } static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) @@ -200,7 +214,7 @@ static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) return -EBUSY; sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val); - sh_dmae_writel(sh_chan, val, CHCR); + chcr_write(sh_chan, val); return 0; } @@ -840,7 +854,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data) spin_lock(&sh_chan->desc_lock); - chcr = sh_dmae_readl(sh_chan, CHCR); + chcr = chcr_read(sh_chan); if (chcr & CHCR_TE) { /* DMA stop */ @@ -1138,6 +1152,11 @@ static int __init sh_dmae_probe(struct platform_device *pdev) /* platform data */ shdev->pdata = pdata; + if (pdata->chcr_offset) + shdev->chcr_offset = pdata->chcr_offset; + else + shdev->chcr_offset = CHCR; + platform_set_drvdata(pdev, shdev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 6c73b654a5c..6f064cad6c0 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -47,6 +47,7 @@ struct sh_dmae_device { struct list_head node; u32 __iomem *chan_reg; u16 __iomem *dmars; + unsigned int chcr_offset; }; #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common) diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index b08cd4efa15..41fe4c2d648 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -62,6 +62,7 @@ struct sh_dmae_pdata { const unsigned int *ts_shift; int ts_shift_num; u16 dmaor_init; + unsigned int chcr_offset; }; /* DMA register */ -- cgit v1.2.3-70-g09d2 From 67c6269e5c998b53c2c08ce2befbbe20a7b6f57f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 17 Jun 2011 08:20:51 +0000 Subject: dmaengine: shdma: add chcr_ie_bit IE bit position on CHCR register is not same in all DMAC. This patch adds new "chcr_ie_bit" to decide it. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 11 +++++++++-- drivers/dma/shdma.h | 1 + include/linux/sh_dma.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 40900c1cee9..9412de3ef89 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -181,17 +181,19 @@ static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw) static void dmae_start(struct sh_dmae_chan *sh_chan) { + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); u32 chcr = chcr_read(sh_chan); - chcr |= CHCR_DE | CHCR_IE; + chcr |= CHCR_DE | shdev->chcr_ie_bit; chcr_write(sh_chan, chcr & ~CHCR_TE); } static void dmae_halt(struct sh_dmae_chan *sh_chan) { + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); u32 chcr = chcr_read(sh_chan); - chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); + chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit); chcr_write(sh_chan, chcr); } @@ -1157,6 +1159,11 @@ static int __init sh_dmae_probe(struct platform_device *pdev) else shdev->chcr_offset = CHCR; + if (pdata->chcr_ie_bit) + shdev->chcr_ie_bit = pdata->chcr_ie_bit; + else + shdev->chcr_ie_bit = CHCR_IE; + platform_set_drvdata(pdev, shdev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 6f064cad6c0..dc56576f9fd 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -48,6 +48,7 @@ struct sh_dmae_device { u32 __iomem *chan_reg; u16 __iomem *dmars; unsigned int chcr_offset; + u32 chcr_ie_bit; }; #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common) diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 41fe4c2d648..96803aa7b6b 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -63,6 +63,7 @@ struct sh_dmae_pdata { int ts_shift_num; u16 dmaor_init; unsigned int chcr_offset; + u32 chcr_ie_bit; }; /* DMA register */ -- cgit v1.2.3-70-g09d2 From e76c3af873025f5a704d56a28882be761e15657b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 17 Jun 2011 08:20:56 +0000 Subject: dmaengine: shdma: add dmaor_is_32bit flag Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 14 ++++++++++++-- include/linux/sh_dma.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 9412de3ef89..6a21cd843ab 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -70,12 +70,22 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg) static u16 dmaor_read(struct sh_dmae_device *shdev) { - return __raw_readw(shdev->chan_reg + DMAOR / sizeof(u32)); + u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32); + + if (shdev->pdata->dmaor_is_32bit) + return __raw_readl(addr); + else + return __raw_readw(addr); } static void dmaor_write(struct sh_dmae_device *shdev, u16 data) { - __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32)); + u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32); + + if (shdev->pdata->dmaor_is_32bit) + __raw_writel(data, addr); + else + __raw_writew(data, addr); } static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data) diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 96803aa7b6b..f25afc61e1c 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -64,6 +64,8 @@ struct sh_dmae_pdata { u16 dmaor_init; unsigned int chcr_offset; u32 chcr_ie_bit; + + unsigned int dmaor_is_32bit:1; }; /* DMA register */ -- cgit v1.2.3-70-g09d2 From 260bf2c5f69f419b04b6861ca91565b5fb16ce48 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 17 Jun 2011 08:21:05 +0000 Subject: dmaengine: shdma: add .needs_tend_set / .no_dmars flags Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/dma/shdma.c | 6 ++++++ include/linux/sh_dma.h | 4 ++++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 6a21cd843ab..0f3ec8d57a7 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -194,6 +194,9 @@ static void dmae_start(struct sh_dmae_chan *sh_chan) struct sh_dmae_device *shdev = to_sh_dev(sh_chan); u32 chcr = chcr_read(sh_chan); + if (shdev->pdata->needs_tend_set) + sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND); + chcr |= CHCR_DE | shdev->chcr_ie_bit; chcr_write(sh_chan, chcr & ~CHCR_TE); } @@ -242,6 +245,9 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) if (dmae_is_busy(sh_chan)) return -EBUSY; + if (pdata->no_dmars) + return 0; + /* in the case of a missing DMARS resource use first memory window */ if (!addr) addr = (u16 __iomem *)shdev->chan_reg; diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index f25afc61e1c..cb2dd118cc0 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -66,6 +66,8 @@ struct sh_dmae_pdata { u32 chcr_ie_bit; unsigned int dmaor_is_32bit:1; + unsigned int needs_tend_set:1; + unsigned int no_dmars:1; }; /* DMA register */ @@ -75,6 +77,8 @@ struct sh_dmae_pdata { #define CHCR 0x0C #define DMAOR 0x40 +#define TEND 0x18 /* USB-DMAC */ + /* DMAOR definitions */ #define DMAOR_AE 0x00000004 #define DMAOR_NMIF 0x00000002 -- cgit v1.2.3-70-g09d2 From 794d78fea51504bad3880d14f354a9847f318f25 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 21 Jun 2011 07:55:12 +0000 Subject: drivers: sh: late disabling of clocks V2 This V2 patch changes the clock disabling behavior during boot. Two different changes are made: 1) Delay disabling of clocks until late in the boot process. This fixes an existing issue where in-use clocks without software reference are disabled by mistake during boot. One example of this is the handling of the Mackerel serial console output that shares clock with the I2C controller. 2) Write out the "disabled" state to the hardware for clocks that not have been used by the kernel. In other words, make sure so far unused clocks actually get turned off. Signed-off-by: Magnus Damm Acked-by: Simon Horman Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index 7e9c39951ec..ebeaa9e9f06 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -34,6 +34,9 @@ static LIST_HEAD(clock_list); static DEFINE_SPINLOCK(clock_lock); static DEFINE_MUTEX(clock_list_sem); +/* clock disable operations are not passed on to hardware during boot */ +static int allow_disable; + void clk_rate_table_build(struct clk *clk, struct cpufreq_frequency_table *freq_table, int nr_freqs, @@ -228,7 +231,7 @@ static void __clk_disable(struct clk *clk) return; if (!(--clk->usecount)) { - if (likely(clk->ops && clk->ops->disable)) + if (likely(allow_disable && clk->ops && clk->ops->disable)) clk->ops->disable(clk); if (likely(clk->parent)) __clk_disable(clk->parent); @@ -747,3 +750,25 @@ err_out: return err; } late_initcall(clk_debugfs_init); + +static int __init clk_late_init(void) +{ + unsigned long flags; + struct clk *clk; + + /* disable all clocks with zero use count */ + mutex_lock(&clock_list_sem); + spin_lock_irqsave(&clock_lock, flags); + + list_for_each_entry(clk, &clock_list, node) + if (!clk->usecount && clk->ops && clk->ops->disable) + clk->ops->disable(clk); + + /* from now on allow clock disable operations */ + allow_disable = 1; + + spin_unlock_irqrestore(&clock_lock, flags); + mutex_unlock(&clock_list_sem); + return 0; +} +late_initcall(clk_late_init); -- cgit v1.2.3-70-g09d2 From 225ca45c3c64964163ea1fa85e2081af85956eed Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 24 Jun 2011 17:35:40 +0900 Subject: sh: clkfwk: Convert to IS_ERR_OR_NULL. Trivial cleanup. Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index ebeaa9e9f06..229ad0991f2 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -396,7 +396,7 @@ int clk_register(struct clk *clk) { int ret; - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return -EINVAL; /* -- cgit v1.2.3-70-g09d2 From a03a202e95fdaa3ff52ccfc2594ec531e5917816 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 20 Jun 2011 17:02:47 +0200 Subject: dmaengine: failure to get a specific DMA channel is not critical There exist systems with multiple DMA controllers with different capabilities. For example, on some sh-mobile / rmobile systems there are DMA controllers, whose channels can be configured to be used with SD- and MMC-host controllers, serial ports etc. Besides there are also DMA controllers, that can only be used for one special function, e.g., for USB. In such cases the DMA client filter function can just choose to specify to the DMA driver, which channel it needs. Then the .device_alloc_chan_resources() method of the DMA driver will check, whether it can provide that dunction. If not, it will fail and the loop in __dma_request_channel() will continue to the next DMA device, until it finds a suitable one. This works fine with just one minor glitch: the kernel logs error messages like dmaengine: failed to get : (-) after each such non-critical failure. This patch lowers priority of this message to the debug level. Reported-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Tested-by: Kuninori Morimoto Tested-by: Magnus Damm Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 8bcb15fb959..f7f21a5de3e 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -509,8 +509,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan)); list_del_rcu(&device->global_node); } else if (err) - pr_err("dmaengine: failed to get %s: (%d)\n", - dma_chan_name(chan), err); + pr_debug("dmaengine: failed to get %s: (%d)\n", + dma_chan_name(chan), err); else break; if (--device->privatecnt == 0) -- cgit v1.2.3-70-g09d2 From 3127c6b225c6893bdfcd4db64d4316ce317fc10f Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 28 Jun 2011 13:44:37 +0900 Subject: serial: sh-sci: Regtype probing doesn't need to be fatal. This was using a BUG_ON(), but it's not strictly necessary, so relax the constraints a bit. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 8e55e0a2733..5ff6657fd14 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1825,6 +1825,7 @@ static int __devinit sci_init_single(struct platform_device *dev, struct plat_sci_port *p) { struct uart_port *port = &sci_port->port; + int ret; port->ops = &sci_uart_ops; port->iotype = UPIO_MEM; @@ -1845,8 +1846,11 @@ static int __devinit sci_init_single(struct platform_device *dev, break; } - if (p->regtype == SCIx_PROBE_REGTYPE) - BUG_ON(sci_probe_regmap(p) != 0); + if (p->regtype == SCIx_PROBE_REGTYPE) { + ret = sci_probe_regmap(p); + if (unlikely(!ret)) + return ret; + } if (dev) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); -- cgit v1.2.3-70-g09d2 From 7f405f9c3117acfa8a9775c467ab433b23abc5a7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 28 Jun 2011 13:47:40 +0900 Subject: serial: sh-sci: Add missing module description/author bits. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5ff6657fd14..fa99b006315 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2211,3 +2211,5 @@ module_exit(sci_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:sh-sci"); +MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("SuperH SCI(F) serial driver"); -- cgit v1.2.3-70-g09d2 From 23241d43eac88f63a7f0bf4d5c12bbc496651585 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 28 Jun 2011 13:55:31 +0900 Subject: serial: sh-sci: Kill off per-port enable/disable callbacks. Ultimately we want everything to be going through the clock framework and runtime pm, so kill off the per-port callbacks that enabled ports to bypass the common infrastructure. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 79 ++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index fa99b006315..9c8624d9c80 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -62,12 +62,6 @@ struct sci_port { /* Platform configuration */ struct plat_sci_port *cfg; - /* Port enable callback */ - void (*enable)(struct uart_port *port); - - /* Port disable callback */ - void (*disable)(struct uart_port *port); - /* Break timer */ struct timer_list break_timer; int break_flag; @@ -366,6 +360,29 @@ static int sci_probe_regmap(struct plat_sci_port *cfg) return 0; } +static void sci_port_enable(struct sci_port *sci_port) +{ + if (!sci_port->port.dev) + return; + + pm_runtime_get_sync(sci_port->port.dev); + + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); +} + +static void sci_port_disable(struct sci_port *sci_port) +{ + if (!sci_port->port.dev) + return; + + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); + + pm_runtime_put_sync(sci_port->port.dev); +} + #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #ifdef CONFIG_CONSOLE_POLL @@ -651,8 +668,7 @@ static void sci_break_timer(unsigned long data) { struct sci_port *port = (struct sci_port *)data; - if (port->enable) - port->enable(&port->port); + sci_port_enable(port); if (sci_rxd_in(&port->port) == 0) { port->break_flag = 1; @@ -664,8 +680,7 @@ static void sci_break_timer(unsigned long data) } else port->break_flag = 0; - if (port->disable) - port->disable(&port->port); + sci_port_disable(port); } static int sci_handle_errors(struct uart_port *port) @@ -939,27 +954,6 @@ static int sci_notifier(struct notifier_block *self, return NOTIFY_OK; } -static void sci_clk_enable(struct uart_port *port) -{ - struct sci_port *sci_port = to_sci_port(port); - - pm_runtime_get_sync(port->dev); - - clk_enable(sci_port->iclk); - sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - clk_enable(sci_port->fclk); -} - -static void sci_clk_disable(struct uart_port *port) -{ - struct sci_port *sci_port = to_sci_port(port); - - clk_disable(sci_port->fclk); - clk_disable(sci_port->iclk); - - pm_runtime_put_sync(port->dev); -} - static int sci_request_irq(struct sci_port *port) { int i; @@ -1537,8 +1531,7 @@ static int sci_startup(struct uart_port *port) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - if (s->enable) - s->enable(port); + sci_port_enable(s); ret = sci_request_irq(s); if (unlikely(ret < 0)) @@ -1564,8 +1557,7 @@ static void sci_shutdown(struct uart_port *port) sci_free_dma(port); sci_free_irq(s); - if (s->disable) - s->disable(port); + sci_port_disable(s); } static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, @@ -1612,8 +1604,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, if (likely(baud && port->uartclk)) t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk); - if (s->enable) - s->enable(port); + sci_port_enable(s); do { status = sci_in(port, SCxSR); @@ -1683,8 +1674,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); - if (s->disable) - s->disable(port); + sci_port_disable(s); } static const char *sci_type(struct uart_port *port) @@ -1870,8 +1860,6 @@ static int __devinit sci_init_single(struct platform_device *dev, if (IS_ERR(sci_port->fclk)) sci_port->fclk = NULL; - sci_port->enable = sci_clk_enable; - sci_port->disable = sci_clk_disable; port->dev = &dev->dev; pm_runtime_enable(&dev->dev); @@ -1950,8 +1938,7 @@ static void serial_console_write(struct console *co, const char *s, struct uart_port *port = &sci_port->port; unsigned short bits; - if (sci_port->enable) - sci_port->enable(port); + sci_port_enable(sci_port); uart_console_write(port, s, count, serial_console_putchar); @@ -1960,8 +1947,7 @@ static void serial_console_write(struct console *co, const char *s, while ((sci_in(port, SCxSR) & bits) != bits) cpu_relax(); - if (sci_port->disable) - sci_port->disable(port); + sci_port_disable(sci_port); } static int __devinit serial_console_setup(struct console *co, char *options) @@ -1993,8 +1979,7 @@ static int __devinit serial_console_setup(struct console *co, char *options) if (unlikely(ret != 0)) return ret; - if (sci_port->enable) - sci_port->enable(port); + sci_port_enable(sci_port); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); -- cgit v1.2.3-70-g09d2 From 9174fc8f111982e024a00512c521ad8f1056fccb Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 28 Jun 2011 15:25:36 +0900 Subject: serial: sh-sci: Fix up pretty name printing for port IRQs. Presently these were all using the same static string with no regard to dev_name() and the like. This implements a bit of rework to name the IRQ dynamically, as it should have been doing all along anyways. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 118 +++++++++++++++++++++++++++++++------------- include/linux/serial_sci.h | 7 +++ 2 files changed, 92 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9c8624d9c80..d0a56235c50 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -71,6 +71,8 @@ struct sci_port { /* Function clock */ struct clk *fclk; + char *irqstr[SCIx_NR_IRQS]; + struct dma_chan *chan_tx; struct dma_chan *chan_rx; @@ -954,53 +956,102 @@ static int sci_notifier(struct notifier_block *self, return NOTIFY_OK; } +static struct sci_irq_desc { + const char *desc; + irq_handler_t handler; +} sci_irq_desc[] = { + /* + * Split out handlers, the default case. + */ + [SCIx_ERI_IRQ] = { + .desc = "rx err", + .handler = sci_er_interrupt, + }, + + [SCIx_RXI_IRQ] = { + .desc = "rx full", + .handler = sci_rx_interrupt, + }, + + [SCIx_TXI_IRQ] = { + .desc = "tx empty", + .handler = sci_tx_interrupt, + }, + + [SCIx_BRI_IRQ] = { + .desc = "break", + .handler = sci_br_interrupt, + }, + + /* + * Special muxed handler. + */ + [SCIx_MUX_IRQ] = { + .desc = "mux", + .handler = sci_mpxed_interrupt, + }, +}; + static int sci_request_irq(struct sci_port *port) { - int i; - irqreturn_t (*handlers[4])(int irq, void *ptr) = { - sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, - sci_br_interrupt, - }; - const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", - "SCI Transmit Data Empty", "SCI Break" }; - - if (port->cfg->irqs[0] == port->cfg->irqs[1]) { - if (unlikely(!port->cfg->irqs[0])) - return -ENODEV; - - if (request_irq(port->cfg->irqs[0], sci_mpxed_interrupt, - IRQF_DISABLED, "sci", port)) { - dev_err(port->port.dev, "Can't allocate IRQ\n"); - return -ENODEV; + struct uart_port *up = &port->port; + int i, j, ret = 0; + + for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { + struct sci_irq_desc *desc; + unsigned int irq; + + if (SCIx_IRQ_IS_MUXED(port)) { + i = SCIx_MUX_IRQ; + irq = up->irq; + } else + irq = port->cfg->irqs[i]; + + desc = sci_irq_desc + i; + port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", + dev_name(up->dev), desc->desc); + if (!port->irqstr[j]) { + dev_err(up->dev, "Failed to allocate %s IRQ string\n", + desc->desc); + goto out_nomem; } - } else { - for (i = 0; i < ARRAY_SIZE(handlers); i++) { - if (unlikely(!port->cfg->irqs[i])) - continue; - - if (request_irq(port->cfg->irqs[i], handlers[i], - IRQF_DISABLED, desc[i], port)) { - dev_err(port->port.dev, "Can't allocate IRQ\n"); - return -ENODEV; - } + + ret = request_irq(irq, desc->handler, up->irqflags, + port->irqstr[j], port); + if (unlikely(ret)) { + dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc); + goto out_noirq; } } return 0; + +out_noirq: + while (--i >= 0) + free_irq(port->cfg->irqs[i], port); + +out_nomem: + while (--j >= 0) + kfree(port->irqstr[j]); + + return ret; } static void sci_free_irq(struct sci_port *port) { int i; - if (port->cfg->irqs[0] == port->cfg->irqs[1]) - free_irq(port->cfg->irqs[0], port); - else { - for (i = 0; i < ARRAY_SIZE(port->cfg->irqs); i++) { - if (!port->cfg->irqs[i]) - continue; + /* + * Intentionally in reverse order so we iterate over the muxed + * IRQ first. + */ + for (i = 0; i < SCIx_NR_IRQS; i++) { + free_irq(port->cfg->irqs[i], port); + kfree(port->irqstr[i]); - free_irq(port->cfg->irqs[i], port); + if (SCIx_IRQ_IS_MUXED(port)) { + /* If there's only one IRQ, we're done. */ + return; } } } @@ -1910,6 +1961,7 @@ static int __devinit sci_init_single(struct platform_device *dev, * For the muxed case there's nothing more to do. */ port->irq = p->irqs[SCIx_RXI_IRQ]; + port->irqflags = IRQF_DISABLED; port->serial_in = sci_serial_in; port->serial_out = sci_serial_out; diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 4ca130a90ea..8bffe9ae2ca 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -56,6 +56,8 @@ enum { SCIx_TXI_IRQ, SCIx_BRI_IRQ, SCIx_NR_IRQS, + + SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ }; enum { @@ -82,6 +84,11 @@ enum { [SCIx_BRI_IRQ] = (irq), \ } +#define SCIx_IRQ_IS_MUXED(port) \ + ((port)->cfg->irqs[SCIx_ERI_IRQ] == \ + (port)->cfg->irqs[SCIx_RXI_IRQ]) || \ + ((port)->cfg->irqs[SCIx_ERI_IRQ] && \ + !(port)->cfg->irqs[SCIx_RXI_IRQ]) /* * SCI register subset common for all port types. * Not all registers will exist on all parts. -- cgit v1.2.3-70-g09d2 From 24e6289c029b0cf5b4f75e12c1b66000d441c9ed Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 23 May 2011 11:51:18 +0300 Subject: OMAP: DSS2: remove extra includes from include/video/omapdss.h omapdss.h included platform_device.h and atomic.h, neither of which is needed by omapdss.h. Remove those includes from omapdss.h, and fix the affected .c files which did not include platform_device.h even though they should. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.c | 1 + drivers/video/omap2/dss/dss.c | 1 + drivers/video/omap2/dss/hdmi.c | 1 + drivers/video/omap2/dss/rfbi.c | 1 + include/video/omapdss.h | 2 -- 5 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 7a9a2e7d968..a9eebd8e79f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d9489d5c4f0..d0b3f81b372 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -28,6 +28,7 @@ #include #include #include +#include #include