From 40b367d95fb3d60fc1edb9ba8f6ef52272e48936 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 7 Nov 2013 11:35:29 +0100 Subject: irqchip: irq-dove: Add PMU interrupt controller. Dove has a Power Management Unit with its own interrupt controller. This is chained on the main interrupt controller. Add a driver, making use of generic chip where possible. Signed-off-by: Andrew Lunn Tested-by: Sebastian Hesselbarth cc: devicetree@vger.kernel.org cc: pawel.moll@arm.com cc: mark.rutland@arm.com cc: swarren@wwwdotorg.org cc: ian.campbell@citrix.com Signed-off-by: Jason Cooper --- .../interrupt-controller/marvell,dove-pmu-intc.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt new file mode 100644 index 00000000000..1feb5825d37 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt @@ -0,0 +1,17 @@ +Marvell Dove Power Management Unit interrupt controller + +Required properties: +- compatible: shall be "marvell,dove-pmu-intc" +- reg: base address of PMU interrupt registers starting with CAUSE register +- interrupts: PMU interrupt of the main interrupt controller +- interrupt-controller: identifies the node as an interrupt controller +- #interrupt-cells: number of cells to encode an interrupt source, shall be 1 + +Example: + pmu_intc: pmu-interrupt-ctrl@d0050 { + compatible = "marvell,dove-pmu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0xd0050 0x8>; + interrupts = <33>; + }; -- cgit v1.2.3-70-g09d2 From 354e57f3a0a26120af3bcd6c92c355ad00a057c1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 7 Nov 2013 10:25:55 +0100 Subject: ARM/serial: at91: switch atmel serial to use gpiolib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This passes the errata fix using a GPIO to control the RTS pin on one of the AT91 chips to use gpiolib instead of the AT91-specific interfaces. Also remove the reliance on compile-time #defines and the cpu_* check and rely on the platform passing down the proper GPIO pin through platform data. This is a prerequisite for getting rid of the local GPIO implementation in the AT91 platform and move toward multiplatform. The patch also adds device tree support for getting the RTS GPIO pin from the device tree on DT boot paths. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Linus Walleij Acked-by: Greg Kroah-Hartman Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/serial/atmel-usart.txt | 3 ++ arch/arm/mach-at91/at91rm9200_devices.c | 10 ++++- arch/arm/mach-at91/at91sam9260_devices.c | 7 ++++ arch/arm/mach-at91/at91sam9261_devices.c | 4 ++ arch/arm/mach-at91/at91sam9263_devices.c | 4 ++ arch/arm/mach-at91/at91sam9g45_devices.c | 5 +++ arch/arm/mach-at91/at91sam9rl_devices.c | 5 +++ drivers/tty/serial/atmel_serial.c | 49 ++++++++++++++-------- include/linux/platform_data/atmel.h | 1 + 9 files changed, 68 insertions(+), 20 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt index 2191dcb9f1d..3adc61c2e4c 100644 --- a/Documentation/devicetree/bindings/serial/atmel-usart.txt +++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt @@ -10,6 +10,8 @@ Required properties: Optional properties: - atmel,use-dma-rx: use of PDC or DMA for receiving data - atmel,use-dma-tx: use of PDC or DMA for transmitting data +- rts-gpios: specify a GPIO for RTS line. It will use specified PIO instead of the peripheral + function pin for the USART RTS feature. If unsure, don't specify this property. - add dma bindings for dma transfer: - dmas: DMA specifier, consisting of a phandle to DMA controller node, memory peripheral interface and USART DMA channel ID, FIFO configuration. @@ -28,6 +30,7 @@ Example: interrupts = <7>; atmel,use-dma-rx; atmel,use-dma-tx; + rts-gpios = <&pioD 15 0>; }; - use DMA: diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index 3ebc9792560..605add05af7 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c @@ -922,6 +922,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -960,6 +961,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -987,9 +989,10 @@ static inline void configure_usart0_pins(unsigned pins) if (pins & ATMEL_UART_RTS) { /* * AT91RM9200 Errata #39 - RTS0 is not internally connected to PA21. - * We need to drive the pin manually. Default is off (RTS is active low). + * We need to drive the pin manually. The serial driver will driver + * this to high when initializing. */ - at91_set_gpio_output(AT91_PIN_PA21, 1); + uart0_data.rts_gpio = AT91_PIN_PA21; } } @@ -1009,6 +1012,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -1060,6 +1064,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); @@ -1103,6 +1108,7 @@ static struct resource uart3_resources[] = { static struct atmel_uart_data uart3_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart3_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index eda8d1679d4..b52527c78b1 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -819,6 +819,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -857,6 +858,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -908,6 +910,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -951,6 +954,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); @@ -994,6 +998,7 @@ static struct resource uart3_resources[] = { static struct atmel_uart_data uart3_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart3_dmamask = DMA_BIT_MASK(32); @@ -1037,6 +1042,7 @@ static struct resource uart4_resources[] = { static struct atmel_uart_data uart4_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart4_dmamask = DMA_BIT_MASK(32); @@ -1075,6 +1081,7 @@ static struct resource uart5_resources[] = { static struct atmel_uart_data uart5_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart5_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index b2a34740146..6c1a2ecc306 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -880,6 +880,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -918,6 +919,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -961,6 +963,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -1004,6 +1007,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 4aeadddbc18..97cc2a0d6f9 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -1324,6 +1324,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -1362,6 +1363,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -1405,6 +1407,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -1448,6 +1451,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index cb36fa872d3..c10149588e2 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -1587,6 +1587,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -1625,6 +1626,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -1668,6 +1670,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -1711,6 +1714,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); @@ -1754,6 +1758,7 @@ static struct resource uart3_resources[] = { static struct atmel_uart_data uart3_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart3_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c index a698bdab2cc..4120af972b6 100644 --- a/arch/arm/mach-at91/at91sam9rl_devices.c +++ b/arch/arm/mach-at91/at91sam9rl_devices.c @@ -956,6 +956,7 @@ static struct resource dbgu_resources[] = { static struct atmel_uart_data dbgu_data = { .use_dma_tx = 0, .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .rts_gpio = -EINVAL, }; static u64 dbgu_dmamask = DMA_BIT_MASK(32); @@ -994,6 +995,7 @@ static struct resource uart0_resources[] = { static struct atmel_uart_data uart0_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart0_dmamask = DMA_BIT_MASK(32); @@ -1045,6 +1047,7 @@ static struct resource uart1_resources[] = { static struct atmel_uart_data uart1_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart1_dmamask = DMA_BIT_MASK(32); @@ -1088,6 +1091,7 @@ static struct resource uart2_resources[] = { static struct atmel_uart_data uart2_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart2_dmamask = DMA_BIT_MASK(32); @@ -1131,6 +1135,7 @@ static struct resource uart3_resources[] = { static struct atmel_uart_data uart3_data = { .use_dma_tx = 1, .use_dma_rx = 1, + .rts_gpio = -EINVAL, }; static u64 uart3_dmamask = DMA_BIT_MASK(32); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index c7d99af46a9..40d6c9b0d98 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -35,21 +35,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include -#ifdef CONFIG_ARM -#include -#include -#endif - #define PDC_BUFFER_SIZE 512 /* Revisit: We should calculate this based on the actual port settings */ #define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ @@ -168,6 +165,7 @@ struct atmel_uart_port { struct circ_buf rx_ring; struct serial_rs485 rs485; /* rs485 settings */ + int rts_gpio; /* optional RTS GPIO */ unsigned int tx_done_mask; bool is_usart; /* usart or uart */ struct timer_list uart_timer; /* uart timer */ @@ -301,20 +299,16 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) unsigned int mode; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); -#ifdef CONFIG_ARCH_AT91RM9200 - if (cpu_is_at91rm9200()) { - /* - * AT91RM9200 Errata #39: RTS0 is not internally connected - * to PA21. We need to drive the pin manually. - */ - if (port->mapbase == AT91RM9200_BASE_US0) { - if (mctrl & TIOCM_RTS) - at91_set_gpio_value(AT91_PIN_PA21, 0); - else - at91_set_gpio_value(AT91_PIN_PA21, 1); - } + /* + * AT91RM9200 Errata #39: RTS0 is not internally connected + * to PA21. We need to drive the pin as a GPIO. + */ + if (gpio_is_valid(atmel_port->rts_gpio)) { + if (mctrl & TIOCM_RTS) + gpio_set_value(atmel_port->rts_gpio, 0); + else + gpio_set_value(atmel_port->rts_gpio, 1); } -#endif if (mctrl & TIOCM_RTS) control |= ATMEL_US_RTSEN; @@ -2379,6 +2373,25 @@ static int atmel_serial_probe(struct platform_device *pdev) port = &atmel_ports[ret]; port->backup_imr = 0; port->uart.line = ret; + port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */ + if (pdata) + port->rts_gpio = pdata->rts_gpio; + else if (np) + port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0); + + if (gpio_is_valid(port->rts_gpio)) { + ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS"); + if (ret) { + dev_err(&pdev->dev, "error requesting RTS GPIO\n"); + goto err; + } + /* Default to 1 as RTS is active low */ + ret = gpio_direction_output(port->rts_gpio, 1); + if (ret) { + dev_err(&pdev->dev, "error setting up RTS GPIO\n"); + goto err; + } + } ret = atmel_init_port(port, pdev); if (ret) diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index cea9f70133c..e26b0c14ede 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -84,6 +84,7 @@ struct atmel_uart_data { short use_dma_rx; /* use receive DMA? */ void __iomem *regs; /* virt. base address, if any */ struct serial_rs485 rs485; /* rs485 settings */ + int rts_gpio; /* optional RTS GPIO */ }; /* Touchscreen Controller */ -- cgit v1.2.3-70-g09d2 From 1a5b7d41f8f6ec087372bef348c46f5844a472b6 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 17 Dec 2013 16:16:00 +0000 Subject: iio: at91: document ADC clock properties Document the clock properties required by the at91 ADC driver. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/arm/atmel-adc.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt index d1061469f63..9a1175b46f4 100644 --- a/Documentation/devicetree/bindings/arm/atmel-adc.txt +++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt @@ -5,6 +5,9 @@ Required properties: can be "at91sam9260", "at91sam9g45" or "at91sam9x5" - reg: Should contain ADC registers location and length - interrupts: Should contain the IRQ line for the ADC + - clock-names: tuple listing input clock names. + Required elements: "adc_clk", "adc_op_clk". + - clocks: phandles to input clocks. - atmel,adc-channels-used: Bitmask of the channels muxed and enable for this device - atmel,adc-startup-time: Startup Time of the ADC in microseconds as @@ -44,6 +47,8 @@ adc0: adc@fffb0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffb0000 0x100>; interrupts = <20 4>; + clocks = <&adc_clk>, <&adc_op_clk>; + clock-names = "adc_clk", "adc_op_clk"; atmel,adc-channel-base = <0x30>; atmel,adc-channels-used = <0xff>; atmel,adc-drdy-mask = <0x10000>; -- cgit v1.2.3-70-g09d2 From 5ad8865b009bc8ad35adcbcb60c0679d437e8036 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 27 Jan 2014 17:37:51 +0200 Subject: ASoC: davinci-evm: Add named clock reference to DT bindings The referenced clock is used to get codec clock rate and the clock is disabled and enabled in startup and shutdown snd_soc_ops call backs. The change is also documented in DT bindigs document. Signed-off-by: Jyri Sarha cc: bcousson@baylibre.com Signed-off-by: Mark Brown --- .../bindings/sound/davinci-evm-audio.txt | 9 +++- sound/soc/davinci/davinci-evm.c | 58 +++++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt index 865178d5cdf..963e100514c 100644 --- a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt @@ -5,12 +5,19 @@ Required properties: - ti,model : The user-visible name of this sound complex. - ti,audio-codec : The phandle of the TLV320AIC3x audio codec - ti,mcasp-controller : The phandle of the McASP controller -- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec - ti,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and sinks are the codec's pins, and the jacks on the board: +Optional properties: +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec. +- clocks : Reference to the master clock +- clock-names : The clock should be named "mclk" +- Either codec-clock-rate or the codec-clock reference has to be defined. If + the both are defined the driver attempts to set referenced clock to the + defined rate and takes the rate from the clock reference. + Board connectors: * Headphone Jack diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 70ff3772079..d3e4cb0655d 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -30,9 +31,34 @@ #include "davinci-i2s.h" struct snd_soc_card_drvdata_davinci { + struct clk *mclk; unsigned sysclk; }; +static int evm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + return clk_prepare_enable(drvdata->mclk); + + return 0; +} + +static void evm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + clk_disable_unprepare(drvdata->mclk); +} + static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -59,6 +85,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops evm_ops = { + .startup = evm_startup, + .shutdown = evm_shutdown, .hw_params = evm_hw_params, }; @@ -348,6 +376,7 @@ static int davinci_evm_probe(struct platform_device *pdev) of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data; struct snd_soc_card_drvdata_davinci *drvdata = NULL; + struct clk *mclk; int ret = 0; evm_soc_card.dai_link = dai; @@ -367,13 +396,38 @@ static int davinci_evm_probe(struct platform_device *pdev) if (ret) return ret; + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (PTR_ERR(mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(mclk)) { + dev_dbg(&pdev->dev, "mclk not found.\n"); + mclk = NULL; + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; + drvdata->mclk = mclk; + ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk); - if (ret < 0) - return -EINVAL; + + if (ret < 0) { + if (!drvdata->mclk) { + dev_err(&pdev->dev, + "No clock or clock rate defined.\n"); + return -EINVAL; + } + drvdata->sysclk = clk_get_rate(drvdata->mclk); + } else if (drvdata->mclk) { + unsigned int requestd_rate = drvdata->sysclk; + clk_set_rate(drvdata->mclk, drvdata->sysclk); + drvdata->sysclk = clk_get_rate(drvdata->mclk); + if (drvdata->sysclk != requestd_rate) + dev_warn(&pdev->dev, + "Could not get requested rate %u using %u.\n", + requestd_rate, drvdata->sysclk); + } snd_soc_card_set_drvdata(&evm_soc_card, drvdata); ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); -- cgit v1.2.3-70-g09d2 From 3471c1224cb5f543b617e2d14000db1fcf71924b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Jan 2014 14:37:58 +0100 Subject: regulator: s5m8767: Document new binding for Buck9 GPIO control Add documentation for new binding for controlling (enable/disable) the Buck9 Converter by GPIO (BUCK9EN). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/s5m8767-regulator.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt index fc6b38f035b..d290988ed97 100644 --- a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt @@ -69,13 +69,16 @@ sub-node should be of the format as listed below. }; }; The above regulator entries are defined in regulator bindings documentation -except op_mode description. +except these properties: - op_mode: describes the different operating modes of the LDO's with power mode change in SOC. The different possible values are, 0 - always off mode 1 - on in normal mode 2 - low power mode 3 - suspend mode + - s5m8767,pmic-ext-control-gpios: (optional) GPIO specifier for one + GPIO controlling this regulator (enable/disable); This is + valid only for buck9. The following are the names of the regulators that the s5m8767 pmic block supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number @@ -148,5 +151,13 @@ Example: regulator-always-on; regulator-boot-on; }; + + vemmc_reg: BUCK9 { + regulator-name = "VMEM_VDD_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + op_mode = <3>; /* Standby Mode */ + s5m8767,pmic-ext-control-gpios = <&gpk0 2 0>; + }; }; }; -- cgit v1.2.3-70-g09d2 From 426ef76dd8a394a0e04d096941cd9acb49539a3e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 28 Jan 2014 10:21:38 +0100 Subject: spi: rspi: Add DT support Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-rspi.txt | 59 ++++++++++++ drivers/spi/spi-rspi.c | 106 +++++++++++++++------ 2 files changed, 136 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/spi-rspi.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt new file mode 100644 index 00000000000..95f9b21d239 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-rspi.txt @@ -0,0 +1,59 @@ +Device tree configuration for Renesas RSPI/QSPI driver + +Required properties: +- compatible : For Renesas Serial Peripheral Interface on legacy SH: + "renesas,rspi-", "renesas,rspi" as fallback. + For Renesas Serial Peripheral Interface on RZ/A1H: + "renesas,rspi-", "renesas,rspi-rz" as fallback. + For Quad Serial Peripheral Interface on R-Car Gen2: + "renesas,qspi-", "renesas,qspi" as fallback. + Examples of valid soctypes are "sh7757" (SH), + "r7s72100" (RZ/A1H), "r8a7790" (R-Car H2), and + "r8a7791" (R-Car M2). +- reg : Address start and address range size of the device +- interrupts : A list of interrupt-specifiers, one for each entry in + interrupt-names. + If interrupt-names is not present, an interrupt specifier + for a single muxed interrupt. +- interrupt-names : A list of interrupt names. Should contain (if present): + - "error" for SPEI, + - "rx" for SPRI, + - "tx" to SPTI, + - "mux" for a single muxed interrupt. +- interrupt-parent : The phandle for the interrupt controller that + services interrupts for this device. +- num-cs : Number of chip selects. Some RSPI cores have more than 1. +- #address-cells : Must be <1> +- #size-cells : Must be <0> + +Optional properties: +- clocks: : Must contain a reference to the functional clock. + +Pinctrl properties might be needed, too. See +Documentation/devicetree/bindings/pinctrl/renesas,*. + +Examples: + + spi0: spi@e800c800 { + compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz"; + reg = <0xe800c800 0x24>; + interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>, + <0 239 IRQ_TYPE_LEVEL_HIGH>, + <0 240 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "error", "rx", "tx"; + interrupt-parent = <&gic>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + spi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7791", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupt-parent = <&gic>; + interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d79a7ed9b92..e56fcb5f7f9 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -985,6 +986,56 @@ static int rspi_remove(struct platform_device *pdev) return 0; } +static const struct spi_ops rspi_ops = { + .set_config_register = rspi_set_config_register, + .transfer_one = rspi_transfer_one, +}; + +static const struct spi_ops rspi_rz_ops = { + .set_config_register = rspi_rz_set_config_register, + .transfer_one = rspi_rz_transfer_one, +}; + +static const struct spi_ops qspi_ops = { + .set_config_register = qspi_set_config_register, + .transfer_one = qspi_transfer_one, +}; + +#ifdef CONFIG_OF +static const struct of_device_id rspi_of_match[] = { + /* RSPI on legacy SH */ + { .compatible = "renesas,rspi", .data = &rspi_ops }, + /* RSPI on RZ/A1H */ + { .compatible = "renesas,rspi-rz", .data = &rspi_rz_ops }, + /* QSPI on R-Car Gen2 */ + { .compatible = "renesas,qspi", .data = &qspi_ops }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rspi_of_match); + +static int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + u32 num_cs; + int error; + + /* Parse DT properties */ + error = of_property_read_u32(dev->of_node, "num-cs", &num_cs); + if (error) { + dev_err(dev, "of_property_read_u32 num-cs failed %d\n", error); + return error; + } + + master->num_chipselect = num_cs; + return 0; +} +#else +static inline int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + return -EINVAL; +} +#endif /* CONFIG_OF */ + static int rspi_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, const char *suffix, void *dev_id) @@ -1004,16 +1055,9 @@ static int rspi_probe(struct platform_device *pdev) struct spi_master *master; struct rspi_data *rspi; int ret; - const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); + const struct of_device_id *of_id; + const struct rspi_plat_data *rspi_pd; const struct spi_ops *ops; - const struct platform_device_id *id_entry = pdev->id_entry; - - ops = (struct spi_ops *)id_entry->driver_data; - /* ops parameter check */ - if (!ops->set_config_register) { - dev_err(&pdev->dev, "there is no set_config_register\n"); - return -ENODEV; - } master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data)); if (master == NULL) { @@ -1021,6 +1065,28 @@ static int rspi_probe(struct platform_device *pdev) return -ENOMEM; } + of_id = of_match_device(rspi_of_match, &pdev->dev); + if (of_id) { + ops = of_id->data; + ret = rspi_parse_dt(&pdev->dev, master); + if (ret) + goto error1; + } else { + ops = (struct spi_ops *)pdev->id_entry->driver_data; + rspi_pd = dev_get_platdata(&pdev->dev); + if (rspi_pd && rspi_pd->num_chipselect) + master->num_chipselect = rspi_pd->num_chipselect; + else + master->num_chipselect = 2; /* default */ + }; + + /* ops parameter check */ + if (!ops->set_config_register) { + dev_err(&pdev->dev, "there is no set_config_register\n"); + ret = -ENODEV; + goto error1; + } + rspi = spi_master_get_devdata(master); platform_set_drvdata(pdev, rspi); rspi->ops = ops; @@ -1048,11 +1114,6 @@ static int rspi_probe(struct platform_device *pdev) init_waitqueue_head(&rspi->wait); - if (rspi_pd && rspi_pd->num_chipselect) - master->num_chipselect = rspi_pd->num_chipselect; - else - master->num_chipselect = 2; /* default */ - master->bus_num = pdev->id; master->setup = rspi_setup; master->transfer_one = ops->transfer_one; @@ -1060,6 +1121,7 @@ static int rspi_probe(struct platform_device *pdev) master->prepare_message = rspi_prepare_message; master->unprepare_message = rspi_unprepare_message; master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP; + master->dev.of_node = pdev->dev.of_node; ret = platform_get_irq_byname(pdev, "rx"); if (ret < 0) { @@ -1122,21 +1184,6 @@ error1: return ret; } -static struct spi_ops rspi_ops = { - .set_config_register = rspi_set_config_register, - .transfer_one = rspi_transfer_one, -}; - -static struct spi_ops rspi_rz_ops = { - .set_config_register = rspi_rz_set_config_register, - .transfer_one = rspi_rz_transfer_one, -}; - -static struct spi_ops qspi_ops = { - .set_config_register = qspi_set_config_register, - .transfer_one = qspi_transfer_one, -}; - static struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, @@ -1153,6 +1200,7 @@ static struct platform_driver rspi_driver = { .driver = { .name = "renesas_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rspi_of_match), }, }; module_platform_driver(rspi_driver); -- cgit v1.2.3-70-g09d2 From 373d4e6bb8e9611e56cfbefa8d9246b004276791 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 3 Feb 2014 09:51:38 +0800 Subject: clk: sunxi: update clock-output-names dt binding documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clock-output-names is now required for most of sunxi clock nodes, to provide the name of the corresponding clock. Add the new requirements, exceptions, as well as examples. Signed-off-by: Chen-Yu Tsai Signed-off-by: Emilio López --- Documentation/devicetree/bindings/clock/sunxi.txt | 32 ++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index c2cb7621ad2..0cf679b8384 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -44,10 +44,11 @@ Required properties for all clocks: multiplexed clocks, the list order must match the hardware programming order. - #clock-cells : from common clock binding; shall be set to 0 except for - "allwinner,*-gates-clk" where it shall be set to 1 - -Additionally, "allwinner,*-gates-clk" clocks require: -- clock-output-names : the corresponding gate names that the clock controls + "allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk" and + "allwinner,sun4i-pll6-clk" where it shall be set to 1 +- clock-output-names : shall be the corresponding names of the outputs. + If the clock module only has one output, the name shall be the + module name. Clock consumers should specify the desired clocks they use with a "clocks" phandle cell. Consumers that are using a gated clock should @@ -56,18 +57,28 @@ offset of the bit controlling this particular gate in the register. For example: -osc24M: osc24M@01c20050 { +osc24M: clk@01c20050 { #clock-cells = <0>; compatible = "allwinner,sun4i-osc-clk"; reg = <0x01c20050 0x4>; clocks = <&osc24M_fixed>; + clock-output-names = "osc24M"; }; -pll1: pll1@01c20000 { +pll1: clk@01c20000 { #clock-cells = <0>; compatible = "allwinner,sun4i-pll1-clk"; reg = <0x01c20000 0x4>; clocks = <&osc24M>; + clock-output-names = "pll1"; +}; + +pll5: clk@01c20020 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll5-clk"; + reg = <0x01c20020 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll5_ddr", "pll5_other"; }; cpu: cpu@01c20054 { @@ -75,4 +86,13 @@ cpu: cpu@01c20054 { compatible = "allwinner,sun4i-cpu-clk"; reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; + clock-output-names = "cpu"; +}; + +mmc0_clk: clk@01c20088 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-mod0-clk"; + reg = <0x01c20088 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; }; -- cgit v1.2.3-70-g09d2 From 6127daa85094e20b42d306dde800e0d745847990 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 23 Jan 2014 11:57:27 -0600 Subject: regulator: ti-abb: Add support for interleaved LDO registers Certain platforms such as DRA7 have quirky memory maps such as: PRM_ABBLDO_DSPEVE_CTRL 0x4ae07e20 PRM_ABBLDO_IVA_CTRL 0x4ae07e24 other-registers PRM_ABBLDO_DSPEVE_SETUP 0x4ae07e30 PRM_ABBLDO_IVA_SETUP 0x4ae07e34 These need the address range allocation to be either not reserved OR unique allocation per register instance or use something like syscon based solution. By going with unique allocation per register, we are able to now have a single compatible driver for all instances on all platforms which use the IP block. So, introduce a new "ti,abb-v3" compatible to allow for definitions where explicit register definitions are provided, while maintaining backward compatibility of older predefined register offsets provided by "ti-abb-v1" and "ti-abb-v2". As part of this change, we rename a few variables to indicate the appropriate meaning. Signed-off-by: Nishanth Menon Signed-off-by: Mark Brown --- .../bindings/regulator/ti-abb-regulator.txt | 6 +- drivers/regulator/ti-abb-regulator.c | 86 ++++++++++++++-------- 2 files changed, 62 insertions(+), 30 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt index 2e57a33e902..c58db75f959 100644 --- a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt @@ -4,10 +4,14 @@ Required Properties: - compatible: Should be one of: - "ti,abb-v1" for older SoCs like OMAP3 - "ti,abb-v2" for newer SoCs like OMAP4, OMAP5 + - "ti,abb-v3" for a generic definition where setup and control registers are + provided (example: DRA7) - reg: Address and length of the register set for the device. It contains the information of registers in the same order as described by reg-names - reg-names: Should contain the reg names - - "base-address" - contains base address of ABB module + - "base-address" - contains base address of ABB module (ti,abb-v1,ti,abb-v2) + - "control-address" - contains control register address of ABB module (ti,abb-v3) + - "setup-address" - contains setup register address of ABB module (ti,abb-v3) - "int-address" - contains address of interrupt register for ABB module (also see Optional properties) - #address-cell: should be 0 diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index b187b6bba7a..a97d0c5e109 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -54,8 +54,8 @@ struct ti_abb_info { /** * struct ti_abb_reg - Register description for ABB block - * @setup_reg: setup register offset from base - * @control_reg: control register offset from base + * @setup_off: setup register offset from base + * @control_off: control register offset from base * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask * @fbb_sel_mask: setup register- FBB sel mask * @rbb_sel_mask: setup register- RBB sel mask @@ -64,8 +64,8 @@ struct ti_abb_info { * @opp_sel_mask: control register - mask for mode to operate */ struct ti_abb_reg { - u32 setup_reg; - u32 control_reg; + u32 setup_off; + u32 control_off; /* Setup register fields */ u32 sr2_wtcnt_value_mask; @@ -83,6 +83,8 @@ struct ti_abb_reg { * @rdesc: regulator descriptor * @clk: clock(usually sysclk) supplying ABB block * @base: base address of ABB block + * @setup_reg: setup register of ABB block + * @control_reg: control register of ABB block * @int_base: interrupt register base address * @efuse_base: (optional) efuse base address for ABB modes * @ldo_base: (optional) LDOVBB vset override base address @@ -99,6 +101,8 @@ struct ti_abb { struct regulator_desc rdesc; struct clk *clk; void __iomem *base; + void __iomem *setup_reg; + void __iomem *control_reg; void __iomem *int_base; void __iomem *efuse_base; void __iomem *ldo_base; @@ -118,20 +122,18 @@ struct ti_abb { * ti_abb_rmw() - handy wrapper to set specific register bits * @mask: mask for register field * @value: value shifted to mask location and written - * @offset: offset of register - * @base: base address + * @reg: register address * * Return: final register value (may be unused) */ -static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, - void __iomem *base) +static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg) { u32 val; - val = readl(base + offset); + val = readl(reg); val &= ~mask; val |= (value << __ffs(mask)) & mask; - writel(val, base + offset); + writel(val, reg); return val; } @@ -263,21 +265,19 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, if (ret) goto out; - ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg); switch (info->opp_sel) { case TI_ABB_SLOW_OPP: - ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg); break; case TI_ABB_FAST_OPP: - ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg); break; } /* program next state of ABB ldo */ - ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, - abb->base); + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg); /* * program LDO VBB vset override if needed for !bypass mode @@ -288,7 +288,7 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, ti_abb_program_ldovbb(dev, abb, info); /* Initiate ABB ldo change */ - ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg); /* Wait for ABB LDO to complete transition to new Bias setting */ ret = ti_abb_wait_txdone(dev, abb); @@ -490,8 +490,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, clk_get_rate(abb->clk), sr2_wt_cnt_val); - ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg); return 0; } @@ -648,8 +647,8 @@ static struct regulator_ops ti_abb_reg_ops = { /* Default ABB block offsets, IF this changes in future, create new one */ static const struct ti_abb_reg abb_regs_v1 = { /* WARNING: registers are wrongly documented in TRM */ - .setup_reg = 0x04, - .control_reg = 0x00, + .setup_off = 0x04, + .control_off = 0x00, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -661,8 +660,8 @@ static const struct ti_abb_reg abb_regs_v1 = { }; static const struct ti_abb_reg abb_regs_v2 = { - .setup_reg = 0x00, - .control_reg = 0x04, + .setup_off = 0x00, + .control_off = 0x04, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -673,9 +672,20 @@ static const struct ti_abb_reg abb_regs_v2 = { .opp_sel_mask = (0x03 << 0), }; +static const struct ti_abb_reg abb_regs_generic = { + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + static const struct of_device_id ti_abb_of_match[] = { {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + {.compatible = "ti,abb-v3", .data = &abb_regs_generic}, { }, }; @@ -722,11 +732,29 @@ static int ti_abb_probe(struct platform_device *pdev) abb->regs = match->data; /* Map ABB resources */ - pname = "base-address"; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); - abb->base = devm_ioremap_resource(dev, res); - if (IS_ERR(abb->base)) - return PTR_ERR(abb->base); + if (abb->regs->setup_off || abb->regs->control_off) { + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->base)) + return PTR_ERR(abb->base); + + abb->setup_reg = abb->base + abb->regs->setup_off; + abb->control_reg = abb->base + abb->regs->control_off; + + } else { + pname = "control-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->control_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->control_reg)) + return PTR_ERR(abb->control_reg); + + pname = "setup-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->setup_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->setup_reg)) + return PTR_ERR(abb->setup_reg); + } pname = "int-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); @@ -860,7 +888,7 @@ skip_opt: platform_set_drvdata(pdev, rdev); /* Enable the ldo if not already done by bootloader */ - ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg); return 0; } -- cgit v1.2.3-70-g09d2 From 4d16700dd926d4c4a66a91a138c34eef4fd342b4 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 27 Jan 2014 13:03:08 +0100 Subject: ASoC: tlv320aic32x4: DT support Add DT support for this codec. The bindings differ a bit from the aic3x codec bindings, so I created a new binding documentation. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tlv320aic32x4.txt | 18 ++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 25 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tlv320aic32x4.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt b/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt new file mode 100644 index 00000000000..db0551088cc --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt @@ -0,0 +1,18 @@ +Texas Instruments - tlv320aic32x4 Codec module + +The tlv320aic32x4 serial control bus communicates through I2C protocols + +Required properties: + - compatible: Should be "ti,tlv320aic32x4" + - reg: I2C slave address + +Optional properties: + - reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt + + +Example: + +codec: tlv320aic32x4@18 { + compatible = "ti,tlv320aic32x4"; + reg = <0x18>; +}; diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index c541213b1ed..1dd50e48934 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -669,11 +670,22 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), }; +static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, + struct device_node *np) +{ + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + + return 0; +} + static int aic32x4_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct aic32x4_pdata *pdata = i2c->dev.platform_data; struct aic32x4_priv *aic32x4; + struct device_node *np = i2c->dev.of_node; int ret; aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv), @@ -692,6 +704,12 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, aic32x4->swapdacs = pdata->swapdacs; aic32x4->micpga_routing = pdata->micpga_routing; aic32x4->rstn_gpio = pdata->rstn_gpio; + } else if (np) { + ret = aic32x4_parse_dt(aic32x4, np); + if (ret) { + dev_err(&i2c->dev, "Failed to parse DT node\n"); + return ret; + } } else { aic32x4->power_cfg = 0; aic32x4->swapdacs = false; @@ -723,10 +741,17 @@ static const struct i2c_device_id aic32x4_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + static struct i2c_driver aic32x4_i2c_driver = { .driver = { .name = "tlv320aic32x4", .owner = THIS_MODULE, + .of_match_table = aic32x4_of_id, }, .probe = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, -- cgit v1.2.3-70-g09d2 From 73c90d943e34f5db2998c1bbb3327581fb87be1f Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Fri, 31 Jan 2014 14:34:36 +0100 Subject: Documentation: devicetree: sja1000: add reg-io-width binding Add the reg-io-width property to describe the width of the memory accesses. Cc: Grant Likely Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Signed-off-by: Florian Vaussard Tested-by: Andreas Larsson Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/sja1000.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/net/can/sja1000.txt b/Documentation/devicetree/bindings/net/can/sja1000.txt index f2105a47ec8..b4a6d53fb01 100644 --- a/Documentation/devicetree/bindings/net/can/sja1000.txt +++ b/Documentation/devicetree/bindings/net/can/sja1000.txt @@ -12,6 +12,10 @@ Required properties: Optional properties: +- reg-io-width : Specify the size (in bytes) of the IO accesses that + should be performed on the device. Valid value is 1, 2 or 4. + Default to 1 (8 bits). + - nxp,external-clock-frequency : Frequency of the external oscillator clock in Hz. Note that the internal clock frequency used by the SJA1000 is half of that value. If not specified, a default value -- cgit v1.2.3-70-g09d2 From 0094050d783bbadffe83effef11a0bda901153ce Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 30 Jan 2014 21:25:14 -0800 Subject: regulator: gpio: add gpios-status for DT config->gpios[x].flags indicates initial pin status, and it will be used for drvdata->state on gpio_regulator_probe(). But, current of_get_gpio_regulator_config() doesn't care about this flags. This patch adds new gpios-status property in order to care about initial pin status. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/gpio-regulator.txt | 1 + drivers/regulator/gpio-regulator.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index 63c659800c0..3ecb585e786 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -8,6 +8,7 @@ Required properties: Optional properties: - enable-gpio : GPIO to use to enable/disable the regulator. - gpios : GPIO group used to control voltage. +- gpios-states : gpios pin's initial states. 1 means HIGH - startup-delay-us : Startup time in microseconds. - enable-active-high : Polarity of GPIO is active high (default is low). diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index bad44f366b3..ac3a8c732dd 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -172,11 +172,22 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) if (!config->gpios) return ERR_PTR(-ENOMEM); + prop = of_find_property(np, "gpios-states", NULL); + if (prop) { + proplen = prop->length / sizeof(int); + if (proplen != config->nr_gpios) { + /* gpios <-> gpios-states mismatch */ + prop = NULL; + } + } + for (i = 0; i < config->nr_gpios; i++) { gpio = of_get_named_gpio(np, "gpios", i); if (gpio < 0) break; config->gpios[i].gpio = gpio; + if (prop && be32_to_cpup((int *)prop->value + i)) + config->gpios[i].flags = GPIOF_OUT_INIT_HIGH; } /* Fetch states. */ -- cgit v1.2.3-70-g09d2 From 006e983bbc805431c44e2135e13841f66059a045 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Tue, 3 Dec 2013 15:57:22 +0530 Subject: DRIVERS: IRQCHIP: IRQ-GIC: Add support for routable irqs In some socs the gic can be preceded by a crossbar IP which routes the peripheral interrupts to the gic inputs. The peripheral interrupts are associated with a fixed crossbar input line and the crossbar routes that to one of the free gic input line. The DT entries for peripherals provides the fixed crossbar input line as its interrupt number and the mapping code should associate this with a free gic input line. This patch adds the support inside the gic irqchip to handle such routable irqs. The routable irqs are registered in a linear domain. The registered routable domain's callback should be implemented to get a free irq and to configure the IP to route it. Cc: Thomas Gleixner Cc: Linus Walleij Cc: Santosh Shilimkar Cc: Russell King Cc: Tony Lindgren Cc: Rajendra Nayak Cc: Marc Zyngier Cc: Grant Likely Cc: Rob Herring Signed-off-by: Sricharan R Reviewed-by: Thomas Gleixner Acked-by: Santosh Shilimkar Acked-by: Linus Walleij --- Documentation/devicetree/bindings/arm/gic.txt | 6 ++ drivers/irqchip/irq-gic.c | 82 +++++++++++++++++++++++---- include/linux/irqchip/arm-gic.h | 7 ++- 3 files changed, 84 insertions(+), 11 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index bae0d87a38b..5573c08d318 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt @@ -50,6 +50,11 @@ Optional regions, used when the GIC doesn't have banked registers. The offset is cpu-offset * cpu-nr. +- arm,routable-irqs : Total number of gic irq inputs which are not directly + connected from the peripherals, but are routed dynamically + by a crossbar/multiplexer preceding the GIC. The GIC irq + input line is assigned dynamically when the corresponding + peripheral's crossbar line is mapped. Example: intc: interrupt-controller@fff11000 { @@ -57,6 +62,7 @@ Example: #interrupt-cells = <3>; #address-cells = <1>; interrupt-controller; + arm,routable-irqs = <160>; reg = <0xfff11000 0x1000>, <0xfff10100 0x100>; }; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 341c6016812..07a7050841e 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + + gic_routable_irq_domain_ops->map(d, irq, hw); } irq_set_chip_data(irq, d->host_data); return 0; } +static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + gic_routable_irq_domain_ops->unmap(d, irq); +} + static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *controller, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type) { + unsigned long ret = 0; + if (d->of_node != controller) return -EINVAL; if (intsize < 3) @@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d, *out_hwirq = intspec[1] + 16; /* For SPIs, we need to add 16 more to get the GIC irq ID number */ - if (!intspec[0]) - *out_hwirq += 16; + if (!intspec[0]) { + ret = gic_routable_irq_domain_ops->xlate(d, controller, + intspec, + intsize, + out_hwirq, + out_type); + + if (IS_ERR_VALUE(ret)) + return ret; + } *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; - return 0; + + return ret; } #ifdef CONFIG_SMP @@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = { const struct irq_domain_ops gic_irq_domain_ops = { .map = gic_irq_domain_map, + .unmap = gic_irq_domain_unmap, .xlate = gic_irq_domain_xlate, }; +/* Default functions for routable irq domain */ +static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + return 0; +} + +static void gic_routable_irq_domain_unmap(struct irq_domain *d, + unsigned int irq) +{ +} + +static int gic_routable_irq_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + *out_hwirq += 16; + return 0; +} + +const struct irq_domain_ops gic_default_routable_irq_domain_ops = { + .map = gic_routable_irq_domain_map, + .unmap = gic_routable_irq_domain_unmap, + .xlate = gic_routable_irq_domain_xlate, +}; + +const struct irq_domain_ops *gic_routable_irq_domain_ops = + &gic_default_routable_irq_domain_ops; + void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node) @@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; + int nr_routable_irqs; BUG_ON(gic_nr >= MAX_GIC_NR); @@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic->gic_irqs = gic_irqs; gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ - irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); - if (IS_ERR_VALUE(irq_base)) { - WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", - irq_start); - irq_base = irq_start; + + if (of_property_read_u32(node, "arm,routable-irqs", + &nr_routable_irqs)) { + irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, + numa_node_id()); + if (IS_ERR_VALUE(irq_base)) { + WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", + irq_start); + irq_base = irq_start; + } + + gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, + hwirq_base, &gic_irq_domain_ops, gic); + } else { + gic->domain = irq_domain_add_linear(node, nr_routable_irqs, + &gic_irq_domain_ops, + gic); } - gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, - hwirq_base, &gic_irq_domain_ops, gic); + if (WARN_ON(!gic->domain)) return; diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 0ceb389dba6..7ed92d0560d 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu); void gic_migrate_target(unsigned int new_cpu_id); unsigned long gic_get_sgir_physaddr(void); +extern const struct irq_domain_ops *gic_routable_irq_domain_ops; +static inline void __init register_routable_domain_ops + (const struct irq_domain_ops *ops) +{ + gic_routable_irq_domain_ops = ops; +} #endif /* __ASSEMBLY */ - #endif -- cgit v1.2.3-70-g09d2 From 96ca848ef7ea1be7e92d1cceb34ef3aa86053828 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Tue, 3 Dec 2013 15:57:23 +0530 Subject: DRIVERS: IRQCHIP: CROSSBAR: Add support for Crossbar IP Some socs have a large number of interrupts requests to service the needs of its many peripherals and subsystems. All of the interrupt lines from the subsystems are not needed at the same time, so they have to be muxed to the irq-controller appropriately. In such places a interrupt controllers are preceded by an CROSSBAR that provides flexibility in muxing the device requests to the controller inputs. This driver takes care a allocating a free irq and then configuring the crossbar IP as a part of the mpu's irqchip callbacks. crossbar_init should be called right before the irqchip_init, so that it is setup to handle the irqchip callbacks. Cc: Thomas Gleixner Cc: Linus Walleij Cc: Santosh Shilimkar Cc: Russell King Cc: Tony Lindgren Cc: Rajendra Nayak Cc: Marc Zyngier Cc: Grant Likely Cc: Rob Herring Signed-off-by: Sricharan R Acked-by: Kumar Gala (for DT binding portion) Acked-by: Santosh Shilimkar Acked-by: Linus Walleij Acked-by: Thomas Gleixner --- .../devicetree/bindings/arm/omap/crossbar.txt | 27 +++ drivers/irqchip/Kconfig | 8 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-crossbar.c | 208 +++++++++++++++++++++ include/linux/irqchip/irq-crossbar.h | 11 ++ 5 files changed, 255 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/crossbar.txt create mode 100644 drivers/irqchip/irq-crossbar.c create mode 100644 include/linux/irqchip/irq-crossbar.h (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt new file mode 100644 index 00000000000..fb88585cfb9 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -0,0 +1,27 @@ +Some socs have a large number of interrupts requests to service +the needs of its many peripherals and subsystems. All of the +interrupt lines from the subsystems are not needed at the same +time, so they have to be muxed to the irq-controller appropriately. +In such places a interrupt controllers are preceded by an CROSSBAR +that provides flexibility in muxing the device requests to the controller +inputs. + +Required properties: +- compatible : Should be "ti,irq-crossbar" +- reg: Base address and the size of the crossbar registers. +- ti,max-irqs: Total number of irqs available at the interrupt controller. +- ti,reg-size: Size of a individual register in bytes. Every individual + register is assumed to be of same size. Valid sizes are 1, 2, 4. +- ti,irqs-reserved: List of the reserved irq lines that are not muxed using + crossbar. These interrupt lines are reserved in the soc, + so crossbar bar driver should not consider them as free + lines. + +Examples: + crossbar_mpu: @4a020000 { + compatible = "ti,irq-crossbar"; + reg = <0x4a002a48 0x130>; + ti,max-irqs = <160>; + ti,reg-size = <2>; + ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; + }; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 61ffdca96e2..111068782da 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -69,3 +69,11 @@ config VERSATILE_FPGA_IRQ_NR config XTENSA_MX bool select IRQ_DOMAIN + +config IRQ_CROSSBAR + bool + help + Support for a CROSSBAR ip that preceeds the main interrupt controller. + The primary irqchip invokes the crossbar's callback which inturn allocates + a free irq and configures the IP. Thus the peripheral interrupts are + routed to one of the free irqchip interrupt lines. diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 86b484cb3ec..3e776cb8dd4 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o +obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c new file mode 100644 index 00000000000..fc817d28d1f --- /dev/null +++ b/drivers/irqchip/irq-crossbar.c @@ -0,0 +1,208 @@ +/* + * drivers/irqchip/irq-crossbar.c + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * Author: Sricharan R + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include + +#define IRQ_FREE -1 +#define GIC_IRQ_START 32 + +/* + * @int_max: maximum number of supported interrupts + * @irq_map: array of interrupts to crossbar number mapping + * @crossbar_base: crossbar base address + * @register_offsets: offsets for each irq number + */ +struct crossbar_device { + uint int_max; + uint *irq_map; + void __iomem *crossbar_base; + int *register_offsets; + void (*write) (int, int); +}; + +static struct crossbar_device *cb; + +static inline void crossbar_writel(int irq_no, int cb_no) +{ + writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline void crossbar_writew(int irq_no, int cb_no) +{ + writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline void crossbar_writeb(int irq_no, int cb_no) +{ + writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline int allocate_free_irq(int cb_no) +{ + int i; + + for (i = 0; i < cb->int_max; i++) { + if (cb->irq_map[i] == IRQ_FREE) { + cb->irq_map[i] = cb_no; + return i; + } + } + + return -ENODEV; +} + +static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + return 0; +} + +static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; + + if (hw > GIC_IRQ_START) + cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; +} + +static int crossbar_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + unsigned long ret; + + ret = allocate_free_irq(intspec[1]); + + if (IS_ERR_VALUE(ret)) + return ret; + + *out_hwirq = ret + GIC_IRQ_START; + return 0; +} + +const struct irq_domain_ops routable_irq_domain_ops = { + .map = crossbar_domain_map, + .unmap = crossbar_domain_unmap, + .xlate = crossbar_domain_xlate +}; + +static int __init crossbar_of_init(struct device_node *node) +{ + int i, size, max, reserved = 0, entry; + const __be32 *irqsr; + + cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL); + + if (!cb) + return -ENOMEM; + + cb->crossbar_base = of_iomap(node, 0); + if (!cb->crossbar_base) + goto err1; + + of_property_read_u32(node, "ti,max-irqs", &max); + cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); + if (!cb->irq_map) + goto err2; + + cb->int_max = max; + + for (i = 0; i < max; i++) + cb->irq_map[i] = IRQ_FREE; + + /* Get and mark reserved irqs */ + irqsr = of_get_property(node, "ti,irqs-reserved", &size); + if (irqsr) { + size /= sizeof(__be32); + + for (i = 0; i < size; i++) { + of_property_read_u32_index(node, + "ti,irqs-reserved", + i, &entry); + if (entry > max) { + pr_err("Invalid reserved entry\n"); + goto err3; + } + cb->irq_map[entry] = 0; + } + } + + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); + if (!cb->register_offsets) + goto err3; + + of_property_read_u32(node, "ti,reg-size", &size); + + switch (size) { + case 1: + cb->write = crossbar_writeb; + break; + case 2: + cb->write = crossbar_writew; + break; + case 4: + cb->write = crossbar_writel; + break; + default: + pr_err("Invalid reg-size property\n"); + goto err4; + break; + } + + /* + * Register offsets are not linear because of the + * reserved irqs. so find and store the offsets once. + */ + for (i = 0; i < max; i++) { + if (!cb->irq_map[i]) + continue; + + cb->register_offsets[i] = reserved; + reserved += size; + } + + register_routable_domain_ops(&routable_irq_domain_ops); + return 0; + +err4: + kfree(cb->register_offsets); +err3: + kfree(cb->irq_map); +err2: + iounmap(cb->crossbar_base); +err1: + kfree(cb); + return -ENOMEM; +} + +static const struct of_device_id crossbar_match[] __initconst = { + { .compatible = "ti,irq-crossbar" }, + {} +}; + +int __init irqcrossbar_init(void) +{ + struct device_node *np; + np = of_find_matching_node(NULL, crossbar_match); + if (!np) + return -ENODEV; + + crossbar_of_init(np); + return 0; +} diff --git a/include/linux/irqchip/irq-crossbar.h b/include/linux/irqchip/irq-crossbar.h new file mode 100644 index 00000000000..e5537b81df8 --- /dev/null +++ b/include/linux/irqchip/irq-crossbar.h @@ -0,0 +1,11 @@ +/* + * drivers/irqchip/irq-crossbar.h + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +int irqcrossbar_init(void); -- cgit v1.2.3-70-g09d2 From 3558fe900e8af6c3bfadeff24a12ffb19ac9b108 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 5 Feb 2014 14:05:05 +0100 Subject: spi: sunxi: Add Allwinner A31 SPI controller driver The Allwinner A31 has a new SPI controller IP compared to the older Allwinner SoCs. It supports DMA, but the driver only does PIO for now, and DMA will be supported eventually. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sun6i.txt | 24 + drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-sun6i.c | 483 +++++++++++++++++++++ 4 files changed, 514 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-sun6i.txt create mode 100644 drivers/spi/spi-sun6i.c (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/spi/spi-sun6i.txt b/Documentation/devicetree/bindings/spi/spi-sun6i.txt new file mode 100644 index 00000000000..21de73db6a0 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-sun6i.txt @@ -0,0 +1,24 @@ +Allwinner A31 SPI controller + +Required properties: +- compatible: Should be "allwinner,sun6i-a31-spi". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks: phandle to the clocks feeding the SPI controller. Two are + needed: + - "ahb": the gated AHB parent clock + - "mod": the parent module clock +- clock-names: Must contain the clock names described just above +- resets: phandle to the reset controller asserting this device in + reset + +Example: + +spi1: spi@01c69000 { + compatible = "allwinner,sun6i-a31-spi"; + reg = <0x01c69000 0x1000>; + interrupts = <0 66 4>; + clocks = <&ahb1_gates 21>, <&spi1_clk>; + clock-names = "ahb", "mod"; + resets = <&ahb1_rst 21>; +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ba9310bc9ac..7cfe0eed8df 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -446,6 +446,12 @@ config SPI_SIRF help SPI driver for CSR SiRFprimaII SoCs +config SPI_SUN6I + tristate "Allwinner A31 SPI controller" + depends on ARCH_SUNXI || COMPILE_TEST + help + This enables using the SPI controller on the Allwinner A31 SoCs. + config SPI_MXS tristate "Freescale MXS SPI controller" depends on ARCH_MXS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 95af48d2d36..13b6ccf904d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o +obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c new file mode 100644 index 00000000000..94d38d0a86c --- /dev/null +++ b/drivers/spi/spi-sun6i.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2012 - 2014 Allwinner Tech + * Pan Nan + * + * Copyright (C) 2014 Maxime Ripard + * Maxime Ripard + * + * 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 +#include +#include +#include + +#include + +#define SUN6I_FIFO_DEPTH 128 + +#define SUN6I_GBL_CTL_REG 0x04 +#define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) +#define SUN6I_GBL_CTL_MASTER BIT(1) +#define SUN6I_GBL_CTL_TP BIT(7) +#define SUN6I_GBL_CTL_RST BIT(31) + +#define SUN6I_TFR_CTL_REG 0x08 +#define SUN6I_TFR_CTL_CPHA BIT(0) +#define SUN6I_TFR_CTL_CPOL BIT(1) +#define SUN6I_TFR_CTL_SPOL BIT(2) +#define SUN6I_TFR_CTL_CS_MASK 0x3 +#define SUN6I_TFR_CTL_CS(cs) (((cs) & SUN6I_TFR_CTL_CS_MASK) << 4) +#define SUN6I_TFR_CTL_CS_MANUAL BIT(6) +#define SUN6I_TFR_CTL_CS_LEVEL BIT(7) +#define SUN6I_TFR_CTL_DHB BIT(8) +#define SUN6I_TFR_CTL_FBS BIT(12) +#define SUN6I_TFR_CTL_XCH BIT(31) + +#define SUN6I_INT_CTL_REG 0x10 +#define SUN6I_INT_CTL_RF_OVF BIT(8) +#define SUN6I_INT_CTL_TC BIT(12) + +#define SUN6I_INT_STA_REG 0x14 + +#define SUN6I_FIFO_CTL_REG 0x18 +#define SUN6I_FIFO_CTL_RF_RST BIT(15) +#define SUN6I_FIFO_CTL_TF_RST BIT(31) + +#define SUN6I_FIFO_STA_REG 0x1c +#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f +#define SUN6I_FIFO_STA_RF_CNT_BITS 0 +#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f +#define SUN6I_FIFO_STA_TF_CNT_BITS 16 + +#define SUN6I_CLK_CTL_REG 0x24 +#define SUN6I_CLK_CTL_CDR2_MASK 0xff +#define SUN6I_CLK_CTL_CDR2(div) (((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0) +#define SUN6I_CLK_CTL_CDR1_MASK 0xf +#define SUN6I_CLK_CTL_CDR1(div) (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8) +#define SUN6I_CLK_CTL_DRS BIT(12) + +#define SUN6I_BURST_CNT_REG 0x30 +#define SUN6I_BURST_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN6I_XMIT_CNT_REG 0x34 +#define SUN6I_XMIT_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN6I_BURST_CTL_CNT_REG 0x38 +#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & 0xffffff) + +#define SUN6I_TXDATA_REG 0x200 +#define SUN6I_RXDATA_REG 0x300 + +struct sun6i_spi { + struct spi_master *master; + void __iomem *base_addr; + struct clk *hclk; + struct clk *mclk; + struct reset_control *rstc; + + struct completion done; + + const u8 *tx_buf; + u8 *rx_buf; + int len; +}; + +static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) +{ + return readl(sspi->base_addr + reg); +} + +static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) +{ + writel(value, sspi->base_addr + reg); +} + +static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) +{ + u32 reg, cnt; + u8 byte; + + /* See how much data is available */ + reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); + reg &= SUN6I_FIFO_STA_RF_CNT_MASK; + cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS; + + if (len > cnt) + len = cnt; + + while (len--) { + byte = readb(sspi->base_addr + SUN6I_RXDATA_REG); + if (sspi->rx_buf) + *sspi->rx_buf++ = byte; + } +} + +static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) +{ + u8 byte; + + if (len > sspi->len) + len = sspi->len; + + while (len--) { + byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; + writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG); + sspi->len--; + } +} + +static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); + u32 reg; + + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + reg &= ~SUN6I_TFR_CTL_CS_MASK; + reg |= SUN6I_TFR_CTL_CS(spi->chip_select); + + if (enable) + reg |= SUN6I_TFR_CTL_CS_LEVEL; + else + reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); +} + + +static int sun6i_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + unsigned int mclk_rate, div, timeout; + unsigned int tx_len = 0; + int ret = 0; + u32 reg; + + /* We don't support transfer larger than the FIFO */ + if (tfr->len > SUN6I_FIFO_DEPTH) + return -EINVAL; + + reinit_completion(&sspi->done); + sspi->tx_buf = tfr->tx_buf; + sspi->rx_buf = tfr->rx_buf; + sspi->len = tfr->len; + + /* Clear pending interrupts */ + sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0); + + /* Reset FIFO */ + sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, + SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); + + /* + * Setup the transfer control register: Chip Select, + * polarities, etc. + */ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + + if (spi->mode & SPI_CPOL) + reg |= SUN6I_TFR_CTL_CPOL; + else + reg &= ~SUN6I_TFR_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN6I_TFR_CTL_CPHA; + else + reg &= ~SUN6I_TFR_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN6I_TFR_CTL_FBS; + else + reg &= ~SUN6I_TFR_CTL_FBS; + + /* + * If it's a TX only transfer, we don't want to fill the RX + * FIFO with bogus data + */ + if (sspi->rx_buf) + reg &= ~SUN6I_TFR_CTL_DHB; + else + reg |= SUN6I_TFR_CTL_DHB; + + /* We want to control the chip select manually */ + reg |= SUN6I_TFR_CTL_CS_MANUAL; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + + /* Ensure that we have a parent clock fast enough */ + mclk_rate = clk_get_rate(sspi->mclk); + if (mclk_rate < (2 * spi->max_speed_hz)) { + clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } + + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ cdr) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div = mclk_rate / (2 * spi->max_speed_hz); + if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { + if (div > 0) + div--; + + reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; + } else { + div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + reg = SUN6I_CLK_CTL_CDR1(div); + } + + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); + + /* Setup the transfer now... */ + if (sspi->tx_buf) + tx_len = tfr->len; + + /* Setup the counters */ + sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len)); + sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len)); + sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, + SUN6I_BURST_CTL_CNT_STC(tx_len)); + + /* Fill the TX FIFO */ + sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); + + /* Enable the interrupts */ + sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); + + /* Start the transfer */ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); + + timeout = wait_for_completion_timeout(&sspi->done, + msecs_to_jiffies(1000)); + if (!timeout) { + ret = -ETIMEDOUT; + goto out; + } + + sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); + +out: + sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); + + return ret; +} + +static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) +{ + struct sun6i_spi *sspi = dev_id; + u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + + /* Transfer complete */ + if (status & SUN6I_INT_CTL_TC) { + sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); + complete(&sspi->done); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int sun6i_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun6i_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(sspi->hclk); + if (ret) { + dev_err(dev, "Couldn't enable AHB clock\n"); + goto out; + } + + ret = clk_prepare_enable(sspi->mclk); + if (ret) { + dev_err(dev, "Couldn't enable module clock\n"); + goto err; + } + + ret = reset_control_deassert(sspi->rstc); + if (ret) { + dev_err(dev, "Couldn't deassert the device from reset\n"); + goto err2; + } + + sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, + SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); + + return 0; + +err2: + clk_disable_unprepare(sspi->mclk); +err: + clk_disable_unprepare(sspi->hclk); +out: + return ret; +} + +static int sun6i_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + reset_control_assert(sspi->rstc); + clk_disable_unprepare(sspi->mclk); + clk_disable_unprepare(sspi->hclk); + + return 0; +} + +static int sun6i_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct sun6i_spi *sspi; + struct resource *res; + int ret = 0, irq; + + master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); + if (!master) { + dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + sspi = spi_master_get_devdata(master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sspi->base_addr)) { + ret = PTR_ERR(sspi->base_addr); + goto err_free_master; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No spi IRQ specified\n"); + ret = -ENXIO; + goto err_free_master; + } + + ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, + 0, "sun6i-spi", sspi); + if (ret) { + dev_err(&pdev->dev, "Cannot request IRQ\n"); + goto err_free_master; + } + + sspi->master = master; + master->set_cs = sun6i_spi_set_cs; + master->transfer_one = sun6i_spi_transfer_one; + master->num_chipselect = 4; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + + sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(sspi->hclk)) { + dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); + ret = PTR_ERR(sspi->hclk); + goto err_free_master; + } + + sspi->mclk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(sspi->mclk)) { + dev_err(&pdev->dev, "Unable to acquire module clock\n"); + ret = PTR_ERR(sspi->mclk); + goto err_free_master; + } + + init_completion(&sspi->done); + + sspi->rstc = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(sspi->rstc)) { + dev_err(&pdev->dev, "Couldn't get reset controller\n"); + ret = PTR_ERR(sspi->rstc); + goto err_free_master; + } + + /* + * This wake-up/shutdown pattern is to be able to have the + * device woken up, even if runtime_pm is disabled + */ + ret = sun6i_spi_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't resume the device\n"); + goto err_free_master; + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "cannot register SPI master\n"); + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + sun6i_spi_runtime_suspend(&pdev->dev); +err_free_master: + spi_master_put(master); + return ret; +} + +static int sun6i_spi_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id sun6i_spi_match[] = { + { .compatible = "allwinner,sun6i-a31-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, sun6i_spi_match); + +static const struct dev_pm_ops sun6i_spi_pm_ops = { + .runtime_resume = sun6i_spi_runtime_resume, + .runtime_suspend = sun6i_spi_runtime_suspend, +}; + +static struct platform_driver sun6i_spi_driver = { + .probe = sun6i_spi_probe, + .remove = sun6i_spi_remove, + .driver = { + .name = "sun6i-spi", + .owner = THIS_MODULE, + .of_match_table = sun6i_spi_match, + .pm = &sun6i_spi_pm_ops, + }, +}; +module_platform_driver(sun6i_spi_driver); + +MODULE_AUTHOR("Pan Nan "); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_DESCRIPTION("Allwinner A31 SPI controller driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 5a3af1293194d07610fd7fdf680b3e7f916dca84 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Feb 2014 12:03:27 +0000 Subject: ASoC: pcm512x: Add PCM512x driver The PCM512x devices are a family of monolithic CMOS integrated circuits that include a stereo digital-to-analog converter and additional support circuitry. This is an initial driver which supports some core functionality for the device which covers common use cases but does not cover all features. Currently only slave clocking modes with automatic clock configuration are supported and most of the DSP configuration for the device is not enabled. Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/pcm512x.txt | 30 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/pcm512x.c | 672 +++++++++++++++++++++ sound/soc/codecs/pcm512x.h | 142 +++++ 5 files changed, 850 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/pcm512x.txt create mode 100644 sound/soc/codecs/pcm512x.c create mode 100644 sound/soc/codecs/pcm512x.h (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt new file mode 100644 index 00000000000..faff75e6457 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -0,0 +1,30 @@ +PCM512x audio CODECs + +These devices support both I2C and SPI (configured with pin strapping +on the board). + +Required properties: + + - compatible : One of "ti,pcm5121" or "ti,pcm5122" + + - reg : the I2C address of the device for I2C, the chip select + number for SPI. + + - AVDD-supply, DVDD-supply, and CPVDD-supply : power supplies for the + device, as covered in bindings/regulator/regulator.txt + +Optional properties: + + - clocks : A clock specifier for the clock connected as SCLK. If this + is absent the device will be configured to clock from BCLK. + +Example: + + pcm5122: pcm5122@4c { + compatible = "ti,pcm5122"; + reg = <0x4c>; + + AVDD-supply = <®_3v3_analog>; + DVDD-supply = <®_1v8>; + CPVDD-supply = <®_3v3>; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 983d087aa92..56d0c284568 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 + select SND_SOC_PCM512x if SND_SOC_I2C_AND_SPI select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C select SND_SOC_SGTL5000 if I2C @@ -313,6 +314,9 @@ config SND_SOC_PCM1792A config SND_SOC_PCM3008 tristate +config SND_SOC_PCM512x + tristate "Texas Instruments PCM512x CODECs" + config SND_SOC_RT5631 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index bc126764a44..d3b536fc075 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-pcm512x-objs := pcm512x.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -179,6 +180,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c new file mode 100644 index 00000000000..5ad3e9aa3cb --- /dev/null +++ b/sound/soc/codecs/pcm512x.c @@ -0,0 +1,672 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcm512x.h" + +#define PCM512x_NUM_SUPPLIES 3 +static const char *pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "CPVDD", +}; + +struct pcm512x_priv { + struct regmap *regmap; + struct clk *sclk; + struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; + struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; +}; + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define PCM512x_REGULATOR_EVENT(n) \ +static int pcm512x_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \ + supply_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(pcm512x->regmap); \ + regcache_cache_only(pcm512x->regmap, true); \ + } \ + return 0; \ +} + +PCM512x_REGULATOR_EVENT(0) +PCM512x_REGULATOR_EVENT(1) +PCM512x_REGULATOR_EVENT(2) + +static const struct reg_default pcm512x_reg_defaults[] = { + { PCM512x_RESET, 0x00 }, + { PCM512x_POWER, 0x00 }, + { PCM512x_MUTE, 0x00 }, + { PCM512x_DSP, 0x00 }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, +}; + +static bool pcm512x_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_RESET: + case PCM512x_POWER: + case PCM512x_MUTE: + case PCM512x_PLL_EN: + case PCM512x_SPI_MISO_FUNCTION: + case PCM512x_DSP: + case PCM512x_GPIO_EN: + case PCM512x_BCLK_LRCLK_CFG: + case PCM512x_DSP_GPIO_INPUT: + case PCM512x_MASTER_MODE: + case PCM512x_PLL_REF: + case PCM512x_PLL_COEFF_0: + case PCM512x_PLL_COEFF_1: + case PCM512x_PLL_COEFF_2: + case PCM512x_PLL_COEFF_3: + case PCM512x_PLL_COEFF_4: + case PCM512x_DSP_CLKDIV: + case PCM512x_DAC_CLKDIV: + case PCM512x_NCP_CLKDIV: + case PCM512x_OSR_CLKDIV: + case PCM512x_MASTER_CLKDIV_1: + case PCM512x_MASTER_CLKDIV_2: + case PCM512x_FS_SPEED_MODE: + case PCM512x_IDAC_1: + case PCM512x_IDAC_2: + case PCM512x_ERROR_DETECT: + case PCM512x_I2S_1: + case PCM512x_I2S_2: + case PCM512x_DAC_ROUTING: + case PCM512x_DSP_PROGRAM: + case PCM512x_CLKDET: + case PCM512x_AUTO_MUTE: + case PCM512x_DIGITAL_VOLUME_1: + case PCM512x_DIGITAL_VOLUME_2: + case PCM512x_DIGITAL_VOLUME_3: + case PCM512x_DIGITAL_MUTE_1: + case PCM512x_DIGITAL_MUTE_2: + case PCM512x_DIGITAL_MUTE_3: + case PCM512x_GPIO_OUTPUT_1: + case PCM512x_GPIO_OUTPUT_2: + case PCM512x_GPIO_OUTPUT_3: + case PCM512x_GPIO_OUTPUT_4: + case PCM512x_GPIO_OUTPUT_5: + case PCM512x_GPIO_OUTPUT_6: + case PCM512x_GPIO_CONTROL_1: + case PCM512x_GPIO_CONTROL_2: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + return true; + default: + return false; + } +} + +static bool pcm512x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_PLL_EN: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); + +static const char *pcm512x_dsp_program_texts[] = { + "FIR interpolation with de-emphasis", + "Low latency IIR with de-emphasis", + "Fixed process flow", + "High attenuation with de-emphasis", + "Ringing-less low latency FIR", +}; + +static const unsigned int pcm512x_dsp_program_values[] = { + 1, + 2, + 3, + 5, + 7, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program, + PCM512x_DSP_PROGRAM, 0, 0x1f, + pcm512x_dsp_program_texts, + pcm512x_dsp_program_values); + +static const char *pcm512x_clk_missing_text[] = { + "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s" +}; + +static const struct soc_enum pcm512x_clk_missing = + SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text); + +static const char *pcm512x_autom_text[] = { + "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s" +}; + +static const struct soc_enum pcm512x_autom_l = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8, + pcm512x_autom_text); + +static const struct soc_enum pcm512x_autom_r = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8, + pcm512x_autom_text); + +static const char *pcm512x_ramp_rate_text[] = { + "1 sample/update", "2 samples/update", "4 samples/update", + "Immediate" +}; + +static const struct soc_enum pcm512x_vndf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vnuf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vedf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const char *pcm512x_ramp_step_text[] = { + "4dB/step", "2dB/step", "1dB/step", "0.5dB/step" +}; + +static const struct soc_enum pcm512x_vnds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_vnus = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_veds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct snd_kcontrol_new pcm512x_controls[] = { +SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2, + PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), +SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, + PCM512x_RQMR_SHIFT, 1, 1), + +SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), +SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program), + +SOC_ENUM("Clock Missing Period", pcm512x_clk_missing), +SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l), +SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r), +SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3, + PCM512x_ACTL_SHIFT, 1, 0), +SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, + PCM512x_AMLR_SHIFT, 1, 0), + +SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf), +SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds), +SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), +SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), +SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), +SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), +}; + +static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +static int pcm512x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, 0); + if (ret != 0) { + dev_err(codec->dev, "Failed to remove standby: %d\n", + ret); + return ret; + } + break; + + case SND_SOC_BIAS_OFF: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(codec->dev, "Failed to request standby: %d\n", + ret); + return ret; + } + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static struct snd_soc_dai_driver pcm512x_dai = { + .name = "pcm512x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_codec_driver pcm512x_codec_driver = { + .set_bias_level = pcm512x_set_bias_level, + .idle_bias_off = true, + + .controls = pcm512x_controls, + .num_controls = ARRAY_SIZE(pcm512x_controls), + .dapm_widgets = pcm512x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), + .dapm_routes = pcm512x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), +}; + +static const struct regmap_config pcm512x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = pcm512x_readable, + .volatile_reg = pcm512x_volatile, + + .max_register = PCM512x_MAX_REGISTER, + .reg_defaults = pcm512x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct of_device_id pcm512x_of_match[] = { + { .compatible = "ti,pcm5121", }, + { .compatible = "ti,pcm5122", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm512x_of_match); + +static int pcm512x_probe(struct device *dev, struct regmap *regmap) +{ + struct pcm512x_priv *pcm512x; + int i, ret; + + pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL); + if (!pcm512x) + return -ENOMEM; + + dev_set_drvdata(dev, pcm512x); + pcm512x->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) + pcm512x->supplies[i].supply = pcm512x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0; + pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1; + pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { + ret = regulator_register_notifier(pcm512x->supplies[i].consumer, + &pcm512x->supply_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the device, verifying I/O in the process for I2C */ + ret = regmap_write(regmap, PCM512x_RESET, + PCM512x_RSTM | PCM512x_RSTR); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + ret = regmap_write(regmap, PCM512x_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + pcm512x->sclk = devm_clk_get(dev, NULL); + if (IS_ERR(pcm512x->sclk)) { + if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_info(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ + regmap_update_bits(regmap, PCM512x_ERROR_DETECT, + PCM512x_IDCH, PCM512x_IDCH); + + /* Switch PLL input to BCLK */ + regmap_update_bits(regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF); + } else { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + return ret; + } + } + + /* Default to standby mode */ + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(dev, "Failed to request standby: %d\n", + ret); + goto err_clk; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = snd_soc_register_codec(dev, &pcm512x_codec_driver, + &pcm512x_dai, 1); + if (ret != 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_pm; + } + + return 0; + +err_pm: + pm_runtime_disable(dev); +err_clk: + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); +err: + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + return ret; +} + +static void pcm512x_remove(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + + snd_soc_unregister_codec(dev); + pm_runtime_disable(dev); + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); +} + +static int pcm512x_suspend(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, PCM512x_RQPD); + if (ret != 0) { + dev_err(dev, "Failed to request power down: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to disable supplies: %d\n", ret); + return ret; + } + + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + + return 0; +} + +static int pcm512x_resume(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(pcm512x->sclk)) { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(pcm512x->regmap, false); + ret = regcache_sync(pcm512x->regmap); + if (ret != 0) { + dev_err(dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, 0); + if (ret != 0) { + dev_err(dev, "Failed to remove power down: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops pcm512x_pm_ops = { + SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) +}; + +#if IS_ENABLED(CONFIG_I2C) +static int pcm512x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm512x_probe(&i2c->dev, regmap); +} + +static int pcm512x_i2c_remove(struct i2c_client *i2c) +{ + pcm512x_remove(&i2c->dev); + return 0; +} + +static const struct i2c_device_id pcm512x_i2c_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); + +static struct i2c_driver pcm512x_i2c_driver = { + .probe = pcm512x_i2c_probe, + .remove = pcm512x_i2c_remove, + .id_table = pcm512x_i2c_id, + .driver = { + .name = "pcm512x", + .owner = THIS_MODULE, + .of_match_table = pcm512x_of_match, + .pm = &pcm512x_pm_ops, + }, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static int pcm512x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm512x_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + return ret; + } + + return pcm512x_probe(&spi->dev, regmap); +} + +static int pcm512x_spi_remove(struct spi_device *spi) +{ + pcm512x_remove(&spi->dev); + return 0; +} + +static const struct spi_device_id pcm512x_spi_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); + +static struct spi_driver pcm512x_spi_driver = { + .probe = pcm512x_spi_probe, + .remove = pcm512x_spi_remove, + .id_table = pcm512x_spi_id, + .driver = { + .name = "pcm512x", + .owner = THIS_MODULE, + .of_match_table = pcm512x_of_match, + .pm = &pcm512x_pm_ops, + }, +}; +#endif + +static int __init pcm512x_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&pcm512x_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register pcm512x I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&pcm512x_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register pcm512x SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(pcm512x_modinit); + +static void __exit pcm512x_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&pcm512x_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&pcm512x_spi_driver); +#endif +} +module_exit(pcm512x_exit); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h new file mode 100644 index 00000000000..b2f518ecb35 --- /dev/null +++ b/sound/soc/codecs/pcm512x.h @@ -0,0 +1,142 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _SND_SOC_PCM512X +#define _SND_SOC_PCM512X + +#define PCM512x_PAGE_0_BASE 0 + +#define PCM512x_PAGE 0 + +#define PCM512x_RESET (PCM512x_PAGE_0_BASE + 1) +#define PCM512x_POWER (PCM512x_PAGE_0_BASE + 2) +#define PCM512x_MUTE (PCM512x_PAGE_0_BASE + 3) +#define PCM512x_PLL_EN (PCM512x_PAGE_0_BASE + 4) +#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_0_BASE + 6) +#define PCM512x_DSP (PCM512x_PAGE_0_BASE + 7) +#define PCM512x_GPIO_EN (PCM512x_PAGE_0_BASE + 8) +#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_0_BASE + 9) +#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_0_BASE + 10) +#define PCM512x_MASTER_MODE (PCM512x_PAGE_0_BASE + 12) +#define PCM512x_PLL_REF (PCM512x_PAGE_0_BASE + 13) +#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_0_BASE + 20) +#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_0_BASE + 21) +#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_0_BASE + 22) +#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_0_BASE + 23) +#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_0_BASE + 24) +#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_0_BASE + 27) +#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_0_BASE + 28) +#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_0_BASE + 29) +#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_0_BASE + 30) +#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_0_BASE + 32) +#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_0_BASE + 33) +#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_0_BASE + 34) +#define PCM512x_IDAC_1 (PCM512x_PAGE_0_BASE + 35) +#define PCM512x_IDAC_2 (PCM512x_PAGE_0_BASE + 36) +#define PCM512x_ERROR_DETECT (PCM512x_PAGE_0_BASE + 37) +#define PCM512x_I2S_1 (PCM512x_PAGE_0_BASE + 40) +#define PCM512x_I2S_2 (PCM512x_PAGE_0_BASE + 41) +#define PCM512x_DAC_ROUTING (PCM512x_PAGE_0_BASE + 42) +#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_0_BASE + 43) +#define PCM512x_CLKDET (PCM512x_PAGE_0_BASE + 44) +#define PCM512x_AUTO_MUTE (PCM512x_PAGE_0_BASE + 59) +#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_0_BASE + 60) +#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_0_BASE + 61) +#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_0_BASE + 62) +#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_0_BASE + 63) +#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_0_BASE + 64) +#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_0_BASE + 65) +#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_0_BASE + 80) +#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_0_BASE + 81) +#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_0_BASE + 82) +#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_0_BASE + 83) +#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_0_BASE + 84) +#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_0_BASE + 85) +#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_0_BASE + 86) +#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_0_BASE + 87) +#define PCM512x_OVERFLOW (PCM512x_PAGE_0_BASE + 90) +#define PCM512x_RATE_DET_1 (PCM512x_PAGE_0_BASE + 91) +#define PCM512x_RATE_DET_2 (PCM512x_PAGE_0_BASE + 92) +#define PCM512x_RATE_DET_3 (PCM512x_PAGE_0_BASE + 93) +#define PCM512x_RATE_DET_4 (PCM512x_PAGE_0_BASE + 94) +#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_0_BASE + 108) +#define PCM512x_GPIN (PCM512x_PAGE_0_BASE + 119) +#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_0_BASE + 120) + +#define PCM512x_MAX_REGISTER (PCM512x_PAGE_0_BASE + 120) + +/* Page 0, Register 1 - reset */ +#define PCM512x_RSTR (1 << 0) +#define PCM512x_RSTM (1 << 4) + +/* Page 0, Register 2 - power */ +#define PCM512x_RQPD (1 << 0) +#define PCM512x_RQPD_SHIFT 0 +#define PCM512x_RQST (1 << 4) +#define PCM512x_RQST_SHIFT 4 + +/* Page 0, Register 3 - mute */ +#define PCM512x_RQMR_SHIFT 0 +#define PCM512x_RQML_SHIFT 4 + +/* Page 0, Register 4 - PLL */ +#define PCM512x_PLCE (1 << 0) +#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLCK (1 << 4) +#define PCM512x_PLCK_SHIFT 4 + +/* Page 0, Register 7 - DSP */ +#define PCM512x_SDSL (1 << 0) +#define PCM512x_SDSL_SHIFT 0 +#define PCM512x_DEMP (1 << 4) +#define PCM512x_DEMP_SHIFT 4 + +/* Page 0, Register 13 - PLL reference */ +#define PCM512x_SREF (1 << 4) + +/* Page 0, Register 37 - Error detection */ +#define PCM512x_IPLK (1 << 0) +#define PCM512x_DCAS (1 << 1) +#define PCM512x_IDCM (1 << 2) +#define PCM512x_IDCH (1 << 3) +#define PCM512x_IDSK (1 << 4) +#define PCM512x_IDBK (1 << 5) +#define PCM512x_IDFS (1 << 6) + +/* Page 0, Register 42 - DAC routing */ +#define PCM512x_AUPR_SHIFT 0 +#define PCM512x_AUPL_SHIFT 4 + +/* Page 0, Register 59 - auto mute */ +#define PCM512x_ATMR_SHIFT 0 +#define PCM512x_ATML_SHIFT 4 + +/* Page 0, Register 63 - ramp rates */ +#define PCM512x_VNDF_SHIFT 6 +#define PCM512x_VNDS_SHIFT 4 +#define PCM512x_VNUF_SHIFT 2 +#define PCM512x_VNUS_SHIFT 0 + +/* Page 0, Register 64 - emergency ramp rates */ +#define PCM512x_VEDF_SHIFT 6 +#define PCM512x_VEDS_SHIFT 4 + +/* Page 0, Register 65 - Digital mute enables */ +#define PCM512x_ACTL_SHIFT 2 +#define PCM512x_AMLE_SHIFT 1 +#define PCM512x_AMLR_SHIFT 0 + +#endif -- cgit v1.2.3-70-g09d2 From 7f196ec938967af7c3aa2506b8edcd7da5f5ebc6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Dec 2013 19:08:55 +0000 Subject: regulator: gpio: Document regulator-type property Reported-by: Laurent Pinchart Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/gpio-regulator.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index 63c659800c0..1229541f48a 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -10,6 +10,8 @@ Optional properties: - gpios : GPIO group used to control voltage. - startup-delay-us : Startup time in microseconds. - enable-active-high : Polarity of GPIO is active high (default is low). +- regulator-type : Specifies what is being regulated, must be either + "voltage" or "current", defaults to current. Any property defined as part of the core regulator binding defined in regulator.txt can also be used. -- cgit v1.2.3-70-g09d2 From ca52a17ba975dbf47e87c9bc63086aca0cf92806 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:40 +0100 Subject: ohci-platform: Add support for devicetree instantiation Add support for ohci-platform instantiation from devicetree, including optionally getting clks and a phy from devicetree, and enabling / disabling those on power_on / off. This should allow using ohci-platform from devicetree in various cases. Specifically after this commit it can be used for the ohci controller found on Allwinner sunxi SoCs. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-ohci.txt | 22 +++ drivers/usb/host/ohci-platform.c | 162 ++++++++++++++++++--- 2 files changed, 162 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/usb-ohci.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt new file mode 100644 index 00000000000..6ba38d990d1 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt @@ -0,0 +1,22 @@ +USB OHCI controllers + +Required properties: +- compatible : "usb-ohci" +- reg : ohci controller register range (address and length) +- interrupts : ohci controller interrupt + +Optional properties: +- clocks : a list of phandle + clock specifier pairs +- phys : phandle + phy specifier pair +- phy-names : "usb" + +Example: + + ohci0: usb@01c14400 { + compatible = "allwinner,sun4i-a10-ohci", "usb-ohci"; + reg = <0x01c14400 0x100>; + interrupts = <64>; + clocks = <&usb_clk 6>, <&ahb_gates 2>; + phys = <&usbphy 1>; + phy-names = "usb"; + }; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 68f674cd095..49304ddde42 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Michael Buesch * Copyright 2011-2012 Hauke Mehrtens + * Copyright 2014 Hans de Goede * * Derived from the OCHI-SSB driver * Derived from the OHCI-PCI driver @@ -14,11 +15,14 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include +#include #include #include #include #include #include +#include #include #include #include @@ -27,6 +31,13 @@ #include "ohci.h" #define DRIVER_DESC "OHCI generic platform driver" +#define OHCI_MAX_CLKS 3 +#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) + +struct ohci_platform_priv { + struct clk *clks[OHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ohci-platform"; @@ -48,11 +59,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd) return ohci_setup(hcd); } +static int ohci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ohci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ohci_platform_hc_driver; static const struct ohci_driver_overrides platform_overrides __initconst = { - .product_desc = "Generic Platform OHCI controller", - .reset = ohci_platform_reset, + .product_desc = "Generic Platform OHCI controller", + .reset = ohci_platform_reset, + .extra_priv_size = sizeof(struct ohci_platform_priv), +}; + +static struct usb_ohci_pdata ohci_platform_defaults = { + .power_on = ohci_platform_power_on, + .power_suspend = ohci_platform_power_off, + .power_off = ohci_platform_power_off, }; static int ohci_platform_probe(struct platform_device *dev) @@ -60,17 +127,23 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); - int irq; - int err = -ENOMEM; - - if (!pdata) { - WARN_ON(1); - return -ENODEV; - } + struct ohci_platform_priv *priv; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; + /* + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. + */ + if (!pdata) + pdata = &ohci_platform_defaults; + + err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + if (err) + return err; + irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -83,17 +156,40 @@ static int ohci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ohci_priv(hcd); + + if (pdata == &ohci_platform_defaults && dev->dev.of_node) { + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -102,11 +198,11 @@ static int ohci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); @@ -114,11 +210,17 @@ static int ohci_platform_probe(struct platform_device *dev) return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -127,13 +229,22 @@ static int ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + return 0; } @@ -180,6 +291,12 @@ static int ohci_platform_resume(struct device *dev) #define ohci_platform_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id ohci_platform_ids[] = { + { .compatible = "usb-ohci", }, + { } +}; +MODULE_DEVICE_TABLE(of, ohci_platform_ids); + static const struct platform_device_id ohci_platform_table[] = { { "ohci-platform", 0 }, { } @@ -200,6 +317,7 @@ static struct platform_driver ohci_platform_driver = { .owner = THIS_MODULE, .name = "ohci-platform", .pm = &ohci_platform_pm_ops, + .of_match_table = ohci_platform_ids, } }; -- cgit v1.2.3-70-g09d2 From a4aeb2117571292f4e002c54b3f91e138722bf7a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:41 +0100 Subject: ehci-platform: Add support for clks and phy passed through devicetree Currently ehci-platform is only used in combination with devicetree when used with some Via socs. By extending it to (optionally) get clks and a phy from devicetree, and enabling / disabling those on power_on / off, it can be used more generically. Specifically after this commit it can be used for the ehci controller on Allwinner sunxi SoCs. Since ehci-platform is intended to handle any generic enough non pci ehci device, add a "usb-ehci" compatibility string. There already is a usb-ehci device-tree bindings document, update this with clks and phy bindings info. Although actually quite generic so far the via,vt8500 compatibilty string had its own bindings document. Somehow we even ended up with 2 of them. Since these provide no extra information over the generic usb-ehci documentation, this patch removes them. The ehci-ppc-of.c driver also claims the usb-ehci compatibility string, even though it mostly is ibm,usb-ehci-440epx specific. ehci-platform.c is not needed on ppc platforms, so add a !PPC_OF dependency to it to avoid 2 drivers claiming the same compatibility string getting build on ppc. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-ehci.txt | 25 +++- .../devicetree/bindings/usb/via,vt8500-ehci.txt | 15 --- .../devicetree/bindings/usb/vt8500-ehci.txt | 12 -- drivers/usb/host/Kconfig | 1 + drivers/usb/host/ehci-platform.c | 147 +++++++++++++++++---- 5 files changed, 142 insertions(+), 58 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt delete mode 100644 Documentation/devicetree/bindings/usb/vt8500-ehci.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt index fa18612f757..2c1aeebe12b 100644 --- a/Documentation/devicetree/bindings/usb/usb-ehci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt @@ -7,13 +7,14 @@ Required properties: (debug-port or other) can be also specified here, but only after definition of standard EHCI registers. - interrupts : one EHCI interrupt should be described here. -If device registers are implemented in big endian mode, the device -node should have "big-endian-regs" property. -If controller implementation operates with big endian descriptors, -"big-endian-desc" property should be specified. -If both big endian registers and descriptors are used by the controller -implementation, "big-endian" property can be specified instead of having -both "big-endian-regs" and "big-endian-desc". + +Optional properties: + - big-endian-regs : boolean, set this for hcds with big-endian registers + - big-endian-desc : boolean, set this for hcds with big-endian descriptors + - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc + - clocks : a list of phandle + clock specifier pairs + - phys : phandle + phy specifier pair + - phy-names : "usb" Example (Sequoia 440EPx): ehci@e0000300 { @@ -23,3 +24,13 @@ Example (Sequoia 440EPx): reg = <0 e0000300 90 0 e0000390 70>; big-endian; }; + +Example (Allwinner sun4i A10 SoC): + ehci0: usb@01c14000 { + compatible = "allwinner,sun4i-a10-ehci", "usb-ehci"; + reg = <0x01c14000 0x100>; + interrupts = <39>; + clocks = <&ahb_gates 1>; + phys = <&usbphy 1>; + phy-names = "usb"; + }; diff --git a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt deleted file mode 100644 index 17b3ad1d97e..00000000000 --- a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt +++ /dev/null @@ -1,15 +0,0 @@ -VIA/Wondermedia VT8500 EHCI Controller ------------------------------------------------------ - -Required properties: -- compatible : "via,vt8500-ehci" -- reg : Should contain 1 register ranges(address and length) -- interrupts : ehci controller interrupt - -Example: - - ehci@d8007900 { - compatible = "via,vt8500-ehci"; - reg = <0xd8007900 0x200>; - interrupts = <43>; - }; diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt deleted file mode 100644 index 5fb8fd6e250..00000000000 --- a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt +++ /dev/null @@ -1,12 +0,0 @@ -VIA VT8500 and Wondermedia WM8xxx SoC USB controllers. - -Required properties: - - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci". - - reg: Address range of the ehci registers. size should be 0x200 - - interrupts: Should contain the ehci interrupt. - -usb: ehci@D8007100 { - compatible = "wm,prizm-ehci", "usb-ehci"; - reg = <0xD8007100 0x200>; - interrupts = <1>; -}; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a9707da7da0..e28cbe0e1f5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -255,6 +255,7 @@ config USB_EHCI_ATH79 config USB_EHCI_HCD_PLATFORM tristate "Generic EHCI driver for a platform device" + depends on !PPC_OF default n ---help--- Adds an EHCI host driver for a generic platform device, which diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 01536cfd361..5ebd0b75b58 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Steven Brown * Copyright 2010-2012 Hauke Mehrtens + * Copyright 2014 Hans de Goede * * Derived from the ohci-ssb driver * Copyright 2007 Michael Buesch @@ -18,6 +19,7 @@ * * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +36,13 @@ #include "ehci.h" #define DRIVER_DESC "EHCI generic platform driver" +#define EHCI_MAX_CLKS 3 +#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) + +struct ehci_platform_priv { + struct clk *clks[EHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ehci-platform"; @@ -64,38 +74,90 @@ static int ehci_platform_reset(struct usb_hcd *hcd) return 0; } +static int ehci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ehci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ehci_platform_hc_driver; static const struct ehci_driver_overrides platform_overrides __initconst = { - .reset = ehci_platform_reset, + .reset = ehci_platform_reset, + .extra_priv_size = sizeof(struct ehci_platform_priv), }; -static struct usb_ehci_pdata ehci_platform_defaults; +static struct usb_ehci_pdata ehci_platform_defaults = { + .power_on = ehci_platform_power_on, + .power_suspend = ehci_platform_power_off, + .power_off = ehci_platform_power_off, +}; static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct resource *res_mem; - struct usb_ehci_pdata *pdata; - int irq; - int err; + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; /* - * use reasonable defaults so platforms don't have to provide these. - * with DT probing on ARM, none of these are set. + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. */ - if (!dev_get_platdata(&dev->dev)) - dev->dev.platform_data = &ehci_platform_defaults; + if (!pdata) + pdata = &ehci_platform_defaults; err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); if (err) return err; - pdata = dev_get_platdata(&dev->dev); - irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -107,17 +169,40 @@ static int ehci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ehci_priv(hcd); + + if (pdata == &ehci_platform_defaults && dev->dev.of_node) { + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -126,22 +211,28 @@ static int ehci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); platform_set_drvdata(dev, hcd); return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ehci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -150,13 +241,19 @@ static int ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; @@ -207,8 +304,10 @@ static int ehci_platform_resume(struct device *dev) static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "via,vt8500-ehci", }, { .compatible = "wm,prizm-ehci", }, + { .compatible = "usb-ehci", }, {} }; +MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); static const struct platform_device_id ehci_platform_table[] = { { "ehci-platform", 0 }, -- cgit v1.2.3-70-g09d2 From b1034412570f38d32b88dfd5da5cb2f86b5c321c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:42 +0100 Subject: ohci-platform: Add support for controllers with big-endian regs / descriptors Note this commit uses the same devicetree booleans for this as the ones already existing in the usb-ehci bindings, see: Documentation/devicetree/bindings/usb/usb-ehci.txt Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-ohci.txt | 3 +++ drivers/usb/host/ohci-platform.c | 27 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt index 6ba38d990d1..6933b0c6487 100644 --- a/Documentation/devicetree/bindings/usb/usb-ohci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt @@ -6,6 +6,9 @@ Required properties: - interrupts : ohci controller interrupt Optional properties: +- big-endian-regs : boolean, set this for hcds with big-endian registers +- big-endian-desc : boolean, set this for hcds with big-endian descriptors +- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc - clocks : a list of phandle + clock specifier pairs - phys : phandle + phy specifier pair - phy-names : "usb" diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 49304ddde42..e2c28fd0348 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -128,6 +128,7 @@ static int ohci_platform_probe(struct platform_device *dev) struct resource *res_mem; struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; + struct ohci_hcd *ohci; int err, irq, clk = 0; if (usb_disabled()) @@ -164,8 +165,34 @@ static int ohci_platform_probe(struct platform_device *dev) platform_set_drvdata(dev, hcd); dev->dev.platform_data = pdata; priv = hcd_to_ohci_priv(hcd); + ohci = hcd_to_ohci(hcd); if (pdata == &ohci_platform_defaults && dev->dev.of_node) { + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) + ohci->flags |= OHCI_QUIRK_BE_DESC; + + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + if (ohci->flags & OHCI_QUIRK_BE_MMIO) { + dev_err(&dev->dev, + "Error big-endian-regs not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC + if (ohci->flags & OHCI_QUIRK_BE_DESC) { + dev_err(&dev->dev, + "Error big-endian-desc not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); -- cgit v1.2.3-70-g09d2 From 155bae4eda81505ffb43784e2af0e32a096a3b7a Mon Sep 17 00:00:00 2001 From: Silvio F Date: Thu, 7 Nov 2013 14:54:26 +0100 Subject: DT: Add Data Modul vendor prefix Signed-off-by: Silvio F Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 3f900cd51bf..7877c2f5552 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -28,6 +28,7 @@ cortina Cortina Systems, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) davicom DAVICOM Semiconductor, Inc. denx Denx Software Engineering +dmo Data Modul AG edt Emerging Display Technologies emmicro EM Microelectronic epfl Ecole Polytechnique Fédérale de Lausanne -- cgit v1.2.3-70-g09d2 From 4963b45e33134b0e6fbd7d06e0e623af6ff98bde Mon Sep 17 00:00:00 2001 From: Denis Carikli Date: Wed, 23 Oct 2013 10:29:26 +0200 Subject: of: add vendor prefix for Eukrea Electromatique. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial patch to add Eukréa Electromatique to the list of devicetree vendor prefixes. Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Cc: Sascha Hauer Cc: linux-arm-kernel@lists.infradead.org Cc: Eric Bénard Signed-off-by: Denis Carikli Acked-by: Rob Herring Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 3f900cd51bf..ca5ebe74ba7 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -33,6 +33,7 @@ emmicro EM Microelectronic epfl Ecole Polytechnique Fédérale de Lausanne epson Seiko Epson Corp. est ESTeem Wireless Modems +eukrea Eukréa Electromatique fsl Freescale Semiconductor GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. -- cgit v1.2.3-70-g09d2 From 6f653a9f086fdd4f2f95680e95ee1f4076c2f9cd Mon Sep 17 00:00:00 2001 From: Rostislav Lisovy Date: Thu, 24 Oct 2013 23:56:07 +0200 Subject: ARM: dts: Add vendor prefix for Voipac Technologies s.r.o. Signed-off-by: Rostislav Lisovy Acked-by: Kumar Gala Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ca5ebe74ba7..534bcbadc46 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -88,6 +88,7 @@ toshiba Toshiba Corporation toumaz Toumaz v3 V3 Semiconductor via VIA Technologies, Inc. +voipac Voipac Technologies s.r.o. winbond Winbond Electronics corp. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. -- cgit v1.2.3-70-g09d2 From 727b0f71a56de64c0994e24f2504b1b954fd6f93 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 16 Jan 2014 15:36:53 +0000 Subject: pinctrl: st: Add Interrupt support This patch add interrupt support to the pincontroller driver. ST Pincontroller GPIO bank can have one of the two possible types of interrupt-wirings. First type is via irqmux, single interrupt is used by multiple gpio banks. This reduces number of overall interrupts numbers required. All these banks belong to a single pincontroller. _________ | |----> [gpio-bank (n) ] | |----> [gpio-bank (n + 1)] [irqN]-- | irq-mux |----> [gpio-bank (n + 2)] | |----> [gpio-bank (... )] |_________|----> [gpio-bank (n + 7)] Second type has a dedicated interrupt per gpio bank. [irqN]----> [gpio-bank (n)] Signed-off-by: Srinivas Kandagatla Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/pinctrl-st.txt | 73 ++++++- drivers/pinctrl/pinctrl-st.c | 225 ++++++++++++++++++++- 2 files changed, 290 insertions(+), 8 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt index 05bf82a07df..4bd5be0e5e7 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt @@ -11,18 +11,68 @@ Pull Up (PU) are driven by the related PIO block. ST pinctrl driver controls PIO multiplexing block and also interacts with gpio driver to configure a pin. -Required properties: (PIO multiplexing block) +GPIO bank can have one of the two possible types of interrupt-wirings. + +First type is via irqmux, single interrupt is used by multiple gpio banks. This +reduces number of overall interrupts numbers required. All these banks belong to +a single pincontroller. + _________ + | |----> [gpio-bank (n) ] + | |----> [gpio-bank (n + 1)] + [irqN]-- | irq-mux |----> [gpio-bank (n + 2)] + | |----> [gpio-bank (... )] + |_________|----> [gpio-bank (n + 7)] + +Second type has a dedicated interrupt per gpio bank. + + [irqN]----> [gpio-bank (n)] + + +Pin controller node: +Required properties: - compatible : should be "st,--pinctrl" like st,stih415-sbc-pinctrl, st,stih415-front-pinctrl and so on. -- gpio-controller : Indicates this device is a GPIO controller -- #gpio-cells : Should be one. The first cell is the pin number. +- st,syscfg : Should be a phandle of the syscfg node. - st,retime-pin-mask : Should be mask to specify which pins can be retimed. If the property is not present, it is assumed that all the pins in the bank are capable of retiming. Retiming is mainly used to improve the IO timing margins of external synchronous interfaces. -- st,bank-name : Should be a name string for this bank as - specified in datasheet. -- st,syscfg : Should be a phandle of the syscfg node. +- ranges : defines mapping between pin controller node (parent) to gpio-bank + node (children). + +Optional properties: +- interrupts : Interrupt number of the irqmux. If the interrupt is shared + with other gpio banks via irqmux. + a irqline and gpio banks. +- reg : irqmux memory resource. If irqmux is present. +- reg-names : irqmux resource should be named as "irqmux". + +GPIO controller/bank node. +Required properties: +- gpio-controller : Indicates this device is a GPIO controller +- #gpio-cells : Should be one. The first cell is the pin number. +- st,bank-name : Should be a name string for this bank as specified in + datasheet. + +Optional properties: +- interrupts : Interrupt number for this gpio bank. If there is a dedicated + interrupt wired up for this gpio bank. + +- interrupt-controller : Indicates this device is a interrupt controller. GPIO + bank can be an interrupt controller iff one of the interrupt type either via +irqmux or a dedicated interrupt per bank is specified. + +- #interrupt-cells: the value of this property should be 2. + - First Cell: represents the external gpio interrupt number local to the + gpio interrupt space of the controller. + - Second Cell: flags to identify the type of the interrupt + - 1 = rising edge triggered + - 2 = falling edge triggered + - 3 = rising and falling edge triggered + - 4 = high level triggered + - 8 = low level triggered +for related macros look in: +include/dt-bindings/interrupt-controller/irq.h Example: pin-controller-sbc { @@ -30,10 +80,17 @@ Example: #size-cells = <1>; compatible = "st,stih415-sbc-pinctrl"; st,syscfg = <&syscfg_sbc>; + reg = <0xfe61f080 0x4>; + reg-names = "irqmux"; + interrupts = ; + interrupts-names = "irqmux"; ranges = <0 0xfe610000 0x5000>; + PIO0: gpio@fe610000 { gpio-controller; #gpio-cells = <1>; + interrupt-controller; + #interrupt-cells = <2>; reg = <0 0x100>; st,bank-name = "PIO0"; }; @@ -105,6 +162,10 @@ pin-controller { sdhci0:sdhci@fe810000{ ... + interrupt-parent = <&PIO3>; + #interrupt-cells = <2>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; /* Interrupt line via PIO3-3 */ + interrupts-names = "card-detect"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mmc>; }; diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 320c27363cc..51e4f3a1d24 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -13,7 +13,12 @@ #include #include #include +#include +#include +#include +#include #include +#include #include #include #include @@ -271,6 +276,7 @@ struct st_gpio_bank { struct pinctrl_gpio_range range; void __iomem *base; struct st_pio_control pc; + struct irq_domain *domain; }; struct st_pinctrl { @@ -284,6 +290,7 @@ struct st_pinctrl { int ngroups; struct regmap *regmap; const struct st_pctl_data *data; + void __iomem *irqmux_base; }; /* SOC specific data */ @@ -1200,6 +1207,130 @@ static int st_pctl_parse_functions(struct device_node *np, return 0; } +static int st_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + int irq = -ENXIO; + + if (offset < chip->ngpio) + irq = irq_find_mapping(bank->domain, offset); + + dev_info(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, irq); + return irq; +} + +static void st_gpio_irq_mask(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), bank->base + REG_PIO_CLR_PMASK); +} + +static void st_gpio_irq_unmask(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), bank->base + REG_PIO_SET_PMASK); +} + +static unsigned int st_gpio_irq_startup(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + if (gpio_lock_as_irq(&bank->gpio_chip, d->hwirq)) + dev_err(bank->gpio_chip.dev, + "unable to lock HW IRQ %lu for IRQ\n", + d->hwirq); + + st_gpio_irq_unmask(d); + + return 0; +} + +static void st_gpio_irq_shutdown(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + st_gpio_irq_mask(d); + gpio_unlock_as_irq(&bank->gpio_chip, d->hwirq); +} + +static int st_gpio_irq_set_type(struct irq_data *d, unsigned type) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + unsigned long flags; + int comp, pin = d->hwirq; + u32 val; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + comp = 0; + break; + case IRQ_TYPE_LEVEL_LOW: + comp = 1; + break; + default: + return -EINVAL; + } + + val = readl(bank->base + REG_PIO_PCOMP); + val &= ~BIT(pin); + val |= (comp << pin); + writel(val, bank->base + REG_PIO_PCOMP); + + return 0; +} + +static void __gpio_irq_handler(struct st_gpio_bank *bank) +{ + unsigned long port_in, port_mask, port_comp, active_irqs; + int n; + + for (;;) { + port_in = readl(bank->base + REG_PIO_PIN); + port_comp = readl(bank->base + REG_PIO_PCOMP); + port_mask = readl(bank->base + REG_PIO_PMASK); + + active_irqs = (port_in ^ port_comp) & port_mask; + + if (active_irqs == 0) + break; + + for_each_set_bit(n, &active_irqs, BITS_PER_LONG) { + generic_handle_irq(irq_find_mapping(bank->domain, n)); + } + } +} + +static void st_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + /* interrupt dedicated per bank */ + struct irq_chip *chip = irq_get_chip(irq); + struct st_gpio_bank *bank = irq_get_handler_data(irq); + + chained_irq_enter(chip, desc); + __gpio_irq_handler(bank); + chained_irq_exit(chip, desc); +} + +static void st_gpio_irqmux_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct st_pinctrl *info = irq_get_handler_data(irq); + unsigned long status; + int n; + + chained_irq_enter(chip, desc); + + status = readl(info->irqmux_base); + + for_each_set_bit(n, &status, ST_GPIO_PINS_PER_BANK) + __gpio_irq_handler(&info->banks[n]); + + chained_irq_exit(chip, desc); +} + static struct gpio_chip st_gpio_template = { .request = st_gpio_request, .free = st_gpio_free, @@ -1210,6 +1341,34 @@ static struct gpio_chip st_gpio_template = { .ngpio = ST_GPIO_PINS_PER_BANK, .of_gpio_n_cells = 1, .of_xlate = st_gpio_xlate, + .to_irq = st_gpio_to_irq, +}; + +static struct irq_chip st_gpio_irqchip = { + .name = "GPIO", + .irq_mask = st_gpio_irq_mask, + .irq_unmask = st_gpio_irq_unmask, + .irq_set_type = st_gpio_irq_set_type, + .irq_startup = st_gpio_irq_startup, + .irq_shutdown = st_gpio_irq_shutdown, +}; + +static int st_gpio_irq_domain_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hw) +{ + struct st_gpio_bank *bank = h->host_data; + + irq_set_chip(virq, &st_gpio_irqchip); + irq_set_handler(virq, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, bank); + + return 0; +} + +static struct irq_domain_ops st_gpio_irq_ops = { + .map = st_gpio_irq_domain_map, + .xlate = irq_domain_xlate_twocell, }; static int st_gpiolib_register_bank(struct st_pinctrl *info, @@ -1219,8 +1378,8 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, struct pinctrl_gpio_range *range = &bank->range; struct device *dev = info->dev; int bank_num = of_alias_get_id(np, "gpio"); - struct resource res; - int err; + struct resource res, irq_res; + int gpio_irq = 0, err, i; if (of_address_to_resource(np, 0, &res)) return -ENODEV; @@ -1248,6 +1407,51 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, } dev_info(dev, "%s bank added.\n", range->name); + /** + * GPIO bank can have one of the two possible types of + * interrupt-wirings. + * + * First type is via irqmux, single interrupt is used by multiple + * gpio banks. This reduces number of overall interrupts numbers + * required. All these banks belong to a single pincontroller. + * _________ + * | |----> [gpio-bank (n) ] + * | |----> [gpio-bank (n + 1)] + * [irqN]-- | irq-mux |----> [gpio-bank (n + 2)] + * | |----> [gpio-bank (... )] + * |_________|----> [gpio-bank (n + 7)] + * + * Second type has a dedicated interrupt per each gpio bank. + * + * [irqN]----> [gpio-bank (n)] + */ + + if (!of_irq_to_resource(np, 0, &irq_res)) { + gpio_irq = irq_res.start; + irq_set_chained_handler(gpio_irq, st_gpio_irq_handler); + irq_set_handler_data(gpio_irq, bank); + } + + if (info->irqmux_base > 0 || gpio_irq > 0) { + /* Setup IRQ domain */ + bank->domain = irq_domain_add_linear(np, + ST_GPIO_PINS_PER_BANK, + &st_gpio_irq_ops, bank); + if (!bank->domain) { + dev_err(dev, "Failed to add irq domain for %s\n", + np->full_name); + } else { + for (i = 0; i < ST_GPIO_PINS_PER_BANK; i++) { + if (irq_create_mapping(bank->domain, i) < 0) + dev_err(dev, + "Failed to map IRQ %i\n", i); + } + } + + } else { + dev_info(dev, "No IRQ support for %s bank\n", np->full_name); + } + return 0; } @@ -1276,6 +1480,8 @@ static int st_pctl_probe_dt(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct device_node *child; int grp_index = 0; + int irq = 0; + struct resource *res; st_pctl_dt_child_count(info, np); if (!info->nbanks) { @@ -1306,6 +1512,21 @@ static int st_pctl_probe_dt(struct platform_device *pdev, } info->data = of_match_node(st_pctl_of_match, np)->data; + irq = platform_get_irq(pdev, 0); + + if (irq > 0) { + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "irqmux"); + info->irqmux_base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(info->irqmux_base)) + return PTR_ERR(info->irqmux_base); + + irq_set_chained_handler(irq, st_gpio_irqmux_handler); + irq_set_handler_data(irq, info); + + } + pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK; pdesc = devm_kzalloc(&pdev->dev, sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL); -- cgit v1.2.3-70-g09d2 From 4bd7547756af5c71d55e4d77f41db3d06c18b3e0 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Tue, 28 Jan 2014 15:20:44 +0800 Subject: pinctrl: single: add low power mode support For some silicons, the pin configuration register can control the output of the pin when the pad including the pin enter low power mode. For example, the pin can be "Drive 1", "Drive 0", "Float" when the pad including the pin enter low power mode. It is very useful when you want to control the power leakeage when the SOC enter low power mode, and can save more power for the low power mode. Signed-off-by: Chao Xie Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt | 7 +++++++ drivers/pinctrl/pinctrl-single.c | 3 +++ 2 files changed, 10 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt index bc0dfdfdb14..66dcaa9efd7 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt @@ -63,6 +63,13 @@ Optional properties: /* input, enable bits, disable bits, mask */ pinctrl-single,input-schmitt-enable = <0x30 0x40 0 0x70>; +- pinctrl-single,low-power-mode : array of value that are used to configure + low power mode of this pin. For some silicons, the low power mode will + control the output of the pin when the pad including the pin enter low + power mode. + /* low power mode value, mask */ + pinctrl-single,low-power-mode = <0x288 0x388>; + - pinctrl-single,gpio-range : list of value that are used to configure a GPIO range. They're value of subnode phandle, pin base in pinctrl device, pin number in this range, GPIO function value of this GPIO range. diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index de6459628b4..81075f2a1d3 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -662,6 +662,7 @@ static int pcs_pinconf_get(struct pinctrl_dev *pctldev, break; case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: + case PIN_CONFIG_LOW_POWER_MODE: default: *config = data; break; @@ -699,6 +700,7 @@ static int pcs_pinconf_set(struct pinctrl_dev *pctldev, case PIN_CONFIG_INPUT_SCHMITT: case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: + case PIN_CONFIG_LOW_POWER_MODE: shift = ffs(func->conf[i].mask) - 1; data &= ~func->conf[i].mask; data |= (arg << shift) & func->conf[i].mask; @@ -1101,6 +1103,7 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, { "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, }, { "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, }, { "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, }, + { "pinctrl-single,low-power-mode", PIN_CONFIG_LOW_POWER_MODE, }, }; struct pcs_conf_type prop4[] = { { "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, }, -- cgit v1.2.3-70-g09d2 From b0504e39c27b00101c9c1fa2c58fd896ae0f64f5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 18 Nov 2013 16:48:19 +0100 Subject: ARM: zynq: Map I/O memory on clkc init The clkc has its registers in the range of the slcr. Instead of passing around the slcr base address pointer, let the clkc get the address from the DT. This prepares the slcr to be a real driver with multiple memory ranges (slcr, clocks, pinctrl,...) Signed-off-by: Steffen Trumtrar Signed-off-by: Michal Simek --- .../devicetree/bindings/clock/zynq-7000.txt | 4 +- arch/arm/boot/dts/zynq-7000.dtsi | 42 +++++----- arch/arm/mach-zynq/common.c | 2 +- drivers/clk/zynq/clkc.c | 89 +++++++++++++++------- include/linux/clk/zynq.h | 2 +- 5 files changed, 88 insertions(+), 51 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/clock/zynq-7000.txt b/Documentation/devicetree/bindings/clock/zynq-7000.txt index 17b4a94916d..d93746cf297 100644 --- a/Documentation/devicetree/bindings/clock/zynq-7000.txt +++ b/Documentation/devicetree/bindings/clock/zynq-7000.txt @@ -14,6 +14,7 @@ for all clock consumers of PS clocks. Required properties: - #clock-cells : Must be 1 - compatible : "xlnx,ps7-clkc" + - reg : SLCR offset and size taken via syscon < 0x100 0x100 > - ps-clk-frequency : Frequency of the oscillator providing ps_clk in HZ (usually 33 MHz oscillators are used for Zynq platforms) - clock-output-names : List of strings used to name the clock outputs. Shall be @@ -87,10 +88,11 @@ Clock outputs: 47: dbg_apb Example: - clkc: clkc { + clkc: clkc@100 { #clock-cells = <1>; compatible = "xlnx,ps7-clkc"; ps-clk-frequency = <33333333>; + reg = <0x100 0x100>; clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", "lqspi", "smc", "pcap", "gem0", "gem1", diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi index 81e5677f25a..602e12eedb0 100644 --- a/arch/arm/boot/dts/zynq-7000.dtsi +++ b/arch/arm/boot/dts/zynq-7000.dtsi @@ -123,30 +123,28 @@ } ; slcr: slcr@f8000000 { + #address-cells = <1>; + #size-cells = <1>; compatible = "xlnx,zynq-slcr", "syscon"; reg = <0xF8000000 0x1000>; - - clocks { - #address-cells = <1>; - #size-cells = <0>; - - clkc: clkc { - #clock-cells = <1>; - compatible = "xlnx,ps7-clkc"; - ps-clk-frequency = <33333333>; - fclk-enable = <0>; - clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", - "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", - "dci", "lqspi", "smc", "pcap", "gem0", "gem1", - "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", - "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", - "dma", "usb0_aper", "usb1_aper", "gem0_aper", - "gem1_aper", "sdio0_aper", "sdio1_aper", - "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", - "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", - "gpio_aper", "lqspi_aper", "smc_aper", "swdt", - "dbg_trc", "dbg_apb"; - }; + ranges; + clkc: clkc@100 { + #clock-cells = <1>; + compatible = "xlnx,ps7-clkc"; + ps-clk-frequency = <33333333>; + fclk-enable = <0>; + clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", + "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", + "dci", "lqspi", "smc", "pcap", "gem0", "gem1", + "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", + "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", + "dma", "usb0_aper", "usb1_aper", "gem0_aper", + "gem1_aper", "sdio0_aper", "sdio1_aper", + "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", + "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", + "gpio_aper", "lqspi_aper", "smc_aper", "swdt", + "dbg_trc", "dbg_apb"; + reg = <0x100 0x100>; }; }; diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 38401cf7838..93ea19b13e6 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -67,7 +67,7 @@ static void __init zynq_timer_init(void) { zynq_early_slcr_init(); - zynq_clock_init(zynq_slcr_base); + zynq_clock_init(); clocksource_of_init(); } diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 09dd0173ea0..03052d67b19 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -21,34 +21,35 @@ #include #include #include +#include #include #include #include -static void __iomem *zynq_slcr_base_priv; - -#define SLCR_ARMPLL_CTRL (zynq_slcr_base_priv + 0x100) -#define SLCR_DDRPLL_CTRL (zynq_slcr_base_priv + 0x104) -#define SLCR_IOPLL_CTRL (zynq_slcr_base_priv + 0x108) -#define SLCR_PLL_STATUS (zynq_slcr_base_priv + 0x10c) -#define SLCR_ARM_CLK_CTRL (zynq_slcr_base_priv + 0x120) -#define SLCR_DDR_CLK_CTRL (zynq_slcr_base_priv + 0x124) -#define SLCR_DCI_CLK_CTRL (zynq_slcr_base_priv + 0x128) -#define SLCR_APER_CLK_CTRL (zynq_slcr_base_priv + 0x12c) -#define SLCR_GEM0_CLK_CTRL (zynq_slcr_base_priv + 0x140) -#define SLCR_GEM1_CLK_CTRL (zynq_slcr_base_priv + 0x144) -#define SLCR_SMC_CLK_CTRL (zynq_slcr_base_priv + 0x148) -#define SLCR_LQSPI_CLK_CTRL (zynq_slcr_base_priv + 0x14c) -#define SLCR_SDIO_CLK_CTRL (zynq_slcr_base_priv + 0x150) -#define SLCR_UART_CLK_CTRL (zynq_slcr_base_priv + 0x154) -#define SLCR_SPI_CLK_CTRL (zynq_slcr_base_priv + 0x158) -#define SLCR_CAN_CLK_CTRL (zynq_slcr_base_priv + 0x15c) -#define SLCR_CAN_MIOCLK_CTRL (zynq_slcr_base_priv + 0x160) -#define SLCR_DBG_CLK_CTRL (zynq_slcr_base_priv + 0x164) -#define SLCR_PCAP_CLK_CTRL (zynq_slcr_base_priv + 0x168) -#define SLCR_FPGA0_CLK_CTRL (zynq_slcr_base_priv + 0x170) -#define SLCR_621_TRUE (zynq_slcr_base_priv + 0x1c4) -#define SLCR_SWDT_CLK_SEL (zynq_slcr_base_priv + 0x304) +static void __iomem *zynq_clkc_base; + +#define SLCR_ARMPLL_CTRL (zynq_clkc_base + 0x00) +#define SLCR_DDRPLL_CTRL (zynq_clkc_base + 0x04) +#define SLCR_IOPLL_CTRL (zynq_clkc_base + 0x08) +#define SLCR_PLL_STATUS (zynq_clkc_base + 0x0c) +#define SLCR_ARM_CLK_CTRL (zynq_clkc_base + 0x20) +#define SLCR_DDR_CLK_CTRL (zynq_clkc_base + 0x24) +#define SLCR_DCI_CLK_CTRL (zynq_clkc_base + 0x28) +#define SLCR_APER_CLK_CTRL (zynq_clkc_base + 0x2c) +#define SLCR_GEM0_CLK_CTRL (zynq_clkc_base + 0x40) +#define SLCR_GEM1_CLK_CTRL (zynq_clkc_base + 0x44) +#define SLCR_SMC_CLK_CTRL (zynq_clkc_base + 0x48) +#define SLCR_LQSPI_CLK_CTRL (zynq_clkc_base + 0x4c) +#define SLCR_SDIO_CLK_CTRL (zynq_clkc_base + 0x50) +#define SLCR_UART_CLK_CTRL (zynq_clkc_base + 0x54) +#define SLCR_SPI_CLK_CTRL (zynq_clkc_base + 0x58) +#define SLCR_CAN_CLK_CTRL (zynq_clkc_base + 0x5c) +#define SLCR_CAN_MIOCLK_CTRL (zynq_clkc_base + 0x60) +#define SLCR_DBG_CLK_CTRL (zynq_clkc_base + 0x64) +#define SLCR_PCAP_CLK_CTRL (zynq_clkc_base + 0x68) +#define SLCR_FPGA0_CLK_CTRL (zynq_clkc_base + 0x70) +#define SLCR_621_TRUE (zynq_clkc_base + 0xc4) +#define SLCR_SWDT_CLK_SEL (zynq_clkc_base + 0x204) #define NUM_MIO_PINS 54 @@ -569,8 +570,44 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_OF_DECLARE(zynq_clkc, "xlnx,ps7-clkc", zynq_clk_setup); -void __init zynq_clock_init(void __iomem *slcr_base) +void __init zynq_clock_init(void) { - zynq_slcr_base_priv = slcr_base; + struct device_node *np; + struct device_node *slcr; + struct resource res; + + np = of_find_compatible_node(NULL, NULL, "xlnx,ps7-clkc"); + if (!np) { + pr_err("%s: clkc node not found\n", __func__); + goto np_err; + } + + if (of_address_to_resource(np, 0, &res)) { + pr_err("%s: failed to get resource\n", np->name); + goto np_err; + } + + slcr = of_get_parent(np); + + if (slcr->data) { + zynq_clkc_base = (__force void __iomem *)slcr->data + res.start; + } else { + pr_err("%s: Unable to get I/O memory\n", np->name); + of_node_put(slcr); + goto np_err; + } + + pr_info("%s: clkc starts at %p\n", __func__, zynq_clkc_base); + + of_node_put(slcr); + of_node_put(np); + of_clk_init(NULL); + + return; + +np_err: + of_node_put(np); + BUG(); + return; } diff --git a/include/linux/clk/zynq.h b/include/linux/clk/zynq.h index e062d317ccc..7a5633b7153 100644 --- a/include/linux/clk/zynq.h +++ b/include/linux/clk/zynq.h @@ -22,7 +22,7 @@ #include -void zynq_clock_init(void __iomem *slcr); +void zynq_clock_init(void); struct clk *clk_register_zynq_pll(const char *name, const char *parent, void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index, -- cgit v1.2.3-70-g09d2 From ac7f4820301b6b6029f01e4c276c2cd42272d9cb Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sat, 8 Feb 2014 15:59:54 +0800 Subject: ASoC: binding: add widgets.txt Suggested-by: Mark Brown Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/widgets.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/widgets.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/widgets.txt b/Documentation/devicetree/bindings/sound/widgets.txt new file mode 100644 index 00000000000..b6de5ba3b2d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/widgets.txt @@ -0,0 +1,20 @@ +Widgets: + +This mainly specifies audio off-codec DAPM widgets. + +Each entry is a pair of strings in DT: + + "template-wname", "user-supplied-wname" + +The "template-wname" being the template widget name and currently includes: +"Microphone", "Line", "Headphone" and "Speaker". + +The "user-supplied-wname" being the user specified widget name. + +For instance: + simple-audio-widgets = + "Microphone", "Microphone Jack", + "Line", "Line In Jack", + "Line", "Line Out Jack", + "Headphone", "Headphone Jack", + "Speaker", "Speaker External"; -- cgit v1.2.3-70-g09d2 From 66841345073b049c0c194486bac4d7f07266a57e Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sat, 8 Feb 2014 15:59:55 +0800 Subject: ASoC: simple-card: for new properties documenting and usage This add the following three new properties documenting and usage for simple card: "simple-audio-card,name", "simple-audio-card,widgets", Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-card.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 19c84df5fff..05273583490 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -8,9 +8,12 @@ Required properties: Optional properties: +- simple-audio-card,name : User specified audio sound card name, one string + property. - simple-audio-card,format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb" +- simple-audio-card,widgets : Please refer to widgets.txt. - simple-audio-card,routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's @@ -42,11 +45,16 @@ Example: sound { compatible = "simple-audio-card"; + simple-audio-card,name = "VF610-Tower-Sound-Card"; simple-audio-card,format = "left_j"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Speaker", "External Speaker"; simple-audio-card,routing = - "MIC_IN", "Mic Jack", + "MIC_IN", "Microphone Jack", "Headphone Jack", "HP_OUT", - "Ext Spk", "LINE_OUT"; + "External Speaker", "LINE_OUT"; simple-audio-card,cpu { sound-dai = <&sh_fsi2 0>; -- cgit v1.2.3-70-g09d2 From 9f10b36ffde2b732def037c1e764a0c71745a372 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Thu, 6 Feb 2014 18:03:09 +0000 Subject: ASoC: da9055: Add DT support for CODEC Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/da9055.txt | 22 ++++++++++++++++++++++ sound/soc/codecs/da9055.c | 8 ++++++++ 2 files changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/da9055.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/da9055.txt b/Documentation/devicetree/bindings/sound/da9055.txt new file mode 100644 index 00000000000..ed1b7cc6f24 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/da9055.txt @@ -0,0 +1,22 @@ +* Dialog DA9055 Audio CODEC + +DA9055 provides Audio CODEC support (I2C only). + +The Audio CODEC device in DA9055 has it's own I2C address which is configurable, +so the device is instantiated separately from the PMIC (MFD) device. + +For details on accompanying PMIC I2C device, see the following: +Documentation/devicetree/bindings/mfd/da9055.txt + +Required properties: + + - compatible: "dlg,da9055-codec" + - reg: Specifies the I2C slave address + + +Example: + + codec: da9055-codec@1a { + compatible = "dlg,da9055-codec"; + reg = <0x1a>; + }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 422812613a2..be31f3cfd46 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -1536,11 +1538,17 @@ static const struct i2c_device_id da9055_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); +static const struct of_device_id da9055_of_match[] = { + { .compatible = "dlg,da9055-codec", }, + { } +}; + /* I2C codec control layer */ static struct i2c_driver da9055_i2c_driver = { .driver = { .name = "da9055-codec", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(da9055_of_match), }, .probe = da9055_i2c_probe, .remove = da9055_remove, -- cgit v1.2.3-70-g09d2 From 9a8b6079721ff2e59439fa688916c8e364599eef Mon Sep 17 00:00:00 2001 From: Young-Gun Jang Date: Wed, 5 Feb 2014 11:51:28 +0530 Subject: pinctrl: exynos: add exynos5260 SoC specific data Adds pinctrl support for all platforms based on EXYNOS5260 SoC. Acked-by: Tomasz Figa Signed-off-by: Pankaj Dubey Signed-off-by: Young-Gun Jang Signed-off-by: Rahul Sharma Signed-off-by: Arun Kumar K Signed-off-by: Linus Walleij --- .../bindings/pinctrl/samsung-pinctrl.txt | 1 + drivers/pinctrl/pinctrl-exynos.c | 82 ++++++++++++++++++++++ drivers/pinctrl/pinctrl-samsung.c | 2 + drivers/pinctrl/pinctrl-samsung.h | 1 + 4 files changed, 86 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt index 257677de3e6..2b32783ba82 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt @@ -16,6 +16,7 @@ Required Properties: - "samsung,exynos4210-pinctrl": for Exynos4210 compatible pin-controller. - "samsung,exynos4x12-pinctrl": for Exynos4x12 compatible pin-controller. - "samsung,exynos5250-pinctrl": for Exynos5250 compatible pin-controller. + - "samsung,exynos5260-pinctrl": for Exynos5260 compatible pin-controller. - "samsung,exynos5420-pinctrl": for Exynos5420 compatible pin-controller. - reg: Base address of the pin controller hardware module and length of diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 155b1b3a0e7..07c81306f2f 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -1042,6 +1042,88 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { }, }; +/* pin banks of exynos5260 pin-controller 0 */ +static struct samsung_pin_bank exynos5260_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTG(4, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(7, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c), + EXYNOS_PIN_BANK_EINTG(4, 0x080, "gpb1", 0x10), + EXYNOS_PIN_BANK_EINTG(5, 0x0a0, "gpb2", 0x14), + EXYNOS_PIN_BANK_EINTG(8, 0x0c0, "gpb3", 0x18), + EXYNOS_PIN_BANK_EINTG(8, 0x0e0, "gpb4", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpb5", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x120, "gpd0", 0x24), + EXYNOS_PIN_BANK_EINTG(7, 0x140, "gpd1", 0x28), + EXYNOS_PIN_BANK_EINTG(5, 0x160, "gpd2", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpe0", 0x30), + EXYNOS_PIN_BANK_EINTG(5, 0x1a0, "gpe1", 0x34), + EXYNOS_PIN_BANK_EINTG(4, 0x1c0, "gpf0", 0x38), + EXYNOS_PIN_BANK_EINTG(8, 0x1e0, "gpf1", 0x3c), + EXYNOS_PIN_BANK_EINTG(2, 0x200, "gpk0", 0x40), + EXYNOS_PIN_BANK_EINTW(8, 0xc00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xc20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xc40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xc60, "gpx3", 0x0c), +}; + +/* pin banks of exynos5260 pin-controller 1 */ +static struct samsung_pin_bank exynos5260_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpc0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpc1", 0x04), + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpc2", 0x08), + EXYNOS_PIN_BANK_EINTG(4, 0x060, "gpc3", 0x0c), + EXYNOS_PIN_BANK_EINTG(4, 0x080, "gpc4", 0x10), +}; + +/* pin banks of exynos5260 pin-controller 2 */ +static struct samsung_pin_bank exynos5260_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz0", 0x00), + EXYNOS_PIN_BANK_EINTG(4, 0x020, "gpz1", 0x04), +}; + +/* + * Samsung pinctrl driver data for Exynos5260 SoC. Exynos5260 SoC includes + * three gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos5260_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos5260_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks0), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .weint_con = EXYNOS_WKUP_ECON_OFFSET, + .weint_mask = EXYNOS_WKUP_EMASK_OFFSET, + .weint_pend = EXYNOS_WKUP_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .label = "exynos5260-gpio-ctrl0", + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos5260_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks1), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5260-gpio-ctrl1", + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos5260_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks2), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5260-gpio-ctrl2", + }, +}; + /* pin banks of exynos5420 pin-controller 0 */ static struct samsung_pin_bank exynos5420_pin_banks0[] = { EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpy7", 0x00), diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 47ec2e8741e..0324d4cb19b 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -1120,6 +1120,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = (void *)exynos4x12_pin_ctrl }, { .compatible = "samsung,exynos5250-pinctrl", .data = (void *)exynos5250_pin_ctrl }, + { .compatible = "samsung,exynos5260-pinctrl", + .data = (void *)exynos5260_pin_ctrl }, { .compatible = "samsung,exynos5420-pinctrl", .data = (void *)exynos5420_pin_ctrl }, { .compatible = "samsung,s5pv210-pinctrl", diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 30622d9afa2..bab9c212255 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -254,6 +254,7 @@ struct samsung_pmx_func { extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; extern struct samsung_pin_ctrl exynos5250_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos5260_pin_ctrl[]; extern struct samsung_pin_ctrl exynos5420_pin_ctrl[]; extern struct samsung_pin_ctrl s3c64xx_pin_ctrl[]; extern struct samsung_pin_ctrl s3c2412_pin_ctrl[]; -- cgit v1.2.3-70-g09d2 From dfd72a68aa0f6cf87575f3181319bde8a2d4c01b Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 30 Jan 2014 18:14:05 +0100 Subject: ASoC: cs42l51: add Device Tree binding to cs42l51 This commit adds a trivial Device Tree binding to the I2C-based cs42l51 sound codec, so that it can be used from Device Tree based platforms. Signed-off-by: Thomas Petazzoni Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/i2c/trivial-devices.txt | 1 + sound/soc/codecs/cs42l51.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 1a1ac2e560e..f47e56bcf78 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -18,6 +18,7 @@ atmel,24c02 i2c serial eeprom (24cxx) atmel,at97sc3204t i2c trusted platform module (TPM) capella,cm32181 CM32181: Ambient Light Sensor catalyst,24c32 i2c serial eeprom +cirrus,cs42l51 Cirrus Logic CS42L51 audio codec dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock dallas,ds1338 I2C RTC with 56-Byte NV RAM dallas,ds1339 I2C Serial Real-Time Clock diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 6e9ea8379a9..824cdf4d497 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -600,10 +600,17 @@ static const struct i2c_device_id cs42l51_id[] = { }; MODULE_DEVICE_TABLE(i2c, cs42l51_id); +static const struct of_device_id cs42l51_of_match[] = { + { .compatible = "cirrus,cs42l51", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs42l51_of_match); + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51-codec", .owner = THIS_MODULE, + .of_match_table = cs42l51_of_match, }, .id_table = cs42l51_id, .probe = cs42l51_i2c_probe, -- cgit v1.2.3-70-g09d2 From 9a0d5113ac0ee513224ca2e5011b3a566de16207 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 30 Jan 2014 18:14:06 +0100 Subject: ASoC: kirkwood: enable Kirkwood driver for mvebu platforms The audio unit found in the Armada 370 SoC is similar to the one used in the Marvell Kirkwood and Marvell Dove SoCs. Therefore, this commit allows the Kirkwood audio driver to be built on mvebu platforms, and adds an additional compatible string to identify the Armada 370 variant of the audio unit. Signed-off-by: Thomas Petazzoni Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/mvebu-audio.txt | 1 + sound/soc/kirkwood/Kconfig | 2 +- sound/soc/kirkwood/kirkwood-i2s.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/mvebu-audio.txt b/Documentation/devicetree/bindings/sound/mvebu-audio.txt index f0062c5871b..cb8c07c81ce 100644 --- a/Documentation/devicetree/bindings/sound/mvebu-audio.txt +++ b/Documentation/devicetree/bindings/sound/mvebu-audio.txt @@ -5,6 +5,7 @@ Required properties: - compatible: "marvell,kirkwood-audio" for Kirkwood platforms "marvell,dove-audio" for Dove platforms + "marvell,armada370-audio" for Armada 370 platforms - reg: physical base address of the controller and length of memory mapped region. diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 78ed4a42ad2..764a0ef6b26 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,6 +1,6 @@ config SND_KIRKWOOD_SOC tristate "SoC Audio for the Marvell Kirkwood and Dove chips" - depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST + depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the Kirkwood I2S interface. You will also need to select the diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 3920a5e8125..9f842222e79 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -633,6 +633,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) static struct of_device_id mvebu_audio_of_match[] = { { .compatible = "marvell,kirkwood-audio" }, { .compatible = "marvell,dove-audio" }, + { .compatible = "marvell,armada370-audio" }, { } }; MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); -- cgit v1.2.3-70-g09d2 From 9fe21fdc5f3d3aa7d6e78ad25668e234330b6974 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 21 Dec 2013 15:08:00 +0400 Subject: video: imxfb: Use regulator API with LCD class for powering This patch replaces custom lcd_power() callback with regulator API over LCD class. Signed-off-by: Alexander Shiyan Acked-by: Shawn Guo Signed-off-by: Tomi Valkeinen --- .../devicetree/bindings/video/fsl,imx-fb.txt | 1 + arch/arm/mach-imx/mach-mx27ads.c | 55 +++++++++++++++-- drivers/video/imxfb.c | 71 +++++++++++++++++++--- include/linux/platform_data/video-imxfb.h | 1 - 4 files changed, 114 insertions(+), 14 deletions(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt index 46da08db186..e6b1ee9b8e2 100644 --- a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt +++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt @@ -15,6 +15,7 @@ Required nodes: - fsl,pcr: LCDC PCR value Optional properties: +- lcd-supply: Regulator for LCD supply voltage. - fsl,dmacr: DMA Control Register value. This is optional. By default, the register is not modified as recommended by the datasheet. - fsl,lscr1: LCDC Sharp Configuration Register value. diff --git a/arch/arm/mach-imx/mach-mx27ads.c b/arch/arm/mach-imx/mach-mx27ads.c index 9821b824dca..a7a4a9c6761 100644 --- a/arch/arm/mach-imx/mach-mx27ads.c +++ b/arch/arm/mach-imx/mach-mx27ads.c @@ -21,6 +21,10 @@ #include #include #include + +#include +#include + #include #include #include @@ -195,14 +199,58 @@ static const struct imxi2c_platform_data mx27ads_i2c1_data __initconst = { static struct i2c_board_info mx27ads_i2c_devices[] = { }; -void lcd_power(int on) +static void vgpio_set(struct gpio_chip *chip, unsigned offset, int value) { - if (on) + if (value) __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_SET_REG); else __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG); } +static int vgpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) +{ + return 0; +} + +#define MX27ADS_LCD_GPIO (6 * 32) + +static struct regulator_consumer_supply mx27ads_lcd_regulator_consumer = + REGULATOR_SUPPLY("lcd", "imx-fb.0"); + +static struct regulator_init_data mx27ads_lcd_regulator_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, +}, + .consumer_supplies = &mx27ads_lcd_regulator_consumer, + .num_consumer_supplies = 1, +}; + +static struct fixed_voltage_config mx27ads_lcd_regulator_pdata = { + .supply_name = "LCD", + .microvolts = 3300000, + .gpio = MX27ADS_LCD_GPIO, + .init_data = &mx27ads_lcd_regulator_init_data, +}; + +static void __init mx27ads_regulator_init(void) +{ + struct gpio_chip *vchip; + + vchip = kzalloc(sizeof(*vchip), GFP_KERNEL); + vchip->owner = THIS_MODULE; + vchip->label = "LCD"; + vchip->base = MX27ADS_LCD_GPIO; + vchip->ngpio = 1; + vchip->direction_output = vgpio_dir_out; + vchip->set = vgpio_set; + gpiochip_add(vchip); + + platform_device_register_data(&platform_bus, "reg-fixed-voltage", + PLATFORM_DEVID_AUTO, + &mx27ads_lcd_regulator_pdata, + sizeof(mx27ads_lcd_regulator_pdata)); +} + static struct imx_fb_videomode mx27ads_modes[] = { { .mode = { @@ -239,8 +287,6 @@ static const struct imx_fb_platform_data mx27ads_fb_data __initconst = { .pwmr = 0x00A903FF, .lscr1 = 0x00120300, .dmacr = 0x00020010, - - .lcd_power = lcd_power, }; static int mx27ads_sdhc1_init(struct device *dev, irq_handler_t detect_irq, @@ -304,6 +350,7 @@ static void __init mx27ads_board_init(void) i2c_register_board_info(1, mx27ads_i2c_devices, ARRAY_SIZE(mx27ads_i2c_devices)); imx27_add_imx_i2c(1, &mx27ads_i2c1_data); + mx27ads_regulator_init(); imx27_add_imx_fb(&mx27ads_fb_data); imx27_add_mxc_mmc(0, &sdhc1_pdata); imx27_add_mxc_mmc(1, &sdhc2_pdata); diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 44ee678481d..e50b67fada5 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -30,10 +30,13 @@ #include #include #include +#include #include #include #include +#include + #include