From: Srinivas Kandagatla <[email protected]>
Thankyou all for reviewing the v2 patches.
Here is patch-set incorporating all the review comments from v2.
This patch-set adds basic support for STMicroelectronics STixxxx SOCs
which includes STiH415 and STiH416 with B2000 and B2020 board support.
STiH415 and STiH416 are dual-core ARM Cortex-A9 CPU, designed for use in
Set-top-boxes. The SOC support is available in mach-stixxxx which contains
support code for STiH415, STiH416 SOCs including the generic board support.
Only device drivers included in this patch set are UART and pinctrl, other
drivers will be added in future.
The reason for adding two SOCs at this patch set is to show that no new
C code is required for second SOC(STiH416) support.
Thanks to Mark B for picking up the regmap_field apis.
I would appreciate any feedback on these patches.
Changes since v2:
- st-asc driver made POSIX compatible based on Russell K comment.
- cleaned up arm-global-timer code for non-dt as suggested by Linus W and
- fixed minmum clock tick setting pointed by Linus W.
- Moved to using syscon driver, as suggested by Arnd and Linus W.
- Fixed few plat-* specfic references in comments pointed by Michal S.
- Removed un-used variables in mach code as pointed by Michal S.
- Cleaned up pinctrl nodes not to use simple-bus as suggested by Arnd
and Linus W.
- In pinctrl driver moved lot of magic numbers in to proper defines or
constants as suggested by Linus W.
- Renamed pinctrl-stixxxx to pinctrl-st as discussed with Linus W.
- Split adding SOC to multi_v7_defconfig in to two patches as discussed
with Mark R.
Changes since RFC:
- st-asc Serial driver:
- modified kconfig to remove default y
- removed all the forward declaration.
- use dynamic major numbering.
- merge header-file in to driver.
- ARM Global timer:
- moved to drivers/clocksource.
- added revision check in driver.
- removed unused header file.
- moved to u64 from union gt_counter
- comments added in get_counter
- removed leftover debug code.
- moved code to use __raw_readl/writel.
- used DIV_ROUND_CLOSEST
- added check in interrupt handler.
- expanded CE and CS acronyms usage.
- Fixed minimum clock ticks value.
- move to use clocksource_register_hz
- added arch sched_clock support.
- added ERRATA 740657 workaround.
- ST-System Configuration Registers:
- moved from syscon usage to st specific driver.
- ST Pinctrl support:
- fixed few typos in the documentation.
- moved some of the retime offset information to driver.
- STixxxx Support:
- all the SOC support code is moved to mach-stixxxx
- seperated DEBUG_LL code to new patch.
- removed unnecessary #interrupt-cells
- renamed uart to serial
- moved to multi_v7_defconfig
- used menuconfig in mach-stixxx/Kconfig
- removed of_platform_populate as generic code does it for you.
- scu address is ioremaped.
Srinivas Kandagatla (9):
serial:st-asc: Add ST ASC driver.
pinctrl:st: Add pinctrl and pinconf support.
ARM:stixxxx: Add STiH415 SOC support
ARM:stixxxx: Add STiH416 SOC support
ARM:stixxxx: Add DEBUG_LL console support
ARM:multi_v7_defconfig: rerun savedefconfig
ARM:stixxxx: Add stixxxx options to multi_v7_defconfig
ARM:stih41x: Add B2000 board support
ARM:stih41x: Add B2020 board support
Stuart Menefy (1):
clocksource:arm_global_timer: Add ARM global timer support.
Documentation/arm/stixxxx/overview.txt | 33 +
Documentation/arm/stixxxx/stih415-overview.txt | 12 +
Documentation/arm/stixxxx/stih416-overview.txt | 12 +
.../devicetree/bindings/arm/global_timer.txt | 21 +
.../devicetree/bindings/pinctrl/pinctrl-st.txt | 110 ++
.../devicetree/bindings/tty/serial/st-asc.txt | 18 +
MAINTAINERS | 11 +
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 38 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 4 +
arch/arm/boot/dts/st-pincfg.h | 71 +
arch/arm/boot/dts/stih415-b2000.dts | 15 +
arch/arm/boot/dts/stih415-b2020.dts | 15 +
arch/arm/boot/dts/stih415-clock.dtsi | 38 +
arch/arm/boot/dts/stih415-pinctrl.dtsi | 268 ++++
arch/arm/boot/dts/stih415.dtsi | 87 ++
arch/arm/boot/dts/stih416-b2000.dts | 16 +
arch/arm/boot/dts/stih416-b2020.dts | 16 +
arch/arm/boot/dts/stih416-clock.dtsi | 41 +
arch/arm/boot/dts/stih416-pinctrl.dtsi | 295 ++++
arch/arm/boot/dts/stih416.dtsi | 96 ++
arch/arm/boot/dts/stih41x-b2000.dtsi | 41 +
arch/arm/boot/dts/stih41x-b2020.dtsi | 42 +
arch/arm/boot/dts/stih41x.dtsi | 38 +
arch/arm/configs/multi_v7_defconfig | 31 +-
arch/arm/include/debug/stixxxx.S | 61 +
arch/arm/mach-stixxxx/Kconfig | 45 +
arch/arm/mach-stixxxx/Makefile | 2 +
arch/arm/mach-stixxxx/board-dt.c | 50 +
arch/arm/mach-stixxxx/headsmp.S | 44 +
arch/arm/mach-stixxxx/platsmp.c | 117 ++
arch/arm/mach-stixxxx/smp.h | 17 +
drivers/clocksource/Kconfig | 13 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/arm_global_timer.c | 330 +++++
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-st.c | 1403 ++++++++++++++++++++
drivers/tty/serial/Kconfig | 16 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/st-asc.c | 939 +++++++++++++
include/uapi/linux/serial_core.h | 3 +
43 files changed, 4404 insertions(+), 17 deletions(-)
create mode 100644 Documentation/arm/stixxxx/overview.txt
create mode 100644 Documentation/arm/stixxxx/stih415-overview.txt
create mode 100644 Documentation/arm/stixxxx/stih416-overview.txt
create mode 100644 Documentation/devicetree/bindings/arm/global_timer.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
create mode 100644 Documentation/devicetree/bindings/tty/serial/st-asc.txt
create mode 100644 arch/arm/boot/dts/st-pincfg.h
create mode 100644 arch/arm/boot/dts/stih415-b2000.dts
create mode 100644 arch/arm/boot/dts/stih415-b2020.dts
create mode 100644 arch/arm/boot/dts/stih415-clock.dtsi
create mode 100644 arch/arm/boot/dts/stih415-pinctrl.dtsi
create mode 100644 arch/arm/boot/dts/stih415.dtsi
create mode 100644 arch/arm/boot/dts/stih416-b2000.dts
create mode 100644 arch/arm/boot/dts/stih416-b2020.dts
create mode 100644 arch/arm/boot/dts/stih416-clock.dtsi
create mode 100644 arch/arm/boot/dts/stih416-pinctrl.dtsi
create mode 100644 arch/arm/boot/dts/stih416.dtsi
create mode 100644 arch/arm/boot/dts/stih41x-b2000.dtsi
create mode 100644 arch/arm/boot/dts/stih41x-b2020.dtsi
create mode 100644 arch/arm/boot/dts/stih41x.dtsi
create mode 100644 arch/arm/include/debug/stixxxx.S
create mode 100644 arch/arm/mach-stixxxx/Kconfig
create mode 100644 arch/arm/mach-stixxxx/Makefile
create mode 100644 arch/arm/mach-stixxxx/board-dt.c
create mode 100644 arch/arm/mach-stixxxx/headsmp.S
create mode 100644 arch/arm/mach-stixxxx/platsmp.c
create mode 100644 arch/arm/mach-stixxxx/smp.h
create mode 100644 drivers/clocksource/arm_global_timer.c
create mode 100644 drivers/pinctrl/pinctrl-st.c
create mode 100644 drivers/tty/serial/st-asc.c
--
1.7.6.5
This patch adds stih415 and stih416 support to multi_v7_defconfig.
CONFIG_ARM_ERRATA_754322 is removed as it is selected by the stixxxx
mach level kconfig.
Signed-off-by: Srinivas Kandagatla <[email protected]>
---
arch/arm/configs/multi_v7_defconfig | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index f2b4957..c0cfac0 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -9,11 +9,11 @@ CONFIG_PLAT_SPEAR=y
CONFIG_ARCH_SPEAR13XX=y
CONFIG_MACH_SPEAR1310=y
CONFIG_MACH_SPEAR1340=y
+CONFIG_ARCH_STIXXXX=y
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_SIRF=y
CONFIG_ARCH_WM8850=y
CONFIG_ARCH_ZYNQ=y
-CONFIG_ARM_ERRATA_754322=y
CONFIG_SMP=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
@@ -43,6 +43,8 @@ CONFIG_SERIAL_SIRFSOC_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_SERIAL_VT8500_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_ST_ASC=y
+CONFIG_SERIAL_ST_ASC_CONSOLE=y
CONFIG_IPMI_HANDLER=y
CONFIG_IPMI_SI=y
CONFIG_I2C=y
--
1.7.6.5
B2000 board is reference board for STIH415/416 SOCs, it has
2 x UART, 4x USB, 2 x Ethernet, 1 x SATA, 1 x PCIe, and 1GB RAM.
This patch add initial support to b2000 with STiH415/416 with UART2 as
console and a heard beat LED.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/stih415-b2000.dts | 15 ++++++++++++
arch/arm/boot/dts/stih416-b2000.dts | 16 +++++++++++++
arch/arm/boot/dts/stih41x-b2000.dtsi | 41 ++++++++++++++++++++++++++++++++++
4 files changed, 74 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/stih415-b2000.dts
create mode 100644 arch/arm/boot/dts/stih416-b2000.dts
create mode 100644 arch/arm/boot/dts/stih41x-b2000.dtsi
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index f0895c5..d4615fd 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -177,6 +177,8 @@ dtb-$(CONFIG_ARCH_SPEAR3XX)+= spear300-evb.dtb \
spear320-evb.dtb \
spear320-hmi.dtb
dtb-$(CONFIG_ARCH_SPEAR6XX)+= spear600-evb.dtb
+dtb-$(CONFIG_ARCH_STIXXXX)+= stih415-b2000.dtb \
+ stih416-b2000.dtb
dtb-$(CONFIG_ARCH_SUNXI) += \
sun4i-a10-cubieboard.dtb \
sun4i-a10-mini-xplus.dtb \
diff --git a/arch/arm/boot/dts/stih415-b2000.dts b/arch/arm/boot/dts/stih415-b2000.dts
new file mode 100644
index 0000000..d4af531
--- /dev/null
+++ b/arch/arm/boot/dts/stih415-b2000.dts
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/dts-v1/;
+#include "stih415.dtsi"
+#include "stih41x-b2000.dtsi"
+/ {
+ model = "STiH415 B2000 Board";
+ compatible = "st,stih415", "st,stih415-b2000";
+};
diff --git a/arch/arm/boot/dts/stih416-b2000.dts b/arch/arm/boot/dts/stih416-b2000.dts
new file mode 100644
index 0000000..a5eb6ee
--- /dev/null
+++ b/arch/arm/boot/dts/stih416-b2000.dts
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/dts-v1/;
+#include "stih416.dtsi"
+#include "stih41x-b2000.dtsi"
+
+/ {
+ compatible = "st,stih416", "st,stih416-b2000";
+ model = "STiH416 B2000";
+};
diff --git a/arch/arm/boot/dts/stih41x-b2000.dtsi b/arch/arm/boot/dts/stih41x-b2000.dtsi
new file mode 100644
index 0000000..8e694d2
--- /dev/null
+++ b/arch/arm/boot/dts/stih41x-b2000.dtsi
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/ {
+
+ memory{
+ device_type = "memory";
+ reg = <0x60000000 0x40000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyAS0,115200";
+ linux,stdout-path = &serial2;
+ };
+
+ aliases {
+ ttyAS0 = &serial2;
+ };
+
+ soc {
+ serial2: serial@fed32000 {
+ status = "okay";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ fp_led {
+ #gpio-cells = <1>;
+ label = "Front Panel LED";
+ gpios = <&PIO105 7>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ };
+};
--
1.7.6.5
This patch adds support to ASC (asynchronous serial controller)
driver, which is basically a standard serial driver. This IP is common
across all the ST parts for settop box platforms.
ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
It support all industry standard baud rates.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Stuart Menefy <[email protected]>
CC: Arnd Bergmann <[email protected]>
CC: Greg Kroah-Hartman <[email protected]>
---
.../devicetree/bindings/tty/serial/st-asc.txt | 18 +
drivers/tty/serial/Kconfig | 16 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/st-asc.c | 939 ++++++++++++++++++++
include/uapi/linux/serial_core.h | 3 +
5 files changed, 977 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/tty/serial/st-asc.txt
create mode 100644 drivers/tty/serial/st-asc.c
diff --git a/Documentation/devicetree/bindings/tty/serial/st-asc.txt b/Documentation/devicetree/bindings/tty/serial/st-asc.txt
new file mode 100644
index 0000000..75d877f
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/st-asc.txt
@@ -0,0 +1,18 @@
+*st-asc(Serial Port)
+
+Required properties:
+- compatible : Should be "st,asc".
+- reg, reg-names, interrupts, interrupt-names : Standard way to define device
+ resources with names. look in
+ Documentation/devicetree/bindings/resource-names.txt
+
+Optional properties:
+- st,hw-flow-ctrl bool flag to enable hardware flow control.
+- st,force-m1 bool flat to force asc to be in Mode-1 recommeded
+ for high bit rates (above 19.2K)
+Example:
+serial@fe440000{
+ compatible = "st,asc";
+ reg = <0xfe440000 0x2c>;
+ interrupts = <0 209 0>;
+};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 7e7006f..0c6ddf6 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1484,6 +1484,22 @@ config SERIAL_RP2_NR_UARTS
If multiple cards are present, the default limit of 32 ports may
need to be increased.
+config SERIAL_ST_ASC
+ tristate "ST ASC serial port support"
+ select SERIAL_CORE
+ help
+ This driver is for the on-chip Asychronous Serial Controller on
+ STMicroelectronics STixxxx SoCs.
+ ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
+ It support all industry standard baud rates.
+
+ If unsure, say N.
+
+config SERIAL_ST_ASC_CONSOLE
+ bool "Support for console on ST ASC"
+ depends on SERIAL_ST_ASC=y
+ select SERIAL_CORE_CONSOLE
+
endmenu
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index eedfec4..536ccc7 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
new file mode 100644
index 0000000..5edf82f
--- /dev/null
+++ b/drivers/tty/serial/st-asc.c
@@ -0,0 +1,939 @@
+/*
+ * st-asc.c: ST Asynchronous serial controller (ASC) driver
+ *
+ * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited
+ *
+ * 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.
+ *
+ */
+
+#if defined(CONFIG_SERIAL_ST_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+
+#define DRIVER_NAME "st-asc"
+#define ASC_SERIAL_NAME "ttyAS"
+#define ASC_FIFO_SIZE 16
+#define ASC_MAX_PORTS 8
+
+struct asc_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int hw_flow_control:1;
+ unsigned int force_m1:1;
+};
+
+static struct asc_port asc_ports[ASC_MAX_PORTS];
+static struct uart_driver asc_uart_driver;
+
+/*---- UART Register definitions ------------------------------*/
+
+/* Register offsets */
+
+#define ASC_BAUDRATE 0x00
+#define ASC_TXBUF 0x04
+#define ASC_RXBUF 0x08
+#define ASC_CTL 0x0C
+#define ASC_INTEN 0x10
+#define ASC_STA 0x14
+#define ASC_GUARDTIME 0x18
+#define ASC_TIMEOUT 0x1C
+#define ASC_TXRESET 0x20
+#define ASC_RXRESET 0x24
+#define ASC_RETRIES 0x28
+
+/* ASC_RXBUF */
+#define ASC_RXBUF_PE 0x100
+#define ASC_RXBUF_FE 0x200
+/**
+ * Some of status comes from higher bits of the character and some come from
+ * the status register. Combining both of them in to single status using dummy
+ * bits.
+ */
+#define ASC_RXBUF_DUMMY_RX 0x10000
+#define ASC_RXBUF_DUMMY_BE 0x20000
+#define ASC_RXBUF_DUMMY_OE 0x40000
+
+/* ASC_CTL */
+
+#define ASC_CTL_MODE_MSK 0x0007
+#define ASC_CTL_MODE_8BIT 0x0001
+#define ASC_CTL_MODE_7BIT_PAR 0x0003
+#define ASC_CTL_MODE_9BIT 0x0004
+#define ASC_CTL_MODE_8BIT_WKUP 0x0005
+#define ASC_CTL_MODE_8BIT_PAR 0x0007
+#define ASC_CTL_STOP_MSK 0x0018
+#define ASC_CTL_STOP_HALFBIT 0x0000
+#define ASC_CTL_STOP_1BIT 0x0008
+#define ASC_CTL_STOP_1_HALFBIT 0x0010
+#define ASC_CTL_STOP_2BIT 0x0018
+#define ASC_CTL_PARITYODD 0x0020
+#define ASC_CTL_LOOPBACK 0x0040
+#define ASC_CTL_RUN 0x0080
+#define ASC_CTL_RXENABLE 0x0100
+#define ASC_CTL_SCENABLE 0x0200
+#define ASC_CTL_FIFOENABLE 0x0400
+#define ASC_CTL_CTSENABLE 0x0800
+#define ASC_CTL_BAUDMODE 0x1000
+
+/* ASC_GUARDTIME */
+
+#define ASC_GUARDTIME_MSK 0x00FF
+
+/* ASC_INTEN */
+
+#define ASC_INTEN_RBE 0x0001
+#define ASC_INTEN_TE 0x0002
+#define ASC_INTEN_THE 0x0004
+#define ASC_INTEN_PE 0x0008
+#define ASC_INTEN_FE 0x0010
+#define ASC_INTEN_OE 0x0020
+#define ASC_INTEN_TNE 0x0040
+#define ASC_INTEN_TOI 0x0080
+#define ASC_INTEN_RHF 0x0100
+
+/* ASC_RETRIES */
+
+#define ASC_RETRIES_MSK 0x00FF
+
+/* ASC_RXBUF */
+
+#define ASC_RXBUF_MSK 0x03FF
+
+/* ASC_STA */
+
+#define ASC_STA_RBF 0x0001
+#define ASC_STA_TE 0x0002
+#define ASC_STA_THE 0x0004
+#define ASC_STA_PE 0x0008
+#define ASC_STA_FE 0x0010
+#define ASC_STA_OE 0x0020
+#define ASC_STA_TNE 0x0040
+#define ASC_STA_TOI 0x0080
+#define ASC_STA_RHF 0x0100
+#define ASC_STA_TF 0x0200
+#define ASC_STA_NKD 0x0400
+
+/* ASC_TIMEOUT */
+
+#define ASC_TIMEOUT_MSK 0x00FF
+
+/* ASC_TXBUF */
+
+#define ASC_TXBUF_MSK 0x01FF
+
+/*---- Inline function definitions ---------------------------*/
+
+static inline struct asc_port *to_asc_port(struct uart_port *port)
+{
+ return container_of(port, struct asc_port, port);
+}
+
+static inline u32 asc_in(struct uart_port *port, u32 offset)
+{
+ return readl(port->membase + offset);
+}
+
+static inline void asc_out(struct uart_port *port, u32 offset, u32 value)
+{
+ writel(value, port->membase + offset);
+}
+
+/*
+ * Some simple utility functions to enable and disable interrupts.
+ * Note that these need to be called with interrupts disabled.
+ */
+static inline void asc_disable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline void asc_disable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline u32 asc_txfifo_is_empty(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_TE;
+}
+
+static inline int asc_txfifo_is_full(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_TF;
+}
+
+static inline const char *asc_port_name(struct uart_port *port)
+{
+ return to_platform_device(port->dev)->name;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * This section contains code to support the use of the ASC as a
+ * generic serial port.
+ */
+
+static inline unsigned asc_hw_txroom(struct uart_port *port)
+{
+ u32 status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_THE)
+ return port->fifosize / 2;
+ else if (!(status & ASC_STA_TF))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Start transmitting chars.
+ * This is called from both interrupt and task level.
+ * Either way interrupts are disabled.
+ */
+static void asc_transmit_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int txroom;
+ unsigned char c;
+
+ txroom = asc_hw_txroom(port);
+
+ if ((txroom != 0) && port->x_char) {
+ c = port->x_char;
+ port->x_char = 0;
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom = asc_hw_txroom(port);
+ }
+
+ if (uart_tx_stopped(port)) {
+ /*
+ * We should try and stop the hardware here, but I
+ * don't think the ASC has any way to do that.
+ */
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (uart_circ_empty(xmit)) {
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (txroom == 0)
+ return;
+
+ do {
+ c = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom--;
+ } while ((txroom > 0) && (!uart_circ_empty(xmit)));
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ asc_disable_tx_interrupts(port);
+}
+
+static void asc_receive_chars(struct uart_port *port)
+{
+ struct tty_port *tport = &port->state->port;
+ unsigned long status;
+ unsigned long c = 0;
+ char flag;
+
+ if (port->irq_wake)
+ pm_wakeup_event(tport->tty->dev, 0);
+
+ while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) {
+ c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX;
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+
+ if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) ||
+ status & ASC_STA_OE) {
+
+ if (c & ASC_RXBUF_FE) {
+ if (c == ASC_RXBUF_FE) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ c |= ASC_RXBUF_DUMMY_BE;
+ } else {
+ port->icount.frame++;
+ }
+ } else if (c & ASC_RXBUF_PE) {
+ port->icount.parity++;
+ }
+ /*
+ * Reading any data from the RX FIFO clears the
+ * overflow error condition.
+ */
+ if (status & ASC_STA_OE) {
+ port->icount.overrun++;
+ c |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ c &= port->read_status_mask;
+
+ if (c & ASC_RXBUF_DUMMY_BE)
+ flag = TTY_BREAK;
+ else if (c & ASC_RXBUF_PE)
+ flag = TTY_PARITY;
+ else if (c & ASC_RXBUF_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(port, c))
+ continue;
+
+ uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag);
+ }
+
+ /* Tell the rest of the system the news. New characters! */
+ tty_flip_buffer_push(tport);
+}
+
+static irqreturn_t asc_interrupt(int irq, void *ptr)
+{
+ struct uart_port *port = ptr;
+ u32 status;
+
+ spin_lock(&port->lock);
+
+ status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_RBF) {
+ /* Receive FIFO not empty */
+ asc_receive_chars(port);
+ }
+
+ if ((status & ASC_STA_THE) &&
+ (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) {
+ /* Transmitter FIFO at least half empty */
+ asc_transmit_chars(port);
+ }
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * UART Functions
+ */
+
+static unsigned int asc_tx_empty(struct uart_port *port)
+{
+ return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0;
+}
+
+static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /*
+ * This routine is used for seting signals of: DTR, DCD, CTS/RTS
+ * We use ASC's hardware for CTS/RTS, so don't need any for that.
+ * Some boards have DTR and DCD implemented using PIO pins,
+ * code to do this should be hooked in here.
+ */
+}
+
+static unsigned int asc_get_mctrl(struct uart_port *port)
+{
+ /*
+ * This routine is used for geting signals of: DTR, DCD, DSR, RI,
+ * and CTS/RTS
+ */
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/* There are probably characters waiting to be transmitted. */
+static void asc_start_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (!uart_circ_empty(xmit))
+ asc_enable_tx_interrupts(port);
+}
+
+/* Transmit stop */
+static void asc_stop_tx(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+}
+
+/* Receive stop */
+static void asc_stop_rx(struct uart_port *port)
+{
+ asc_disable_rx_interrupts(port);
+}
+
+/* Force modem status interrupts on */
+static void asc_enable_ms(struct uart_port *port)
+{
+ /* Nothing here yet .. */
+}
+
+/* Handle breaks - ignored by us */
+static void asc_break_ctl(struct uart_port *port, int break_state)
+{
+ /* Nothing here yet .. */
+}
+
+/*
+ * Enable port for reception.
+ */
+static int asc_startup(struct uart_port *port)
+{
+ if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND,
+ asc_port_name(port), port)) {
+ dev_err(port->dev, "cannot allocate irq.\n");
+ return -ENODEV;
+ }
+
+ asc_transmit_chars(port);
+ asc_enable_rx_interrupts(port);
+
+ return 0;
+}
+
+static void asc_shutdown(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+ asc_disable_rx_interrupts(port);
+ free_irq(port->irq, port);
+}
+
+static void asc_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned long flags = 0;
+ u32 ctl;
+
+ switch (state) {
+ case UART_PM_STATE_ON:
+ clk_prepare_enable(ascport->clk);
+ break;
+ case UART_PM_STATE_OFF:
+ /*
+ * Disable the ASC baud rate generator, which is as close as
+ * we can come to turning it off. Note this is not called with
+ * the port spinlock held.
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN;
+ asc_out(port, ASC_CTL, ctl);
+ spin_unlock_irqrestore(&port->lock, flags);
+ clk_disable_unprepare(ascport->clk);
+ break;
+ }
+}
+
+static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned int baud;
+ u32 ctrl_val;
+ tcflag_t cflag;
+ unsigned long flags;
+
+ /* Update termios to reflect hardware capabilities */
+ termios->c_cflag &= ~(CMSPAR |
+ (ascport->hw_flow_control ? 0 : CRTSCTS));
+
+ port->uartclk = clk_get_rate(ascport->clk);
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ cflag = termios->c_cflag;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* read control register */
+ ctrl_val = asc_in(port, ASC_CTL);
+
+ /* stop serial port and reset value */
+ asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN));
+ ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE;
+
+ /* reset fifo rx & tx */
+ asc_out(port, ASC_TXRESET, 1);
+ asc_out(port, ASC_RXRESET, 1);
+
+ /* set character length */
+ if ((cflag & CSIZE) == CS7) {
+ ctrl_val |= ASC_CTL_MODE_7BIT_PAR;
+ } else {
+ ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR :
+ ASC_CTL_MODE_8BIT;
+ }
+
+ /* set stop bit */
+ ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT;
+
+ /* odd parity */
+ if (cflag & PARODD)
+ ctrl_val |= ASC_CTL_PARITYODD;
+
+ /* hardware flow control */
+ if ((cflag & CRTSCTS))
+ ctrl_val |= ASC_CTL_CTSENABLE;
+
+ if ((baud < 19200) && !ascport->force_m1) {
+ asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud)));
+ } else {
+ /*
+ * MODE 1: recommended for high bit rates (above 19.2K)
+ *
+ * baudrate * 16 * 2^16
+ * ASCBaudRate = ------------------------
+ * inputclock
+ *
+ * However to keep the maths inside 32bits we divide top and
+ * bottom by 64. The +1 is to avoid a divide by zero if the
+ * input clock rate is something unexpected.
+ */
+ u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1);
+ asc_out(port, ASC_BAUDRATE, counter);
+ ctrl_val |= ASC_CTL_BAUDMODE;
+ }
+
+ uart_update_timeout(port, cflag, baud);
+
+ ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE;
+ if (termios->c_iflag & INPCK)
+ ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE;
+
+ /*
+ * Characters to ignore
+ */
+ ascport->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & IGNBRK) {
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set.
+ */
+ if (!(termios->c_cflag & CREAD))
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX;
+
+ /* Set the timeout */
+ asc_out(port, ASC_TIMEOUT, 20);
+
+ /* write final value and enable port */
+ asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN));
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *asc_type(struct uart_port *port)
+{
+ return (port->type == PORT_ASC) ? DRIVER_NAME : NULL;
+}
+
+static void asc_release_port(struct uart_port *port)
+{
+}
+
+static int asc_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set
+ * Set type field if successful
+ */
+static void asc_config_port(struct uart_port *port, int flags)
+{
+ if ((flags & UART_CONFIG_TYPE))
+ port->type = PORT_ASC;
+}
+
+static int
+asc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* No user changeable parameters */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context (i.e. kgdb).
+ */
+
+static int asc_get_poll_char(struct uart_port *port)
+{
+ if (!(asc_in(port, ASC_STA) & ASC_STA_RBF))
+ return NO_POLL_CHAR;
+
+ return asc_in(port, ASC_RXBUF);
+}
+
+static void asc_put_poll_char(struct uart_port *port, unsigned char c)
+{
+ while (asc_txfifo_is_full(port))
+ cpu_relax();
+ asc_out(port, ASC_TXBUF, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+/*---------------------------------------------------------------------*/
+
+static struct uart_ops asc_uart_ops = {
+ .tx_empty = asc_tx_empty,
+ .set_mctrl = asc_set_mctrl,
+ .get_mctrl = asc_get_mctrl,
+ .start_tx = asc_start_tx,
+ .stop_tx = asc_stop_tx,
+ .stop_rx = asc_stop_rx,
+ .enable_ms = asc_enable_ms,
+ .break_ctl = asc_break_ctl,
+ .startup = asc_startup,
+ .shutdown = asc_shutdown,
+ .set_termios = asc_set_termios,
+ .type = asc_type,
+ .release_port = asc_release_port,
+ .request_port = asc_request_port,
+ .config_port = asc_config_port,
+ .verify_port = asc_verify_port,
+ .pm = asc_pm,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = asc_get_poll_char,
+ .poll_put_char = asc_put_poll_char,
+#endif /* CONFIG_CONSOLE_POLL */
+};
+
+static int asc_init_port(struct asc_port *ascport,
+ struct platform_device *pdev)
+{
+ struct uart_port *port = &ascport->port;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get io resource\n");
+ return -ENODEV;
+ }
+
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &asc_uart_ops;
+ port->fifosize = ASC_FIFO_SIZE;
+ port->dev = &pdev->dev;
+ port->mapbase = res->start;
+ port->irq = platform_get_irq(pdev, 0);
+
+ port->membase = devm_request_and_ioremap(&pdev->dev, res);
+ if (!port->membase) {
+ dev_err(&pdev->dev, "Unable to request io memory\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&port->lock);
+
+ ascport->clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (WARN_ON(IS_ERR(ascport->clk)))
+ return -EINVAL;
+ /* ensure that clk rate is correct by enabling the clk */
+ clk_prepare_enable(ascport->clk);
+ ascport->port.uartclk = clk_get_rate(ascport->clk);
+ WARN_ON(ascport->port.uartclk == 0);
+ clk_disable_unprepare(ascport->clk);
+
+ return 0;
+}
+
+static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int id;
+
+ if (!np)
+ return NULL;
+
+ id = of_alias_get_id(np, ASC_SERIAL_NAME);
+ if (WARN_ON(id >= ASC_MAX_PORTS))
+ return NULL;
+
+ asc_ports[id].hw_flow_control = of_property_read_bool(np,
+ "st,hw-flow-control");
+ asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1");
+ asc_ports[id].port.line = id;
+ return &asc_ports[id];
+}
+
+static struct of_device_id asc_match[] = {
+ { .compatible = "st,asc", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, asc_match);
+
+static int asc_serial_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct asc_port *ascport;
+
+ ascport = asc_of_get_asc_port(pdev);
+ if (!ascport)
+ return -ENODEV;
+
+ ret = asc_init_port(ascport, pdev);
+ if (ret)
+ return ret;
+
+ ret = uart_add_one_port(&asc_uart_driver, &ascport->port);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, &ascport->port);
+
+ return 0;
+}
+
+static int asc_serial_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ return uart_remove_one_port(&asc_uart_driver, port);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int asc_serial_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_suspend_port(&asc_uart_driver, port);
+}
+
+static int asc_serial_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ if (pdev->dev.pins->default_state) {
+ if (pinctrl_select_state(pdev->dev.pins->p,
+ pdev->dev.pins->default_state) < 0)
+ return -EBUSY;
+ }
+
+ return uart_resume_port(&asc_uart_driver, port);
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE
+static void asc_console_putchar(struct uart_port *port, int ch)
+{
+ unsigned int timeout = 1000000;
+
+ /* Wait for upto 1 second in case flow control is stopping us. */
+ while (--timeout && asc_txfifo_is_full(port))
+ udelay(1);
+
+ asc_out(port, ASC_TXBUF, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ */
+
+static void asc_console_write(struct console *co, const char *s, unsigned count)
+{
+ struct uart_port *port = &asc_ports[co->index].port;
+ unsigned long flags;
+ unsigned long timeout = 1000000;
+ int locked = 1;
+ u32 intenable;
+
+ local_irq_save(flags);
+ if (port->sysrq)
+ locked = 0; /* asc_interrupt has already claimed the lock */
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else
+ spin_lock(&port->lock);
+
+ /*
+ * Disable interrupts so we don't get the IRQ line bouncing
+ * up and down while interrupts are disabled.
+ */
+ intenable = asc_in(port, ASC_INTEN);
+ asc_out(port, ASC_INTEN, 0);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+
+ uart_console_write(port, s, count, asc_console_putchar);
+
+ while (--timeout && !asc_txfifo_is_empty(port))
+ udelay(1);
+
+ asc_out(port, ASC_INTEN, intenable);
+
+ if (locked)
+ spin_unlock(&port->lock);
+ local_irq_restore(flags);
+}
+
+static int asc_console_setup(struct console *co, char *options)
+{
+ struct asc_port *ascport;
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= ASC_MAX_PORTS)
+ return -ENODEV;
+
+ ascport = &asc_ports[co->index];
+
+ /*
+ * This driver does not support early console initialization
+ * (use ARM early printk support instead), so we only expect
+ * this to be called during the uart port registration when the
+ * driver gets probed and the port should be mapped at that point.
+ */
+ BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&ascport->port, co, baud, parity, bits, flow);
+}
+
+static struct console asc_console = {
+ .name = ASC_SERIAL_NAME,
+ .device = uart_console_device,
+ .write = asc_console_write,
+ .setup = asc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &asc_uart_driver,
+};
+
+#define ASC_SERIAL_CONSOLE (&asc_console)
+
+#else
+#define ASC_SERIAL_CONSOLE NULL
+#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */
+
+static struct uart_driver asc_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = ASC_SERIAL_NAME,
+ .major = 0,
+ .minor = 0,
+ .nr = ASC_MAX_PORTS,
+ .cons = ASC_SERIAL_CONSOLE,
+};
+
+static const struct dev_pm_ops asc_serial_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume)
+};
+
+static struct platform_driver asc_serial_driver = {
+ .probe = asc_serial_probe,
+ .remove = asc_serial_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &asc_serial_pm_ops,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(asc_match),
+ },
+};
+
+static int __init asc_init(void)
+{
+ int ret;
+ static char banner[] __initdata =
+ KERN_INFO "STMicroelectronics ASC driver initialized\n";
+
+ printk(banner);
+
+ ret = uart_register_driver(&asc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&asc_serial_driver);
+ if (ret)
+ uart_unregister_driver(&asc_uart_driver);
+
+ return ret;
+}
+
+static void __exit asc_exit(void)
+{
+ platform_driver_unregister(&asc_serial_driver);
+ uart_unregister_driver(&asc_uart_driver);
+}
+
+module_init(asc_init);
+module_exit(asc_exit);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("STMicroelectronics (R&D) Limited");
+MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 74c2bf7..ec80e01 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -226,4 +226,7 @@
/* Rocketport EXPRESS/INFINITY */
#define PORT_RP2 102
+/* ST ASC type numbers */
+#define PORT_ASC 103
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.6.5
This patch adds low level debug uart support to stixxxx based SOCs.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
arch/arm/Kconfig.debug | 38 +++++++++++++++++++++++
arch/arm/include/debug/stixxxx.S | 61 ++++++++++++++++++++++++++++++++++++++
arch/arm/mach-stixxxx/board-dt.c | 2 +
3 files changed, 101 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/debug/stixxxx.S
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 1d41908..cc98ef3 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -483,6 +483,16 @@ choice
This option selects UART0 on VIA/Wondermedia System-on-a-chip
devices, including VT8500, WM8505, WM8650 and WM8850.
+ config DEBUG_STIXXXX_UART
+ depends on ARCH_STIXXXX
+ bool "Use StiH415/416 ASC for low-level debug"
+ help
+ Say Y here if you want kernel low-level debugging support
+ on StiH415/416 based platforms like B2000, B2020.
+ It support UART2 and SBC_UART1.
+
+ If unsure, say N.
+
config DEBUG_LL_UART_NONE
bool "No low-level debugging UART"
depends on !ARCH_MULTIPLATFORM
@@ -617,6 +627,33 @@ choice
endchoice
+choice
+ prompt "Low-level debug console UART"
+ depends on DEBUG_LL && DEBUG_STIXXXX_UART
+
+ config STIH41X_DEBUG_ASC2
+ bool "ASC2 UART"
+ help
+ Say Y here if you want kernel low-level debugging support
+ on STiH415/416 based platforms like b2000, which has
+ default UART wired up to ASC2.
+
+ If unsure, say N.
+
+ config STIH41X_DEBUG_SBC_ASC1
+ bool "SBC ASC1 UART"
+ help
+ Say Y here if you want kernel low-level debugging support
+ on STiH415/416 based platforms like b2020. which has
+ default UART wired up to SBC ASC1.
+
+ If unsure, say N.
+
+endchoice
+
+
+
+
config DEBUG_LL_INCLUDE
string
default "debug/bcm2835.S" if DEBUG_BCM2835
@@ -641,6 +678,7 @@ config DEBUG_LL_INCLUDE
DEBUG_MMP_UART3
default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
+ default "debug/stixxxx.S" if DEBUG_STIXXXX_UART
default "debug/sunxi.S" if DEBUG_SUNXI_UART0 || DEBUG_SUNXI_UART1
default "debug/tegra.S" if DEBUG_TEGRA_UART
default "debug/ux500.S" if DEBUG_UX500_UART
diff --git a/arch/arm/include/debug/stixxxx.S b/arch/arm/include/debug/stixxxx.S
new file mode 100644
index 0000000..7bc02a7
--- /dev/null
+++ b/arch/arm/include/debug/stixxxx.S
@@ -0,0 +1,61 @@
+/*
+ * arch/arm/include/debug/stixxxx.S
+ *
+ * Debugging macro include header
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ *
+ * 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.
+ */
+
+#define STIH41X_COMMS_BASE 0xfed00000
+#define STIH41X_ASC2_BASE (STIH41X_COMMS_BASE+0x32000)
+
+#define STIH41X_SBC_LPM_BASE 0xfe400000
+#define STIH41X_SBC_COMMS_BASE (STIH41X_SBC_LPM_BASE + 0x100000)
+#define STIH41X_SBC_ASC1_BASE (STIH41X_SBC_COMMS_BASE + 0x31000)
+
+
+#define VIRT_ADDRESS(x) (x - 0x1000000)
+
+#if IS_ENABLED(CONFIG_STIH41X_DEBUG_ASC2)
+#define DEBUG_LL_UART_BASE STIH41X_ASC2_BASE
+#endif
+
+#if IS_ENABLED(CONFIG_STIH41X_DEBUG_SBC_ASC1)
+#define DEBUG_LL_UART_BASE STIH41X_SBC_ASC1_BASE
+#endif
+
+#ifndef DEBUG_LL_UART_BASE
+#error "DEBUG UART is not Configured"
+#endif
+
+#define ASC_TX_BUF_OFF 0x04
+#define ASC_CTRL_OFF 0x0c
+#define ASC_STA_OFF 0x14
+
+#define ASC_STA_TX_FULL (1<<9)
+#define ASC_STA_TX_EMPTY (1<<1)
+
+
+ .macro addruart, rp, rv, tmp
+ ldr \rp, =DEBUG_LL_UART_BASE @ physical base
+ ldr \rv, =VIRT_ADDRESS(DEBUG_LL_UART_BASE) @ virt base
+ .endm
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx, #ASC_TX_BUF_OFF]
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldr \rd, [\rx, #ASC_STA_OFF]
+ tst \rd, #ASC_STA_TX_FULL
+ bne 1001b
+ .endm
+
+ .macro busyuart,rd,rx
+1001: ldr \rd, [\rx, #ASC_STA_OFF]
+ tst \rd, #ASC_STA_TX_EMPTY
+ beq 1001b
+ .endm
diff --git a/arch/arm/mach-stixxxx/board-dt.c b/arch/arm/mach-stixxxx/board-dt.c
index 6487a12..2c4e427 100644
--- a/arch/arm/mach-stixxxx/board-dt.c
+++ b/arch/arm/mach-stixxxx/board-dt.c
@@ -11,6 +11,7 @@
#include <linux/clocksource.h>
#include <linux/irq.h>
#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/map.h>
#include <asm/mach/arch.h>
#include "smp.h"
@@ -42,6 +43,7 @@ static const char *stih41x_dt_match[] __initdata = {
};
DT_MACHINE_START(STM, "STiH415/416 SoC with Flattened Device Tree")
+ .map_io = debug_ll_io_init,
.init_time = stih41x_timer_init,
.smp = smp_ops(stixxxx_smp_ops),
.dt_compat = stih41x_dt_match,
--
1.7.6.5
B2020 ADI board is reference board for STIH415/416 SOCs, it has 2 x
UART, 4x USB, 1 x Ethernet, 1 x SATA, 1 x PCIe, and 2GB RAM with
standard set-top box IPs.
This patch adds initial support to B2020 with STiH415/416 with SBC_UART1
as console and a heard beat LED.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Stuart Menefy <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
arch/arm/boot/dts/Makefile | 4 ++-
arch/arm/boot/dts/stih415-b2020.dts | 15 ++++++++++++
arch/arm/boot/dts/stih416-b2020.dts | 16 +++++++++++++
arch/arm/boot/dts/stih41x-b2020.dtsi | 42 ++++++++++++++++++++++++++++++++++
4 files changed, 76 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/boot/dts/stih415-b2020.dts
create mode 100644 arch/arm/boot/dts/stih416-b2020.dts
create mode 100644 arch/arm/boot/dts/stih41x-b2020.dtsi
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d4615fd..0d24c95 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -178,7 +178,9 @@ dtb-$(CONFIG_ARCH_SPEAR3XX)+= spear300-evb.dtb \
spear320-hmi.dtb
dtb-$(CONFIG_ARCH_SPEAR6XX)+= spear600-evb.dtb
dtb-$(CONFIG_ARCH_STIXXXX)+= stih415-b2000.dtb \
- stih416-b2000.dtb
+ stih416-b2000.dtb \
+ stih415-b2020.dtb \
+ stih416-b2020.dtb
dtb-$(CONFIG_ARCH_SUNXI) += \
sun4i-a10-cubieboard.dtb \
sun4i-a10-mini-xplus.dtb \
diff --git a/arch/arm/boot/dts/stih415-b2020.dts b/arch/arm/boot/dts/stih415-b2020.dts
new file mode 100644
index 0000000..442b019
--- /dev/null
+++ b/arch/arm/boot/dts/stih415-b2020.dts
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/dts-v1/;
+#include "stih415.dtsi"
+#include "stih41x-b2020.dtsi"
+/ {
+ model = "STiH415 B2020 Board";
+ compatible = "st,stih415", "st,stih415-b2020";
+};
diff --git a/arch/arm/boot/dts/stih416-b2020.dts b/arch/arm/boot/dts/stih416-b2020.dts
new file mode 100644
index 0000000..276f28d
--- /dev/null
+++ b/arch/arm/boot/dts/stih416-b2020.dts
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/dts-v1/;
+#include "stih416.dtsi"
+#include "stih41x-b2020.dtsi"
+/ {
+ model = "STiH416 B2020";
+ compatible = "st,stih416", "st,stih416-b2020";
+
+};
diff --git a/arch/arm/boot/dts/stih41x-b2020.dtsi b/arch/arm/boot/dts/stih41x-b2020.dtsi
new file mode 100644
index 0000000..133e181
--- /dev/null
+++ b/arch/arm/boot/dts/stih41x-b2020.dtsi
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+/ {
+ memory{
+ device_type = "memory";
+ reg = <0x40000000 0x80000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyAS0,115200";
+ linux,stdout-path = &sbc_serial1;
+ };
+
+ aliases {
+ ttyAS0 = &sbc_serial1;
+ };
+ soc {
+ sbc_serial1: serial@fe531000 {
+ status = "okay";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ red {
+ #gpio-cells = <1>;
+ label = "Front Panel LED";
+ gpios = <&PIO4 1>;
+ linux,default-trigger = "heartbeat";
+ };
+ green {
+ gpios = <&PIO4 7>;
+ default-state = "off";
+ };
+ };
+ };
+};
--
1.7.6.5
The STiH416 is advanced HD AVC processor with 3D graphics acceleration
and 1.2-GHz ARM Cortex-A9 SMP CPU.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Stuart Menefy <[email protected]>
CC: Arnd Bergmann <[email protected]>
CC: Linus Walleij <[email protected]>
---
Documentation/arm/stixxxx/stih416-overview.txt | 12 +
arch/arm/boot/dts/stih416-clock.dtsi | 41 ++++
arch/arm/boot/dts/stih416-pinctrl.dtsi | 295 ++++++++++++++++++++++++
arch/arm/boot/dts/stih416.dtsi | 96 ++++++++
arch/arm/mach-stixxxx/Kconfig | 9 +
arch/arm/mach-stixxxx/board-dt.c | 3 +-
6 files changed, 455 insertions(+), 1 deletions(-)
create mode 100644 Documentation/arm/stixxxx/stih416-overview.txt
create mode 100644 arch/arm/boot/dts/stih416-clock.dtsi
create mode 100644 arch/arm/boot/dts/stih416-pinctrl.dtsi
create mode 100644 arch/arm/boot/dts/stih416.dtsi
diff --git a/Documentation/arm/stixxxx/stih416-overview.txt b/Documentation/arm/stixxxx/stih416-overview.txt
new file mode 100644
index 0000000..558444c
--- /dev/null
+++ b/Documentation/arm/stixxxx/stih416-overview.txt
@@ -0,0 +1,12 @@
+ STiH416 Overview
+ ================
+
+Introduction
+------------
+
+ The STiH416 is the next generation of HD, AVC set-top box processors
+ for satellite, cable, terrestrial and IP-STB markets.
+
+ Features
+ - ARM Cortex-A9 1.2 GHz dual core CPU
+ - SATA2x2,USB 2.0x3, PCIe, Gbit Ethernet MACx2
diff --git a/arch/arm/boot/dts/stih416-clock.dtsi b/arch/arm/boot/dts/stih416-clock.dtsi
new file mode 100644
index 0000000..7026bf1
--- /dev/null
+++ b/arch/arm/boot/dts/stih416-clock.dtsi
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics R&D Limited
+ * <[email protected]>
+ *
+ * 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.
+ */
+/ {
+ clocks {
+ /*
+ * Fixed 30MHz oscillator inputs to SoC
+ */
+ CLK_SYSIN: CLK_SYSIN {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <30000000>;
+ clock-output-names = "CLK_SYSIN";
+ };
+
+ /*
+ * ARM Peripheral clock for timers
+ */
+ arm_periph_clk: arm_periph_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <600000000>;
+ };
+
+ /*
+ * Bootloader initialized system infrastructure clock for
+ * serial devices.
+ */
+ CLK_S_ICN_REG_0: clockgenA0@4 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <100000000>;
+ clock-output-names = "CLK_S_ICN_REG_0";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/stih416-pinctrl.dtsi b/arch/arm/boot/dts/stih416-pinctrl.dtsi
new file mode 100644
index 0000000..957b21a
--- /dev/null
+++ b/arch/arm/boot/dts/stih416-pinctrl.dtsi
@@ -0,0 +1,295 @@
+
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+#include "st-pincfg.h"
+/ {
+
+ aliases {
+ gpio0 = &PIO0;
+ gpio1 = &PIO1;
+ gpio2 = &PIO2;
+ gpio3 = &PIO3;
+ gpio4 = &PIO4;
+ gpio5 = &PIO40;
+ gpio6 = &PIO5;
+ gpio7 = &PIO6;
+ gpio8 = &PIO7;
+ gpio9 = &PIO8;
+ gpio10 = &PIO9;
+ gpio11 = &PIO10;
+ gpio12 = &PIO11;
+ gpio13 = &PIO12;
+ gpio14 = &PIO30;
+ gpio15 = &PIO31;
+ gpio16 = &PIO13;
+ gpio17 = &PIO14;
+ gpio18 = &PIO15;
+ gpio19 = &PIO16;
+ gpio20 = &PIO17;
+ gpio21 = &PIO18;
+ gpio22 = &PIO100;
+ gpio23 = &PIO101;
+ gpio24 = &PIO102;
+ gpio25 = &PIO103;
+ gpio26 = &PIO104;
+ gpio27 = &PIO105;
+ gpio28 = &PIO106;
+ gpio29 = &PIO107;
+ };
+
+ soc {
+ pin-controller-sbc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih416-sbc-pinctrl";
+ st,syscfg = <&syscfg_sbc>;
+ ranges = <0 0xfe610000 0x6000>;
+
+ PIO0: gpio@fe610000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO0";
+ };
+ PIO1: gpio@fe611000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO1";
+ };
+ PIO2: gpio@fe612000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO2";
+ };
+ PIO3: gpio@fe613000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO3";
+ };
+ PIO4: gpio@fe614000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO4";
+ };
+ PIO40: gpio@fe615000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x5000 0x100>;
+ st,bank-name = "PIO40";
+ st,retime-pin-mask = <0x7f>;
+ };
+
+ sbc_serial1 {
+ pinctrl_sbc_serial1: sbc_serial1 {
+ st,pins {
+ tx = <&PIO2 6 ALT3 OUT>;
+ rx = <&PIO2 7 ALT3 IN>;
+ };
+ };
+ };
+ };
+
+ pin-controller-front {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih416-front-pinctrl";
+ st,syscfg = <&syscfg_front>;
+ ranges = <0 0xfee00000 0x10000>;
+
+ PIO5: gpio@fee00000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO5";
+ };
+ PIO6: gpio@fee01000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO6";
+ };
+ PIO7: gpio@fee02000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO7";
+ };
+ PIO8: gpio@fee03000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO8";
+ };
+ PIO9: gpio@fee04000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO9";
+ };
+ PIO10: gpio@fee05000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x5000 0x100>;
+ st,bank-name = "PIO10";
+ };
+ PIO11: gpio@fee06000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x6000 0x100>;
+ st,bank-name = "PIO11";
+ };
+ PIO12: gpio@fee07000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x7000 0x100>;
+ st,bank-name = "PIO12";
+ };
+ PIO30: gpio@fee08000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x8000 0x100>;
+ st,bank-name = "PIO30";
+ };
+ PIO31: gpio@fee09000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x9000 0x100>;
+ st,bank-name = "PIO31";
+ };
+ };
+
+ pin-controller-rear {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih416-rear-pinctrl";
+ st,syscfg = <&syscfg_rear>;
+ ranges = <0 0xfe820000 0x6000>;
+
+ PIO13: gpio@fe820000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO13";
+ };
+ PIO14: gpio@fe821000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO14";
+ };
+ PIO15: gpio@fe822000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO15";
+ };
+ PIO16: gpio@fe823000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO16";
+ };
+ PIO17: gpio@fe824000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO17";
+ };
+ PIO18: gpio@fe825000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x5000 0x100>;
+ st,bank-name = "PIO18";
+ st,retime-pin-mask = <0xf>;
+ };
+
+ serial2 {
+ pinctrl_serial2: serial2-0 {
+ st,pins {
+ tx = <&PIO17 4 ALT2 OUT>;
+ rx = <&PIO17 5 ALT2 IN>;
+ output-enable = <&PIO11 3 ALT2 OUT>;
+ };
+ };
+ };
+ };
+
+ pin-controller-fvdp-fe {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih416-fvdp-fe-pinctrl";
+ st,syscfg = <&syscfg_fvdp_fe>;
+ ranges = <0 0xfd6b0000 0x3000>;
+
+ PIO100: gpio@fd6b0000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO100";
+ };
+ PIO101: gpio@fd6b1000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO101";
+ };
+ PIO102: gpio@fd6b2000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO102";
+ };
+ };
+
+ pin-controller-fvdp-lite {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih416-fvdp-lite-pinctrl";
+ st,syscfg = <&syscfg_fvdp_lite>;
+ ranges = <0 0xfd330000 0x5000>;
+
+ PIO103: gpio@fd330000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO103";
+ };
+ PIO104: gpio@fd331000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO104";
+ };
+ PIO105: gpio@fd332000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO105";
+ };
+ PIO106: gpio@fd333000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO106";
+ };
+
+ PIO107: gpio@fd334000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO107";
+ st,retime-pin-mask = <0xf>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi
new file mode 100644
index 0000000..3cecd96
--- /dev/null
+++ b/arch/arm/boot/dts/stih416.dtsi
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 STMicroelectronics Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+#include "stih41x.dtsi"
+#include "stih416-clock.dtsi"
+#include "stih416-pinctrl.dtsi"
+/ {
+ L2: cache-controller {
+ compatible = "arm,pl310-cache";
+ reg = <0xfffe2000 0x1000>;
+ arm,data-latency = <3 3 3>;
+ arm,tag-latency = <2 2 2>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&intc>;
+ ranges;
+ compatible = "simple-bus";
+
+ syscfg_sbc:sbc-syscfg@fe600000{
+ compatible = "st,stih416-sbc-syscfg", "syscon";
+ reg = <0xfe600000 0x1000>;
+ };
+
+ syscfg_front:front-syscfg@fee10000{
+ compatible = "st,stih416-front-syscfg", "syscon";
+ reg = <0xfee10000 0x1000>;
+ };
+
+ syscfg_rear:rear-syscfg@fe830000{
+ compatible = "st,stih416-rear-syscfg", "syscon";
+ reg = <0xfe830000 0x1000>;
+ };
+
+ /* MPE */
+ syscfg_fvdp_fe:fvdp-fe-syscfg@fddf0000{
+ compatible = "st,stih416-fvdp-fe-syscfg", "syscon";
+ reg = <0xfddf0000 0x1000>;
+ };
+
+ syscfg_fvdp_lite:fvdp-lite-syscfg@fd6a0000{
+ compatible = "st,stih416-fvdp-lite-syscfg", "syscon";
+ reg = <0xfd6a0000 0x1000>;
+ };
+
+ syscfg_cpu:cpu-syscfg@fdde0000{
+ compatible = "st,stih416-cpu-syscfg", "syscon";
+ reg = <0xfdde0000 0x1000>;
+ };
+
+ syscfg_compo:compo-syscfg@fd320000{
+ compatible = "st,stih416-compo-syscfg", "syscon";
+ reg = <0xfd320000 0x1000>;
+ };
+
+ syscfg_transport:transport-syscfg@fd690000{
+ compatible = "st,stih416-transport-syscfg", "syscon";
+ reg = <0xfd690000 0x1000>;
+ };
+
+ syscfg_lpm:lpm-syscfg@fe4b5100{
+ compatible = "st,stih416-lpm-syscfg", "syscon";
+ reg = <0xfe4b5100 0x8>;
+ };
+
+ serial2: serial@fed32000{
+ compatible = "st,asc";
+ status = "disabled";
+ reg = <0xfed32000 0x2c>;
+ interrupts = <0 197 0>;
+ clocks = <&CLK_S_ICN_REG_0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_serial2>;
+ };
+
+ /* SBC_UART1 */
+ sbc_serial1: serial@fe531000 {
+ compatible = "st,asc";
+ status = "disabled";
+ reg = <0xfe531000 0x2c>;
+ interrupts = <0 210 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sbc_serial1>;
+ clocks = <&CLK_SYSIN>;
+ };
+ };
+};
diff --git a/arch/arm/mach-stixxxx/Kconfig b/arch/arm/mach-stixxxx/Kconfig
index df0aff2..3e93711 100644
--- a/arch/arm/mach-stixxxx/Kconfig
+++ b/arch/arm/mach-stixxxx/Kconfig
@@ -33,4 +33,13 @@ config SOC_STIH415
and other digital audio/video applications using Flattned Device
Trees.
+config SOC_STIH416
+ bool "STiH416 STMicroelectronics Consumer Electronics family"
+ default y
+ help
+ This enables support for STMicroelectronics Digital Consumer
+ Electronics family StiH416 parts, primarily targetted at set-top-box
+ and other digital audio/video applications using Flattened Device
+ Trees.
+
endif
diff --git a/arch/arm/mach-stixxxx/board-dt.c b/arch/arm/mach-stixxxx/board-dt.c
index 9c5addd..6487a12 100644
--- a/arch/arm/mach-stixxxx/board-dt.c
+++ b/arch/arm/mach-stixxxx/board-dt.c
@@ -37,10 +37,11 @@ static void __init stih41x_timer_init(void)
static const char *stih41x_dt_match[] __initdata = {
"st,stih415",
+ "st,stih416",
NULL
};
-DT_MACHINE_START(STM, "STiH415 SoC with Flattened Device Tree")
+DT_MACHINE_START(STM, "STiH415/416 SoC with Flattened Device Tree")
.init_time = stih41x_timer_init,
.smp = smp_ops(stixxxx_smp_ops),
.dt_compat = stih41x_dt_match,
--
1.7.6.5
This patch add pinctrl support to ST SoCs.
About hardware:
ST Set-Top-Box parts have two blocks called PIO and PIO-mux which handle
pin configurations.
Each multi-function pin is controlled, driven and routed through the PIO
multiplexing block. Each pin supports GPIO functionality (ALT0) and
multiple alternate functions(ALT1 - ALTx) that directly connect the pin
to different hardware blocks. When a pin is in GPIO mode, Output Enable
(OE), Open Drain(OD), and Pull Up (PU) are driven by the related PIO
block. Otherwise the PIO multiplexing block configures these parameters
and retiming the signal.
About driver:
This pinctrl driver manages both PIO and PIO-mux block using pinctrl,
pinconf, pinmux, gpio subsystems. All the pinctrl related config
information can only come from device trees.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Stuart Menefy <[email protected]>
CC: Arnd Bergmann <[email protected]>
CC: Linus Walleij <[email protected]>
---
.../devicetree/bindings/pinctrl/pinctrl-st.txt | 110 ++
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-st.c | 1403 ++++++++++++++++++++
4 files changed, 1520 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
create mode 100644 drivers/pinctrl/pinctrl-st.c
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
new file mode 100644
index 0000000..05bf82a
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
@@ -0,0 +1,110 @@
+*ST pin controller.
+
+Each multi-function pin is controlled, driven and routed through the
+PIO multiplexing block. Each pin supports GPIO functionality (ALT0)
+and multiple alternate functions(ALT1 - ALTx) that directly connect
+the pin to different hardware blocks.
+
+When a pin is in GPIO mode, Output Enable (OE), Open Drain(OD), and
+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)
+- compatible : should be "st,<SOC>-<pio-block>-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,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.
+
+Example:
+ pin-controller-sbc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-sbc-pinctrl";
+ st,syscfg = <&syscfg_sbc>;
+ ranges = <0 0xfe610000 0x5000>;
+ PIO0: gpio@fe610000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO0";
+ };
+ ...
+ pin-functions nodes follow...
+ };
+
+
+Contents of function subnode node:
+----------------------
+Required properties for pin configuration node:
+- st,pins : Child node with list of pins with configuration.
+
+Below is the format of how each pin conf should look like.
+
+<bank offset mux mode rt_type rt_delay rt_clk>
+
+Every PIO is represented with 4-7 parameters depending on retime configuration.
+Each parameter is explained as below.
+
+-bank : Should be bank phandle to which this PIO belongs.
+-offset : Offset in the PIO bank.
+-mux : Should be alternate function number associated this pin.
+ Use same numbers from datasheet.
+-mode :pin configuration is selected from one of the below values.
+ IN
+ IN_PU
+ OUT
+ BIDIR
+ BIDIR_PU
+
+-rt_type Retiming Configuration for the pin.
+ Possible retime configuration are:
+
+ ------- -------------
+ value args
+ ------- -------------
+ NICLK <delay> <clk>
+ ICLK_IO <delay> <clk>
+ BYPASS <delay>
+ DE_IO <delay> <clk>
+ SE_ICLK_IO <delay> <clk>
+ SE_NICLK_IO <delay> <clk>
+
+- delay is retime delay in pico seconds as mentioned in data sheet.
+
+- rt_clk :clk to be use for retime.
+ Possible values are:
+ CLK_A
+ CLK_B
+ CLK_C
+ CLK_D
+
+Example of mmcclk pin which is a bi-direction pull pu with retime config
+as non inverted clock retimed with CLK_B and delay of 0 pico seconds:
+
+pin-controller {
+ ...
+ mmc0 {
+ pinctrl_mmc: mmc {
+ st,pins {
+ mmcclk = <&PIO13 4 ALT4 BIDIR_PU NICLK 0 CLK_B>;
+ ...
+ };
+ };
+ ...
+ };
+};
+
+sdhci0:sdhci@fe810000{
+ ...
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mmc>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..1a147cf 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -169,6 +169,12 @@ config PINCTRL_SUNXI
select PINMUX
select GENERIC_PINCONF
+config PINCTRL_ST
+ bool
+ depends on OF
+ select PINMUX
+ select PINCONF
+
config PINCTRL_TEGRA
bool
select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9bdaeb8..6fc7844 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o
obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/
diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c
new file mode 100644
index 0000000..7effedf
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-st.c
@@ -0,0 +1,1403 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Authors:
+ * Srinivas Kandagatla <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/platform_device.h>
+#include "core.h"
+
+/* PIO Block registers */
+/* PIO output */
+#define REG_PIO_POUT 0x00
+/* Set bits of POUT */
+#define REG_PIO_SET_POUT 0x04
+/* Clear bits of POUT */
+#define REG_PIO_CLR_POUT 0x08
+/* PIO input */
+#define REG_PIO_PIN 0x10
+/* PIO configuration */
+#define REG_PIO_PC(n) (0x20 + (n) * 0x10)
+/* Set bits of PC[2:0] */
+#define REG_PIO_SET_PC(n) (0x24 + (n) * 0x10)
+/* Clear bits of PC[2:0] */
+#define REG_PIO_CLR_PC(n) (0x28 + (n) * 0x10)
+/* PIO input comparison */
+#define REG_PIO_PCOMP 0x50
+/* Set bits of PCOMP */
+#define REG_PIO_SET_PCOMP 0x54
+/* Clear bits of PCOMP */
+#define REG_PIO_CLR_PCOMP 0x58
+/* PIO input comparison mask */
+#define REG_PIO_PMASK 0x60
+/* Set bits of PMASK */
+#define REG_PIO_SET_PMASK 0x64
+/* Clear bits of PMASK */
+#define REG_PIO_CLR_PMASK 0x68
+
+#define ST_GPIO_DIRECTION_BIDIR 0x1
+#define ST_GPIO_DIRECTION_OUT 0x2
+#define ST_GPIO_DIRECTION_IN 0x4
+
+/**
+ * Packed style retime configuration.
+ * There are two registers cfg0 and cfg1 in this style for each bank.
+ * Each field in this register is 8 bit corresponding to 8 pins in the bank.
+ */
+#define RT_P_CFGS_PER_BANK 2
+#define RT_P_CFG0_CLK1NOTCLK0_FIELD(reg) REG_FIELD(reg, 0, 7)
+#define RT_P_CFG0_DELAY_0_FIELD(reg) REG_FIELD(reg, 16, 23)
+#define RT_P_CFG0_DELAY_1_FIELD(reg) REG_FIELD(reg, 24, 31)
+#define RT_P_CFG1_INVERTCLK_FIELD(reg) REG_FIELD(reg, 0, 7)
+#define RT_P_CFG1_RETIME_FIELD(reg) REG_FIELD(reg, 8, 15)
+#define RT_P_CFG1_CLKNOTDATA_FIELD(reg) REG_FIELD(reg, 16, 23)
+#define RT_P_CFG1_DOUBLE_EDGE_FIELD(reg) REG_FIELD(reg, 24, 31)
+
+/**
+ * Dedicated style retime Configuration register
+ * each register is dedicated per pin.
+ */
+#define RT_D_CFGS_PER_BANK 8
+#define RT_D_CFG_CLK_SHIFT 0
+#define RT_D_CFG_CLK_MASK (0x3 << 0)
+#define RT_D_CFG_CLKNOTDATA_SHIFT 2
+#define RT_D_CFG_CLKNOTDATA_MASK BIT(2)
+#define RT_D_CFG_DELAY_SHIFT 3
+#define RT_D_CFG_DELAY_MASK (0xf << 3)
+#define RT_D_CFG_DELAY_INNOTOUT_SHIFT 7
+#define RT_D_CFG_DELAY_INNOTOUT_MASK BIT(7)
+#define RT_D_CFG_DOUBLE_EDGE_SHIFT 8
+#define RT_D_CFG_DOUBLE_EDGE_MASK BIT(8)
+#define RT_D_CFG_INVERTCLK_SHIFT 9
+#define RT_D_CFG_INVERTCLK_MASK BIT(9)
+#define RT_D_CFG_RETIME_SHIFT 10
+#define RT_D_CFG_RETIME_MASK BIT(10)
+
+/*
+ * Pinconf is represented in an opaque unsigned long variable.
+ * Below is the bit allocation details for each possible configuration.
+ * All the bit fields can be encapsulated into four variables
+ * (direction, retime-type, retime-clk, retime-delay)
+ *
+ * +----------------+
+ *[31:28]| reserved-3 |
+ * +----------------+-------------
+ *[27] | oe | |
+ * +----------------+ v
+ *[26] | pu | [Direction ]
+ * +----------------+ ^
+ *[25] | od | |
+ * +----------------+-------------
+ *[24] | reserved-2 |
+ * +----------------+-------------
+ *[23] | retime | |
+ * +----------------+ |
+ *[22] | retime-invclk | |
+ * +----------------+ v
+ *[21] |retime-clknotdat| [Retime-type ]
+ * +----------------+ ^
+ *[20] | retime-de | |
+ * +----------------+-------------
+ *[19:18]| retime-clk |------>[Retime-Clk ]
+ * +----------------+
+ *[17:16]| reserved-1 |
+ * +----------------+
+ *[15..0]| retime-delay |------>[Retime Delay]
+ * +----------------+
+ */
+
+#define ST_PINCONF_UNPACK(conf, param)\
+ ((conf >> ST_PINCONF_ ##param ##_SHIFT) \
+ & ST_PINCONF_ ##param ##_MASK)
+
+#define ST_PINCONF_PACK(conf, val, param) (conf |=\
+ ((val & ST_PINCONF_ ##param ##_MASK) << \
+ ST_PINCONF_ ##param ##_SHIFT))
+
+/* Output enable */
+#define ST_PINCONF_OE_MASK 0x1
+#define ST_PINCONF_OE_SHIFT 27
+#define ST_PINCONF_OE BIT(27)
+#define ST_PINCONF_UNPACK_OE(conf) ST_PINCONF_UNPACK(conf, OE)
+#define ST_PINCONF_PACK_OE(conf) ST_PINCONF_PACK(conf, 1, OE)
+
+/* Pull Up */
+#define ST_PINCONF_PU_MASK 0x1
+#define ST_PINCONF_PU_SHIFT 26
+#define ST_PINCONF_PU BIT(26)
+#define ST_PINCONF_UNPACK_PU(conf) ST_PINCONF_UNPACK(conf, PU)
+#define ST_PINCONF_PACK_PU(conf) ST_PINCONF_PACK(conf, 1, PU)
+
+/* Open Drain */
+#define ST_PINCONF_OD_MASK 0x1
+#define ST_PINCONF_OD_SHIFT 25
+#define ST_PINCONF_OD BIT(25)
+#define ST_PINCONF_UNPACK_OD(conf) ST_PINCONF_UNPACK(conf, OD)
+#define ST_PINCONF_PACK_OD(conf) ST_PINCONF_PACK(conf, 1, OD)
+
+#define ST_PINCONF_RT_MASK 0x1
+#define ST_PINCONF_RT_SHIFT 23
+#define ST_PINCONF_RT BIT(23)
+#define ST_PINCONF_UNPACK_RT(conf) ST_PINCONF_UNPACK(conf, RT)
+#define ST_PINCONF_PACK_RT(conf) ST_PINCONF_PACK(conf, 1, RT)
+
+#define ST_PINCONF_RT_INVERTCLK_MASK 0x1
+#define ST_PINCONF_RT_INVERTCLK_SHIFT 22
+#define ST_PINCONF_RT_INVERTCLK BIT(22)
+#define ST_PINCONF_UNPACK_RT_INVERTCLK(conf) \
+ ST_PINCONF_UNPACK(conf, RT_INVERTCLK)
+#define ST_PINCONF_PACK_RT_INVERTCLK(conf) \
+ ST_PINCONF_PACK(conf, 1, RT_INVERTCLK)
+
+#define ST_PINCONF_RT_CLKNOTDATA_MASK 0x1
+#define ST_PINCONF_RT_CLKNOTDATA_SHIFT 21
+#define ST_PINCONF_RT_CLKNOTDATA BIT(21)
+#define ST_PINCONF_UNPACK_RT_CLKNOTDATA(conf) \
+ ST_PINCONF_UNPACK(conf, RT_CLKNOTDATA)
+#define ST_PINCONF_PACK_RT_CLKNOTDATA(conf) \
+ ST_PINCONF_PACK(conf, 1, RT_CLKNOTDATA)
+
+#define ST_PINCONF_RT_DOUBLE_EDGE_MASK 0x1
+#define ST_PINCONF_RT_DOUBLE_EDGE_SHIFT 20
+#define ST_PINCONF_RT_DOUBLE_EDGE BIT(20)
+#define ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(conf) \
+ ST_PINCONF_UNPACK(conf, RT_DOUBLE_EDGE)
+#define ST_PINCONF_PACK_RT_DOUBLE_EDGE(conf) \
+ ST_PINCONF_PACK(conf, 1, RT_DOUBLE_EDGE)
+
+#define ST_PINCONF_RT_CLK_MASK 0x3
+#define ST_PINCONF_RT_CLK_SHIFT 18
+#define ST_PINCONF_RT_CLK BIT(18)
+#define ST_PINCONF_UNPACK_RT_CLK(conf) ST_PINCONF_UNPACK(conf, RT_CLK)
+#define ST_PINCONF_PACK_RT_CLK(conf, val) ST_PINCONF_PACK(conf, val, RT_CLK)
+
+/* RETIME_DELAY in Pico Secs */
+#define ST_PINCONF_RT_DELAY_MASK 0xffff
+#define ST_PINCONF_RT_DELAY_SHIFT 0
+#define ST_PINCONF_UNPACK_RT_DELAY(conf) ST_PINCONF_UNPACK(conf, RT_DELAY)
+#define ST_PINCONF_PACK_RT_DELAY(conf, val) \
+ ST_PINCONF_PACK(conf, val, RT_DELAY)
+
+#define ST_GPIO_PINS_PER_BANK (8)
+#define OF_GPIO_ARGS_MIN (4)
+#define OF_RT_ARGS_MIN (2)
+
+#define gpio_range_to_bank(chip) \
+ container_of(chip, struct st_gpio_bank, range)
+
+#define gpio_chip_to_bank(chip) \
+ container_of(chip, struct st_gpio_bank, gpio_chip)
+
+
+enum st_retime_style {
+ st_retime_style_none,
+ st_retime_style_packed,
+ st_retime_style_dedicated,
+};
+
+struct st_retime_dedicated {
+ struct regmap_field *rt[ST_GPIO_PINS_PER_BANK];
+};
+
+struct st_retime_packed {
+ struct regmap_field *clk1notclk0;
+ struct regmap_field *delay_0;
+ struct regmap_field *delay_1;
+ struct regmap_field *invertclk;
+ struct regmap_field *retime;
+ struct regmap_field *clknotdata;
+ struct regmap_field *double_edge;
+};
+
+struct st_pio_control {
+ u32 rt_pin_mask;
+ struct regmap_field *alt, *oe, *pu, *od;
+ /* retiming */
+ union {
+ struct st_retime_packed rt_p;
+ struct st_retime_dedicated rt_d;
+ } rt;
+};
+
+struct st_pctl_data {
+ enum st_retime_style rt_style;
+ unsigned int *input_delays;
+ int ninput_delays;
+ unsigned int *output_delays;
+ int noutput_delays;
+ /* register offset information */
+ int alt, oe, pu, od, rt;
+};
+
+struct st_pinconf {
+ int pin;
+ const char *name;
+ unsigned long config;
+ int altfunc;
+};
+
+struct st_pmx_func {
+ const char *name;
+ const char **groups;
+ unsigned ngroups;
+};
+
+struct st_pctl_group {
+ const char *name;
+ unsigned int *pins;
+ unsigned npins;
+ struct st_pinconf *pin_conf;
+};
+
+struct st_gpio_bank {
+ struct gpio_chip gpio_chip;
+ struct pinctrl_gpio_range range;
+ void __iomem *base;
+ struct st_pio_control pc;
+};
+
+struct st_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+ struct st_gpio_bank *banks;
+ int nbanks;
+ struct st_pmx_func *functions;
+ int nfunctions;
+ struct st_pctl_group *groups;
+ int ngroups;
+ struct regmap *regmap;
+ const struct st_pctl_data *data;
+};
+
+/* SOC specific data */
+/* STiH415 data */
+unsigned int stih415_input_delays[] = {0, 500, 1000, 1500};
+unsigned int stih415_output_delays[] = {0, 1000, 2000, 3000};
+
+#define STIH415_PCTRL_COMMON_DATA \
+ .rt_style = st_retime_style_packed, \
+ .input_delays = stih415_input_delays, \
+ .ninput_delays = 4, \
+ .output_delays = stih415_output_delays, \
+ .noutput_delays = 4
+
+static const struct st_pctl_data stih415_sbc_data = {
+ STIH415_PCTRL_COMMON_DATA,
+ .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 16,
+};
+
+static const struct st_pctl_data stih415_front_data = {
+ STIH415_PCTRL_COMMON_DATA,
+ .alt = 0, .oe = 8, .pu = 10, .od = 12, .rt = 16,
+};
+
+static const struct st_pctl_data stih415_rear_data = {
+ STIH415_PCTRL_COMMON_DATA,
+ .alt = 0, .oe = 6, .pu = 8, .od = 10, .rt = 38,
+};
+
+static const struct st_pctl_data stih415_left_data = {
+ STIH415_PCTRL_COMMON_DATA,
+ .alt = 0, .oe = 3, .pu = 4, .od = 5, .rt = 6,
+};
+
+static const struct st_pctl_data stih415_right_data = {
+ STIH415_PCTRL_COMMON_DATA,
+ .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 11,
+};
+
+/* STiH416 data */
+unsigned int stih416_delays[] = {0, 300, 500, 750, 1000, 1250, 1500,
+ 1750, 2000, 2250, 2500, 2750, 3000, 3250 };
+
+static const struct st_pctl_data stih416_data = {
+ .rt_style = st_retime_style_dedicated,
+ .input_delays = stih416_delays,
+ .ninput_delays = 14,
+ .output_delays = stih416_delays,
+ .noutput_delays = 14,
+ .alt = 0, .oe = 40, .pu = 50, .od = 60, .rt = 100,
+};
+
+/* Low level functions.. */
+static inline int st_gpio_bank(int gpio)
+{
+ return gpio/ST_GPIO_PINS_PER_BANK;
+}
+
+static inline int st_gpio_pin(int gpio)
+{
+ return gpio%ST_GPIO_PINS_PER_BANK;
+}
+
+static void st_pinconf_set_config(struct st_pio_control *pc,
+ int pin, unsigned long config)
+{
+ struct regmap_field *output_enable = pc->oe;
+ struct regmap_field *pull_up = pc->pu;
+ struct regmap_field *open_drain = pc->od;
+ unsigned int oe_value, pu_value, od_value;
+ unsigned long mask = BIT(pin);
+
+ regmap_field_read(output_enable, &oe_value);
+ regmap_field_read(pull_up, &pu_value);
+ regmap_field_read(open_drain, &od_value);
+
+ /* Clear old values */
+ oe_value &= ~mask;
+ pu_value &= ~mask;
+ od_value &= ~mask;
+
+ if (config & ST_PINCONF_OE)
+ oe_value |= mask;
+ if (config & ST_PINCONF_PU)
+ pu_value |= mask;
+ if (config & ST_PINCONF_OD)
+ od_value |= mask;
+
+ regmap_field_write(output_enable, oe_value);
+ regmap_field_write(pull_up, pu_value);
+ regmap_field_write(open_drain, od_value);
+}
+
+static void st_pctl_set_function(struct st_pio_control *pc,
+ int pin_id, int function)
+{
+ struct regmap_field *alt = pc->alt;
+ unsigned int val;
+ int pin = st_gpio_pin(pin_id);
+ int offset = pin * 4;
+
+ regmap_field_read(alt, &val);
+ val &= ~(0xf << offset);
+ val |= function << offset;
+ regmap_field_write(alt, val);
+}
+
+static unsigned long st_pinconf_delay_to_bit(unsigned int delay,
+ const struct st_pctl_data *data, unsigned long config)
+{
+ unsigned int *delay_times;
+ int num_delay_times, i, closest_index = -1;
+ unsigned int closest_divergence = UINT_MAX;
+
+ if (ST_PINCONF_UNPACK_OE(config)) {
+ delay_times = data->output_delays;
+ num_delay_times = data->noutput_delays;
+ } else {
+ delay_times = data->input_delays;
+ num_delay_times = data->ninput_delays;
+ }
+
+ for (i = 0; i < num_delay_times; i++) {
+ unsigned int divergence = abs(delay - delay_times[i]);
+
+ if (divergence == 0)
+ return i;
+
+ if (divergence < closest_divergence) {
+ closest_divergence = divergence;
+ closest_index = i;
+ }
+ }
+
+ pr_warn("Attempt to set delay %d, closest available %d\n",
+ delay, delay_times[closest_index]);
+
+ return closest_index;
+}
+
+static unsigned long st_pinconf_bit_to_delay(unsigned int index,
+ const struct st_pctl_data *data, unsigned long output)
+{
+ unsigned int *delay_times;
+ int num_delay_times;
+
+ if (output) {
+ delay_times = data->output_delays;
+ num_delay_times = data->noutput_delays;
+ } else {
+ delay_times = data->input_delays;
+ num_delay_times = data->ninput_delays;
+ }
+
+ if (index < num_delay_times) {
+ return delay_times[index];
+ } else {
+ pr_warn("Delay not found in/out delay list\n");
+ return 0;
+ }
+}
+
+static void st_regmap_field_bit_set_clear_pin(struct regmap_field *field,
+ int enable, int pin)
+{
+ unsigned int val = 0;
+
+ regmap_field_read(field, &val);
+ if (enable)
+ val |= BIT(pin);
+ else
+ val &= ~BIT(pin);
+ regmap_field_write(field, val);
+}
+
+static void st_pinconf_set_retime_packed(struct st_pinctrl *info,
+ struct st_pio_control *pc, unsigned long config, int pin)
+{
+ const struct st_pctl_data *data = info->data;
+ struct st_retime_packed *rt_p = &pc->rt.rt_p;
+ unsigned int delay;
+
+ st_regmap_field_bit_set_clear_pin(rt_p->clk1notclk0,
+ ST_PINCONF_UNPACK_RT_CLK(config), pin);
+
+ st_regmap_field_bit_set_clear_pin(rt_p->clknotdata,
+ ST_PINCONF_UNPACK_RT_CLKNOTDATA(config), pin);
+
+ st_regmap_field_bit_set_clear_pin(rt_p->double_edge,
+ ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config), pin);
+
+ st_regmap_field_bit_set_clear_pin(rt_p->invertclk,
+ ST_PINCONF_UNPACK_RT_INVERTCLK(config), pin);
+
+ st_regmap_field_bit_set_clear_pin(rt_p->retime,
+ ST_PINCONF_UNPACK_RT(config), pin);
+
+ delay = st_pinconf_delay_to_bit(ST_PINCONF_UNPACK_RT_DELAY(config),
+ data, config);
+ /* 2 bit delay, lsb */
+ st_regmap_field_bit_set_clear_pin(rt_p->delay_0, delay & 0x1, pin);
+ /* 2 bit delay, msb */
+ st_regmap_field_bit_set_clear_pin(rt_p->delay_1, delay & 0x2, pin);
+
+}
+
+static void st_pinconf_set_retime_dedicated(struct st_pinctrl *info,
+ struct st_pio_control *pc, unsigned long config, int pin)
+{
+ int input = ST_PINCONF_UNPACK_OE(config) ? 0 : 1;
+ int clk = ST_PINCONF_UNPACK_RT_CLK(config);
+ int clknotdata = ST_PINCONF_UNPACK_RT_CLKNOTDATA(config);
+ int double_edge = ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config);
+ int invertclk = ST_PINCONF_UNPACK_RT_INVERTCLK(config);
+ int retime = ST_PINCONF_UNPACK_RT(config);
+
+ unsigned long delay = st_pinconf_delay_to_bit(
+ ST_PINCONF_UNPACK_RT_DELAY(config),
+ info->data, config);
+ struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
+
+ unsigned long retime_config =
+ ((clk) << RT_D_CFG_CLK_SHIFT) |
+ ((delay) << RT_D_CFG_DELAY_SHIFT) |
+ ((input) << RT_D_CFG_DELAY_INNOTOUT_SHIFT) |
+ ((retime) << RT_D_CFG_RETIME_SHIFT) |
+ ((clknotdata) << RT_D_CFG_CLKNOTDATA_SHIFT) |
+ ((invertclk) << RT_D_CFG_INVERTCLK_SHIFT) |
+ ((double_edge) << RT_D_CFG_DOUBLE_EDGE_SHIFT);
+
+ regmap_field_write(rt_d->rt[pin], retime_config);
+}
+
+static void st_pinconf_get_direction(struct st_pio_control *pc,
+ int pin, unsigned long *config)
+{
+ unsigned int oe_value, pu_value, od_value;
+
+ regmap_field_read(pc->oe, &oe_value);
+ regmap_field_read(pc->pu, &pu_value);
+ regmap_field_read(pc->od, &od_value);
+
+ if (oe_value & BIT(pin))
+ ST_PINCONF_PACK_OE(*config);
+ if (pu_value & BIT(pin))
+ ST_PINCONF_PACK_PU(*config);
+ if (od_value & BIT(pin))
+ ST_PINCONF_PACK_OD(*config);
+
+}
+
+static int st_pinconf_get_retime_packed(struct st_pinctrl *info,
+ struct st_pio_control *pc, int pin, unsigned long *config)
+{
+ const struct st_pctl_data *data = info->data;
+ struct st_retime_packed *rt_p = &pc->rt.rt_p;
+ unsigned int delay_bits, delay, delay0, delay1, val;
+ int output = ST_PINCONF_UNPACK_OE(*config);
+
+ if (!regmap_field_read(rt_p->retime, &val) && (val & BIT(pin)))
+ ST_PINCONF_PACK_RT(*config);
+
+ if (!regmap_field_read(rt_p->clk1notclk0, &val) && (val & BIT(pin)))
+ ST_PINCONF_PACK_RT_CLK(*config, 1);
+
+ if (!regmap_field_read(rt_p->clknotdata, &val) && (val & BIT(pin)))
+ ST_PINCONF_PACK_RT_CLKNOTDATA(*config);
+
+ if (!regmap_field_read(rt_p->double_edge, &val) && (val & BIT(pin)))
+ ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config);
+
+ if (!regmap_field_read(rt_p->invertclk, &val) && (val & BIT(pin)))
+ ST_PINCONF_PACK_RT_INVERTCLK(*config);
+
+ regmap_field_read(rt_p->delay_0, &delay0);
+ regmap_field_read(rt_p->delay_1, &delay1);
+ delay_bits = (((delay1 & BIT(pin)) ? 1 : 0) << 1) |
+ (((delay0 & BIT(pin)) ? 1 : 0));
+ delay = st_pinconf_bit_to_delay(delay_bits, data, output);
+ ST_PINCONF_PACK_RT_DELAY(*config, delay);
+
+ return 0;
+}
+
+static int st_pinconf_get_retime_dedicated(struct st_pinctrl *info,
+ struct st_pio_control *pc, int pin, unsigned long *config)
+{
+ unsigned int value;
+ unsigned long delay_bits, delay, rt_clk;
+ int output = ST_PINCONF_UNPACK_OE(*config);
+ struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
+
+ regmap_field_read(rt_d->rt[pin], &value);
+
+ rt_clk = (value & RT_D_CFG_CLK_MASK) >> RT_D_CFG_CLK_SHIFT;
+ ST_PINCONF_PACK_RT_CLK(*config, rt_clk);
+
+ delay_bits = (value & RT_D_CFG_DELAY_MASK) >> RT_D_CFG_DELAY_SHIFT;
+ delay = st_pinconf_bit_to_delay(delay_bits, info->data, output);
+ ST_PINCONF_PACK_RT_DELAY(*config, delay);
+
+ if (value & RT_D_CFG_CLKNOTDATA_MASK)
+ ST_PINCONF_PACK_RT_CLKNOTDATA(*config);
+
+ if (value & RT_D_CFG_DOUBLE_EDGE_MASK)
+ ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config);
+
+ if (value & RT_D_CFG_INVERTCLK_MASK)
+ ST_PINCONF_PACK_RT_INVERTCLK(*config);
+
+ if (value & RT_D_CFG_RETIME_MASK)
+ ST_PINCONF_PACK_RT(*config);
+
+ return 0;
+}
+
+/* GPIO related functions */
+
+static inline void __st_gpio_set(struct st_gpio_bank *bank,
+ unsigned offset, int value)
+{
+ if (value)
+ writel(BIT(offset), bank->base + REG_PIO_SET_POUT);
+ else
+ writel(BIT(offset), bank->base + REG_PIO_CLR_POUT);
+}
+
+static void st_gpio_direction(struct st_gpio_bank *bank,
+ unsigned int gpio, unsigned int direction)
+{
+ int offset = st_gpio_pin(gpio);
+ int i = 0;
+ /**
+ * There are three configuration registers (PIOn_PC0, PIOn_PC1
+ * and PIOn_PC2) for each port. These are used to configure the
+ * PIO port pins. Each pin can be configured as an input, output,
+ * bidirectional, or alternative function pin. Three bits, one bit
+ * from each of the three registers, configure the corresponding bit of
+ * the port. Valid bit settings is:
+ *
+ * PC2 PC1 PC0 Direction.
+ * 0 0 0 [Input Weak pull-up]
+ * 0 0 or 1 1 [Bidirection]
+ * 0 1 0 [Output]
+ * 1 0 0 [Input]
+ *
+ * PIOn_SET_PC and PIOn_CLR_PC registers are used to set and clear bits
+ * individually.
+ */
+ for (i = 0; i <= 2; i++) {
+ if (direction & BIT(i))
+ writel(BIT(offset), bank->base + REG_PIO_SET_PC(i));
+ else
+ writel(BIT(offset), bank->base + REG_PIO_CLR_PC(i));
+ }
+}
+
+static int st_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void st_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int st_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
+
+ return !!(readl(bank->base + REG_PIO_PIN) & BIT(offset));
+}
+
+static void st_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
+ __st_gpio_set(bank, offset, value);
+}
+
+static int st_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_gpio_direction_input(chip->base + offset);
+
+ return 0;
+}
+
+static int st_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
+
+ __st_gpio_set(bank, offset, value);
+ pinctrl_gpio_direction_output(chip->base + offset);
+
+ return 0;
+}
+
+static int st_gpio_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ if (WARN_ON(gc->of_gpio_n_cells < 1))
+ return -EINVAL;
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] > gc->ngpio)
+ return -EINVAL;
+
+ return gpiospec->args[0];
+}
+
+/* Pinctrl Groups */
+static int st_pctl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->ngroups;
+}
+
+static const char *st_pctl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->groups[selector].name;
+}
+
+static int st_pctl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned selector, const unsigned **pins, unsigned *npins)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ if (selector >= info->ngroups)
+ return -EINVAL;
+
+ *pins = info->groups[selector].pins;
+ *npins = info->groups[selector].npins;
+
+ return 0;
+}
+
+static const inline struct st_pctl_group *st_pctl_find_group_by_name(
+ const struct st_pinctrl *info, const char *name)
+{
+ int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (!strcmp(info->groups[i].name, name))
+ return &info->groups[i];
+ }
+
+ return NULL;
+}
+
+static int st_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np, struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct st_pctl_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num, i;
+
+ grp = st_pctl_find_group_by_name(info, np->name);
+ if (!grp) {
+ dev_err(info->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ map_num = grp->npins + 1;
+ new_map = devm_kzalloc(pctldev->dev,
+ sizeof(*new_map) * map_num, GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ parent = of_get_parent(np);
+ if (!parent) {
+ devm_kfree(pctldev->dev, new_map);
+ return -EINVAL;
+ }
+
+ *map = new_map;
+ *num_maps = map_num;
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* create config map per pin */
+ new_map++;
+ for (i = 0; i < grp->npins; i++) {
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin =
+ pin_get_name(pctldev, grp->pins[i]);
+ new_map[i].data.configs.configs = &grp->pin_conf[i].config;
+ new_map[i].data.configs.num_configs = 1;
+ }
+ dev_info(pctldev->dev, "maps: function %s group %s num %d\n",
+ (*map)->data.mux.function, grp->name, map_num);
+
+ return 0;
+}
+
+static void st_pctl_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+}
+
+static struct pinctrl_ops st_pctlops = {
+ .get_groups_count = st_pctl_get_groups_count,
+ .get_group_pins = st_pctl_get_group_pins,
+ .get_group_name = st_pctl_get_group_name,
+ .dt_node_to_map = st_pctl_dt_node_to_map,
+ .dt_free_map = st_pctl_dt_free_map,
+};
+
+/* Pinmux */
+static int st_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->nfunctions;
+}
+
+const char *st_pmx_get_fname(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->functions[selector].name;
+}
+
+static int st_pmx_get_groups(struct pinctrl_dev *pctldev,
+ unsigned selector, const char * const **grps, unsigned * const ngrps)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ *grps = info->functions[selector].groups;
+ *ngrps = info->functions[selector].ngroups;
+
+ return 0;
+}
+
+static struct st_pio_control *st_get_pio_control(
+ struct pinctrl_dev *pctldev, int pin)
+{
+ struct pinctrl_gpio_range *range =
+ pinctrl_find_gpio_range_from_pin(pctldev, pin);
+ struct st_gpio_bank *bank = gpio_range_to_bank(range);
+
+ return &bank->pc;
+}
+
+static int st_pmx_enable(struct pinctrl_dev *pctldev, unsigned fselector,
+ unsigned group)
+{
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct st_pinconf *conf = info->groups[group].pin_conf;
+ struct st_pio_control *pc;
+ int i;
+
+ for (i = 0; i < info->groups[group].npins; i++) {
+ pc = st_get_pio_control(pctldev, conf[i].pin);
+ st_pctl_set_function(pc, conf[i].pin, conf[i].altfunc);
+ }
+
+ return 0;
+}
+
+static void st_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+}
+
+static int st_pmx_set_gpio_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range, unsigned gpio,
+ bool input)
+{
+ struct st_gpio_bank *bank = gpio_range_to_bank(range);
+ /*
+ * When a PIO bank is used in its primary function mode (altfunc = 0)
+ * Output Enable (OE), Open Drain(OD), and Pull Up (PU)
+ * for the primary PIO functions are driven by the related PIO block
+ */
+ st_pctl_set_function(&bank->pc, gpio, 0);
+ st_gpio_direction(bank, gpio, input ?
+ ST_GPIO_DIRECTION_IN : ST_GPIO_DIRECTION_OUT);
+
+ return 0;
+}
+
+static struct pinmux_ops st_pmxops = {
+ .get_functions_count = st_pmx_get_funcs_count,
+ .get_function_name = st_pmx_get_fname,
+ .get_function_groups = st_pmx_get_groups,
+ .enable = st_pmx_enable,
+ .disable = st_pmx_disable,
+ .gpio_set_direction = st_pmx_set_gpio_direction,
+};
+
+/* Pinconf */
+static void st_pinconf_get_retime(struct st_pinctrl *info,
+ struct st_pio_control *pc, int pin, unsigned long *config)
+{
+ if (info->data->rt_style == st_retime_style_packed)
+ st_pinconf_get_retime_packed(info, pc, pin, config);
+ else if (info->data->rt_style == st_retime_style_dedicated)
+ if ((BIT(pin) & pc->rt_pin_mask))
+ st_pinconf_get_retime_dedicated(info, pc,
+ pin, config);
+}
+
+static void st_pinconf_set_retime(struct st_pinctrl *info,
+ struct st_pio_control *pc, int pin, unsigned long config)
+{
+ if (info->data->rt_style == st_retime_style_packed)
+ st_pinconf_set_retime_packed(info, pc, config, pin);
+ else if (info->data->rt_style == st_retime_style_dedicated)
+ if ((BIT(pin) & pc->rt_pin_mask))
+ st_pinconf_set_retime_dedicated(info, pc,
+ config, pin);
+}
+
+static int st_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long config)
+{
+ int pin = st_gpio_pin(pin_id);
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id);
+
+ st_pinconf_set_config(pc, pin, config);
+ st_pinconf_set_retime(info, pc, pin, config);
+
+ return 0;
+}
+
+static int st_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *config)
+{
+ int pin = st_gpio_pin(pin_id);
+ struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id);
+
+ *config = 0;
+ st_pinconf_get_direction(pc, pin, config);
+ st_pinconf_get_retime(info, pc, pin, config);
+
+ return 0;
+}
+
+static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin_id)
+{
+ unsigned long config;
+ st_pinconf_get(pctldev, pin_id, &config);
+
+ seq_printf(s, "[OE:%ld,PU:%ld,OD:%ld]\n"
+ "\t\t[retime:%ld,invclk:%ld,clknotdat:%ld,"
+ "de:%ld,rt-clk:%ld,rt-delay:%ld]",
+ ST_PINCONF_UNPACK_OE(config),
+ ST_PINCONF_UNPACK_PU(config),
+ ST_PINCONF_UNPACK_OD(config),
+ ST_PINCONF_UNPACK_RT(config),
+ ST_PINCONF_UNPACK_RT_INVERTCLK(config),
+ ST_PINCONF_UNPACK_RT_CLKNOTDATA(config),
+ ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config),
+ ST_PINCONF_UNPACK_RT_CLK(config),
+ ST_PINCONF_UNPACK_RT_DELAY(config));
+}
+
+static struct pinconf_ops st_confops = {
+ .pin_config_get = st_pinconf_get,
+ .pin_config_set = st_pinconf_set,
+ .pin_config_dbg_show = st_pinconf_dbg_show,
+};
+
+static void st_pctl_dt_child_count(struct st_pinctrl *info,
+ struct device_node *np)
+{
+ struct device_node *child;
+ for_each_child_of_node(np, child) {
+ if (of_property_read_bool(child, "gpio-controller")) {
+ info->nbanks++;
+ } else {
+ info->nfunctions++;
+ info->ngroups += of_get_child_count(child);
+ }
+ }
+}
+
+static int st_pctl_dt_setup_retime_packed(struct st_pinctrl *info,
+ int bank, struct st_pio_control *pc)
+{
+ struct device *dev = info->dev;
+ struct regmap *rm = info->regmap;
+ const struct st_pctl_data *data = info->data;
+ /* 2 registers per bank */
+ int reg = (data->rt + bank * RT_P_CFGS_PER_BANK) * 4;
+ struct st_retime_packed *rt_p = &pc->rt.rt_p;
+ /* cfg0 */
+ struct reg_field clk1notclk0 = RT_P_CFG0_CLK1NOTCLK0_FIELD(reg);
+ struct reg_field delay_0 = RT_P_CFG0_DELAY_0_FIELD(reg);
+ struct reg_field delay_1 = RT_P_CFG0_DELAY_1_FIELD(reg);
+ /* cfg1 */
+ struct reg_field invertclk = RT_P_CFG1_INVERTCLK_FIELD(reg + 4);
+ struct reg_field retime = RT_P_CFG1_RETIME_FIELD(reg + 4);
+ struct reg_field clknotdata = RT_P_CFG1_CLKNOTDATA_FIELD(reg + 4);
+ struct reg_field double_edge = RT_P_CFG1_DOUBLE_EDGE_FIELD(reg + 4);
+
+ rt_p->clk1notclk0 = devm_regmap_field_alloc(dev, rm, clk1notclk0);
+ rt_p->delay_0 = devm_regmap_field_alloc(dev, rm, delay_0);
+ rt_p->delay_1 = devm_regmap_field_alloc(dev, rm, delay_1);
+ rt_p->invertclk = devm_regmap_field_alloc(dev, rm, invertclk);
+ rt_p->retime = devm_regmap_field_alloc(dev, rm, retime);
+ rt_p->clknotdata = devm_regmap_field_alloc(dev, rm, clknotdata);
+ rt_p->double_edge = devm_regmap_field_alloc(dev, rm, double_edge);
+
+ if (IS_ERR(rt_p->clk1notclk0) || IS_ERR(rt_p->delay_0) ||
+ IS_ERR(rt_p->delay_1) || IS_ERR(rt_p->invertclk) ||
+ IS_ERR(rt_p->retime) || IS_ERR(rt_p->clknotdata) ||
+ IS_ERR(rt_p->double_edge))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int st_pctl_dt_setup_retime_dedicated(struct st_pinctrl *info,
+ int bank, struct st_pio_control *pc)
+{
+ struct device *dev = info->dev;
+ struct regmap *rm = info->regmap;
+ const struct st_pctl_data *data = info->data;
+ /* 8 registers per bank */
+ int reg_offset = (data->rt + bank * RT_D_CFGS_PER_BANK) * 4;
+ struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
+ unsigned int j;
+ u32 pin_mask = pc->rt_pin_mask;
+
+ for (j = 0; j < RT_D_CFGS_PER_BANK; j++) {
+ if (BIT(j) & pin_mask) {
+ struct reg_field reg = REG_FIELD(reg_offset, 0, 31);
+ rt_d->rt[j] = devm_regmap_field_alloc(dev, rm, reg);
+ if (IS_ERR(rt_d->rt[j]))
+ return -EINVAL;
+ reg_offset += 4;
+ }
+ }
+ return 0;
+}
+
+static int st_pctl_dt_setup_retime(struct st_pinctrl *info,
+ int bank, struct st_pio_control *pc)
+{
+ const struct st_pctl_data *data = info->data;
+ if (data->rt_style == st_retime_style_packed)
+ return st_pctl_dt_setup_retime_packed(info, bank, pc);
+ else if (data->rt_style == st_retime_style_dedicated)
+ return st_pctl_dt_setup_retime_dedicated(info, bank, pc);
+
+ return -EINVAL;
+}
+
+static int st_parse_syscfgs(struct st_pinctrl *info,
+ int bank, struct device_node *np)
+{
+ const struct st_pctl_data *data = info->data;
+ /**
+ * For a given shared register like OE/PU/OD, there are 8 bits per bank
+ * 0:7 belongs to bank0, 8:15 belongs to bank1 ...
+ * So each register is shared across 4 banks.
+ */
+ int lsb = (bank%4) * ST_GPIO_PINS_PER_BANK;
+ int msb = lsb + ST_GPIO_PINS_PER_BANK - 1;
+ struct reg_field alt_reg = REG_FIELD((data->alt + bank) * 4, 0, 31);
+ struct reg_field oe_reg = REG_FIELD((data->oe + bank/4) * 4, lsb, msb);
+ struct reg_field pu_reg = REG_FIELD((data->pu + bank/4) * 4, lsb, msb);
+ struct reg_field od_reg = REG_FIELD((data->od + bank/4) * 4, lsb, msb);
+ struct st_pio_control *pc = &info->banks[bank].pc;
+ struct device *dev = info->dev;
+ struct regmap *regmap = info->regmap;
+
+ pc->alt = devm_regmap_field_alloc(dev, regmap, alt_reg);
+ pc->oe = devm_regmap_field_alloc(dev, regmap, oe_reg);
+ pc->pu = devm_regmap_field_alloc(dev, regmap, pu_reg);
+ pc->od = devm_regmap_field_alloc(dev, regmap, od_reg);
+
+ if (IS_ERR(pc->alt) || IS_ERR(pc->oe) ||
+ IS_ERR(pc->pu) || IS_ERR(pc->od))
+ return -EINVAL;
+
+ /* retime avaiable for all pins by default */
+ pc->rt_pin_mask = 0xff;
+ of_property_read_u32(np, "st,retime-pin-mask", &pc->rt_pin_mask);
+ st_pctl_dt_setup_retime(info, bank, pc);
+
+ return 0;
+}
+
+/*
+ * Each pin is represented in of the below forms.
+ * <bank offset mux direction rt_type rt_delay rt_clk>
+ */
+static int st_pctl_dt_parse_groups(struct device_node *np,
+ struct st_pctl_group *grp, struct st_pinctrl *info, int idx)
+{
+ /* bank pad direction val altfunction */
+ const __be32 *list;
+ struct property *pp;
+ struct st_pinconf *conf;
+ phandle phandle;
+ struct device_node *pins;
+ u32 pin;
+ int i = 0, npins = 0, nr_props;
+
+ pins = of_get_child_by_name(np, "st,pins");
+ if (!pins)
+ return -ENODATA;
+
+ for_each_property_of_node(pins, pp) {
+ /* Skip those we do not want to proceed */
+ if (!strcmp(pp->name, "name"))
+ continue;
+
+ if (pp && (pp->length/sizeof(__be32)) >= OF_GPIO_ARGS_MIN) {
+ npins++;
+ } else {
+ pr_warn("Invalid st,pins in %s node\n", np->name);
+ return -EINVAL;
+ }
+ }
+
+ grp->npins = npins;
+ grp->name = np->name;
+ grp->pins = devm_kzalloc(info->dev, npins * sizeof(u32), GFP_KERNEL);
+ grp->pin_conf = devm_kzalloc(info->dev,
+ npins * sizeof(*conf), GFP_KERNEL);
+
+ if (!grp->pins || !grp->pin_conf)
+ return -ENOMEM;
+
+ /* <bank offset mux direction rt_type rt_delay rt_clk> */
+ for_each_property_of_node(pins, pp) {
+ if (!strcmp(pp->name, "name"))
+ continue;
+ nr_props = pp->length/sizeof(u32);
+ list = pp->value;
+ conf = &grp->pin_conf[i];
+
+ /* bank & offset */
+ phandle = be32_to_cpup(list++);
+ pin = be32_to_cpup(list++);
+ conf->pin = of_get_named_gpio(pins, pp->name, 0);
+ conf->name = pp->name;
+ grp->pins[i] = conf->pin;
+ /* mux */
+ conf->altfunc = be32_to_cpup(list++);
+ conf->config = 0;
+ /* direction */
+ conf->config |= be32_to_cpup(list++);
+ /* rt_type rt_delay rt_clk */
+ if (nr_props >= OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN) {
+ /* rt_type */
+ conf->config |= be32_to_cpup(list++);
+ /* rt_delay */
+ conf->config |= be32_to_cpup(list++);
+ /* rt_clk */
+ if (nr_props > OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN)
+ conf->config |= be32_to_cpup(list++);
+ }
+ i++;
+ }
+ of_node_put(pins);
+
+ return 0;
+}
+
+static int st_pctl_parse_functions(struct device_node *np,
+ struct st_pinctrl *info, u32 index, int *grp_index)
+{
+ struct device_node *child;
+ struct st_pmx_func *func;
+ struct st_pctl_group *grp;
+ int ret, i;
+
+ func = &info->functions[index];
+ func->name = np->name;
+ func->ngroups = of_get_child_count(np);
+ if (func->ngroups <= 0) {
+ dev_err(info->dev, "No groups defined\n");
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(info->dev,
+ func->ngroups * sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(np, child) {
+ func->groups[i] = child->name;
+ grp = &info->groups[*grp_index];
+ *grp_index += 1;
+ ret = st_pctl_dt_parse_groups(child, grp, info, i++);
+ if (ret)
+ return ret;
+ }
+ dev_info(info->dev, "Function[%d\t name:%s,\tgroups:%d]\n",
+ index, func->name, func->ngroups);
+
+ return 0;
+}
+
+static struct gpio_chip st_gpio_template = {
+ .request = st_gpio_request,
+ .free = st_gpio_free,
+ .get = st_gpio_get,
+ .set = st_gpio_set,
+ .direction_input = st_gpio_direction_input,
+ .direction_output = st_gpio_direction_output,
+ .ngpio = ST_GPIO_PINS_PER_BANK,
+ .of_gpio_n_cells = 1,
+ .of_xlate = st_gpio_xlate,
+};
+
+static int st_gpiolib_register_bank(struct st_pinctrl *info,
+ int bank_nr, struct device_node *np)
+{
+ struct st_gpio_bank *bank = &info->banks[bank_nr];
+ 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;
+
+ if (of_address_to_resource(np, 0, &res))
+ return -ENODEV;
+
+ bank->base = devm_request_and_ioremap(dev, &res);
+ if (!bank->base) {
+ dev_err(dev, "Can't get IO memory mapping!\n");
+ return -ENODEV;
+ }
+
+ bank->gpio_chip = st_gpio_template;
+ bank->gpio_chip.base = bank_num * ST_GPIO_PINS_PER_BANK;
+ bank->gpio_chip.ngpio = ST_GPIO_PINS_PER_BANK;
+ bank->gpio_chip.of_node = np;
+
+ of_property_read_string(np, "st,bank-name", &range->name);
+ bank->gpio_chip.label = range->name;
+
+ range->id = bank_num;
+ range->pin_base = range->base = range->id * ST_GPIO_PINS_PER_BANK;
+ range->npins = bank->gpio_chip.ngpio;
+ range->gc = &bank->gpio_chip;
+ err = gpiochip_add(&bank->gpio_chip);
+ if (err) {
+ dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_num);
+ return err;
+ }
+ dev_info(dev, "%s bank added.\n", range->name);
+
+ return 0;
+}
+
+static struct of_device_id st_pctl_of_match[] = {
+ { .compatible = "st,stih415-sbc-pinctrl", .data = &stih415_sbc_data },
+ { .compatible = "st,stih415-rear-pinctrl", .data = &stih415_rear_data },
+ { .compatible = "st,stih415-left-pinctrl", .data = &stih415_left_data },
+ { .compatible = "st,stih415-right-pinctrl",
+ .data = &stih415_right_data },
+ { .compatible = "st,stih415-front-pinctrl",
+ .data = &stih415_front_data },
+ { .compatible = "st,stih416-sbc-pinctrl", .data = &stih416_data},
+ { .compatible = "st,stih416-front-pinctrl", .data = &stih416_data},
+ { .compatible = "st,stih416-rear-pinctrl", .data = &stih416_data},
+ { .compatible = "st,stih416-fvdp-fe-pinctrl", .data = &stih416_data},
+ { .compatible = "st,stih416-fvdp-lite-pinctrl", .data = &stih416_data},
+ { /* sentinel */ }
+};
+
+static int st_pctl_probe_dt(struct platform_device *pdev,
+ struct pinctrl_desc *pctl_desc, struct st_pinctrl *info)
+{
+ int ret = 0;
+ int i = 0, j = 0, k = 0, bank;
+ struct pinctrl_pin_desc *pdesc;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ int grp_index = 0;
+
+ st_pctl_dt_child_count(info, np);
+ if (!info->nbanks) {
+ dev_err(&pdev->dev, "you need atleast one gpio bank\n");
+ return -EINVAL;
+ }
+
+ dev_info(&pdev->dev, "nbanks = %d\n", info->nbanks);
+ dev_info(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_info(&pdev->dev, "ngroups = %d\n", info->ngroups);
+
+ info->functions = devm_kzalloc(&pdev->dev,
+ info->nfunctions * sizeof(*info->functions), GFP_KERNEL);
+
+ info->groups = devm_kzalloc(&pdev->dev,
+ info->ngroups * sizeof(*info->groups) , GFP_KERNEL);
+
+ info->banks = devm_kzalloc(&pdev->dev,
+ info->nbanks * sizeof(*info->banks), GFP_KERNEL);
+
+ if (!info->functions || !info->groups || !info->banks)
+ return -ENOMEM;
+
+ info->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (!info->regmap) {
+ dev_err(info->dev, "No syscfg phandle specified\n");
+ return -ENOMEM;
+ }
+ info->data = of_match_node(st_pctl_of_match, np)->data;
+
+ pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK;
+ pdesc = devm_kzalloc(&pdev->dev,
+ sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL);
+ if (!pdesc)
+ return -ENOMEM;
+
+ pctl_desc->pins = pdesc;
+
+ bank = 0;
+ for_each_child_of_node(np, child) {
+ if (of_property_read_bool(child, "gpio-controller")) {
+ const char *bank_name = NULL;
+ ret = st_gpiolib_register_bank(info, bank, child);
+ if (ret)
+ return ret;
+
+ k = info->banks[bank].range.pin_base;
+ bank_name = info->banks[bank].range.name;
+ for (j = 0; j < ST_GPIO_PINS_PER_BANK; j++, k++) {
+ pdesc->number = k;
+ pdesc->name = kasprintf(GFP_KERNEL, "%s[%d]",
+ bank_name, j);
+ pdesc++;
+ }
+ st_parse_syscfgs(info, bank, child);
+ bank++;
+ } else {
+ ret = st_pctl_parse_functions(child, info,
+ i++, &grp_index);
+ if (ret) {
+ dev_err(&pdev->dev, "No functions found.\n");
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int st_pctl_probe(struct platform_device *pdev)
+{
+ struct st_pinctrl *info;
+ struct pinctrl_desc *pctl_desc;
+ int ret, i;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "device node not found.\n");
+ return -EINVAL;
+ }
+
+ pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
+ if (!pctl_desc)
+ return -ENOMEM;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ platform_set_drvdata(pdev, info);
+ ret = st_pctl_probe_dt(pdev, pctl_desc, info);
+ if (ret)
+ return ret;
+
+ pctl_desc->owner = THIS_MODULE,
+ pctl_desc->pctlops = &st_pctlops,
+ pctl_desc->pmxops = &st_pmxops,
+ pctl_desc->confops = &st_confops,
+ pctl_desc->name = dev_name(&pdev->dev);
+
+ info->pctl = pinctrl_register(pctl_desc, &pdev->dev, info);
+ if (IS_ERR(info->pctl)) {
+ dev_err(&pdev->dev, "Failed pinctrl registration\n");
+ return PTR_ERR(info->pctl);
+ }
+
+ for (i = 0; i < info->nbanks; i++)
+ pinctrl_add_gpio_range(info->pctl, &info->banks[i].range);
+
+ return 0;
+}
+
+static struct platform_driver st_pctl_driver = {
+ .driver = {
+ .name = "st-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(st_pctl_of_match),
+ },
+ .probe = st_pctl_probe,
+};
+
+static int __init st_pctl_init(void)
+{
+ return platform_driver_register(&st_pctl_driver);
+}
+arch_initcall(st_pctl_init);
--
1.7.6.5
From: Stuart Menefy <[email protected]>
This is a simple driver for the global timer module found in the Cortex
A9-MP cores from revision r1p0 onwards. This should be able to perform
the functions of the system timer and the local timer in an SMP system.
The global timer has the following features:
The global timer is a 64-bit incrementing counter with an
auto-incrementing feature. It continues incrementing after sending
interrupts. The global timer is memory mapped in the private memory
region.
The global timer is accessible to all Cortex-A9 processors in the
cluster. Each Cortex-A9 processor has a private 64-bit comparator that
is used to assert a private interrupt when the global timer has reached
the comparator value. All the Cortex-A9 processors in a design use the
banked ID, ID27, for this interrupt. ID27 is sent to the Interrupt
Controller as a Private Peripheral Interrupt. The global timer is
clocked by PERIPHCLK.
Signed-off-by: Stuart Menefy <[email protected]>
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Arnd Bergmann <[email protected]>
CC: Rob Herring <[email protected]>
CC: Linus Walleij <[email protected]>
CC: Will Deacon <[email protected]>
CC: Thomas Gleixner <[email protected]>
---
.../devicetree/bindings/arm/global_timer.txt | 21 ++
drivers/clocksource/Kconfig | 13 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/arm_global_timer.c | 330 ++++++++++++++++++++
4 files changed, 365 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/global_timer.txt
create mode 100644 drivers/clocksource/arm_global_timer.c
diff --git a/Documentation/devicetree/bindings/arm/global_timer.txt b/Documentation/devicetree/bindings/arm/global_timer.txt
new file mode 100644
index 0000000..b64abac
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/global_timer.txt
@@ -0,0 +1,21 @@
+
+* ARM Global Timer
+ Cortex-A9 are often associated with a per-core Global timer.
+
+** Timer node required properties:
+
+- compatible : Should be "arm,cortex-a9-global-timer"
+ Driver supports versions r2p0 and above.
+
+- interrupts : One interrupt to each core
+
+- reg : Specify the base address and the size of the GT timer
+ register window.
+
+Example:
+
+ timer@2c000600 {
+ compatible = "arm,cortex-a9-global-timer";
+ reg = <0x2c000600 0x20>;
+ interrupts = <1 13 0xf01>;
+ };
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index f151c6c..b0c4c42 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -67,6 +67,19 @@ config ARM_ARCH_TIMER
bool
select CLKSRC_OF if OF
+config ARM_GLOBAL_TIMER
+ bool
+ select CLKSRC_OF if OF
+ help
+ This options enables support for the ARM global timer unit
+
+config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ bool
+ depends on ARM_GLOBAL_TIMER
+ default y
+ help
+ Use ARM global timer clock source as sched_clock
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d979c7..b2363cb 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -28,4 +28,5 @@ obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
+obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
new file mode 100644
index 0000000..43cd32a
--- /dev/null
+++ b/drivers/clocksource/arm_global_timer.c
@@ -0,0 +1,330 @@
+/*
+ * drivers/clocksource/arm_global_timer.c
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Stuart Menefy <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include <asm/sched_clock.h>
+#include <asm/mach/irq.h>
+#include <asm/cputype.h>
+#include <asm/localtimer.h>
+
+#define GT_COUNTER0 0x00
+#define GT_COUNTER1 0x04
+
+#define GT_CONTROL 0x08
+#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */
+#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */
+#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */
+#define GT_CONTROL_AUTO_INC BIT(3) /* banked */
+
+#define GT_INT_STATUS 0x0c
+#define GT_INT_STATUS_EVENT_FLAG BIT(0)
+
+#define GT_COMP0 0x10
+#define GT_COMP1 0x14
+#define GT_AUTO_INC 0x18
+
+/*
+ * We are expecting to be clocked by the ARM peripheral clock.
+ *
+ * Note: it is assumed we are using a prescaler value of zero, so this is
+ * the units for all operations.
+ */
+static void __iomem *gt_base;
+static unsigned long gt_clk_rate;
+static int gt_ppi;
+static struct clock_event_device __percpu **gt_evt;
+static DEFINE_PER_CPU(bool, percpu_init_called);
+static DEFINE_PER_CPU(struct clock_event_device, gt_clockevent);
+
+/*
+ * To get the value from the Global Timer Counter register proceed as follows:
+ * 1. Read the upper 32-bit timer counter register
+ * 2. Read the lower 32-bit timer counter register
+ * 3. Read the upper 32-bit timer counter register again. If the value is
+ * different to the 32-bit upper value read previously, go back to step 2.
+ * Otherwise the 64-bit timer counter value is correct.
+ */
+static u64 gt_counter_read(void)
+{
+ u64 counter;
+ u32 lower;
+ u32 upper, old_upper;
+
+ upper = __raw_readl(gt_base + GT_COUNTER1);
+ do {
+ old_upper = upper;
+ lower = __raw_readl(gt_base + GT_COUNTER0);
+ upper = __raw_readl(gt_base + GT_COUNTER1);
+ } while (upper != old_upper);
+
+ counter = upper;
+ counter <<= 32;
+ counter |= lower;
+ return counter;
+}
+
+/**
+ * To ensure that updates to comparator value register do not set the
+ * Interrupt Status Register proceed as follows:
+ * 1. Clear the Comp Enable bit in the Timer Control Register.
+ * 2. Write the lower 32-bit Comparator Value Register.
+ * 3. Write the upper 32-bit Comparator Value Register.
+ * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
+ */
+static void gt_compare_set(unsigned long delta, int periodic)
+{
+ u64 counter = gt_counter_read();
+ unsigned long ctrl = __raw_readl(gt_base + GT_CONTROL);
+
+ counter += delta;
+ ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE);
+
+ __raw_writel(ctrl, gt_base + GT_CONTROL);
+ __raw_writel(lower_32_bits(counter), gt_base + GT_COMP0);
+ __raw_writel(upper_32_bits(counter), gt_base + GT_COMP1);
+
+ if (periodic) {
+ __raw_writel(delta, gt_base + GT_AUTO_INC);
+ ctrl |= GT_CONTROL_AUTO_INC;
+ }
+
+ ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
+ __raw_writel(ctrl, gt_base + GT_CONTROL);
+}
+
+static void gt_clockevent_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ unsigned long ctrl;
+
+ ctrl = __raw_readl(gt_base + GT_CONTROL);
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ ctrl &= ~(GT_CONTROL_AUTO_INC);
+ __raw_writel(ctrl, gt_base + GT_CONTROL);
+ break;
+ /* Can not shut down it as enable bit is not banked */
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ ctrl &= ~(GT_CONTROL_COMP_ENABLE |
+ GT_CONTROL_IRQ_ENABLE | GT_CONTROL_AUTO_INC);
+ __raw_writel(ctrl, gt_base + GT_CONTROL);
+ break;
+ default:
+ break;
+ }
+}
+
+static int gt_clockevent_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ gt_compare_set(evt, 0);
+ return 0;
+}
+
+static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
+
+ if (__raw_readl(gt_base + GT_INT_STATUS) & GT_INT_STATUS_EVENT_FLAG) {
+ /**
+ * ERRATA 740657( Global Timer can send 2 interrupts for
+ * the same event in single-shot mode)
+ * Workaround:
+ * Either disable single-shot mode.
+ * Or
+ * Modify the Interrupt Handler to avoid the
+ * offending sequence. This is achieved by clearing
+ * the Global Timer flag _after_ having incremented
+ * the Comparator register value to a higher value.
+ */
+ if (!(__raw_readl(gt_base + GT_CONTROL) & GT_CONTROL_AUTO_INC))
+ gt_compare_set(ULONG_MAX, 0);
+
+ __raw_writel(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
+
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int __cpuinit gt_clockevents_init(struct clock_event_device *clk)
+{
+ struct clock_event_device **this_cpu_clk;
+ int cpu = smp_processor_id();
+
+ clk->name = "ARM global timer clock event";
+ clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ clk->set_mode = gt_clockevent_set_mode;
+ clk->set_next_event = gt_clockevent_set_next_event;
+ this_cpu_clk = __this_cpu_ptr(gt_evt);
+ *this_cpu_clk = clk;
+ clk->cpumask = cpumask_of(cpu);
+ clk->irq = gt_ppi;
+ clockevents_config_and_register(clk, gt_clk_rate,
+ 1, 0xffffffff);
+ per_cpu(percpu_init_called, cpu) = true;
+ enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
+ return 0;
+}
+
+static void gt_clockevents_stop(struct clock_event_device *clk)
+{
+ gt_clockevent_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ disable_percpu_irq(clk->irq);
+}
+
+static int __cpuinit gt_clockevents_setup(struct clock_event_device *clk)
+{
+ /* Use existing clock_event for boot cpu */
+ if (per_cpu(percpu_init_called, smp_processor_id()))
+ return 0;
+
+ /* already enabled in gt_clocksource_init. */
+ return gt_clockevents_init(clk);
+}
+
+static cycle_t gt_clocksource_read(struct clocksource *cs)
+{
+ return gt_counter_read();
+}
+
+static struct clocksource gt_clocksource = {
+ .name = "ARM global timer clock source",
+ .rating = 300,
+ .read = gt_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+static u32 gt_sched_clock_read(void)
+{
+ if (!gt_base)
+ return 0;
+
+ return gt_counter_read();
+}
+#endif
+
+static void __init gt_clocksource_init(void)
+{
+ __raw_writel(0, gt_base + GT_CONTROL);
+ __raw_writel(0, gt_base + GT_COUNTER0);
+ __raw_writel(0, gt_base + GT_COUNTER1);
+ /* enables timer on all the cores */
+ __raw_writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
+
+#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ setup_sched_clock(gt_sched_clock_read, 32, gt_clk_rate);
+#endif
+ clocksource_register_hz(>_clocksource, gt_clk_rate);
+}
+
+#ifdef CONFIG_LOCAL_TIMERS
+static struct local_timer_ops gt_lt_ops __cpuinitdata = {
+ .setup = gt_clockevents_setup,
+ .stop = gt_clockevents_stop,
+};
+#endif
+
+static void __init global_timer_of_register(struct device_node *np)
+{
+ unsigned int cpu = smp_processor_id();
+ struct clock_event_device *evt = &per_cpu(gt_clockevent, cpu);
+ struct clk *gt_clk;
+ int err = 0;
+
+ /*
+ * In r2p0 the comparators for each processor with the global timer
+ * fire when the timer value is greater than or equal to. In previous
+ * revisions the comparators fired when the timer value was equal to.
+ */
+ if ((read_cpuid_id() & 0xf0000f) < 0x200000) {
+ pr_warn("global-timer: non support for this cpu version.\n");
+ return;
+ }
+
+ gt_ppi = irq_of_parse_and_map(np, 0);
+ if (!gt_ppi) {
+ pr_warn("global-timer: unable to parse irq\n");
+ return;
+ }
+
+ gt_base = of_iomap(np, 0);
+ if (!gt_base) {
+ pr_warn("global-timer: invalid base address\n");
+ return;
+ }
+
+ gt_clk = of_clk_get(np, 0);
+ if (!IS_ERR(gt_clk)) {
+ err = clk_prepare_enable(gt_clk);
+ } else {
+ pr_warn("global-timer: clk not found\n");
+ err = -EINVAL;
+ goto out_unmap;
+ }
+
+ gt_evt = alloc_percpu(struct clock_event_device *);
+ if (!gt_evt) {
+ pr_warn("global-timer: can't allocate memory\n");
+ err = -ENOMEM;
+ goto out_unmap;
+ }
+
+ err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
+ "gt", gt_evt);
+ if (err) {
+ pr_warn("global-timer: can't register interrupt %d (%d)\n",
+ gt_ppi, err);
+ goto out_free;
+ }
+
+ gt_clk_rate = clk_get_rate(gt_clk);
+ gt_clocksource_init();
+ gt_clockevents_init(evt);
+#ifdef CONFIG_LOCAL_TIMERS
+ err = local_timer_register(>_lt_ops);
+ if (err) {
+ pr_warn("global-timer: unable to register local timer.\n");
+ free_percpu_irq(gt_ppi, gt_evt);
+ goto out_free;
+ }
+#endif
+ return;
+
+out_free:
+ free_percpu(gt_evt);
+out_unmap:
+ iounmap(gt_base);
+ WARN(err, "ARM Global timer register failed (%d)\n", err);
+}
+
+/* Only tested on r2p2 and r3p0 */
+CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
+ global_timer_of_register);
--
1.7.6.5
The STiH415 is the next generation of HD, AVC set-top box processors for
satellite, cable, terrestrial and IP-STB markets. It is an ARM Cortex-A9
1.0 GHz, dual-core CPU.
Signed-off-by: Srinivas Kandagatla <[email protected]>
CC: Stephen Gallimore <[email protected]>
CC: Stuart Menefy <[email protected]>
CC: Arnd Bergmann <[email protected]>
CC: Linus Walleij <[email protected]>
---
Documentation/arm/stixxxx/overview.txt | 33 +++
Documentation/arm/stixxxx/stih415-overview.txt | 12 +
MAINTAINERS | 11 +
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/st-pincfg.h | 71 +++++++
arch/arm/boot/dts/stih415-clock.dtsi | 38 ++++
arch/arm/boot/dts/stih415-pinctrl.dtsi | 268 ++++++++++++++++++++++++
arch/arm/boot/dts/stih415.dtsi | 87 ++++++++
arch/arm/boot/dts/stih41x.dtsi | 38 ++++
arch/arm/mach-stixxxx/Kconfig | 36 ++++
arch/arm/mach-stixxxx/Makefile | 2 +
arch/arm/mach-stixxxx/board-dt.c | 47 ++++
arch/arm/mach-stixxxx/headsmp.S | 44 ++++
arch/arm/mach-stixxxx/platsmp.c | 117 ++++++++++
arch/arm/mach-stixxxx/smp.h | 17 ++
16 files changed, 824 insertions(+), 0 deletions(-)
create mode 100644 Documentation/arm/stixxxx/overview.txt
create mode 100644 Documentation/arm/stixxxx/stih415-overview.txt
create mode 100644 arch/arm/boot/dts/st-pincfg.h
create mode 100644 arch/arm/boot/dts/stih415-clock.dtsi
create mode 100644 arch/arm/boot/dts/stih415-pinctrl.dtsi
create mode 100644 arch/arm/boot/dts/stih415.dtsi
create mode 100644 arch/arm/boot/dts/stih41x.dtsi
create mode 100644 arch/arm/mach-stixxxx/Kconfig
create mode 100644 arch/arm/mach-stixxxx/Makefile
create mode 100644 arch/arm/mach-stixxxx/board-dt.c
create mode 100644 arch/arm/mach-stixxxx/headsmp.S
create mode 100644 arch/arm/mach-stixxxx/platsmp.c
create mode 100644 arch/arm/mach-stixxxx/smp.h
diff --git a/Documentation/arm/stixxxx/overview.txt b/Documentation/arm/stixxxx/overview.txt
new file mode 100644
index 0000000..a2f6390
--- /dev/null
+++ b/Documentation/arm/stixxxx/overview.txt
@@ -0,0 +1,33 @@
+ STixxxx ARM Linux Overview
+ ==========================
+
+Introduction
+------------
+
+ The ST Microelectronics Multimedia and Application Processors range of
+ CortexA9 System-on-Chip are supported by the 'STixxxx' platform of
+ ARM Linux. Currently STiH415, STiH416 SOCs are supported with both
+ B2000 and B2020 Reference boards.
+
+
+ configuration
+ -------------
+
+ A generic configuration is provided for both STiH415/416, and can be used as the
+ default by
+ make stih41x_defconfig
+
+ Layout
+ ------
+ All the files for multiple machine families (STiH415, STiH416, and STiG125)
+ are located in the platform code contained in arch/arm/mach-stixxxx
+
+ There is a generic board board-dt.c in the mach folder which support
+ Flattened Device Tree, which means, It works with any compatible board with
+ Device Trees.
+
+
+ Document Author
+ ---------------
+
+ Srinivas Kandagatla <[email protected]>, (c) 2013 ST Microelectronics
diff --git a/Documentation/arm/stixxxx/stih415-overview.txt b/Documentation/arm/stixxxx/stih415-overview.txt
new file mode 100644
index 0000000..1383e33
--- /dev/null
+++ b/Documentation/arm/stixxxx/stih415-overview.txt
@@ -0,0 +1,12 @@
+ STiH415 Overview
+ ================
+
+Introduction
+------------
+
+ The STiH415 is the next generation of HD, AVC set-top box processors
+ for satellite, cable, terrestrial and IP-STB markets.
+
+ Features
+ - ARM Cortex-A9 1.0 GHz, dual-core CPU
+ - SATA2x2,USB 2.0x3, PCIe, Gbit Ethernet MACx2
diff --git a/MAINTAINERS b/MAINTAINERS
index 5be702c..c12ab85 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1201,6 +1201,17 @@ M: Dinh Nguyen <[email protected]>
S: Maintained
F: drivers/clk/socfpga/
+ARM/STIXXXX ARCHITECTURE
+M: Srinivas Kandagatla <[email protected]>
+M: Stuart Menefy <[email protected]>
+L: [email protected] (moderated for non-subscribers)
+L: [email protected]
+W: http://www.stlinux.com
+S: Maintained
+F: arch/arm/mach-stixxxx/
+F: drivers/pinctrl/pinctrl-stixxxx*
+F: drivers/clocksource/arm_global_timer.c
+
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
M: Lennert Buytenhek <[email protected]>
L: [email protected] (moderated for non-subscribers)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 49d993c..790e321 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -989,6 +989,8 @@ source "arch/arm/mach-socfpga/Kconfig"
source "arch/arm/mach-spear/Kconfig"
+source "arch/arm/mach-stixxxx/Kconfig"
+
source "arch/arm/mach-s3c24xx/Kconfig"
if ARCH_S3C64XX
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 1ba358b..c876e0a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -191,6 +191,7 @@ machine-$(CONFIG_ARCH_W90X900) += w90x900
machine-$(CONFIG_FOOTBRIDGE) += footbridge
machine-$(CONFIG_ARCH_SOCFPGA) += socfpga
machine-$(CONFIG_PLAT_SPEAR) += spear
+machine-$(CONFIG_ARCH_STIXXXX) += stixxxx
machine-$(CONFIG_ARCH_VIRT) += virt
machine-$(CONFIG_ARCH_ZYNQ) += zynq
machine-$(CONFIG_ARCH_SUNXI) += sunxi
diff --git a/arch/arm/boot/dts/st-pincfg.h b/arch/arm/boot/dts/st-pincfg.h
new file mode 100644
index 0000000..8c45d85
--- /dev/null
+++ b/arch/arm/boot/dts/st-pincfg.h
@@ -0,0 +1,71 @@
+#ifndef _ST_PINCFG_H_
+#define _ST_PINCFG_H_
+
+/* Alternate functions */
+#define ALT1 1
+#define ALT2 2
+#define ALT3 3
+#define ALT4 4
+#define ALT5 5
+#define ALT6 6
+#define ALT7 7
+
+/* Output enable */
+#define OE (1 << 27)
+/* Pull Up */
+#define PU (1 << 26)
+/* Open Drain */
+#define OD (1 << 26)
+#define RT (1 << 23)
+#define INVERTCLK (1 << 22)
+#define CLKNOTDATA (1 << 21)
+#define DOUBLE_EDGE (1 << 20)
+#define CLK_A (0 << 18)
+#define CLK_B (1 << 18)
+#define CLK_C (2 << 18)
+#define CLK_D (3 << 18)
+
+/* User-frendly defines for Pin Direction */
+ /* oe = 0, pu = 0, od = 0 */
+#define IN (0)
+ /* oe = 0, pu = 1, od = 0 */
+#define IN_PU (PU)
+ /* oe = 1, pu = 0, od = 0 */
+#define OUT (OE)
+ /* oe = 1, pu = 0, od = 1 */
+#define BIDIR (OE | OD)
+ /* oe = 1, pu = 1, od = 1 */
+#define BIDIR_PU (OE | PU | OD)
+
+/* RETIME_TYPE */
+/*
+ * B Mode
+ * Bypass retime with optional delay parameter
+ */
+#define BYPASS (0)
+/*
+ * R0, R1, R0D, R1D modes
+ * single-edge data non inverted clock, retime data with clk
+ */
+#define SE_NICLK_IO (RT)
+/*
+ * RIV0, RIV1, RIV0D, RIV1D modes
+ * single-edge data inverted clock, retime data with clk
+ */
+#define SE_ICLK_IO (RT | INVERTCLK)
+/*
+ * R0E, R1E, R0ED, R1ED modes
+ * double-edge data, retime data with clk
+ */
+#define DE_IO (RT | DOUBLE_EDGE)
+/*
+ * CIV0, CIV1 modes with inverted clock
+ * Retiming the clk pins will park clock & reduce the noise within the core.
+ */
+#define ICLK (RT | CLKNOTDATA | INVERTCLK)
+/*
+ * CLK0, CLK1 modes with non-inverted clock
+ * Retiming the clk pins will park clock & reduce the noise within the core.
+ */
+#define NICLK (RT | CLKNOTDATA)
+#endif /* _ST_PINCFG_H_ */
diff --git a/arch/arm/boot/dts/stih415-clock.dtsi b/arch/arm/boot/dts/stih415-clock.dtsi
new file mode 100644
index 0000000..174c799
--- /dev/null
+++ b/arch/arm/boot/dts/stih415-clock.dtsi
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ *
+ * 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.
+ */
+/ {
+ clocks {
+ /*
+ * Fixed 30MHz oscillator input to SoC
+ */
+ CLK_SYSIN: CLK_SYSIN {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <30000000>;
+ };
+
+ /*
+ * ARM Peripheral clock for timers
+ */
+ arm_periph_clk: arm_periph_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <500000000>;
+ };
+
+ /*
+ * Bootloader initialized system infrastructure clock for
+ * serial devices.
+ */
+ CLKS_ICN_REG_0: CLKS_ICN_REG_0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <100000000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/stih415-pinctrl.dtsi b/arch/arm/boot/dts/stih415-pinctrl.dtsi
new file mode 100644
index 0000000..1d322b2
--- /dev/null
+++ b/arch/arm/boot/dts/stih415-pinctrl.dtsi
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+#include "st-pincfg.h"
+/ {
+
+ aliases {
+ gpio0 = &PIO0;
+ gpio1 = &PIO1;
+ gpio2 = &PIO2;
+ gpio3 = &PIO3;
+ gpio4 = &PIO4;
+ gpio5 = &PIO5;
+ gpio6 = &PIO6;
+ gpio7 = &PIO7;
+ gpio8 = &PIO8;
+ gpio9 = &PIO9;
+ gpio10 = &PIO10;
+ gpio11 = &PIO11;
+ gpio12 = &PIO12;
+ gpio13 = &PIO13;
+ gpio14 = &PIO14;
+ gpio15 = &PIO15;
+ gpio16 = &PIO16;
+ gpio17 = &PIO17;
+ gpio18 = &PIO18;
+ gpio19 = &PIO100;
+ gpio20 = &PIO101;
+ gpio21 = &PIO102;
+ gpio22 = &PIO103;
+ gpio23 = &PIO104;
+ gpio24 = &PIO105;
+ gpio25 = &PIO106;
+ gpio26 = &PIO107;
+ };
+
+ soc {
+ pin-controller-sbc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-sbc-pinctrl";
+ st,syscfg = <&syscfg_sbc>;
+ ranges = <0 0xfe610000 0x5000>;
+
+ PIO0: gpio@fe610000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO0";
+ };
+ PIO1: gpio@fe611000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO1";
+ };
+ PIO2: gpio@fe612000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO2";
+ };
+ PIO3: gpio@fe613000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO3";
+ };
+ PIO4: gpio@fe614000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO4";
+ };
+
+ sbc_serial1 {
+ pinctrl_sbc_serial1:sbc_serial1 {
+ st,pins {
+ tx = <&PIO2 6 ALT3 OUT>;
+ rx = <&PIO2 7 ALT3 IN>;
+ };
+ };
+ };
+ };
+
+ pin-controller-front {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-front-pinctrl";
+ st,syscfg = <&syscfg_front>;
+ ranges = <0 0xfee00000 0x8000>;
+
+ PIO5: gpio@fee00000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO5";
+ };
+ PIO6: gpio@fee01000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO6";
+ };
+ PIO7: gpio@fee02000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO7";
+ };
+ PIO8: gpio@fee03000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO8";
+ };
+ PIO9: gpio@fee04000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO9";
+ };
+ PIO10: gpio@fee05000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x5000 0x100>;
+ st,bank-name = "PIO10";
+ };
+ PIO11: gpio@fee06000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x6000 0x100>;
+ st,bank-name = "PIO11";
+ };
+ PIO12: gpio@fee07000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x7000 0x100>;
+ st,bank-name = "PIO12";
+ };
+ };
+
+ pin-controller-rear {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-rear-pinctrl";
+ st,syscfg = <&syscfg_rear>;
+ ranges = <0 0xfe820000 0x8000>;
+
+ PIO13: gpio@fe820000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO13";
+ };
+ PIO14: gpio@fe821000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO14";
+ };
+ PIO15: gpio@fe822000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO15";
+ };
+ PIO16: gpio@fe823000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO16";
+ };
+ PIO17: gpio@fe824000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO17";
+ };
+ PIO18: gpio@fe825000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x5000 0x100>;
+ st,bank-name = "PIO18";
+ };
+
+ serial2 {
+ pinctrl_serial2: serial2-0 {
+ st,pins {
+ tx = <&PIO17 4 ALT2 OUT>;
+ rx = <&PIO17 5 ALT2 IN>;
+ };
+ };
+ };
+ };
+
+ pin-controller-left {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-left-pinctrl";
+ st,syscfg = <&syscfg_left>;
+ ranges = <0 0xfd6b0000 0x3000>;
+
+ PIO100: gpio@fd6b0000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO100";
+ };
+ PIO101: gpio@fd6b1000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO101";
+ };
+ PIO102: gpio@fd6b2000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO102";
+ };
+ };
+
+ pin-controller-right {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,stih415-right-pinctrl";
+ st,syscfg = <&syscfg_right>;
+ ranges = <0 0xfd330000 0x5000>;
+
+ PIO103: gpio@fd330000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0 0x100>;
+ st,bank-name = "PIO103";
+ };
+ PIO104: gpio@fd331000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x1000 0x100>;
+ st,bank-name = "PIO104";
+ };
+ PIO105: gpio@fd332000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x2000 0x100>;
+ st,bank-name = "PIO105";
+ };
+ PIO106: gpio@fd333000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x3000 0x100>;
+ st,bank-name = "PIO106";
+ };
+ PIO107: gpio@fd334000 {
+ gpio-controller;
+ #gpio-cells = <1>;
+ reg = <0x4000 0x100>;
+ st,bank-name = "PIO107";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi
new file mode 100644
index 0000000..74ab8de
--- /dev/null
+++ b/arch/arm/boot/dts/stih415.dtsi
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Srinivas Kandagatla <[email protected]>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+#include "stih41x.dtsi"
+#include "stih415-clock.dtsi"
+#include "stih415-pinctrl.dtsi"
+/ {
+
+ L2: cache-controller {
+ compatible = "arm,pl310-cache";
+ reg = <0xfffe2000 0x1000>;
+ arm,data-latency = <3 2 2>;
+ arm,tag-latency = <1 1 1>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&intc>;
+ ranges;
+ compatible = "simple-bus";
+
+ syscfg_sbc: sbc-syscfg@fe600000{
+ compatible = "st,stih415-sbc-syscfg", "syscon";
+ reg = <0xfe600000 0xb4>;
+ };
+
+ syscfg_front: front-syscfg@fee10000{
+ compatible = "st,stih415-front-syscfg", "syscon";
+ reg = <0xfee10000 0x194>;
+ };
+
+ syscfg_rear: rear-syscfg@fe830000{
+ compatible = "st,stih415-rear-syscfg", "syscon";
+ reg = <0xfe830000 0x190>;
+ };
+
+ /* MPE syscfgs */
+ syscfg_left: left-syscfg@fd690000{
+ compatible = "st,stih415-left-syscfg", "syscon";
+ reg = <0xfd690000 0x78>;
+ };
+
+ syscfg_right: right-syscfg@fd320000{
+ compatible = "st,stih415-right-syscfg", "syscon";
+ reg = <0xfd320000 0x180>;
+ };
+
+ syscfg_system: system-syscfg@fdde0000 {
+ compatible = "st,stih415-system-syscfg", "syscon";
+ reg = <0xfdde0000 0x15c>;
+ };
+
+ syscfg_lpm: lpm-syscfg@fe4b5100{
+ compatible = "st,stih415-lpm-syscfg", "syscon";
+ reg = <0xfe4b5100 0x08>;
+ };
+
+ serial2: serial@fed32000 {
+ compatible = "st,asc";
+ status = "disabled";
+ reg = <0xfed32000 0x2c>;
+ interrupts = <0 197 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_serial2>;
+ clocks = <&CLKS_ICN_REG_0>;
+ };
+
+ /* SBC comms block ASCs in SASG1 */
+ sbc_serial1: serial@fe531000 {
+ compatible = "st,asc";
+ status = "disabled";
+ reg = <0xfe531000 0x2c>;
+ interrupts = <0 210 0>;
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sbc_serial1>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/stih41x.dtsi b/arch/arm/boot/dts/stih41x.dtsi
new file mode 100644
index 0000000..7321403
--- /dev/null
+++ b/arch/arm/boot/dts/stih41x.dtsi
@@ -0,0 +1,38 @@
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ cpu@1 {
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+ };
+
+ intc: interrupt-controller@fffe1000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ interrupt-controller;
+ reg = <0xfffe1000 0x1000>,
+ <0xfffe0100 0x100>;
+ };
+
+ scu@fffe0000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0xfffe0000 0x1000>;
+ };
+
+ timer@fffe0200 {
+ interrupt-parent = <&intc>;
+ compatible = "arm,cortex-a9-global-timer";
+ reg = <0xfffe0200 0x100>;
+ interrupts = <1 11 0x04>;
+ clocks = <&arm_periph_clk>;
+ };
+};
diff --git a/arch/arm/mach-stixxxx/Kconfig b/arch/arm/mach-stixxxx/Kconfig
new file mode 100644
index 0000000..df0aff2
--- /dev/null
+++ b/arch/arm/mach-stixxxx/Kconfig
@@ -0,0 +1,36 @@
+menuconfig ARCH_STIXXXX
+ bool "STMicroelectronics Consumer Electronics SOCs with Device Trees" if ARCH_MULTI_V7
+ select GENERIC_CLOCKEVENTS
+ select CLKDEV_LOOKUP
+ select ARM_GIC
+ select ARM_GLOBAL_TIMER
+ select PINCTRL
+ select PINCTRL_ST
+ select MFD_SYSCON
+ select MIGHT_HAVE_CACHE_L2X0
+ select HAVE_SMP
+ select HAVE_ARM_SCU if SMP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_ERRATA_720789
+ select ARM_ERRATA_754322
+ select PL310_ERRATA_753970
+ select PL310_ERRATA_769419
+ help
+ Include support for STiH41x SOCs like STiH415/416 using the device tree
+ for discovery
+ More information at Documentation/arm/STiH41x and
+ at Documentation/devicetree
+
+
+if ARCH_STIXXXX
+
+config SOC_STIH415
+ bool "STiH415 STMicroelectronics Consumer Electronics family"
+ default y
+ help
+ This enables support for STMicroelectronics Digital Consumer
+ Electronics family StiH415 parts, primarily targetted at set-top-box
+ and other digital audio/video applications using Flattned Device
+ Trees.
+
+endif
diff --git a/arch/arm/mach-stixxxx/Makefile b/arch/arm/mach-stixxxx/Makefile
new file mode 100644
index 0000000..50bf128
--- /dev/null
+++ b/arch/arm/mach-stixxxx/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_ARCH_STIXXXX) += board-dt.o
diff --git a/arch/arm/mach-stixxxx/board-dt.c b/arch/arm/mach-stixxxx/board-dt.c
new file mode 100644
index 0000000..9c5addd
--- /dev/null
+++ b/arch/arm/mach-stixxxx/board-dt.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author(s): Srinivas Kandagatla <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/irq.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/arch.h>
+
+#include "smp.h"
+
+void __init stih41x_l2x0_init(void)
+{
+ u32 way_size = 0x4;
+ u32 aux_ctrl;
+ /* may be this can be encoded in macros like BIT*() */
+ aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
+ (0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
+ (0x1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
+ (way_size << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+
+ l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
+}
+
+static void __init stih41x_timer_init(void)
+{
+ of_clk_init(NULL);
+ clocksource_of_init();
+ stih41x_l2x0_init();
+}
+
+static const char *stih41x_dt_match[] __initdata = {
+ "st,stih415",
+ NULL
+};
+
+DT_MACHINE_START(STM, "STiH415 SoC with Flattened Device Tree")
+ .init_time = stih41x_timer_init,
+ .smp = smp_ops(stixxxx_smp_ops),
+ .dt_compat = stih41x_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-stixxxx/headsmp.S b/arch/arm/mach-stixxxx/headsmp.S
new file mode 100644
index 0000000..146cf4f
--- /dev/null
+++ b/arch/arm/mach-stixxxx/headsmp.S
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/mach-stixxxx/headsmp.S
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * http://www.st.com
+ *
+ * Cloned from linux/arch/arm/mach-vexpress/headsmp.S
+ *
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+ __INIT
+
+/*
+ * ST specific entry point for secondary CPUs. This provides
+ * a "holding pen" into which all secondary cores are held until we're
+ * ready for them to initialise.
+ */
+ENTRY(stixxxx_secondary_startup)
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, r0, #15
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ cmp r7, r0
+ bne pen
+
+ /*
+ * we've been released from the holding pen: secondary_stack
+ * should now contain the SVC stack for this core
+ */
+ b secondary_startup
+
+1: .long .
+ .long pen_release
diff --git a/arch/arm/mach-stixxxx/platsmp.c b/arch/arm/mach-stixxxx/platsmp.c
new file mode 100644
index 0000000..0df91df
--- /dev/null
+++ b/arch/arm/mach-stixxxx/platsmp.c
@@ -0,0 +1,117 @@
+/*
+ * arch/arm/mach-stixxxx/platsmp.c
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * http://www.st.com
+ *
+ * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+
+#include "smp.h"
+
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit stixxxx_secondary_init(unsigned int cpu)
+{
+ trace_hardirqs_off();
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+int __cpuinit stixxxx_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ /*
+ * set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ write_pen_release(cpu_logical_map(cpu));
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * it to jump to the secondary entrypoint.
+ */
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return pen_release != -1 ? -ENOSYS : 0;
+}
+
+void __init stixxxx_smp_prepare_cpus(unsigned int max_cpus)
+{
+ void __iomem *scu_base = NULL;
+ struct device_node *np = of_find_compatible_node(
+ NULL, NULL, "arm,cortex-a9-scu");
+ if (np) {
+ scu_base = of_iomap(np, 0);
+ scu_enable(scu_base);
+ of_node_put(np);
+ }
+}
+
+struct smp_operations __initdata stixxxx_smp_ops = {
+ .smp_prepare_cpus = stixxxx_smp_prepare_cpus,
+ .smp_secondary_init = stixxxx_secondary_init,
+ .smp_boot_secondary = stixxxx_boot_secondary,
+};
diff --git a/arch/arm/mach-stixxxx/smp.h b/arch/arm/mach-stixxxx/smp.h
new file mode 100644
index 0000000..97c3689
--- /dev/null
+++ b/arch/arm/mach-stixxxx/smp.h
@@ -0,0 +1,17 @@
+/*
+ * arch/arm/mach-stixxxx/smp.h
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * http://www.st.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.
+ */
+
+#ifndef __MACH_STIXXXX_SMP_H
+#define __MACH_STIXXXX_SMP_H
+
+extern struct smp_operations stixxxx_smp_ops;
+
+#endif
--
1.7.6.5
This patch is generated after re-running savedefconfig on top of
multi_v7_defconfig which gets rid of some of the options, as they are
now slected by mach level or other Kconfigs.
The reason to generate this patch is because, it becomes confusing when
some of these options dissapear as part of new SOC addition to
multi_v7_defconfig.
I wanted this to be in a seperate patch, as these options are not
related any of the new SOC support.
Already some discussion at:https://patchwork.kernel.org/patch/2696481
Signed-off-by: Srinivas Kandagatla <[email protected]>
---
arch/arm/configs/multi_v7_defconfig | 29 ++++++++++++-----------------
1 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 2e67a27..f2b4957 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -1,23 +1,20 @@
-CONFIG_EXPERIMENTAL=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_ARCH_MVEBU=y
CONFIG_MACH_ARMADA_370=y
-CONFIG_ARCH_SIRF=y
CONFIG_MACH_ARMADA_XP=y
CONFIG_ARCH_HIGHBANK=y
CONFIG_ARCH_SOCFPGA=y
-CONFIG_ARCH_SUNXI=y
-CONFIG_ARCH_WM8850=y
-# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set
-CONFIG_ARCH_ZYNQ=y
-CONFIG_ARM_ERRATA_754322=y
CONFIG_PLAT_SPEAR=y
CONFIG_ARCH_SPEAR13XX=y
CONFIG_MACH_SPEAR1310=y
CONFIG_MACH_SPEAR1340=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_SIRF=y
+CONFIG_ARCH_WM8850=y
+CONFIG_ARCH_ZYNQ=y
+CONFIG_ARM_ERRATA_754322=y
CONFIG_SMP=y
-CONFIG_ARM_ARCH_TIMER=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
@@ -27,25 +24,25 @@ CONFIG_NEON=y
CONFIG_NET=y
CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
-CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_SMSC911X=y
CONFIG_STMMAC_ETH=y
+CONFIG_KEYBOARD_SPEAR=y
CONFIG_SERIO_AMBAKMI=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_DW=y
-CONFIG_KEYBOARD_SPEAR=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
-CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_SIRFSOC=y
CONFIG_SERIAL_SIRFSOC_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_SERIAL_VT8500_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_IPMI_HANDLER=y
CONFIG_IPMI_SI=y
CONFIG_I2C=y
@@ -54,7 +51,6 @@ CONFIG_I2C_SIRF=y
CONFIG_SPI=y
CONFIG_SPI_PL022=y
CONFIG_SPI_SIRF=y
-CONFIG_GPIO_PL061=y
CONFIG_FB=y
CONFIG_FB_ARMCLCD=y
CONFIG_FB_WM8505=y
@@ -67,7 +63,6 @@ CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_SPEAR=y
-CONFIG_MMC_WMT=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_HIGHBANK_MC=y
@@ -75,9 +70,9 @@ CONFIG_EDAC_HIGHBANK_L2=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_PL031=y
CONFIG_RTC_DRV_VT8500=y
-CONFIG_PWM=y
-CONFIG_PWM_VT8500=y
CONFIG_DMADEVICES=y
-CONFIG_PL330_DMA=y
-CONFIG_SIRF_DMA=y
CONFIG_DW_DMAC=y
+CONFIG_SIRF_DMA=y
+CONFIG_PL330_DMA=y
+CONFIG_PWM=y
+CONFIG_PWM_VT8500=y
--
1.7.6.5
On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
> +static u64 gt_counter_read(void)
> +{
> + u64 counter;
> + u32 lower;
> + u32 upper, old_upper;
> +
> + upper = __raw_readl(gt_base + GT_COUNTER1);
> + do {
> + old_upper = upper;
> + lower = __raw_readl(gt_base + GT_COUNTER0);
> + upper = __raw_readl(gt_base + GT_COUNTER1);
> + } while (upper != old_upper);
> +
> + counter = upper;
> + counter <<= 32;
> + counter |= lower;
> + return counter;
> +}
Please replace __raw_readl etc with the non-__raw variants
throughout the code.
The __raw accessors are not endian safe and are not meant
for device drivers. If you are worried about latency from
extra barriers, use readl_relaxed().
Arnd
On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
> --- a/arch/arm/mach-stixxxx/board-dt.c
> +++ b/arch/arm/mach-stixxxx/board-dt.c
> @@ -11,6 +11,7 @@
> #include <linux/clocksource.h>
> #include <linux/irq.h>
> #include <asm/hardware/cache-l2x0.h>
> +#include <asm/mach/map.h>
> #include <asm/mach/arch.h>
>
> #include "smp.h"
> @@ -42,6 +43,7 @@ static const char *stih41x_dt_match[] __initdata = {
> };
>
> DT_MACHINE_START(STM, "STiH415/416 SoC with Flattened Device Tree")
> + .map_io = debug_ll_io_init,
> .init_time = stih41x_timer_init,
> .smp = smp_ops(stixxxx_smp_ops),
> .dt_compat = stih41x_dt_match,
This part of the patch is no longer needed, we do the same when .map_io
is not set.
Arnd
On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
> This patch adds support to ASC (asynchronous serial controller)
> driver, which is basically a standard serial driver. This IP is common
> across all the ST parts for settop box platforms.
>
> ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
> It support all industry standard baud rates.
>
> Signed-off-by: Srinivas Kandagatla <[email protected]>
> CC: Stephen Gallimore <[email protected]>
> CC: Stuart Menefy <[email protected]>
> CC: Arnd Bergmann <[email protected]>
> CC: Greg Kroah-Hartman <[email protected]>
>From my point of view the series is ready for merging in 3.11,
I only had a few final comments. I would prefer the first three
patches to go through the respective serial/clocksource/pinctrl
trees, since there are no direct build dependencies to the
platform parts.
If there is a reason to take those through arm-soc, I would
insist on getting Acked-by's from the subsystem maintainers.
Arnd
On 20/06/13 19:55, Arnd Bergmann wrote:
> On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
>> --- a/arch/arm/mach-stixxxx/board-dt.c
>> +++ b/arch/arm/mach-stixxxx/board-dt.c
>> @@ -11,6 +11,7 @@
>> #include <linux/clocksource.h>
>> #include <linux/irq.h>
>> #include <asm/hardware/cache-l2x0.h>
>> +#include <asm/mach/map.h>
>> #include <asm/mach/arch.h>
>>
>> #include "smp.h"
>> @@ -42,6 +43,7 @@ static const char *stih41x_dt_match[] __initdata = {
>> };
>>
>> DT_MACHINE_START(STM, "STiH415/416 SoC with Flattened Device Tree")
>> + .map_io = debug_ll_io_init,
>> .init_time = stih41x_timer_init,
>> .smp = smp_ops(stixxxx_smp_ops),
>> .dt_compat = stih41x_dt_match,
>
> This part of the patch is no longer needed, we do the same when .map_io
> is not set.
I can see the commit bc37324e "ARM: mmu: Call debug_ll_io_init if no
map_io function is specified" in arm-soc.git:for-next branch.
That makes sense to take care of this before this patch gets merged. I
will remove this line in next spin hopefully very soon.
Thanks,
srini
>
> Arnd
>
>
On 20/06/13 20:02, Arnd Bergmann wrote:
> On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
>> +static u64 gt_counter_read(void)
>> +{
>> + u64 counter;
>> + u32 lower;
>> + u32 upper, old_upper;
>> +
>> + upper = __raw_readl(gt_base + GT_COUNTER1);
>> + do {
>> + old_upper = upper;
>> + lower = __raw_readl(gt_base + GT_COUNTER0);
>> + upper = __raw_readl(gt_base + GT_COUNTER1);
>> + } while (upper != old_upper);
>> +
>> + counter = upper;
>> + counter <<= 32;
>> + counter |= lower;
>> + return counter;
>> +}
>
> Please replace __raw_readl etc with the non-__raw variants
> throughout the code.
>
> The __raw accessors are not endian safe and are not meant
> for device drivers. If you are worried about latency from
> extra barriers, use readl_relaxed().
Endian safe is good point. I will move to using readl/writel which does
both ordering and byteswapping.
And use relaxed versions in gt_counter_read to reduce the latency.
looks like smp_twd.c too needs this type of change.
Thanks,
srini
>
> Arnd
>
On 20/06/13 20:01, Arnd Bergmann wrote:
> On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
>> This patch adds support to ASC (asynchronous serial controller)
>> driver, which is basically a standard serial driver. This IP is common
>> across all the ST parts for settop box platforms.
>>
>> ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
>> It support all industry standard baud rates.
>>
>> Signed-off-by: Srinivas Kandagatla <[email protected]>
>> CC: Stephen Gallimore <[email protected]>
>> CC: Stuart Menefy <[email protected]>
>> CC: Arnd Bergmann <[email protected]>
>> CC: Greg Kroah-Hartman <[email protected]>
>
>>From my point of view the series is ready for merging in 3.11,
> I only had a few final comments. I would prefer the first three
> patches to go through the respective serial/clocksource/pinctrl
> trees, since there are no direct build dependencies to the
> platform parts.
>
Thankyou Arnd,
There are no build dependencies on serial/clocksource/pinctrl.
I will re-spin SOC support patches 4-10 for arm-soc inclusion and wait
for comments on serial/clocksource/pinctrl driver v3 patches.
Thanks,
srini
> If there is a reason to take those through arm-soc, I would
> insist on getting Acked-by's from the subsystem maintainers.
>
> Arnd
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>
Hi Linus W,
If its not too late can this patch be considered for 3.11 via pinctrl tree?
There is a build dependecy with regmap_field apis pulled by Mark Brown
in regmap repository.
https://git.kernel.org/cgit/linux/kernel/git/broonie/regmap.git/commit/?h=for-next&id=67252287871113deba96adf7e4df1752f3f08688
Thanks,
srini
On 20/06/13 15:05, Srinivas KANDAGATLA wrote:
> This patch add pinctrl support to ST SoCs.
>
> About hardware:
> ST Set-Top-Box parts have two blocks called PIO and PIO-mux which handle
> pin configurations.
>
> Each multi-function pin is controlled, driven and routed through the PIO
> multiplexing block. Each pin supports GPIO functionality (ALT0) and
> multiple alternate functions(ALT1 - ALTx) that directly connect the pin
> to different hardware blocks. When a pin is in GPIO mode, Output Enable
> (OE), Open Drain(OD), and Pull Up (PU) are driven by the related PIO
> block. Otherwise the PIO multiplexing block configures these parameters
> and retiming the signal.
>
> About driver:
> This pinctrl driver manages both PIO and PIO-mux block using pinctrl,
> pinconf, pinmux, gpio subsystems. All the pinctrl related config
> information can only come from device trees.
>
> Signed-off-by: Srinivas Kandagatla <[email protected]>
> CC: Stephen Gallimore <[email protected]>
> CC: Stuart Menefy <[email protected]>
> CC: Arnd Bergmann <[email protected]>
> CC: Linus Walleij <[email protected]>
> ---
> .../devicetree/bindings/pinctrl/pinctrl-st.txt | 110 ++
> drivers/pinctrl/Kconfig | 6 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-st.c | 1403 ++++++++++++++++++++
> 4 files changed, 1520 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
> create mode 100644 drivers/pinctrl/pinctrl-st.c
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
> new file mode 100644
> index 0000000..05bf82a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt
> @@ -0,0 +1,110 @@
> +*ST pin controller.
> +
> +Each multi-function pin is controlled, driven and routed through the
> +PIO multiplexing block. Each pin supports GPIO functionality (ALT0)
> +and multiple alternate functions(ALT1 - ALTx) that directly connect
> +the pin to different hardware blocks.
> +
> +When a pin is in GPIO mode, Output Enable (OE), Open Drain(OD), and
> +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)
> +- compatible : should be "st,<SOC>-<pio-block>-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,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.
> +
> +Example:
> + pin-controller-sbc {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "st,stih415-sbc-pinctrl";
> + st,syscfg = <&syscfg_sbc>;
> + ranges = <0 0xfe610000 0x5000>;
> + PIO0: gpio@fe610000 {
> + gpio-controller;
> + #gpio-cells = <1>;
> + reg = <0 0x100>;
> + st,bank-name = "PIO0";
> + };
> + ...
> + pin-functions nodes follow...
> + };
> +
> +
> +Contents of function subnode node:
> +----------------------
> +Required properties for pin configuration node:
> +- st,pins : Child node with list of pins with configuration.
> +
> +Below is the format of how each pin conf should look like.
> +
> +<bank offset mux mode rt_type rt_delay rt_clk>
> +
> +Every PIO is represented with 4-7 parameters depending on retime configuration.
> +Each parameter is explained as below.
> +
> +-bank : Should be bank phandle to which this PIO belongs.
> +-offset : Offset in the PIO bank.
> +-mux : Should be alternate function number associated this pin.
> + Use same numbers from datasheet.
> +-mode :pin configuration is selected from one of the below values.
> + IN
> + IN_PU
> + OUT
> + BIDIR
> + BIDIR_PU
> +
> +-rt_type Retiming Configuration for the pin.
> + Possible retime configuration are:
> +
> + ------- -------------
> + value args
> + ------- -------------
> + NICLK <delay> <clk>
> + ICLK_IO <delay> <clk>
> + BYPASS <delay>
> + DE_IO <delay> <clk>
> + SE_ICLK_IO <delay> <clk>
> + SE_NICLK_IO <delay> <clk>
> +
> +- delay is retime delay in pico seconds as mentioned in data sheet.
> +
> +- rt_clk :clk to be use for retime.
> + Possible values are:
> + CLK_A
> + CLK_B
> + CLK_C
> + CLK_D
> +
> +Example of mmcclk pin which is a bi-direction pull pu with retime config
> +as non inverted clock retimed with CLK_B and delay of 0 pico seconds:
> +
> +pin-controller {
> + ...
> + mmc0 {
> + pinctrl_mmc: mmc {
> + st,pins {
> + mmcclk = <&PIO13 4 ALT4 BIDIR_PU NICLK 0 CLK_B>;
> + ...
> + };
> + };
> + ...
> + };
> +};
> +
> +sdhci0:sdhci@fe810000{
> + ...
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_mmc>;
> +};
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 8f66924..1a147cf 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -169,6 +169,12 @@ config PINCTRL_SUNXI
> select PINMUX
> select GENERIC_PINCONF
>
> +config PINCTRL_ST
> + bool
> + depends on OF
> + select PINMUX
> + select PINCONF
> +
> config PINCTRL_TEGRA
> bool
> select PINMUX
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 9bdaeb8..6fc7844 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o
> obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
> obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
> obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
> +obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
>
> obj-$(CONFIG_PLAT_ORION) += mvebu/
> obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/
> diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c
> new file mode 100644
> index 0000000..7effedf
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-st.c
> @@ -0,0 +1,1403 @@
> +/*
> + * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
> + * Authors:
> + * Srinivas Kandagatla <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_address.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/platform_device.h>
> +#include "core.h"
> +
> +/* PIO Block registers */
> +/* PIO output */
> +#define REG_PIO_POUT 0x00
> +/* Set bits of POUT */
> +#define REG_PIO_SET_POUT 0x04
> +/* Clear bits of POUT */
> +#define REG_PIO_CLR_POUT 0x08
> +/* PIO input */
> +#define REG_PIO_PIN 0x10
> +/* PIO configuration */
> +#define REG_PIO_PC(n) (0x20 + (n) * 0x10)
> +/* Set bits of PC[2:0] */
> +#define REG_PIO_SET_PC(n) (0x24 + (n) * 0x10)
> +/* Clear bits of PC[2:0] */
> +#define REG_PIO_CLR_PC(n) (0x28 + (n) * 0x10)
> +/* PIO input comparison */
> +#define REG_PIO_PCOMP 0x50
> +/* Set bits of PCOMP */
> +#define REG_PIO_SET_PCOMP 0x54
> +/* Clear bits of PCOMP */
> +#define REG_PIO_CLR_PCOMP 0x58
> +/* PIO input comparison mask */
> +#define REG_PIO_PMASK 0x60
> +/* Set bits of PMASK */
> +#define REG_PIO_SET_PMASK 0x64
> +/* Clear bits of PMASK */
> +#define REG_PIO_CLR_PMASK 0x68
> +
> +#define ST_GPIO_DIRECTION_BIDIR 0x1
> +#define ST_GPIO_DIRECTION_OUT 0x2
> +#define ST_GPIO_DIRECTION_IN 0x4
> +
> +/**
> + * Packed style retime configuration.
> + * There are two registers cfg0 and cfg1 in this style for each bank.
> + * Each field in this register is 8 bit corresponding to 8 pins in the bank.
> + */
> +#define RT_P_CFGS_PER_BANK 2
> +#define RT_P_CFG0_CLK1NOTCLK0_FIELD(reg) REG_FIELD(reg, 0, 7)
> +#define RT_P_CFG0_DELAY_0_FIELD(reg) REG_FIELD(reg, 16, 23)
> +#define RT_P_CFG0_DELAY_1_FIELD(reg) REG_FIELD(reg, 24, 31)
> +#define RT_P_CFG1_INVERTCLK_FIELD(reg) REG_FIELD(reg, 0, 7)
> +#define RT_P_CFG1_RETIME_FIELD(reg) REG_FIELD(reg, 8, 15)
> +#define RT_P_CFG1_CLKNOTDATA_FIELD(reg) REG_FIELD(reg, 16, 23)
> +#define RT_P_CFG1_DOUBLE_EDGE_FIELD(reg) REG_FIELD(reg, 24, 31)
> +
> +/**
> + * Dedicated style retime Configuration register
> + * each register is dedicated per pin.
> + */
> +#define RT_D_CFGS_PER_BANK 8
> +#define RT_D_CFG_CLK_SHIFT 0
> +#define RT_D_CFG_CLK_MASK (0x3 << 0)
> +#define RT_D_CFG_CLKNOTDATA_SHIFT 2
> +#define RT_D_CFG_CLKNOTDATA_MASK BIT(2)
> +#define RT_D_CFG_DELAY_SHIFT 3
> +#define RT_D_CFG_DELAY_MASK (0xf << 3)
> +#define RT_D_CFG_DELAY_INNOTOUT_SHIFT 7
> +#define RT_D_CFG_DELAY_INNOTOUT_MASK BIT(7)
> +#define RT_D_CFG_DOUBLE_EDGE_SHIFT 8
> +#define RT_D_CFG_DOUBLE_EDGE_MASK BIT(8)
> +#define RT_D_CFG_INVERTCLK_SHIFT 9
> +#define RT_D_CFG_INVERTCLK_MASK BIT(9)
> +#define RT_D_CFG_RETIME_SHIFT 10
> +#define RT_D_CFG_RETIME_MASK BIT(10)
> +
> +/*
> + * Pinconf is represented in an opaque unsigned long variable.
> + * Below is the bit allocation details for each possible configuration.
> + * All the bit fields can be encapsulated into four variables
> + * (direction, retime-type, retime-clk, retime-delay)
> + *
> + * +----------------+
> + *[31:28]| reserved-3 |
> + * +----------------+-------------
> + *[27] | oe | |
> + * +----------------+ v
> + *[26] | pu | [Direction ]
> + * +----------------+ ^
> + *[25] | od | |
> + * +----------------+-------------
> + *[24] | reserved-2 |
> + * +----------------+-------------
> + *[23] | retime | |
> + * +----------------+ |
> + *[22] | retime-invclk | |
> + * +----------------+ v
> + *[21] |retime-clknotdat| [Retime-type ]
> + * +----------------+ ^
> + *[20] | retime-de | |
> + * +----------------+-------------
> + *[19:18]| retime-clk |------>[Retime-Clk ]
> + * +----------------+
> + *[17:16]| reserved-1 |
> + * +----------------+
> + *[15..0]| retime-delay |------>[Retime Delay]
> + * +----------------+
> + */
> +
> +#define ST_PINCONF_UNPACK(conf, param)\
> + ((conf >> ST_PINCONF_ ##param ##_SHIFT) \
> + & ST_PINCONF_ ##param ##_MASK)
> +
> +#define ST_PINCONF_PACK(conf, val, param) (conf |=\
> + ((val & ST_PINCONF_ ##param ##_MASK) << \
> + ST_PINCONF_ ##param ##_SHIFT))
> +
> +/* Output enable */
> +#define ST_PINCONF_OE_MASK 0x1
> +#define ST_PINCONF_OE_SHIFT 27
> +#define ST_PINCONF_OE BIT(27)
> +#define ST_PINCONF_UNPACK_OE(conf) ST_PINCONF_UNPACK(conf, OE)
> +#define ST_PINCONF_PACK_OE(conf) ST_PINCONF_PACK(conf, 1, OE)
> +
> +/* Pull Up */
> +#define ST_PINCONF_PU_MASK 0x1
> +#define ST_PINCONF_PU_SHIFT 26
> +#define ST_PINCONF_PU BIT(26)
> +#define ST_PINCONF_UNPACK_PU(conf) ST_PINCONF_UNPACK(conf, PU)
> +#define ST_PINCONF_PACK_PU(conf) ST_PINCONF_PACK(conf, 1, PU)
> +
> +/* Open Drain */
> +#define ST_PINCONF_OD_MASK 0x1
> +#define ST_PINCONF_OD_SHIFT 25
> +#define ST_PINCONF_OD BIT(25)
> +#define ST_PINCONF_UNPACK_OD(conf) ST_PINCONF_UNPACK(conf, OD)
> +#define ST_PINCONF_PACK_OD(conf) ST_PINCONF_PACK(conf, 1, OD)
> +
> +#define ST_PINCONF_RT_MASK 0x1
> +#define ST_PINCONF_RT_SHIFT 23
> +#define ST_PINCONF_RT BIT(23)
> +#define ST_PINCONF_UNPACK_RT(conf) ST_PINCONF_UNPACK(conf, RT)
> +#define ST_PINCONF_PACK_RT(conf) ST_PINCONF_PACK(conf, 1, RT)
> +
> +#define ST_PINCONF_RT_INVERTCLK_MASK 0x1
> +#define ST_PINCONF_RT_INVERTCLK_SHIFT 22
> +#define ST_PINCONF_RT_INVERTCLK BIT(22)
> +#define ST_PINCONF_UNPACK_RT_INVERTCLK(conf) \
> + ST_PINCONF_UNPACK(conf, RT_INVERTCLK)
> +#define ST_PINCONF_PACK_RT_INVERTCLK(conf) \
> + ST_PINCONF_PACK(conf, 1, RT_INVERTCLK)
> +
> +#define ST_PINCONF_RT_CLKNOTDATA_MASK 0x1
> +#define ST_PINCONF_RT_CLKNOTDATA_SHIFT 21
> +#define ST_PINCONF_RT_CLKNOTDATA BIT(21)
> +#define ST_PINCONF_UNPACK_RT_CLKNOTDATA(conf) \
> + ST_PINCONF_UNPACK(conf, RT_CLKNOTDATA)
> +#define ST_PINCONF_PACK_RT_CLKNOTDATA(conf) \
> + ST_PINCONF_PACK(conf, 1, RT_CLKNOTDATA)
> +
> +#define ST_PINCONF_RT_DOUBLE_EDGE_MASK 0x1
> +#define ST_PINCONF_RT_DOUBLE_EDGE_SHIFT 20
> +#define ST_PINCONF_RT_DOUBLE_EDGE BIT(20)
> +#define ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(conf) \
> + ST_PINCONF_UNPACK(conf, RT_DOUBLE_EDGE)
> +#define ST_PINCONF_PACK_RT_DOUBLE_EDGE(conf) \
> + ST_PINCONF_PACK(conf, 1, RT_DOUBLE_EDGE)
> +
> +#define ST_PINCONF_RT_CLK_MASK 0x3
> +#define ST_PINCONF_RT_CLK_SHIFT 18
> +#define ST_PINCONF_RT_CLK BIT(18)
> +#define ST_PINCONF_UNPACK_RT_CLK(conf) ST_PINCONF_UNPACK(conf, RT_CLK)
> +#define ST_PINCONF_PACK_RT_CLK(conf, val) ST_PINCONF_PACK(conf, val, RT_CLK)
> +
> +/* RETIME_DELAY in Pico Secs */
> +#define ST_PINCONF_RT_DELAY_MASK 0xffff
> +#define ST_PINCONF_RT_DELAY_SHIFT 0
> +#define ST_PINCONF_UNPACK_RT_DELAY(conf) ST_PINCONF_UNPACK(conf, RT_DELAY)
> +#define ST_PINCONF_PACK_RT_DELAY(conf, val) \
> + ST_PINCONF_PACK(conf, val, RT_DELAY)
> +
> +#define ST_GPIO_PINS_PER_BANK (8)
> +#define OF_GPIO_ARGS_MIN (4)
> +#define OF_RT_ARGS_MIN (2)
> +
> +#define gpio_range_to_bank(chip) \
> + container_of(chip, struct st_gpio_bank, range)
> +
> +#define gpio_chip_to_bank(chip) \
> + container_of(chip, struct st_gpio_bank, gpio_chip)
> +
> +
> +enum st_retime_style {
> + st_retime_style_none,
> + st_retime_style_packed,
> + st_retime_style_dedicated,
> +};
> +
> +struct st_retime_dedicated {
> + struct regmap_field *rt[ST_GPIO_PINS_PER_BANK];
> +};
> +
> +struct st_retime_packed {
> + struct regmap_field *clk1notclk0;
> + struct regmap_field *delay_0;
> + struct regmap_field *delay_1;
> + struct regmap_field *invertclk;
> + struct regmap_field *retime;
> + struct regmap_field *clknotdata;
> + struct regmap_field *double_edge;
> +};
> +
> +struct st_pio_control {
> + u32 rt_pin_mask;
> + struct regmap_field *alt, *oe, *pu, *od;
> + /* retiming */
> + union {
> + struct st_retime_packed rt_p;
> + struct st_retime_dedicated rt_d;
> + } rt;
> +};
> +
> +struct st_pctl_data {
> + enum st_retime_style rt_style;
> + unsigned int *input_delays;
> + int ninput_delays;
> + unsigned int *output_delays;
> + int noutput_delays;
> + /* register offset information */
> + int alt, oe, pu, od, rt;
> +};
> +
> +struct st_pinconf {
> + int pin;
> + const char *name;
> + unsigned long config;
> + int altfunc;
> +};
> +
> +struct st_pmx_func {
> + const char *name;
> + const char **groups;
> + unsigned ngroups;
> +};
> +
> +struct st_pctl_group {
> + const char *name;
> + unsigned int *pins;
> + unsigned npins;
> + struct st_pinconf *pin_conf;
> +};
> +
> +struct st_gpio_bank {
> + struct gpio_chip gpio_chip;
> + struct pinctrl_gpio_range range;
> + void __iomem *base;
> + struct st_pio_control pc;
> +};
> +
> +struct st_pinctrl {
> + struct device *dev;
> + struct pinctrl_dev *pctl;
> + struct st_gpio_bank *banks;
> + int nbanks;
> + struct st_pmx_func *functions;
> + int nfunctions;
> + struct st_pctl_group *groups;
> + int ngroups;
> + struct regmap *regmap;
> + const struct st_pctl_data *data;
> +};
> +
> +/* SOC specific data */
> +/* STiH415 data */
> +unsigned int stih415_input_delays[] = {0, 500, 1000, 1500};
> +unsigned int stih415_output_delays[] = {0, 1000, 2000, 3000};
> +
> +#define STIH415_PCTRL_COMMON_DATA \
> + .rt_style = st_retime_style_packed, \
> + .input_delays = stih415_input_delays, \
> + .ninput_delays = 4, \
> + .output_delays = stih415_output_delays, \
> + .noutput_delays = 4
> +
> +static const struct st_pctl_data stih415_sbc_data = {
> + STIH415_PCTRL_COMMON_DATA,
> + .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 16,
> +};
> +
> +static const struct st_pctl_data stih415_front_data = {
> + STIH415_PCTRL_COMMON_DATA,
> + .alt = 0, .oe = 8, .pu = 10, .od = 12, .rt = 16,
> +};
> +
> +static const struct st_pctl_data stih415_rear_data = {
> + STIH415_PCTRL_COMMON_DATA,
> + .alt = 0, .oe = 6, .pu = 8, .od = 10, .rt = 38,
> +};
> +
> +static const struct st_pctl_data stih415_left_data = {
> + STIH415_PCTRL_COMMON_DATA,
> + .alt = 0, .oe = 3, .pu = 4, .od = 5, .rt = 6,
> +};
> +
> +static const struct st_pctl_data stih415_right_data = {
> + STIH415_PCTRL_COMMON_DATA,
> + .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 11,
> +};
> +
> +/* STiH416 data */
> +unsigned int stih416_delays[] = {0, 300, 500, 750, 1000, 1250, 1500,
> + 1750, 2000, 2250, 2500, 2750, 3000, 3250 };
> +
> +static const struct st_pctl_data stih416_data = {
> + .rt_style = st_retime_style_dedicated,
> + .input_delays = stih416_delays,
> + .ninput_delays = 14,
> + .output_delays = stih416_delays,
> + .noutput_delays = 14,
> + .alt = 0, .oe = 40, .pu = 50, .od = 60, .rt = 100,
> +};
> +
> +/* Low level functions.. */
> +static inline int st_gpio_bank(int gpio)
> +{
> + return gpio/ST_GPIO_PINS_PER_BANK;
> +}
> +
> +static inline int st_gpio_pin(int gpio)
> +{
> + return gpio%ST_GPIO_PINS_PER_BANK;
> +}
> +
> +static void st_pinconf_set_config(struct st_pio_control *pc,
> + int pin, unsigned long config)
> +{
> + struct regmap_field *output_enable = pc->oe;
> + struct regmap_field *pull_up = pc->pu;
> + struct regmap_field *open_drain = pc->od;
> + unsigned int oe_value, pu_value, od_value;
> + unsigned long mask = BIT(pin);
> +
> + regmap_field_read(output_enable, &oe_value);
> + regmap_field_read(pull_up, &pu_value);
> + regmap_field_read(open_drain, &od_value);
> +
> + /* Clear old values */
> + oe_value &= ~mask;
> + pu_value &= ~mask;
> + od_value &= ~mask;
> +
> + if (config & ST_PINCONF_OE)
> + oe_value |= mask;
> + if (config & ST_PINCONF_PU)
> + pu_value |= mask;
> + if (config & ST_PINCONF_OD)
> + od_value |= mask;
> +
> + regmap_field_write(output_enable, oe_value);
> + regmap_field_write(pull_up, pu_value);
> + regmap_field_write(open_drain, od_value);
> +}
> +
> +static void st_pctl_set_function(struct st_pio_control *pc,
> + int pin_id, int function)
> +{
> + struct regmap_field *alt = pc->alt;
> + unsigned int val;
> + int pin = st_gpio_pin(pin_id);
> + int offset = pin * 4;
> +
> + regmap_field_read(alt, &val);
> + val &= ~(0xf << offset);
> + val |= function << offset;
> + regmap_field_write(alt, val);
> +}
> +
> +static unsigned long st_pinconf_delay_to_bit(unsigned int delay,
> + const struct st_pctl_data *data, unsigned long config)
> +{
> + unsigned int *delay_times;
> + int num_delay_times, i, closest_index = -1;
> + unsigned int closest_divergence = UINT_MAX;
> +
> + if (ST_PINCONF_UNPACK_OE(config)) {
> + delay_times = data->output_delays;
> + num_delay_times = data->noutput_delays;
> + } else {
> + delay_times = data->input_delays;
> + num_delay_times = data->ninput_delays;
> + }
> +
> + for (i = 0; i < num_delay_times; i++) {
> + unsigned int divergence = abs(delay - delay_times[i]);
> +
> + if (divergence == 0)
> + return i;
> +
> + if (divergence < closest_divergence) {
> + closest_divergence = divergence;
> + closest_index = i;
> + }
> + }
> +
> + pr_warn("Attempt to set delay %d, closest available %d\n",
> + delay, delay_times[closest_index]);
> +
> + return closest_index;
> +}
> +
> +static unsigned long st_pinconf_bit_to_delay(unsigned int index,
> + const struct st_pctl_data *data, unsigned long output)
> +{
> + unsigned int *delay_times;
> + int num_delay_times;
> +
> + if (output) {
> + delay_times = data->output_delays;
> + num_delay_times = data->noutput_delays;
> + } else {
> + delay_times = data->input_delays;
> + num_delay_times = data->ninput_delays;
> + }
> +
> + if (index < num_delay_times) {
> + return delay_times[index];
> + } else {
> + pr_warn("Delay not found in/out delay list\n");
> + return 0;
> + }
> +}
> +
> +static void st_regmap_field_bit_set_clear_pin(struct regmap_field *field,
> + int enable, int pin)
> +{
> + unsigned int val = 0;
> +
> + regmap_field_read(field, &val);
> + if (enable)
> + val |= BIT(pin);
> + else
> + val &= ~BIT(pin);
> + regmap_field_write(field, val);
> +}
> +
> +static void st_pinconf_set_retime_packed(struct st_pinctrl *info,
> + struct st_pio_control *pc, unsigned long config, int pin)
> +{
> + const struct st_pctl_data *data = info->data;
> + struct st_retime_packed *rt_p = &pc->rt.rt_p;
> + unsigned int delay;
> +
> + st_regmap_field_bit_set_clear_pin(rt_p->clk1notclk0,
> + ST_PINCONF_UNPACK_RT_CLK(config), pin);
> +
> + st_regmap_field_bit_set_clear_pin(rt_p->clknotdata,
> + ST_PINCONF_UNPACK_RT_CLKNOTDATA(config), pin);
> +
> + st_regmap_field_bit_set_clear_pin(rt_p->double_edge,
> + ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config), pin);
> +
> + st_regmap_field_bit_set_clear_pin(rt_p->invertclk,
> + ST_PINCONF_UNPACK_RT_INVERTCLK(config), pin);
> +
> + st_regmap_field_bit_set_clear_pin(rt_p->retime,
> + ST_PINCONF_UNPACK_RT(config), pin);
> +
> + delay = st_pinconf_delay_to_bit(ST_PINCONF_UNPACK_RT_DELAY(config),
> + data, config);
> + /* 2 bit delay, lsb */
> + st_regmap_field_bit_set_clear_pin(rt_p->delay_0, delay & 0x1, pin);
> + /* 2 bit delay, msb */
> + st_regmap_field_bit_set_clear_pin(rt_p->delay_1, delay & 0x2, pin);
> +
> +}
> +
> +static void st_pinconf_set_retime_dedicated(struct st_pinctrl *info,
> + struct st_pio_control *pc, unsigned long config, int pin)
> +{
> + int input = ST_PINCONF_UNPACK_OE(config) ? 0 : 1;
> + int clk = ST_PINCONF_UNPACK_RT_CLK(config);
> + int clknotdata = ST_PINCONF_UNPACK_RT_CLKNOTDATA(config);
> + int double_edge = ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config);
> + int invertclk = ST_PINCONF_UNPACK_RT_INVERTCLK(config);
> + int retime = ST_PINCONF_UNPACK_RT(config);
> +
> + unsigned long delay = st_pinconf_delay_to_bit(
> + ST_PINCONF_UNPACK_RT_DELAY(config),
> + info->data, config);
> + struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
> +
> + unsigned long retime_config =
> + ((clk) << RT_D_CFG_CLK_SHIFT) |
> + ((delay) << RT_D_CFG_DELAY_SHIFT) |
> + ((input) << RT_D_CFG_DELAY_INNOTOUT_SHIFT) |
> + ((retime) << RT_D_CFG_RETIME_SHIFT) |
> + ((clknotdata) << RT_D_CFG_CLKNOTDATA_SHIFT) |
> + ((invertclk) << RT_D_CFG_INVERTCLK_SHIFT) |
> + ((double_edge) << RT_D_CFG_DOUBLE_EDGE_SHIFT);
> +
> + regmap_field_write(rt_d->rt[pin], retime_config);
> +}
> +
> +static void st_pinconf_get_direction(struct st_pio_control *pc,
> + int pin, unsigned long *config)
> +{
> + unsigned int oe_value, pu_value, od_value;
> +
> + regmap_field_read(pc->oe, &oe_value);
> + regmap_field_read(pc->pu, &pu_value);
> + regmap_field_read(pc->od, &od_value);
> +
> + if (oe_value & BIT(pin))
> + ST_PINCONF_PACK_OE(*config);
> + if (pu_value & BIT(pin))
> + ST_PINCONF_PACK_PU(*config);
> + if (od_value & BIT(pin))
> + ST_PINCONF_PACK_OD(*config);
> +
> +}
> +
> +static int st_pinconf_get_retime_packed(struct st_pinctrl *info,
> + struct st_pio_control *pc, int pin, unsigned long *config)
> +{
> + const struct st_pctl_data *data = info->data;
> + struct st_retime_packed *rt_p = &pc->rt.rt_p;
> + unsigned int delay_bits, delay, delay0, delay1, val;
> + int output = ST_PINCONF_UNPACK_OE(*config);
> +
> + if (!regmap_field_read(rt_p->retime, &val) && (val & BIT(pin)))
> + ST_PINCONF_PACK_RT(*config);
> +
> + if (!regmap_field_read(rt_p->clk1notclk0, &val) && (val & BIT(pin)))
> + ST_PINCONF_PACK_RT_CLK(*config, 1);
> +
> + if (!regmap_field_read(rt_p->clknotdata, &val) && (val & BIT(pin)))
> + ST_PINCONF_PACK_RT_CLKNOTDATA(*config);
> +
> + if (!regmap_field_read(rt_p->double_edge, &val) && (val & BIT(pin)))
> + ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config);
> +
> + if (!regmap_field_read(rt_p->invertclk, &val) && (val & BIT(pin)))
> + ST_PINCONF_PACK_RT_INVERTCLK(*config);
> +
> + regmap_field_read(rt_p->delay_0, &delay0);
> + regmap_field_read(rt_p->delay_1, &delay1);
> + delay_bits = (((delay1 & BIT(pin)) ? 1 : 0) << 1) |
> + (((delay0 & BIT(pin)) ? 1 : 0));
> + delay = st_pinconf_bit_to_delay(delay_bits, data, output);
> + ST_PINCONF_PACK_RT_DELAY(*config, delay);
> +
> + return 0;
> +}
> +
> +static int st_pinconf_get_retime_dedicated(struct st_pinctrl *info,
> + struct st_pio_control *pc, int pin, unsigned long *config)
> +{
> + unsigned int value;
> + unsigned long delay_bits, delay, rt_clk;
> + int output = ST_PINCONF_UNPACK_OE(*config);
> + struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
> +
> + regmap_field_read(rt_d->rt[pin], &value);
> +
> + rt_clk = (value & RT_D_CFG_CLK_MASK) >> RT_D_CFG_CLK_SHIFT;
> + ST_PINCONF_PACK_RT_CLK(*config, rt_clk);
> +
> + delay_bits = (value & RT_D_CFG_DELAY_MASK) >> RT_D_CFG_DELAY_SHIFT;
> + delay = st_pinconf_bit_to_delay(delay_bits, info->data, output);
> + ST_PINCONF_PACK_RT_DELAY(*config, delay);
> +
> + if (value & RT_D_CFG_CLKNOTDATA_MASK)
> + ST_PINCONF_PACK_RT_CLKNOTDATA(*config);
> +
> + if (value & RT_D_CFG_DOUBLE_EDGE_MASK)
> + ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config);
> +
> + if (value & RT_D_CFG_INVERTCLK_MASK)
> + ST_PINCONF_PACK_RT_INVERTCLK(*config);
> +
> + if (value & RT_D_CFG_RETIME_MASK)
> + ST_PINCONF_PACK_RT(*config);
> +
> + return 0;
> +}
> +
> +/* GPIO related functions */
> +
> +static inline void __st_gpio_set(struct st_gpio_bank *bank,
> + unsigned offset, int value)
> +{
> + if (value)
> + writel(BIT(offset), bank->base + REG_PIO_SET_POUT);
> + else
> + writel(BIT(offset), bank->base + REG_PIO_CLR_POUT);
> +}
> +
> +static void st_gpio_direction(struct st_gpio_bank *bank,
> + unsigned int gpio, unsigned int direction)
> +{
> + int offset = st_gpio_pin(gpio);
> + int i = 0;
> + /**
> + * There are three configuration registers (PIOn_PC0, PIOn_PC1
> + * and PIOn_PC2) for each port. These are used to configure the
> + * PIO port pins. Each pin can be configured as an input, output,
> + * bidirectional, or alternative function pin. Three bits, one bit
> + * from each of the three registers, configure the corresponding bit of
> + * the port. Valid bit settings is:
> + *
> + * PC2 PC1 PC0 Direction.
> + * 0 0 0 [Input Weak pull-up]
> + * 0 0 or 1 1 [Bidirection]
> + * 0 1 0 [Output]
> + * 1 0 0 [Input]
> + *
> + * PIOn_SET_PC and PIOn_CLR_PC registers are used to set and clear bits
> + * individually.
> + */
> + for (i = 0; i <= 2; i++) {
> + if (direction & BIT(i))
> + writel(BIT(offset), bank->base + REG_PIO_SET_PC(i));
> + else
> + writel(BIT(offset), bank->base + REG_PIO_CLR_PC(i));
> + }
> +}
> +
> +static int st_gpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + return pinctrl_request_gpio(chip->base + offset);
> +}
> +
> +static void st_gpio_free(struct gpio_chip *chip, unsigned offset)
> +{
> + pinctrl_free_gpio(chip->base + offset);
> +}
> +
> +static int st_gpio_get(struct gpio_chip *chip, unsigned offset)
> +{
> + struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
> +
> + return !!(readl(bank->base + REG_PIO_PIN) & BIT(offset));
> +}
> +
> +static void st_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
> +{
> + struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
> + __st_gpio_set(bank, offset, value);
> +}
> +
> +static int st_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
> +{
> + pinctrl_gpio_direction_input(chip->base + offset);
> +
> + return 0;
> +}
> +
> +static int st_gpio_direction_output(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
> +
> + __st_gpio_set(bank, offset, value);
> + pinctrl_gpio_direction_output(chip->base + offset);
> +
> + return 0;
> +}
> +
> +static int st_gpio_xlate(struct gpio_chip *gc,
> + const struct of_phandle_args *gpiospec, u32 *flags)
> +{
> + if (WARN_ON(gc->of_gpio_n_cells < 1))
> + return -EINVAL;
> +
> + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
> + return -EINVAL;
> +
> + if (gpiospec->args[0] > gc->ngpio)
> + return -EINVAL;
> +
> + return gpiospec->args[0];
> +}
> +
> +/* Pinctrl Groups */
> +static int st_pctl_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> +
> + return info->ngroups;
> +}
> +
> +static const char *st_pctl_get_group_name(struct pinctrl_dev *pctldev,
> + unsigned selector)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> +
> + return info->groups[selector].name;
> +}
> +
> +static int st_pctl_get_group_pins(struct pinctrl_dev *pctldev,
> + unsigned selector, const unsigned **pins, unsigned *npins)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> +
> + if (selector >= info->ngroups)
> + return -EINVAL;
> +
> + *pins = info->groups[selector].pins;
> + *npins = info->groups[selector].npins;
> +
> + return 0;
> +}
> +
> +static const inline struct st_pctl_group *st_pctl_find_group_by_name(
> + const struct st_pinctrl *info, const char *name)
> +{
> + int i;
> +
> + for (i = 0; i < info->ngroups; i++) {
> + if (!strcmp(info->groups[i].name, name))
> + return &info->groups[i];
> + }
> +
> + return NULL;
> +}
> +
> +static int st_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
> + struct device_node *np, struct pinctrl_map **map, unsigned *num_maps)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> + const struct st_pctl_group *grp;
> + struct pinctrl_map *new_map;
> + struct device_node *parent;
> + int map_num, i;
> +
> + grp = st_pctl_find_group_by_name(info, np->name);
> + if (!grp) {
> + dev_err(info->dev, "unable to find group for node %s\n",
> + np->name);
> + return -EINVAL;
> + }
> +
> + map_num = grp->npins + 1;
> + new_map = devm_kzalloc(pctldev->dev,
> + sizeof(*new_map) * map_num, GFP_KERNEL);
> + if (!new_map)
> + return -ENOMEM;
> +
> + parent = of_get_parent(np);
> + if (!parent) {
> + devm_kfree(pctldev->dev, new_map);
> + return -EINVAL;
> + }
> +
> + *map = new_map;
> + *num_maps = map_num;
> + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
> + new_map[0].data.mux.function = parent->name;
> + new_map[0].data.mux.group = np->name;
> + of_node_put(parent);
> +
> + /* create config map per pin */
> + new_map++;
> + for (i = 0; i < grp->npins; i++) {
> + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
> + new_map[i].data.configs.group_or_pin =
> + pin_get_name(pctldev, grp->pins[i]);
> + new_map[i].data.configs.configs = &grp->pin_conf[i].config;
> + new_map[i].data.configs.num_configs = 1;
> + }
> + dev_info(pctldev->dev, "maps: function %s group %s num %d\n",
> + (*map)->data.mux.function, grp->name, map_num);
> +
> + return 0;
> +}
> +
> +static void st_pctl_dt_free_map(struct pinctrl_dev *pctldev,
> + struct pinctrl_map *map, unsigned num_maps)
> +{
> +}
> +
> +static struct pinctrl_ops st_pctlops = {
> + .get_groups_count = st_pctl_get_groups_count,
> + .get_group_pins = st_pctl_get_group_pins,
> + .get_group_name = st_pctl_get_group_name,
> + .dt_node_to_map = st_pctl_dt_node_to_map,
> + .dt_free_map = st_pctl_dt_free_map,
> +};
> +
> +/* Pinmux */
> +static int st_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> +
> + return info->nfunctions;
> +}
> +
> +const char *st_pmx_get_fname(struct pinctrl_dev *pctldev,
> + unsigned selector)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> +
> + return info->functions[selector].name;
> +}
> +
> +static int st_pmx_get_groups(struct pinctrl_dev *pctldev,
> + unsigned selector, const char * const **grps, unsigned * const ngrps)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> + *grps = info->functions[selector].groups;
> + *ngrps = info->functions[selector].ngroups;
> +
> + return 0;
> +}
> +
> +static struct st_pio_control *st_get_pio_control(
> + struct pinctrl_dev *pctldev, int pin)
> +{
> + struct pinctrl_gpio_range *range =
> + pinctrl_find_gpio_range_from_pin(pctldev, pin);
> + struct st_gpio_bank *bank = gpio_range_to_bank(range);
> +
> + return &bank->pc;
> +}
> +
> +static int st_pmx_enable(struct pinctrl_dev *pctldev, unsigned fselector,
> + unsigned group)
> +{
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> + struct st_pinconf *conf = info->groups[group].pin_conf;
> + struct st_pio_control *pc;
> + int i;
> +
> + for (i = 0; i < info->groups[group].npins; i++) {
> + pc = st_get_pio_control(pctldev, conf[i].pin);
> + st_pctl_set_function(pc, conf[i].pin, conf[i].altfunc);
> + }
> +
> + return 0;
> +}
> +
> +static void st_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector,
> + unsigned group)
> +{
> +}
> +
> +static int st_pmx_set_gpio_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range, unsigned gpio,
> + bool input)
> +{
> + struct st_gpio_bank *bank = gpio_range_to_bank(range);
> + /*
> + * When a PIO bank is used in its primary function mode (altfunc = 0)
> + * Output Enable (OE), Open Drain(OD), and Pull Up (PU)
> + * for the primary PIO functions are driven by the related PIO block
> + */
> + st_pctl_set_function(&bank->pc, gpio, 0);
> + st_gpio_direction(bank, gpio, input ?
> + ST_GPIO_DIRECTION_IN : ST_GPIO_DIRECTION_OUT);
> +
> + return 0;
> +}
> +
> +static struct pinmux_ops st_pmxops = {
> + .get_functions_count = st_pmx_get_funcs_count,
> + .get_function_name = st_pmx_get_fname,
> + .get_function_groups = st_pmx_get_groups,
> + .enable = st_pmx_enable,
> + .disable = st_pmx_disable,
> + .gpio_set_direction = st_pmx_set_gpio_direction,
> +};
> +
> +/* Pinconf */
> +static void st_pinconf_get_retime(struct st_pinctrl *info,
> + struct st_pio_control *pc, int pin, unsigned long *config)
> +{
> + if (info->data->rt_style == st_retime_style_packed)
> + st_pinconf_get_retime_packed(info, pc, pin, config);
> + else if (info->data->rt_style == st_retime_style_dedicated)
> + if ((BIT(pin) & pc->rt_pin_mask))
> + st_pinconf_get_retime_dedicated(info, pc,
> + pin, config);
> +}
> +
> +static void st_pinconf_set_retime(struct st_pinctrl *info,
> + struct st_pio_control *pc, int pin, unsigned long config)
> +{
> + if (info->data->rt_style == st_retime_style_packed)
> + st_pinconf_set_retime_packed(info, pc, config, pin);
> + else if (info->data->rt_style == st_retime_style_dedicated)
> + if ((BIT(pin) & pc->rt_pin_mask))
> + st_pinconf_set_retime_dedicated(info, pc,
> + config, pin);
> +}
> +
> +static int st_pinconf_set(struct pinctrl_dev *pctldev,
> + unsigned pin_id, unsigned long config)
> +{
> + int pin = st_gpio_pin(pin_id);
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> + struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id);
> +
> + st_pinconf_set_config(pc, pin, config);
> + st_pinconf_set_retime(info, pc, pin, config);
> +
> + return 0;
> +}
> +
> +static int st_pinconf_get(struct pinctrl_dev *pctldev,
> + unsigned pin_id, unsigned long *config)
> +{
> + int pin = st_gpio_pin(pin_id);
> + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
> + struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id);
> +
> + *config = 0;
> + st_pinconf_get_direction(pc, pin, config);
> + st_pinconf_get_retime(info, pc, pin, config);
> +
> + return 0;
> +}
> +
> +static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev,
> + struct seq_file *s, unsigned pin_id)
> +{
> + unsigned long config;
> + st_pinconf_get(pctldev, pin_id, &config);
> +
> + seq_printf(s, "[OE:%ld,PU:%ld,OD:%ld]\n"
> + "\t\t[retime:%ld,invclk:%ld,clknotdat:%ld,"
> + "de:%ld,rt-clk:%ld,rt-delay:%ld]",
> + ST_PINCONF_UNPACK_OE(config),
> + ST_PINCONF_UNPACK_PU(config),
> + ST_PINCONF_UNPACK_OD(config),
> + ST_PINCONF_UNPACK_RT(config),
> + ST_PINCONF_UNPACK_RT_INVERTCLK(config),
> + ST_PINCONF_UNPACK_RT_CLKNOTDATA(config),
> + ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config),
> + ST_PINCONF_UNPACK_RT_CLK(config),
> + ST_PINCONF_UNPACK_RT_DELAY(config));
> +}
> +
> +static struct pinconf_ops st_confops = {
> + .pin_config_get = st_pinconf_get,
> + .pin_config_set = st_pinconf_set,
> + .pin_config_dbg_show = st_pinconf_dbg_show,
> +};
> +
> +static void st_pctl_dt_child_count(struct st_pinctrl *info,
> + struct device_node *np)
> +{
> + struct device_node *child;
> + for_each_child_of_node(np, child) {
> + if (of_property_read_bool(child, "gpio-controller")) {
> + info->nbanks++;
> + } else {
> + info->nfunctions++;
> + info->ngroups += of_get_child_count(child);
> + }
> + }
> +}
> +
> +static int st_pctl_dt_setup_retime_packed(struct st_pinctrl *info,
> + int bank, struct st_pio_control *pc)
> +{
> + struct device *dev = info->dev;
> + struct regmap *rm = info->regmap;
> + const struct st_pctl_data *data = info->data;
> + /* 2 registers per bank */
> + int reg = (data->rt + bank * RT_P_CFGS_PER_BANK) * 4;
> + struct st_retime_packed *rt_p = &pc->rt.rt_p;
> + /* cfg0 */
> + struct reg_field clk1notclk0 = RT_P_CFG0_CLK1NOTCLK0_FIELD(reg);
> + struct reg_field delay_0 = RT_P_CFG0_DELAY_0_FIELD(reg);
> + struct reg_field delay_1 = RT_P_CFG0_DELAY_1_FIELD(reg);
> + /* cfg1 */
> + struct reg_field invertclk = RT_P_CFG1_INVERTCLK_FIELD(reg + 4);
> + struct reg_field retime = RT_P_CFG1_RETIME_FIELD(reg + 4);
> + struct reg_field clknotdata = RT_P_CFG1_CLKNOTDATA_FIELD(reg + 4);
> + struct reg_field double_edge = RT_P_CFG1_DOUBLE_EDGE_FIELD(reg + 4);
> +
> + rt_p->clk1notclk0 = devm_regmap_field_alloc(dev, rm, clk1notclk0);
> + rt_p->delay_0 = devm_regmap_field_alloc(dev, rm, delay_0);
> + rt_p->delay_1 = devm_regmap_field_alloc(dev, rm, delay_1);
> + rt_p->invertclk = devm_regmap_field_alloc(dev, rm, invertclk);
> + rt_p->retime = devm_regmap_field_alloc(dev, rm, retime);
> + rt_p->clknotdata = devm_regmap_field_alloc(dev, rm, clknotdata);
> + rt_p->double_edge = devm_regmap_field_alloc(dev, rm, double_edge);
> +
> + if (IS_ERR(rt_p->clk1notclk0) || IS_ERR(rt_p->delay_0) ||
> + IS_ERR(rt_p->delay_1) || IS_ERR(rt_p->invertclk) ||
> + IS_ERR(rt_p->retime) || IS_ERR(rt_p->clknotdata) ||
> + IS_ERR(rt_p->double_edge))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int st_pctl_dt_setup_retime_dedicated(struct st_pinctrl *info,
> + int bank, struct st_pio_control *pc)
> +{
> + struct device *dev = info->dev;
> + struct regmap *rm = info->regmap;
> + const struct st_pctl_data *data = info->data;
> + /* 8 registers per bank */
> + int reg_offset = (data->rt + bank * RT_D_CFGS_PER_BANK) * 4;
> + struct st_retime_dedicated *rt_d = &pc->rt.rt_d;
> + unsigned int j;
> + u32 pin_mask = pc->rt_pin_mask;
> +
> + for (j = 0; j < RT_D_CFGS_PER_BANK; j++) {
> + if (BIT(j) & pin_mask) {
> + struct reg_field reg = REG_FIELD(reg_offset, 0, 31);
> + rt_d->rt[j] = devm_regmap_field_alloc(dev, rm, reg);
> + if (IS_ERR(rt_d->rt[j]))
> + return -EINVAL;
> + reg_offset += 4;
> + }
> + }
> + return 0;
> +}
> +
> +static int st_pctl_dt_setup_retime(struct st_pinctrl *info,
> + int bank, struct st_pio_control *pc)
> +{
> + const struct st_pctl_data *data = info->data;
> + if (data->rt_style == st_retime_style_packed)
> + return st_pctl_dt_setup_retime_packed(info, bank, pc);
> + else if (data->rt_style == st_retime_style_dedicated)
> + return st_pctl_dt_setup_retime_dedicated(info, bank, pc);
> +
> + return -EINVAL;
> +}
> +
> +static int st_parse_syscfgs(struct st_pinctrl *info,
> + int bank, struct device_node *np)
> +{
> + const struct st_pctl_data *data = info->data;
> + /**
> + * For a given shared register like OE/PU/OD, there are 8 bits per bank
> + * 0:7 belongs to bank0, 8:15 belongs to bank1 ...
> + * So each register is shared across 4 banks.
> + */
> + int lsb = (bank%4) * ST_GPIO_PINS_PER_BANK;
> + int msb = lsb + ST_GPIO_PINS_PER_BANK - 1;
> + struct reg_field alt_reg = REG_FIELD((data->alt + bank) * 4, 0, 31);
> + struct reg_field oe_reg = REG_FIELD((data->oe + bank/4) * 4, lsb, msb);
> + struct reg_field pu_reg = REG_FIELD((data->pu + bank/4) * 4, lsb, msb);
> + struct reg_field od_reg = REG_FIELD((data->od + bank/4) * 4, lsb, msb);
> + struct st_pio_control *pc = &info->banks[bank].pc;
> + struct device *dev = info->dev;
> + struct regmap *regmap = info->regmap;
> +
> + pc->alt = devm_regmap_field_alloc(dev, regmap, alt_reg);
> + pc->oe = devm_regmap_field_alloc(dev, regmap, oe_reg);
> + pc->pu = devm_regmap_field_alloc(dev, regmap, pu_reg);
> + pc->od = devm_regmap_field_alloc(dev, regmap, od_reg);
> +
> + if (IS_ERR(pc->alt) || IS_ERR(pc->oe) ||
> + IS_ERR(pc->pu) || IS_ERR(pc->od))
> + return -EINVAL;
> +
> + /* retime avaiable for all pins by default */
> + pc->rt_pin_mask = 0xff;
> + of_property_read_u32(np, "st,retime-pin-mask", &pc->rt_pin_mask);
> + st_pctl_dt_setup_retime(info, bank, pc);
> +
> + return 0;
> +}
> +
> +/*
> + * Each pin is represented in of the below forms.
> + * <bank offset mux direction rt_type rt_delay rt_clk>
> + */
> +static int st_pctl_dt_parse_groups(struct device_node *np,
> + struct st_pctl_group *grp, struct st_pinctrl *info, int idx)
> +{
> + /* bank pad direction val altfunction */
> + const __be32 *list;
> + struct property *pp;
> + struct st_pinconf *conf;
> + phandle phandle;
> + struct device_node *pins;
> + u32 pin;
> + int i = 0, npins = 0, nr_props;
> +
> + pins = of_get_child_by_name(np, "st,pins");
> + if (!pins)
> + return -ENODATA;
> +
> + for_each_property_of_node(pins, pp) {
> + /* Skip those we do not want to proceed */
> + if (!strcmp(pp->name, "name"))
> + continue;
> +
> + if (pp && (pp->length/sizeof(__be32)) >= OF_GPIO_ARGS_MIN) {
> + npins++;
> + } else {
> + pr_warn("Invalid st,pins in %s node\n", np->name);
> + return -EINVAL;
> + }
> + }
> +
> + grp->npins = npins;
> + grp->name = np->name;
> + grp->pins = devm_kzalloc(info->dev, npins * sizeof(u32), GFP_KERNEL);
> + grp->pin_conf = devm_kzalloc(info->dev,
> + npins * sizeof(*conf), GFP_KERNEL);
> +
> + if (!grp->pins || !grp->pin_conf)
> + return -ENOMEM;
> +
> + /* <bank offset mux direction rt_type rt_delay rt_clk> */
> + for_each_property_of_node(pins, pp) {
> + if (!strcmp(pp->name, "name"))
> + continue;
> + nr_props = pp->length/sizeof(u32);
> + list = pp->value;
> + conf = &grp->pin_conf[i];
> +
> + /* bank & offset */
> + phandle = be32_to_cpup(list++);
> + pin = be32_to_cpup(list++);
> + conf->pin = of_get_named_gpio(pins, pp->name, 0);
> + conf->name = pp->name;
> + grp->pins[i] = conf->pin;
> + /* mux */
> + conf->altfunc = be32_to_cpup(list++);
> + conf->config = 0;
> + /* direction */
> + conf->config |= be32_to_cpup(list++);
> + /* rt_type rt_delay rt_clk */
> + if (nr_props >= OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN) {
> + /* rt_type */
> + conf->config |= be32_to_cpup(list++);
> + /* rt_delay */
> + conf->config |= be32_to_cpup(list++);
> + /* rt_clk */
> + if (nr_props > OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN)
> + conf->config |= be32_to_cpup(list++);
> + }
> + i++;
> + }
> + of_node_put(pins);
> +
> + return 0;
> +}
> +
> +static int st_pctl_parse_functions(struct device_node *np,
> + struct st_pinctrl *info, u32 index, int *grp_index)
> +{
> + struct device_node *child;
> + struct st_pmx_func *func;
> + struct st_pctl_group *grp;
> + int ret, i;
> +
> + func = &info->functions[index];
> + func->name = np->name;
> + func->ngroups = of_get_child_count(np);
> + if (func->ngroups <= 0) {
> + dev_err(info->dev, "No groups defined\n");
> + return -EINVAL;
> + }
> + func->groups = devm_kzalloc(info->dev,
> + func->ngroups * sizeof(char *), GFP_KERNEL);
> + if (!func->groups)
> + return -ENOMEM;
> +
> + i = 0;
> + for_each_child_of_node(np, child) {
> + func->groups[i] = child->name;
> + grp = &info->groups[*grp_index];
> + *grp_index += 1;
> + ret = st_pctl_dt_parse_groups(child, grp, info, i++);
> + if (ret)
> + return ret;
> + }
> + dev_info(info->dev, "Function[%d\t name:%s,\tgroups:%d]\n",
> + index, func->name, func->ngroups);
> +
> + return 0;
> +}
> +
> +static struct gpio_chip st_gpio_template = {
> + .request = st_gpio_request,
> + .free = st_gpio_free,
> + .get = st_gpio_get,
> + .set = st_gpio_set,
> + .direction_input = st_gpio_direction_input,
> + .direction_output = st_gpio_direction_output,
> + .ngpio = ST_GPIO_PINS_PER_BANK,
> + .of_gpio_n_cells = 1,
> + .of_xlate = st_gpio_xlate,
> +};
> +
> +static int st_gpiolib_register_bank(struct st_pinctrl *info,
> + int bank_nr, struct device_node *np)
> +{
> + struct st_gpio_bank *bank = &info->banks[bank_nr];
> + 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;
> +
> + if (of_address_to_resource(np, 0, &res))
> + return -ENODEV;
> +
> + bank->base = devm_request_and_ioremap(dev, &res);
> + if (!bank->base) {
> + dev_err(dev, "Can't get IO memory mapping!\n");
> + return -ENODEV;
> + }
> +
> + bank->gpio_chip = st_gpio_template;
> + bank->gpio_chip.base = bank_num * ST_GPIO_PINS_PER_BANK;
> + bank->gpio_chip.ngpio = ST_GPIO_PINS_PER_BANK;
> + bank->gpio_chip.of_node = np;
> +
> + of_property_read_string(np, "st,bank-name", &range->name);
> + bank->gpio_chip.label = range->name;
> +
> + range->id = bank_num;
> + range->pin_base = range->base = range->id * ST_GPIO_PINS_PER_BANK;
> + range->npins = bank->gpio_chip.ngpio;
> + range->gc = &bank->gpio_chip;
> + err = gpiochip_add(&bank->gpio_chip);
> + if (err) {
> + dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_num);
> + return err;
> + }
> + dev_info(dev, "%s bank added.\n", range->name);
> +
> + return 0;
> +}
> +
> +static struct of_device_id st_pctl_of_match[] = {
> + { .compatible = "st,stih415-sbc-pinctrl", .data = &stih415_sbc_data },
> + { .compatible = "st,stih415-rear-pinctrl", .data = &stih415_rear_data },
> + { .compatible = "st,stih415-left-pinctrl", .data = &stih415_left_data },
> + { .compatible = "st,stih415-right-pinctrl",
> + .data = &stih415_right_data },
> + { .compatible = "st,stih415-front-pinctrl",
> + .data = &stih415_front_data },
> + { .compatible = "st,stih416-sbc-pinctrl", .data = &stih416_data},
> + { .compatible = "st,stih416-front-pinctrl", .data = &stih416_data},
> + { .compatible = "st,stih416-rear-pinctrl", .data = &stih416_data},
> + { .compatible = "st,stih416-fvdp-fe-pinctrl", .data = &stih416_data},
> + { .compatible = "st,stih416-fvdp-lite-pinctrl", .data = &stih416_data},
> + { /* sentinel */ }
> +};
> +
> +static int st_pctl_probe_dt(struct platform_device *pdev,
> + struct pinctrl_desc *pctl_desc, struct st_pinctrl *info)
> +{
> + int ret = 0;
> + int i = 0, j = 0, k = 0, bank;
> + struct pinctrl_pin_desc *pdesc;
> + struct device_node *np = pdev->dev.of_node;
> + struct device_node *child;
> + int grp_index = 0;
> +
> + st_pctl_dt_child_count(info, np);
> + if (!info->nbanks) {
> + dev_err(&pdev->dev, "you need atleast one gpio bank\n");
> + return -EINVAL;
> + }
> +
> + dev_info(&pdev->dev, "nbanks = %d\n", info->nbanks);
> + dev_info(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
> + dev_info(&pdev->dev, "ngroups = %d\n", info->ngroups);
> +
> + info->functions = devm_kzalloc(&pdev->dev,
> + info->nfunctions * sizeof(*info->functions), GFP_KERNEL);
> +
> + info->groups = devm_kzalloc(&pdev->dev,
> + info->ngroups * sizeof(*info->groups) , GFP_KERNEL);
> +
> + info->banks = devm_kzalloc(&pdev->dev,
> + info->nbanks * sizeof(*info->banks), GFP_KERNEL);
> +
> + if (!info->functions || !info->groups || !info->banks)
> + return -ENOMEM;
> +
> + info->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> + if (!info->regmap) {
> + dev_err(info->dev, "No syscfg phandle specified\n");
> + return -ENOMEM;
> + }
> + info->data = of_match_node(st_pctl_of_match, np)->data;
> +
> + pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK;
> + pdesc = devm_kzalloc(&pdev->dev,
> + sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL);
> + if (!pdesc)
> + return -ENOMEM;
> +
> + pctl_desc->pins = pdesc;
> +
> + bank = 0;
> + for_each_child_of_node(np, child) {
> + if (of_property_read_bool(child, "gpio-controller")) {
> + const char *bank_name = NULL;
> + ret = st_gpiolib_register_bank(info, bank, child);
> + if (ret)
> + return ret;
> +
> + k = info->banks[bank].range.pin_base;
> + bank_name = info->banks[bank].range.name;
> + for (j = 0; j < ST_GPIO_PINS_PER_BANK; j++, k++) {
> + pdesc->number = k;
> + pdesc->name = kasprintf(GFP_KERNEL, "%s[%d]",
> + bank_name, j);
> + pdesc++;
> + }
> + st_parse_syscfgs(info, bank, child);
> + bank++;
> + } else {
> + ret = st_pctl_parse_functions(child, info,
> + i++, &grp_index);
> + if (ret) {
> + dev_err(&pdev->dev, "No functions found.\n");
> + return ret;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int st_pctl_probe(struct platform_device *pdev)
> +{
> + struct st_pinctrl *info;
> + struct pinctrl_desc *pctl_desc;
> + int ret, i;
> +
> + if (!pdev->dev.of_node) {
> + dev_err(&pdev->dev, "device node not found.\n");
> + return -EINVAL;
> + }
> +
> + pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
> + if (!pctl_desc)
> + return -ENOMEM;
> +
> + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> +
> + info->dev = &pdev->dev;
> + platform_set_drvdata(pdev, info);
> + ret = st_pctl_probe_dt(pdev, pctl_desc, info);
> + if (ret)
> + return ret;
> +
> + pctl_desc->owner = THIS_MODULE,
> + pctl_desc->pctlops = &st_pctlops,
> + pctl_desc->pmxops = &st_pmxops,
> + pctl_desc->confops = &st_confops,
> + pctl_desc->name = dev_name(&pdev->dev);
> +
> + info->pctl = pinctrl_register(pctl_desc, &pdev->dev, info);
> + if (IS_ERR(info->pctl)) {
> + dev_err(&pdev->dev, "Failed pinctrl registration\n");
> + return PTR_ERR(info->pctl);
> + }
> +
> + for (i = 0; i < info->nbanks; i++)
> + pinctrl_add_gpio_range(info->pctl, &info->banks[i].range);
> +
> + return 0;
> +}
> +
> +static struct platform_driver st_pctl_driver = {
> + .driver = {
> + .name = "st-pinctrl",
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(st_pctl_of_match),
> + },
> + .probe = st_pctl_probe,
> +};
> +
> +static int __init st_pctl_init(void)
> +{
> + return platform_driver_register(&st_pctl_driver);
> +}
> +arch_initcall(st_pctl_init);
>
Hi Greg,
If its not too late can this patch be considered for 3.11 via tty tree?
Arnd preferred to take the SOC patches in this series via arm-soc tree.
Also, there is no build dependency for this patch.
Thanks,
srini
On 20/06/13 20:01, Arnd Bergmann wrote:
> On Thursday 20 June 2013, Srinivas KANDAGATLA wrote:
>> This patch adds support to ASC (asynchronous serial controller)
>> driver, which is basically a standard serial driver. This IP is common
>> across all the ST parts for settop box platforms.
>>
>> ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
>> It support all industry standard baud rates.
>>
>> Signed-off-by: Srinivas Kandagatla <[email protected]>
>> CC: Stephen Gallimore <[email protected]>
>> CC: Stuart Menefy <[email protected]>
>> CC: Arnd Bergmann <[email protected]>
>> CC: Greg Kroah-Hartman <[email protected]>
>
>>From my point of view the series is ready for merging in 3.11,
> I only had a few final comments. I would prefer the first three
> patches to go through the respective serial/clocksource/pinctrl
> trees, since there are no direct build dependencies to the
> platform parts.
>
> If there is a reason to take those through arm-soc, I would
> insist on getting Acked-by's from the subsystem maintainers.
>
> Arnd
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>
On Fri, Jun 21, 2013 at 02:50:55PM +0100, Srinivas KANDAGATLA wrote:
> Hi Greg,
> If its not too late can this patch be considered for 3.11 via tty tree?
> Arnd preferred to take the SOC patches in this series via arm-soc tree.
> Also, there is no build dependency for this patch.
Can you please resend it, I no longer seem to have it in my queue
anywhere.
thanks,
greg k-h
On Fri, Jun 21, 2013 at 3:41 PM, Srinivas KANDAGATLA
<[email protected]> wrote:
> Hi Linus W,
> If its not too late can this patch be considered for 3.11 via pinctrl tree?
> There is a build dependecy with regmap_field apis pulled by Mark Brown
> in regmap repository.
This seems fairly complete, but I cannot have such a basic dependency onto
the regmap tree this late in the merge window, i.e. I'm not ready to pull
all of regmap into the pinctrl tree. I'd consider this for merging
for the next kernel cycle so it's more orthogonal.
Yours,
Linus Walleij
On 24/06/13 12:57, Linus Walleij wrote:
> On Fri, Jun 21, 2013 at 3:41 PM, Srinivas KANDAGATLA
> <[email protected]> wrote:
>
>> Hi Linus W,
>> If its not too late can this patch be considered for 3.11 via pinctrl tree?
>> There is a build dependecy with regmap_field apis pulled by Mark Brown
>> in regmap repository.
>
> This seems fairly complete, but I cannot have such a basic dependency onto
> the regmap tree this late in the merge window, i.e. I'm not ready to pull
> all of regmap into the pinctrl tree. I'd consider this for merging
> for the next kernel cycle so it's more orthogonal.
Hmmm..
You mean 3.12*
Am not sure, But..
Is it possible to make just regmap field api patch go via pinctrl tree,
as "Mark Brow" has already accepted it.
Ideally, It would be nice this patch to be part of 3.11-* as, Arnd
already pushed SOC support changes to his next branch.
thanks,
srini
>
> Yours,
> Linus Walleij
>
On Mon, Jun 24, 2013 at 01:57:56PM +0200, Linus Walleij wrote:
> This seems fairly complete, but I cannot have such a basic dependency onto
> the regmap tree this late in the merge window, i.e. I'm not ready to pull
> all of regmap into the pinctrl tree. I'd consider this for merging
> for the next kernel cycle so it's more orthogonal.
I've got the patch on a topic branch in regmap to allow for this sort of
stuff - I can readily provide a signed tag for you to get just that
patch if that'd help? Alternatively I'm happy to carry the pinctrl
patch in regmap?
On 24/06/13 15:12, Mark Brown wrote:
> On Mon, Jun 24, 2013 at 01:57:56PM +0200, Linus Walleij wrote:
>
>> This seems fairly complete, but I cannot have such a basic dependency onto
>> the regmap tree this late in the merge window, i.e. I'm not ready to pull
>> all of regmap into the pinctrl tree. I'd consider this for merging
>> for the next kernel cycle so it's more orthogonal.
>
> I've got the patch on a topic branch in regmap to allow for this sort of
> stuff - I can readily provide a signed tag for you to get just that
> patch if that'd help? Alternatively I'm happy to carry the pinctrl
> patch in regmap?
Thanks Mark,
Am Ok with either way, it is required that these 2 patches to go together.
Linus W, if you are happy with the patch can Mark take it via regmap repo?
Thanks,
srini
>
>
>
> _______________________________________________
> Kernel mailing list
> [email protected]
> http://www.stlinux.com/mailman/listinfo/kernel
>
On Mon, Jun 24, 2013 at 4:12 PM, Mark Brown <[email protected]> wrote:
> On Mon, Jun 24, 2013 at 01:57:56PM +0200, Linus Walleij wrote:
>
>> This seems fairly complete, but I cannot have such a basic dependency onto
>> the regmap tree this late in the merge window, i.e. I'm not ready to pull
>> all of regmap into the pinctrl tree. I'd consider this for merging
>> for the next kernel cycle so it's more orthogonal.
>
> I've got the patch on a topic branch in regmap to allow for this sort of
> stuff - I can readily provide a signed tag for you to get just that
> patch if that'd help? Alternatively I'm happy to carry the pinctrl
> patch in regmap?
I'm OK that you carry this in the regmap tree.
Acked-by: Linus Walleij <[email protected]>
The clock retime weirdness convinced me that this cannot really
use the generic pin config layout...
Yours,
Linus Walleij
On 25/06/13 11:58, Linus Walleij wrote:
> On Mon, Jun 24, 2013 at 4:12 PM, Mark Brown <[email protected]> wrote:
>> On Mon, Jun 24, 2013 at 01:57:56PM +0200, Linus Walleij wrote:
>>
>>> This seems fairly complete, but I cannot have such a basic dependency onto
>>> the regmap tree this late in the merge window, i.e. I'm not ready to pull
>>> all of regmap into the pinctrl tree. I'd consider this for merging
>>> for the next kernel cycle so it's more orthogonal.
>>
>> I've got the patch on a topic branch in regmap to allow for this sort of
>> stuff - I can readily provide a signed tag for you to get just that
>> patch if that'd help? Alternatively I'm happy to carry the pinctrl
>> patch in regmap?
>
> I'm OK that you carry this in the regmap tree.
> Acked-by: Linus Walleij <[email protected]>
Thanks Linus W.
>
> The clock retime weirdness convinced me that this cannot really
> use the generic pin config layout...
>
> Yours,
> Linus Walleij
>
> _______________________________________________
> Kernel mailing list
> [email protected]
> http://www.stlinux.com/mailman/listinfo/kernel
>
On Thu, Jun 20, 2013 at 03:05:38PM +0100, Srinivas KANDAGATLA wrote:
> This patch add pinctrl support to ST SoCs.
>
> About hardware:
> ST Set-Top-Box parts have two blocks called PIO and PIO-mux which handle
> pin configurations.
Applied, thanks.