2014-04-16 17:13:45

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

Hi Arnd,

We had an intensive discussion of the series in the beginning of
December 2013 [1]. The discussion resulted in 3 versions of the series
in less than 3 weeks. Then there was a decision to block this series
until Daniel Mack's DMA-engine-for-PXA series is ready.

Unfortunately Daniel neigther agrees to review my trivial temporary
solution to the DMA-in-device-tree (patch 7 of 21), nor he is active
in DMA series development. There is no progress for 3 months.

As with any big out of tree series, its support is painful. It requires
extensive merging when doing bisects, on each step.

It would great if the decision to block could be reconsidered, and we could
move forward.

Best,

Sergei Ianovich

1. http://linux-kernel.2935.n7.nabble.com/PATCH-00-11-ARM-support-for-ICP-DAS-LP-8x4x-td761919.html


2014-04-16 17:14:33

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 03/21] ARM: dts: fix pxa27x-gpio interrupts

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
v2..v3
v1..v2
* no changes

arch/arm/boot/dts/pxa27x.dtsi | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi
index a705469..39bed5e 100644
--- a/arch/arm/boot/dts/pxa27x.dtsi
+++ b/arch/arm/boot/dts/pxa27x.dtsi
@@ -34,5 +34,11 @@
reg = <0x40c00010 0x10>;
#pwm-cells = <1>;
};
+
+ gpio: gpio@40e00000 {
+ compatible = "intel,pxa27x-gpio";
+ interrupts = <8>, <9>, <10>;
+ interrupt-names = "gpio0", "gpio1", "gpio_mux";
+ };
};
};
--
1.8.4.2

2014-04-16 17:14:52

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 06/21] ARM: dts: provide DMA config to pxamci on PXA27x

Non-dts implementation supply required DMA channel numbers as
IORESOURCE_DMA. We can also get them from the device tree, if it
is present.

This patch updates device tree with the proper dmaengine-based
"marvell,pdma-1.0" DMA.

There is no actual data handling in this patch, because the existing
driver cannot get DMA channel number from dmaengine API. The patch
in the series will provide temporary workaround by manually parsing
node attributes, until Daniel's series is ready to be merged.

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
* no changes

v2..v3
* split into good (this one) and temporary (PATCH 07/21) parts

v1..v2
* add binding for next-gen dma controller
* use correct dma declararion
* number changed from 5 to 3

Documentation/devicetree/bindings/mmc/pxa-mmc.txt | 5 +++++
arch/arm/boot/dts/pxa27x.dtsi | 14 ++++++++++++++
2 files changed, 19 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt
index b7025de..9f54c69 100644
--- a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt
@@ -5,6 +5,8 @@ Driver bindings for the PXA MCI (MMC/SDIO) interfaces
Required properties:
- compatible: Should be "marvell,pxa-mmc".
- vmmc-supply: A regulator for VMMC
+- dmas: Should be DMA specifiers for RX and TX
+- dma-names: Should be "rx" and "tx"

Optional properties:
- marvell,detect-delay-ms: sets the detection delay timeout in ms.
@@ -21,5 +23,8 @@ mmc0: mmc@41100000 {
interrupts = <23>;
cd-gpios = <&gpio 23 0>;
wp-gpios = <&gpio 24 0>;
+ dmas = <&dma 21
+ &dma 22>;
+ dma-names = "rx", "tx";
};

diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi
index 39bed5e..0150531 100644
--- a/arch/arm/boot/dts/pxa27x.dtsi
+++ b/arch/arm/boot/dts/pxa27x.dtsi
@@ -35,10 +35,24 @@
#pwm-cells = <1>;
};

+ dma: dma-controller@40000000 {
+ compatible = "marvell,pdma-1.0";
+ reg = <0x40000000 0x10000>;
+ interrupts = <25>;
+ #dma-cells = <1>;
+ dma-channels = <32>;
+ };
+
gpio: gpio@40e00000 {
compatible = "intel,pxa27x-gpio";
interrupts = <8>, <9>, <10>;
interrupt-names = "gpio0", "gpio1", "gpio_mux";
};
+
+ mmc@41100000 {
+ dmas = <&dma 21
+ &dma 22>;
+ dma-names = "rx", "tx";
+ };
};
};
--
1.8.4.2

2014-04-16 17:14:30

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 02/21] ARM: dts: pxa2xx fix compatible strings

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
v2..v3
v1..v2
* no changes

arch/arm/boot/dts/pxa2xx.dtsi | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/pxa2xx.dtsi b/arch/arm/boot/dts/pxa2xx.dtsi
index a5e90f0..3419f87 100644
--- a/arch/arm/boot/dts/pxa2xx.dtsi
+++ b/arch/arm/boot/dts/pxa2xx.dtsi
@@ -18,7 +18,6 @@
serial1 = &btuart;
serial2 = &stuart;
serial3 = &hwuart;
- i2c0 = &pwri2c;
i2c1 = &pxai2c1;
};

@@ -113,14 +112,14 @@
};

usb0: ohci@4c000000 {
- compatible = "mrvl,pxa-ohci";
+ compatible = "marvell,pxa-ohci";
reg = <0x4c000000 0x10000>;
interrupts = <3>;
status = "disabled";
};

mmc0: mmc@41100000 {
- compatible = "mrvl,pxa-mmc";
+ compatible = "marvell,pxa-mmc";
reg = <0x41100000 0x1000>;
interrupts = <23>;
status = "disabled";
--
1.8.4.2

2014-04-16 17:15:50

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 05/21] ARM: dts: pxa27x: irq init using device tree

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
v2..v3
* no changes

v1..v2
* use of_have_populated_dt() instead of a static variable
* drop #ifdef in header file
* number changed from 8 to 5 (dropped patches)

arch/arm/mach-pxa/include/mach/pxa27x.h | 1 +
arch/arm/mach-pxa/pxa27x.c | 10 ++++++++++
2 files changed, 11 insertions(+)

diff --git a/arch/arm/mach-pxa/include/mach/pxa27x.h b/arch/arm/mach-pxa/include/mach/pxa27x.h
index 7cff640..34a4d6f 100644
--- a/arch/arm/mach-pxa/include/mach/pxa27x.h
+++ b/arch/arm/mach-pxa/include/mach/pxa27x.h
@@ -23,6 +23,7 @@ extern void __init pxa27x_map_io(void);
extern void __init pxa27x_init_irq(void);
extern int __init pxa27x_set_pwrmode(unsigned int mode);
extern void pxa27x_cpu_pm_enter(suspend_state_t state);
+extern void __init pxa27x_dt_init_irq(void);

#define pxa27x_handle_irq ichp_handle_irq

diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index 301471a..c6b54f8 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -398,6 +398,13 @@ void __init pxa27x_init_irq(void)
pxa_init_irq(34, pxa27x_set_wake);
}

+#ifdef CONFIG_OF
+void __init pxa27x_dt_init_irq(void)
+{
+ pxa_dt_irq_init(pxa27x_set_wake);
+}
+#endif /* CONFIG_OF */
+
static struct map_desc pxa27x_io_desc[] __initdata = {
{ /* Mem Ctl */
.virtual = (unsigned long)SMEMC_VIRT,
@@ -471,6 +478,9 @@ static int __init pxa27x_init(void)
register_syscore_ops(&pxa2xx_mfp_syscore_ops);
register_syscore_ops(&pxa2xx_clock_syscore_ops);

+ if (of_have_populated_dt())
+ return 0;
+
pxa_register_device(&pxa27x_device_gpio, &pxa27x_gpio_info);
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
}
--
1.8.4.2

2014-04-16 17:14:26

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 01/21] serial: rewrite pxa2xx-uart to use 8250_core

pxa2xx-uart was a separate uart platform driver. It was declaring
the same device names and numbers as 8250 driver. As a result,
it was impossible to use 8250 driver on PXA SoCs.

Upon closer examination pxa2xx-uart turned out to be a clone of
8250_core driver.

Workaround for Erratum #19 according to Marvel(R) PXA270M Processor
Specification Update (April 19, 2010) is dropped. 8250_core reads
from FIFO immediately after checking DR bit in LSR.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Heikki Krogerus <[email protected]>
Reviewed-by: James Cameron <[email protected]>
Acked-by: Greg Kroah-Hartman <[email protected]>
---
Resenging together with the rest of the series as the series
breaks without this one at [PATCH 12/21]

no changes v3..v4

changes v2..v3
* remove devm_free/put as suggested by Heikki Krogerus
* use SET_SYSTEM_SLEEP_PM_OPS macro to set pm ops as suggested
by Heikki Krogerus

changes v1..v2
* actually implement workaround for E74 in dl_write as spooted
by James Cameron
* added comment about E19 in commit message

arch/arm/configs/am200epdkit_defconfig | 3 +-
arch/arm/configs/cm_x2xx_defconfig | 3 +-
arch/arm/configs/cm_x300_defconfig | 3 +-
arch/arm/configs/colibri_pxa270_defconfig | 3 +-
arch/arm/configs/colibri_pxa300_defconfig | 3 +-
arch/arm/configs/corgi_defconfig | 4 +-
arch/arm/configs/em_x270_defconfig | 3 +-
arch/arm/configs/ezx_defconfig | 3 +-
arch/arm/configs/h5000_defconfig | 3 +-
arch/arm/configs/imote2_defconfig | 3 +-
arch/arm/configs/lpd270_defconfig | 3 +-
arch/arm/configs/lubbock_defconfig | 3 +-
arch/arm/configs/mainstone_defconfig | 3 +-
arch/arm/configs/mmp2_defconfig | 3 +-
arch/arm/configs/pcm027_defconfig | 3 +-
arch/arm/configs/pxa168_defconfig | 3 +-
arch/arm/configs/pxa255-idp_defconfig | 3 +-
arch/arm/configs/pxa3xx_defconfig | 3 +-
arch/arm/configs/pxa910_defconfig | 3 +-
arch/arm/configs/raumfeld_defconfig | 3 +-
arch/arm/configs/spitz_defconfig | 4 +-
arch/arm/configs/trizeps4_defconfig | 3 +-
arch/arm/configs/viper_defconfig | 4 +-
arch/arm/configs/xcep_defconfig | 3 +-
drivers/tty/serial/8250/8250_pxa.c | 178 ++++++
drivers/tty/serial/8250/Kconfig | 9 +
drivers/tty/serial/8250/Makefile | 1 +
drivers/tty/serial/Kconfig | 23 -
drivers/tty/serial/Makefile | 1 -
drivers/tty/serial/pxa.c | 971 ------------------------------
30 files changed, 236 insertions(+), 1022 deletions(-)
create mode 100644 drivers/tty/serial/8250/8250_pxa.c
delete mode 100644 drivers/tty/serial/pxa.c

diff --git a/arch/arm/configs/am200epdkit_defconfig b/arch/arm/configs/am200epdkit_defconfig
index f0dea52..0cde234 100644
--- a/arch/arm/configs/am200epdkit_defconfig
+++ b/arch/arm/configs/am200epdkit_defconfig
@@ -60,8 +60,9 @@ CONFIG_BLK_DEV_IDECS=m
CONFIG_NETDEVICES=y
CONFIG_NET_ETHERNET=y
CONFIG_SMC91X=m
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
diff --git a/arch/arm/configs/cm_x2xx_defconfig b/arch/arm/configs/cm_x2xx_defconfig
index a93ff8d..b9fbe65 100644
--- a/arch/arm/configs/cm_x2xx_defconfig
+++ b/arch/arm/configs/cm_x2xx_defconfig
@@ -96,8 +96,9 @@ CONFIG_KEYBOARD_PXA27x=m
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_UCB1400=m
# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_LEGACY_PTY_COUNT=16
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/cm_x300_defconfig b/arch/arm/configs/cm_x300_defconfig
index f4b7672..53a82ae 100644
--- a/arch/arm/configs/cm_x300_defconfig
+++ b/arch/arm/configs/cm_x300_defconfig
@@ -80,8 +80,9 @@ CONFIG_TOUCHSCREEN_WM97XX=m
# CONFIG_TOUCHSCREEN_WM9713 is not set
# CONFIG_SERIO is not set
# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_PXA=y
diff --git a/arch/arm/configs/colibri_pxa270_defconfig b/arch/arm/configs/colibri_pxa270_defconfig
index 2ef2c5e..1ce0409 100644
--- a/arch/arm/configs/colibri_pxa270_defconfig
+++ b/arch/arm/configs/colibri_pxa270_defconfig
@@ -103,8 +103,9 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
diff --git a/arch/arm/configs/colibri_pxa300_defconfig b/arch/arm/configs/colibri_pxa300_defconfig
index b985334..f96bda0 100644
--- a/arch/arm/configs/colibri_pxa300_defconfig
+++ b/arch/arm/configs/colibri_pxa300_defconfig
@@ -31,8 +31,9 @@ CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
CONFIG_INPUT_GPIO_ROTARY_ENCODER=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_DEBUG_GPIO=y
# CONFIG_HWMON is not set
diff --git a/arch/arm/configs/corgi_defconfig b/arch/arm/configs/corgi_defconfig
index 1fd1d1d..bb4842d 100644
--- a/arch/arm/configs/corgi_defconfig
+++ b/arch/arm/configs/corgi_defconfig
@@ -131,10 +131,10 @@ CONFIG_TOUCHSCREEN_ADS7846=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
# CONFIG_SERIO is not set
-CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_CS=m
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_I2C=y
CONFIG_I2C_PXA=y
diff --git a/arch/arm/configs/em_x270_defconfig b/arch/arm/configs/em_x270_defconfig
index 60a21e0..ec0ec54 100644
--- a/arch/arm/configs/em_x270_defconfig
+++ b/arch/arm/configs/em_x270_defconfig
@@ -90,8 +90,9 @@ CONFIG_TOUCHSCREEN_WM97XX=m
# CONFIG_TOUCHSCREEN_WM9705 is not set
# CONFIG_TOUCHSCREEN_WM9713 is not set
# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_LEGACY_PTY_COUNT=16
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/ezx_defconfig b/arch/arm/configs/ezx_defconfig
index d95763d..631e2ec 100644
--- a/arch/arm/configs/ezx_defconfig
+++ b/arch/arm/configs/ezx_defconfig
@@ -215,8 +215,9 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_PCAP=y
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_LEGACY_PTY_COUNT=8
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/h5000_defconfig b/arch/arm/configs/h5000_defconfig
index 37903e3..655b735 100644
--- a/arch/arm/configs/h5000_defconfig
+++ b/arch/arm/configs/h5000_defconfig
@@ -47,8 +47,9 @@ CONFIG_MTD_PHYSMAP=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_LEGACY_PTY_COUNT=32
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
diff --git a/arch/arm/configs/imote2_defconfig b/arch/arm/configs/imote2_defconfig
index fd996bb..49d45e9 100644
--- a/arch/arm/configs/imote2_defconfig
+++ b/arch/arm/configs/imote2_defconfig
@@ -193,8 +193,9 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_LEGACY_PTY_COUNT=8
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/lpd270_defconfig b/arch/arm/configs/lpd270_defconfig
index 1c8c9ee..c3927b6 100644
--- a/arch/arm/configs/lpd270_defconfig
+++ b/arch/arm/configs/lpd270_defconfig
@@ -38,8 +38,9 @@ CONFIG_SMC91X=y
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_FB=y
CONFIG_FB_PXA=y
diff --git a/arch/arm/configs/lubbock_defconfig b/arch/arm/configs/lubbock_defconfig
index c4ba274..c8b0436 100644
--- a/arch/arm/configs/lubbock_defconfig
+++ b/arch/arm/configs/lubbock_defconfig
@@ -37,8 +37,9 @@ CONFIG_PCMCIA_PCNET=y
CONFIG_INPUT_EVDEV=y
# CONFIG_SERIO_SERPORT is not set
CONFIG_SERIO_SA1111=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_USB_GADGET=y
CONFIG_USB_G_SERIAL=m
diff --git a/arch/arm/configs/mainstone_defconfig b/arch/arm/configs/mainstone_defconfig
index 04efa1b..768892c 100644
--- a/arch/arm/configs/mainstone_defconfig
+++ b/arch/arm/configs/mainstone_defconfig
@@ -34,8 +34,9 @@ CONFIG_SMC91X=y
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_FB=y
CONFIG_FB_PXA=y
# CONFIG_VGA_CONSOLE is not set
diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index f1cb95e..1ced9df 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -44,8 +44,9 @@ CONFIG_SMC91X=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/pcm027_defconfig b/arch/arm/configs/pcm027_defconfig
index 2f136c3..1280128 100644
--- a/arch/arm/configs/pcm027_defconfig
+++ b/arch/arm/configs/pcm027_defconfig
@@ -60,8 +60,9 @@ CONFIG_SMC91X=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/pxa168_defconfig b/arch/arm/configs/pxa168_defconfig
index 74d7e01..1668dac 100644
--- a/arch/arm/configs/pxa168_defconfig
+++ b/arch/arm/configs/pxa168_defconfig
@@ -40,8 +40,9 @@ CONFIG_SMC91X=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
diff --git a/arch/arm/configs/pxa255-idp_defconfig b/arch/arm/configs/pxa255-idp_defconfig
index 917a070..399a706 100644
--- a/arch/arm/configs/pxa255-idp_defconfig
+++ b/arch/arm/configs/pxa255-idp_defconfig
@@ -35,8 +35,9 @@ CONFIG_SMC91X=y
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_FB=y
CONFIG_FB_PXA=y
# CONFIG_VGA_CONSOLE is not set
diff --git a/arch/arm/configs/pxa3xx_defconfig b/arch/arm/configs/pxa3xx_defconfig
index 60e3138..7c3e052 100644
--- a/arch/arm/configs/pxa3xx_defconfig
+++ b/arch/arm/configs/pxa3xx_defconfig
@@ -56,8 +56,9 @@ CONFIG_KEYBOARD_PXA27x=y
CONFIG_KEYBOARD_PXA930_ROTARY=y
CONFIG_MOUSE_PXA930_TRKBALL=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/pxa910_defconfig b/arch/arm/configs/pxa910_defconfig
index 3bb7771..cdacfcb 100644
--- a/arch/arm/configs/pxa910_defconfig
+++ b/arch/arm/configs/pxa910_defconfig
@@ -40,8 +40,9 @@ CONFIG_SMC91X=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_SPI=y
CONFIG_FB=y
CONFIG_MMP_DISP=y
diff --git a/arch/arm/configs/raumfeld_defconfig b/arch/arm/configs/raumfeld_defconfig
index f7caa90..f1e16f2 100644
--- a/arch/arm/configs/raumfeld_defconfig
+++ b/arch/arm/configs/raumfeld_defconfig
@@ -67,8 +67,9 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_EETI=m
CONFIG_INPUT_MISC=y
CONFIG_INPUT_GPIO_ROTARY_ENCODER=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig
index 2e0419d..b6efcf5 100644
--- a/arch/arm/configs/spitz_defconfig
+++ b/arch/arm/configs/spitz_defconfig
@@ -128,10 +128,10 @@ CONFIG_TOUCHSCREEN_ADS7846=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
# CONFIG_SERIO is not set
-CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_CS=m
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_SPI=y
CONFIG_SPI_PXA2XX=y
diff --git a/arch/arm/configs/trizeps4_defconfig b/arch/arm/configs/trizeps4_defconfig
index 3162173..453c79c 100644
--- a/arch/arm/configs/trizeps4_defconfig
+++ b/arch/arm/configs/trizeps4_defconfig
@@ -132,8 +132,9 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
diff --git a/arch/arm/configs/viper_defconfig b/arch/arm/configs/viper_defconfig
index d36e0d3..4efac06 100644
--- a/arch/arm/configs/viper_defconfig
+++ b/arch/arm/configs/viper_defconfig
@@ -101,11 +101,11 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
# CONFIG_CONSOLE_TRANSLATIONS is not set
# CONFIG_VT_CONSOLE is not set
-CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=5
CONFIG_SERIAL_8250_RUNTIME_UARTS=5
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
diff --git a/arch/arm/configs/xcep_defconfig b/arch/arm/configs/xcep_defconfig
index 721832f..b67aeaf 100644
--- a/arch/arm/configs/xcep_defconfig
+++ b/arch/arm/configs/xcep_defconfig
@@ -60,8 +60,9 @@ CONFIG_NET_ETHERNET=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_PXA=y
-CONFIG_SERIAL_PXA_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=m
diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c
new file mode 100644
index 0000000..0da0d40
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_pxa.c
@@ -0,0 +1,178 @@
+/*
+ * drivers/tty/serial/8250/8250_pxa.c -- driver for PXA on-board UARTS
+ * Copyright: (C) 2013 Sergei Ianovich <[email protected]>
+ *
+ * replaces drivers/serial/pxa.c by Nicolas Pitre
+ * Created: Feb 20, 2003
+ * Copyright: (C) 2003 Monta Vista Software, Inc.
+ *
+ * Based on drivers/serial/8250.c by Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+#include "8250.h"
+
+struct pxa8250_data {
+ int line;
+ struct clk *clk;
+};
+
+#ifdef CONFIG_PM
+static int serial_pxa_suspend(struct device *dev)
+{
+ struct pxa8250_data *data = dev_get_drvdata(dev);
+
+ serial8250_suspend_port(data->line);
+
+ return 0;
+}
+
+static int serial_pxa_resume(struct device *dev)
+{
+ struct pxa8250_data *data = dev_get_drvdata(dev);
+
+ serial8250_resume_port(data->line);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops serial_pxa_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume)
+};
+
+static struct of_device_id serial_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-uart", },
+ { .compatible = "mrvl,mmp-uart", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
+
+/* Uart divisor latch write */
+static void serial_pxa_dl_write(struct uart_8250_port *up, int value)
+{
+ unsigned int dll;
+
+ serial_out(up, UART_DLL, value & 0xff);
+ /*
+ * work around Erratum #74 according to Marvel(R) PXA270M Processor
+ * Specification Update (April 19, 2010)
+ */
+ dll = serial_in(up, UART_DLL);
+ WARN_ON(dll != (value & 0xff));
+
+ serial_out(up, UART_DLM, value >> 8 & 0xff);
+}
+
+
+static void serial_pxa_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct pxa8250_data *data = port->private_data;
+
+ if (!state)
+ clk_prepare_enable(data->clk);
+ else
+ clk_disable_unprepare(data->clk);
+}
+
+static int serial_pxa_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port uart = {};
+ struct pxa8250_data *data;
+ struct resource *mmres, *irqres;
+ int ret;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mmres || !irqres)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(data->clk))
+ return PTR_ERR(data->clk);
+
+ ret = clk_prepare(data->clk);
+ if (ret)
+ return ret;
+
+ uart.port.type = PORT_XSCALE;
+ uart.port.iotype = UPIO_MEM32;
+ uart.port.mapbase = mmres->start;
+ uart.port.regshift = 2;
+ uart.port.irq = irqres->start;
+ uart.port.fifosize = 64;
+ uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST;
+ uart.port.dev = &pdev->dev;
+ uart.port.uartclk = clk_get_rate(data->clk);
+ uart.port.pm = serial_pxa_pm;
+ uart.port.private_data = data;
+ uart.dl_write = serial_pxa_dl_write;
+
+ ret = serial8250_register_8250_port(&uart);
+ if (ret < 0)
+ goto err_clk;
+
+ data->line = ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+
+ err_clk:
+ clk_unprepare(data->clk);
+ return ret;
+}
+
+static int serial_pxa_remove(struct platform_device *pdev)
+{
+ struct pxa8250_data *data = platform_get_drvdata(pdev);
+
+ serial8250_unregister_port(data->line);
+
+ clk_unprepare(data->clk);
+
+ return 0;
+}
+
+static struct platform_driver serial_pxa_driver = {
+ .probe = serial_pxa_probe,
+ .remove = serial_pxa_remove,
+
+ .driver = {
+ .name = "pxa2xx-uart",
+ .owner = THIS_MODULE,
+ .pm = &serial_pxa_pm_ops,
+ .of_match_table = serial_pxa_dt_ids,
+ },
+};
+
+module_platform_driver(serial_pxa_driver);
+
+MODULE_AUTHOR("Sergei Ianovich");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-uart");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 2332991..81bd7c9 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -302,3 +302,12 @@ config SERIAL_8250_RT288X
If you have a Ralink RT288x/RT305x SoC based board and want to use the
serial port, say Y to this option. The driver can handle up to 2 serial
ports. If unsure, say N.
+
+config SERIAL_PXA
+ tristate "PXA serial port support"
+ depends on SERIAL_8250 && (ARCH_PXA || ARCH_MMP)
+ help
+ If you have a machine based on an Intel XScale PXA2xx CPU you
+ can enable its onboard serial ports by enabling this option.
+
+ If you choose M here, the module name will be 8250_pxa.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 36d68d0..b7d1b61 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
+obj-$(CONFIG_SERIAL_PXA) += 8250_pxa.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2e6d8dd..da46e31 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -395,29 +395,6 @@ config SERIAL_MPSC_CONSOLE
help
Say Y here if you want to support a serial console on a Marvell MPSC.

-config SERIAL_PXA
- bool "PXA serial port support"
- depends on ARCH_PXA || ARCH_MMP
- select SERIAL_CORE
- help
- If you have a machine based on an Intel XScale PXA2xx CPU you
- can enable its onboard serial ports by enabling this option.
-
-config SERIAL_PXA_CONSOLE
- bool "Console on PXA serial port"
- depends on SERIAL_PXA
- select SERIAL_CORE_CONSOLE
- help
- If you have enabled the serial port on the Intel XScale PXA
- CPU you can make it the console by answering Y to this option.
-
- Even if you say Y here, the currently visible virtual console
- (/dev/tty0) will still be used as the system console by default, but
- you can alter that using a kernel command line option such as
- "console=ttySA0". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
-
config SERIAL_SA1100
bool "SA1100 serial port support"
depends on ARCH_SA1100
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 3680854..fd71d3f 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -20,7 +20,6 @@ obj-$(CONFIG_SERIAL_8250) += 8250/
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
-obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
deleted file mode 100644
index f9f20f3..0000000
--- a/drivers/tty/serial/pxa.c
+++ /dev/null
@@ -1,971 +0,0 @@
-/*
- * Based on drivers/serial/8250.c by Russell King.
- *
- * Author: Nicolas Pitre
- * Created: Feb 20, 2003
- * Copyright: (C) 2003 Monta Vista Software, Inc.
- *
- * 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.
- *
- * Note 1: This driver is made separate from the already too overloaded
- * 8250.c because it needs some kirks of its own and that'll make it
- * easier to add DMA support.
- *
- * Note 2: I'm too sick of device allocation policies for serial ports.
- * If someone else wants to request an "official" allocation of major/minor
- * for this driver please be my guest. And don't forget that new hardware
- * to come from Intel might have more than 3 or 4 of those UARTs. Let's
- * hope for a better port registration and dynamic device allocation scheme
- * with the serial core maintainer satisfaction to appear soon.
- */
-
-
-#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/serial_reg.h>
-#include <linux/circ_buf.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#define PXA_NAME_LEN 8
-
-struct uart_pxa_port {
- struct uart_port port;
- unsigned char ier;
- unsigned char lcr;
- unsigned char mcr;
- unsigned int lsr_break_flag;
- struct clk *clk;
- char name[PXA_NAME_LEN];
-};
-
-static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
-{
- offset <<= 2;
- return readl(up->port.membase + offset);
-}
-
-static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
-{
- offset <<= 2;
- writel(value, up->port.membase + offset);
-}
-
-static void serial_pxa_enable_ms(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- up->ier |= UART_IER_MSI;
- serial_out(up, UART_IER, up->ier);
-}
-
-static void serial_pxa_stop_tx(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- if (up->ier & UART_IER_THRI) {
- up->ier &= ~UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- }
-}
-
-static void serial_pxa_stop_rx(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- up->ier &= ~UART_IER_RLSI;
- up->port.read_status_mask &= ~UART_LSR_DR;
- serial_out(up, UART_IER, up->ier);
-}
-
-static inline void receive_chars(struct uart_pxa_port *up, int *status)
-{
- unsigned int ch, flag;
- int max_count = 256;
-
- do {
- /* work around Errata #20 according to
- * Intel(R) PXA27x Processor Family
- * Specification Update (May 2005)
- *
- * Step 2
- * Disable the Reciever Time Out Interrupt via IER[RTOEI]
- */
- up->ier &= ~UART_IER_RTOIE;
- serial_out(up, UART_IER, up->ier);
-
- ch = serial_in(up, UART_RX);
- flag = TTY_NORMAL;
- up->port.icount.rx++;
-
- if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
- UART_LSR_FE | UART_LSR_OE))) {
- /*
- * For statistics only
- */
- if (*status & UART_LSR_BI) {
- *status &= ~(UART_LSR_FE | UART_LSR_PE);
- up->port.icount.brk++;
- /*
- * We do the SysRQ and SAK checking
- * here because otherwise the break
- * may get masked by ignore_status_mask
- * or read_status_mask.
- */
- if (uart_handle_break(&up->port))
- goto ignore_char;
- } else if (*status & UART_LSR_PE)
- up->port.icount.parity++;
- else if (*status & UART_LSR_FE)
- up->port.icount.frame++;
- if (*status & UART_LSR_OE)
- up->port.icount.overrun++;
-
- /*
- * Mask off conditions which should be ignored.
- */
- *status &= up->port.read_status_mask;
-
-#ifdef CONFIG_SERIAL_PXA_CONSOLE
- if (up->port.line == up->port.cons->index) {
- /* Recover the break flag from console xmit */
- *status |= up->lsr_break_flag;
- up->lsr_break_flag = 0;
- }
-#endif
- if (*status & UART_LSR_BI) {
- flag = TTY_BREAK;
- } else if (*status & UART_LSR_PE)
- flag = TTY_PARITY;
- else if (*status & UART_LSR_FE)
- flag = TTY_FRAME;
- }
-
- if (uart_handle_sysrq_char(&up->port, ch))
- goto ignore_char;
-
- uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
-
- ignore_char:
- *status = serial_in(up, UART_LSR);
- } while ((*status & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(&up->port.state->port);
-
- /* work around Errata #20 according to
- * Intel(R) PXA27x Processor Family
- * Specification Update (May 2005)
- *
- * Step 6:
- * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE]
- */
- up->ier |= UART_IER_RTOIE;
- serial_out(up, UART_IER, up->ier);
-}
-
-static void transmit_chars(struct uart_pxa_port *up)
-{
- struct circ_buf *xmit = &up->port.state->xmit;
- int count;
-
- if (up->port.x_char) {
- serial_out(up, UART_TX, up->port.x_char);
- up->port.icount.tx++;
- up->port.x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- serial_pxa_stop_tx(&up->port);
- return;
- }
-
- count = up->port.fifosize / 2;
- do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-
-
- if (uart_circ_empty(xmit))
- serial_pxa_stop_tx(&up->port);
-}
-
-static void serial_pxa_start_tx(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- if (!(up->ier & UART_IER_THRI)) {
- up->ier |= UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- }
-}
-
-static inline void check_modem_status(struct uart_pxa_port *up)
-{
- int status;
-
- status = serial_in(up, UART_MSR);
-
- if ((status & UART_MSR_ANY_DELTA) == 0)
- return;
-
- if (status & UART_MSR_TERI)
- up->port.icount.rng++;
- if (status & UART_MSR_DDSR)
- up->port.icount.dsr++;
- if (status & UART_MSR_DDCD)
- uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
- if (status & UART_MSR_DCTS)
- uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
-
- wake_up_interruptible(&up->port.state->port.delta_msr_wait);
-}
-
-/*
- * This handles the interrupt from one port.
- */
-static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
-{
- struct uart_pxa_port *up = dev_id;
- unsigned int iir, lsr;
-
- iir = serial_in(up, UART_IIR);
- if (iir & UART_IIR_NO_INT)
- return IRQ_NONE;
- lsr = serial_in(up, UART_LSR);
- if (lsr & UART_LSR_DR)
- receive_chars(up, &lsr);
- check_modem_status(up);
- if (lsr & UART_LSR_THRE)
- transmit_chars(up);
- return IRQ_HANDLED;
-}
-
-static unsigned int serial_pxa_tx_empty(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&up->port.lock, flags);
- ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- return ret;
-}
-
-static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned char status;
- unsigned int ret;
-
- status = serial_in(up, UART_MSR);
-
- ret = 0;
- if (status & UART_MSR_DCD)
- ret |= TIOCM_CAR;
- if (status & UART_MSR_RI)
- ret |= TIOCM_RNG;
- if (status & UART_MSR_DSR)
- ret |= TIOCM_DSR;
- if (status & UART_MSR_CTS)
- ret |= TIOCM_CTS;
- return ret;
-}
-
-static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned char mcr = 0;
-
- if (mctrl & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
- if (mctrl & TIOCM_DTR)
- mcr |= UART_MCR_DTR;
- if (mctrl & TIOCM_OUT1)
- mcr |= UART_MCR_OUT1;
- if (mctrl & TIOCM_OUT2)
- mcr |= UART_MCR_OUT2;
- if (mctrl & TIOCM_LOOP)
- mcr |= UART_MCR_LOOP;
-
- mcr |= up->mcr;
-
- serial_out(up, UART_MCR, mcr);
-}
-
-static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned long flags;
-
- spin_lock_irqsave(&up->port.lock, flags);
- if (break_state == -1)
- up->lcr |= UART_LCR_SBC;
- else
- up->lcr &= ~UART_LCR_SBC;
- serial_out(up, UART_LCR, up->lcr);
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static int serial_pxa_startup(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned long flags;
- int retval;
-
- if (port->line == 3) /* HWUART */
- up->mcr |= UART_MCR_AFE;
- else
- up->mcr = 0;
-
- up->port.uartclk = clk_get_rate(up->clk);
-
- /*
- * Allocate the IRQ
- */
- retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
- if (retval)
- return retval;
-
- /*
- * Clear the FIFO buffers and disable them.
- * (they will be reenabled in set_termios())
- */
- serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
- serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
- serial_out(up, UART_FCR, 0);
-
- /*
- * Clear the interrupt registers.
- */
- (void) serial_in(up, UART_LSR);
- (void) serial_in(up, UART_RX);
- (void) serial_in(up, UART_IIR);
- (void) serial_in(up, UART_MSR);
-
- /*
- * Now, initialize the UART
- */
- serial_out(up, UART_LCR, UART_LCR_WLEN8);
-
- spin_lock_irqsave(&up->port.lock, flags);
- up->port.mctrl |= TIOCM_OUT2;
- serial_pxa_set_mctrl(&up->port, up->port.mctrl);
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- /*
- * Finally, enable interrupts. Note: Modem status interrupts
- * are set via set_termios(), which will be occurring imminently
- * anyway, so we don't enable them here.
- */
- up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
- serial_out(up, UART_IER, up->ier);
-
- /*
- * And clear the interrupt registers again for luck.
- */
- (void) serial_in(up, UART_LSR);
- (void) serial_in(up, UART_RX);
- (void) serial_in(up, UART_IIR);
- (void) serial_in(up, UART_MSR);
-
- return 0;
-}
-
-static void serial_pxa_shutdown(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned long flags;
-
- free_irq(up->port.irq, up);
-
- /*
- * Disable interrupts from this port
- */
- up->ier = 0;
- serial_out(up, UART_IER, 0);
-
- spin_lock_irqsave(&up->port.lock, flags);
- up->port.mctrl &= ~TIOCM_OUT2;
- serial_pxa_set_mctrl(&up->port, up->port.mctrl);
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- /*
- * Disable break condition and FIFOs
- */
- serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
- serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR |
- UART_FCR_CLEAR_XMIT);
- serial_out(up, UART_FCR, 0);
-}
-
-static void
-serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned char cval, fcr = 0;
- unsigned long flags;
- unsigned int baud, quot;
- unsigned int dll;
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- cval = UART_LCR_WLEN5;
- break;
- case CS6:
- cval = UART_LCR_WLEN6;
- break;
- case CS7:
- cval = UART_LCR_WLEN7;
- break;
- default:
- case CS8:
- cval = UART_LCR_WLEN8;
- break;
- }
-
- if (termios->c_cflag & CSTOPB)
- cval |= UART_LCR_STOP;
- if (termios->c_cflag & PARENB)
- cval |= UART_LCR_PARITY;
- if (!(termios->c_cflag & PARODD))
- cval |= UART_LCR_EPAR;
-
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
- quot = uart_get_divisor(port, baud);
-
- if ((up->port.uartclk / quot) < (2400 * 16))
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
- else if ((up->port.uartclk / quot) < (230400 * 16))
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
- else
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
-
- /*
- * Ok, we're now changing the port state. Do it with
- * interrupts disabled.
- */
- spin_lock_irqsave(&up->port.lock, flags);
-
- /*
- * Ensure the port will be enabled.
- * This is required especially for serial console.
- */
- up->ier |= UART_IER_UUE;
-
- /*
- * Update the per-port timeout.
- */
- uart_update_timeout(port, termios->c_cflag, baud);
-
- up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- if (termios->c_iflag & INPCK)
- up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
- up->port.read_status_mask |= UART_LSR_BI;
-
- /*
- * Characters to ignore
- */
- up->port.ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- if (termios->c_iflag & IGNBRK) {
- up->port.ignore_status_mask |= UART_LSR_BI;
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns too (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_OE;
- }
-
- /*
- * ignore all characters if CREAD is not set
- */
- if ((termios->c_cflag & CREAD) == 0)
- up->port.ignore_status_mask |= UART_LSR_DR;
-
- /*
- * CTS flow control flag and modem status interrupts
- */
- up->ier &= ~UART_IER_MSI;
- if (UART_ENABLE_MS(&up->port, termios->c_cflag))
- up->ier |= UART_IER_MSI;
-
- serial_out(up, UART_IER, up->ier);
-
- if (termios->c_cflag & CRTSCTS)
- up->mcr |= UART_MCR_AFE;
- else
- up->mcr &= ~UART_MCR_AFE;
-
- serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
- serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
-
- /*
- * work around Errata #75 according to Intel(R) PXA27x Processor Family
- * Specification Update (Nov 2005)
- */
- dll = serial_in(up, UART_DLL);
- WARN_ON(dll != (quot & 0xff));
-
- serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
- serial_out(up, UART_LCR, cval); /* reset DLAB */
- up->lcr = cval; /* Save LCR */
- serial_pxa_set_mctrl(&up->port, up->port.mctrl);
- serial_out(up, UART_FCR, fcr);
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static void
-serial_pxa_pm(struct uart_port *port, unsigned int state,
- unsigned int oldstate)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- if (!state)
- clk_prepare_enable(up->clk);
- else
- clk_disable_unprepare(up->clk);
-}
-
-static void serial_pxa_release_port(struct uart_port *port)
-{
-}
-
-static int serial_pxa_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-static void serial_pxa_config_port(struct uart_port *port, int flags)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- up->port.type = PORT_PXA;
-}
-
-static int
-serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- /* we don't want the core code to modify any port params */
- return -EINVAL;
-}
-
-static const char *
-serial_pxa_type(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- return up->name;
-}
-
-static struct uart_pxa_port *serial_pxa_ports[4];
-static struct uart_driver serial_pxa_reg;
-
-#ifdef CONFIG_SERIAL_PXA_CONSOLE
-
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
-/*
- * Wait for transmitter & holding register to empty
- */
-static inline void wait_for_xmitr(struct uart_pxa_port *up)
-{
- unsigned int status, tmout = 10000;
-
- /* Wait up to 10ms for the character(s) to be sent. */
- do {
- status = serial_in(up, UART_LSR);
-
- if (status & UART_LSR_BI)
- up->lsr_break_flag = UART_LSR_BI;
-
- if (--tmout == 0)
- break;
- udelay(1);
- } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
-
- /* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
- tmout = 1000000;
- while (--tmout &&
- ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
- udelay(1);
- }
-}
-
-static void serial_pxa_console_putchar(struct uart_port *port, int ch)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- wait_for_xmitr(up);
- serial_out(up, UART_TX, ch);
-}
-
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- * The console_lock must be held when we get here.
- */
-static void
-serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct uart_pxa_port *up = serial_pxa_ports[co->index];
- unsigned int ier;
- unsigned long flags;
- int locked = 1;
-
- clk_enable(up->clk);
- local_irq_save(flags);
- if (up->port.sysrq)
- locked = 0;
- else if (oops_in_progress)
- locked = spin_trylock(&up->port.lock);
- else
- spin_lock(&up->port.lock);
-
- /*
- * First save the IER then disable the interrupts
- */
- ier = serial_in(up, UART_IER);
- serial_out(up, UART_IER, UART_IER_UUE);
-
- uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
-
- /*
- * Finally, wait for transmitter to become empty
- * and restore the IER
- */
- wait_for_xmitr(up);
- serial_out(up, UART_IER, ier);
-
- if (locked)
- spin_unlock(&up->port.lock);
- local_irq_restore(flags);
- clk_disable(up->clk);
-
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-/*
- * Console polling routines for writing and reading from the uart while
- * in an interrupt or debug context.
- */
-
-static int serial_pxa_get_poll_char(struct uart_port *port)
-{
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
- unsigned char lsr = serial_in(up, UART_LSR);
-
- while (!(lsr & UART_LSR_DR))
- lsr = serial_in(up, UART_LSR);
-
- return serial_in(up, UART_RX);
-}
-
-
-static void serial_pxa_put_poll_char(struct uart_port *port,
- unsigned char c)
-{
- unsigned int ier;
- struct uart_pxa_port *up = (struct uart_pxa_port *)port;
-
- /*
- * First save the IER then disable the interrupts
- */
- ier = serial_in(up, UART_IER);
- serial_out(up, UART_IER, UART_IER_UUE);
-
- wait_for_xmitr(up);
- /*
- * Send the character out.
- * If a LF, also do CR...
- */
- serial_out(up, UART_TX, c);
- if (c == 10) {
- wait_for_xmitr(up);
- serial_out(up, UART_TX, 13);
- }
-
- /*
- * Finally, wait for transmitter to become empty
- * and restore the IER
- */
- wait_for_xmitr(up);
- serial_out(up, UART_IER, ier);
-}
-
-#endif /* CONFIG_CONSOLE_POLL */
-
-static int __init
-serial_pxa_console_setup(struct console *co, char *options)
-{
- struct uart_pxa_port *up;
- int baud = 9600;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- if (co->index == -1 || co->index >= serial_pxa_reg.nr)
- co->index = 0;
- up = serial_pxa_ports[co->index];
- if (!up)
- return -ENODEV;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(&up->port, co, baud, parity, bits, flow);
-}
-
-static struct console serial_pxa_console = {
- .name = "ttyS",
- .write = serial_pxa_console_write,
- .device = uart_console_device,
- .setup = serial_pxa_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &serial_pxa_reg,
-};
-
-#define PXA_CONSOLE &serial_pxa_console
-#else
-#define PXA_CONSOLE NULL
-#endif
-
-static struct uart_ops serial_pxa_pops = {
- .tx_empty = serial_pxa_tx_empty,
- .set_mctrl = serial_pxa_set_mctrl,
- .get_mctrl = serial_pxa_get_mctrl,
- .stop_tx = serial_pxa_stop_tx,
- .start_tx = serial_pxa_start_tx,
- .stop_rx = serial_pxa_stop_rx,
- .enable_ms = serial_pxa_enable_ms,
- .break_ctl = serial_pxa_break_ctl,
- .startup = serial_pxa_startup,
- .shutdown = serial_pxa_shutdown,
- .set_termios = serial_pxa_set_termios,
- .pm = serial_pxa_pm,
- .type = serial_pxa_type,
- .release_port = serial_pxa_release_port,
- .request_port = serial_pxa_request_port,
- .config_port = serial_pxa_config_port,
- .verify_port = serial_pxa_verify_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_get_char = serial_pxa_get_poll_char,
- .poll_put_char = serial_pxa_put_poll_char,
-#endif
-};
-
-static struct uart_driver serial_pxa_reg = {
- .owner = THIS_MODULE,
- .driver_name = "PXA serial",
- .dev_name = "ttyS",
- .major = TTY_MAJOR,
- .minor = 64,
- .nr = 4,
- .cons = PXA_CONSOLE,
-};
-
-#ifdef CONFIG_PM
-static int serial_pxa_suspend(struct device *dev)
-{
- struct uart_pxa_port *sport = dev_get_drvdata(dev);
-
- if (sport)
- uart_suspend_port(&serial_pxa_reg, &sport->port);
-
- return 0;
-}
-
-static int serial_pxa_resume(struct device *dev)
-{
- struct uart_pxa_port *sport = dev_get_drvdata(dev);
-
- if (sport)
- uart_resume_port(&serial_pxa_reg, &sport->port);
-
- return 0;
-}
-
-static const struct dev_pm_ops serial_pxa_pm_ops = {
- .suspend = serial_pxa_suspend,
- .resume = serial_pxa_resume,
-};
-#endif
-
-static struct of_device_id serial_pxa_dt_ids[] = {
- { .compatible = "mrvl,pxa-uart", },
- { .compatible = "mrvl,mmp-uart", },
- {}
-};
-MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
-
-static int serial_pxa_probe_dt(struct platform_device *pdev,
- struct uart_pxa_port *sport)
-{
- struct device_node *np = pdev->dev.of_node;
- int ret;
-
- if (!np)
- return 1;
-
- ret = of_alias_get_id(np, "serial");
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
- return ret;
- }
- sport->port.line = ret;
- return 0;
-}
-
-static int serial_pxa_probe(struct platform_device *dev)
-{
- struct uart_pxa_port *sport;
- struct resource *mmres, *irqres;
- int ret;
-
- mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
- irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
- if (!mmres || !irqres)
- return -ENODEV;
-
- sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
- if (!sport)
- return -ENOMEM;
-
- sport->clk = clk_get(&dev->dev, NULL);
- if (IS_ERR(sport->clk)) {
- ret = PTR_ERR(sport->clk);
- goto err_free;
- }
-
- ret = clk_prepare(sport->clk);
- if (ret) {
- clk_put(sport->clk);
- goto err_free;
- }
-
- sport->port.type = PORT_PXA;
- sport->port.iotype = UPIO_MEM;
- sport->port.mapbase = mmres->start;
- sport->port.irq = irqres->start;
- sport->port.fifosize = 64;
- sport->port.ops = &serial_pxa_pops;
- sport->port.dev = &dev->dev;
- sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- sport->port.uartclk = clk_get_rate(sport->clk);
-
- ret = serial_pxa_probe_dt(dev, sport);
- if (ret > 0)
- sport->port.line = dev->id;
- else if (ret < 0)
- goto err_clk;
- snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
-
- sport->port.membase = ioremap(mmres->start, resource_size(mmres));
- if (!sport->port.membase) {
- ret = -ENOMEM;
- goto err_clk;
- }
-
- serial_pxa_ports[sport->port.line] = sport;
-
- uart_add_one_port(&serial_pxa_reg, &sport->port);
- platform_set_drvdata(dev, sport);
-
- return 0;
-
- err_clk:
- clk_unprepare(sport->clk);
- clk_put(sport->clk);
- err_free:
- kfree(sport);
- return ret;
-}
-
-static int serial_pxa_remove(struct platform_device *dev)
-{
- struct uart_pxa_port *sport = platform_get_drvdata(dev);
-
- uart_remove_one_port(&serial_pxa_reg, &sport->port);
-
- clk_unprepare(sport->clk);
- clk_put(sport->clk);
- kfree(sport);
-
- return 0;
-}
-
-static struct platform_driver serial_pxa_driver = {
- .probe = serial_pxa_probe,
- .remove = serial_pxa_remove,
-
- .driver = {
- .name = "pxa2xx-uart",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &serial_pxa_pm_ops,
-#endif
- .of_match_table = serial_pxa_dt_ids,
- },
-};
-
-static int __init serial_pxa_init(void)
-{
- int ret;
-
- ret = uart_register_driver(&serial_pxa_reg);
- if (ret != 0)
- return ret;
-
- ret = platform_driver_register(&serial_pxa_driver);
- if (ret != 0)
- uart_unregister_driver(&serial_pxa_reg);
-
- return ret;
-}
-
-static void __exit serial_pxa_exit(void)
-{
- platform_driver_unregister(&serial_pxa_driver);
- uart_unregister_driver(&serial_pxa_reg);
-}
-
-module_init(serial_pxa_init);
-module_exit(serial_pxa_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pxa2xx-uart");
--
1.8.4.2

2014-04-16 17:16:59

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 04/21] ARM: dts: pxa3xx: move declaration to header

This way it will be more difficult to change the declaration in one
place, but not the other.

In addition, the change allows to use the binding for pxa-gpio on
other PXA CPUs.

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
v2..v3
* no changes

v1..v2
* drop #ifdef in header file
* number changed from 6 to 4 (dropped patches)

arch/arm/mach-pxa/include/mach/irqs.h | 2 ++
arch/arm/mach-pxa/pxa3xx.c | 2 --
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/include/mach/irqs.h b/arch/arm/mach-pxa/include/mach/irqs.h
index 48c2fd8..732315d 100644
--- a/arch/arm/mach-pxa/include/mach/irqs.h
+++ b/arch/arm/mach-pxa/include/mach/irqs.h
@@ -113,4 +113,6 @@ void ichp_handle_irq(struct pt_regs *);
void pxa_init_irq(int irq_nr, int (*set_wake)(struct irq_data *, unsigned int));
#endif

+extern void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int));
+
#endif /* __ASM_MACH_IRQS_H */
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index 87011f3..2397dec 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -42,8 +42,6 @@
#define PECR_IE(n) ((1 << ((n) * 2)) << 28)
#define PECR_IS(n) ((1 << ((n) * 2)) << 29)

-extern void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int));
-
static DEFINE_PXA3_CKEN(pxa3xx_ffuart, FFUART, 14857000, 1);
static DEFINE_PXA3_CKEN(pxa3xx_btuart, BTUART, 14857000, 1);
static DEFINE_PXA3_CKEN(pxa3xx_stuart, STUART, 14857000, 1);
--
1.8.4.2

2014-04-16 17:18:08

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 07/21] ARM: dts: parse DMA config in pxamci

The existing PXA MMC driver cannot get DMA channels in a proper
way from the respective device tree binding.

This patch provides temporary workaround which allows using the
existing driver in DT machines by pointing to the proper
dmaengine-based "marvell,pdma-1.0" DMA.

Even though the new DMA provider is not present we can parse device node
attributes manually and use channel numbers to acquire DMA channel
from the existing non-dmaengine provider.

When Daniel's DMA series is merged there will be no need to manually
parse for "dmas" and this patch can be safely reverted.

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Haojian Zhuang <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
* no changes

v2..v3
* split into good (PATCH 07/21) and temporary (this one) parts

v1..v2
* add binding for next-gen dma controller
* use correct dma declararion
* number changed from 5 to 3

drivers/mmc/host/pxamci.c | 59 ++++++++++++++++++++++++++++++++++++++---------
1 file changed, 48 insertions(+), 11 deletions(-)

diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 32fe113..6b67764 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -613,11 +613,46 @@ static int pxamci_of_init(struct platform_device *pdev)

return 0;
}
+
+static int pxamci_of_init_dma(struct platform_device *pdev,
+ struct pxamci_host *host)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 tmp;
+ int i;
+ int ret;
+
+ i = of_property_match_string(np, "dma-names", "rx");
+ if (i < 0)
+ return i;
+
+ ret = of_property_read_u32_index(np, "dmas", 2 * i + 1, &tmp);
+ if (ret < 0)
+ return ret;
+ host->dma_drcmrrx = tmp;
+
+ i = of_property_match_string(np, "dma-names", "tx");
+ if (i < 0)
+ return i;
+
+ ret = of_property_read_u32_index(np, "dmas", 2 * i + 1, &tmp);
+ if (ret < 0)
+ return ret;
+ host->dma_drcmrtx = tmp;
+
+ return 0;
+}
#else
static int pxamci_of_init(struct platform_device *pdev)
{
return 0;
}
+
+static int pxamci_of_init_dma(struct platform_device *pdev,
+ struct pxamci_host *host)
+{
+ return -ENODATA;
+}
#endif

static int pxamci_probe(struct platform_device *pdev)
@@ -741,19 +776,21 @@ static int pxamci_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, mmc);

- dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmarx) {
- ret = -ENXIO;
- goto out;
- }
- host->dma_drcmrrx = dmarx->start;
+ if (pxamci_of_init_dma(pdev, host) < 0) {
+ dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmarx) {
+ ret = -ENXIO;
+ goto out;
+ }
+ host->dma_drcmrrx = dmarx->start;

- dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!dmatx) {
- ret = -ENXIO;
- goto out;
+ dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmatx) {
+ ret = -ENXIO;
+ goto out;
+ }
+ host->dma_drcmrtx = dmatx->start;
}
- host->dma_drcmrtx = dmatx->start;

if (host->pdata) {
gpio_cd = host->pdata->gpio_card_detect;
--
1.8.4.2

2014-04-16 17:18:27

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 08/21] ARM: pxa27x: device tree support ICP DAS LP-8x4x

ICP DAS calls LP-8x4x 'programmable automation controller'. It is
an industrial computer based on PXA270 SoC. They ship it with a 2.6.19
kernel and proprietary kernel module and userspace library to access
its industrial IO.

This patch allows to boot the device with a modern kernel with device
tree. It adds support for:
* MMC card interface on PXA270
* USB 1.1 port on PXA270
* 2 NOR flash devices
* 2 onboard ethernet Davicom DM9000 devices
* 3 serial UART ports on PXA270
* front panel red LED
* 64bit 1-wire system ID chip
* 16 kiB EEPROM (reading)

Support for these devices will be added in separate patches, since
they are not currently supported by the kernel:
* DS1302 RTC
* 512kiB SRAM
* FPGA irq chip
* 3 built-in 16550A serial UART ports
* industrial IO parallel bus
* 10 position rotary switch
* 8 pin DIP switch
* 16 kiB EEPROM (writing)
* serial interface for digital and analog industrial IO modules on
parallel bus (all I-87... modules)
* digital and analog industrial IO modules for parallel bus:
** I-8024 4 port analog output
** I-8041 32 port digital output
** I-8042 16 port digital output/16 port digital input

Not supported for now:
* VGA interface on PXA270 for lack of dts binding
* the rest of parallel bus (I-8...) modules for lack of hardware
* GPIO reset for lack of relevance (watchdog reset is working)

Newer devices have twice as much flash and a different partition
structure otherwise being the same. When each device is provided
with a correct device tree, all of them can be booted using the
same kernel.

Signed-off-by: Sergei Ianovich <[email protected]>
CC: Daniel Mack <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3..v4
* move all declarations to one commit so the first DTB file can be
used to boot any later kernel as suggested by Arnd Bergmann,
Heikki Krogerus and Brian Norris
* support new flavor of device (i105 suffix)

v2..v3
* added extbus which maps synchronous, static, and variable-latency
I/O (VLIO) interfaces of PXA27x SoC as suggested by Arnd Bergmann
* map is placed into platform include
* configured existing kernel drivers to support:
- front panel LED using gpio-leds
- 64bit 1-wire system ID chip using w1-gpio
- 16 kiB EEPROM using at24 over i2c-gpio
* number change (06/16 -> 08/21)

v1..v2
* drop left-over extern declaration
* use of_have_populated_dt() instead of a static variable
* drop wildcards in compatible
* drop machine-special machine description
* number changed from 9 to 6 (dropped patches)

.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/pxa27x-lp8x4x-i105.dts | 43 ++++
arch/arm/boot/dts/pxa27x-lp8x4x.dts | 228 +++++++++++++++++++++
arch/arm/boot/dts/pxa27x.dtsi | 17 ++
arch/arm/configs/lp8x4x_defconfig | 168 +++++++++++++++
arch/arm/mach-pxa/Kconfig | 15 ++
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/pxa27x-dt.c | 64 ++++++
9 files changed, 539 insertions(+)
create mode 100644 arch/arm/boot/dts/pxa27x-lp8x4x-i105.dts
create mode 100644 arch/arm/boot/dts/pxa27x-lp8x4x.dts
create mode 100644 arch/arm/configs/lp8x4x_defconfig
create mode 100644 arch/arm/mach-pxa/pxa27x-dt.c

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 0f01c9b..1155c6c 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -54,6 +54,7 @@ hisilicon Hisilicon Limited.
honeywell Honeywell
hp Hewlett Packard
ibm International Business Machines (IBM)
+icpdas ICP DAS CO., LTD.
idt Integrated Device Technologies, Inc.
img Imagination Technologies Ltd.
intel Intel Corporation
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 35c146f..28175d7 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -408,6 +408,8 @@ dtb-$(CONFIG_MACH_DOVE) += dove-cm-a510.dtb \
dove-d2plug.dtb \
dove-d3plug.dtb \
dove-dove-db.dtb
+dtb-$(CONFIG_MACH_PXA27X_DT) += pxa27x-lp8x4x.dtb \
+ pxa27x-lp8x4x-i105.dtb

targets += dtbs dtbs_install
targets += $(dtb-y)
diff --git a/arch/arm/boot/dts/pxa27x-lp8x4x-i105.dts b/arch/arm/boot/dts/pxa27x-lp8x4x-i105.dts
new file mode 100644
index 0000000..8ddc9f5
--- /dev/null
+++ b/arch/arm/boot/dts/pxa27x-lp8x4x-i105.dts
@@ -0,0 +1,43 @@
+/* Device tree for ICP DAS LP-8x4x i105 flavor */
+
+#include "pxa27x-lp8x4x.dts"
+
+/ {
+ extbus {
+ flash@0 {
+ compatible = "cfi-flash";
+ reg = <0 0 0x04000000>;
+ bank-width = <4>;
+ device-width = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ fs@0 {
+ label = "u-boot";
+ reg = <0 0x40000>;
+ };
+ fs@40000 {
+ label = "settings";
+ reg = <0x40000 0x40000>;
+ };
+ fs@80000 {
+ label = "device_tree";
+ reg = <0x80000 0x40000>;
+ };
+ fs@c0000 {
+ label = "kernel";
+ reg = <0xc0000 0x2c0000>;
+ };
+ fs@300000 {
+ label = "root_fs";
+ reg = <0x380000 0x3c80000>;
+ };
+ };
+
+ flash@1 {
+ compatible = "cfi-flash";
+ reg = <1 0x0 0x02000000>;
+ bank-width = <2>;
+ device-width = <1>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/pxa27x-lp8x4x.dts b/arch/arm/boot/dts/pxa27x-lp8x4x.dts
new file mode 100644
index 0000000..38a3e46
--- /dev/null
+++ b/arch/arm/boot/dts/pxa27x-lp8x4x.dts
@@ -0,0 +1,228 @@
+/* Device tree for ICP DAS LP-8x4x */
+/dts-v1/;
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "pxa27x.dtsi"
+
+/ {
+ model = "ICP DAS LP-8x4x programmable automation controller";
+ compatible = "marvell,pxa270";
+
+ aliases {
+ ethernet0 = &eth0;
+ ethernet1 = &eth1;
+ };
+
+ memory {
+ reg = <0xa0000000 0x08000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ vmmc: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vmmc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
+ pxabus {
+ pxairq: interrupt-controller@40d00000 {
+ marvell,intc-priority;
+ marvell,intc-nr-irqs = <34>;
+ };
+
+ uart@40100000 {
+ status = "okay";
+ };
+
+ uart@40200000 {
+ status = "okay";
+ };
+
+ uart@40700000 {
+ status = "okay";
+ };
+
+ mmc@41100000 {
+ status = "okay";
+ vmmc-supply = <&vmmc>;
+ };
+
+ ohci@4c000000 {
+ status = "okay";
+ marvell,port-mode = <3>;
+ marvell,oc-mode-perport;
+ marvell,enable-port1;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ status {
+ gpios = <&gpio 84 1>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ i2c: i2c-gpio {
+ compatible = "i2c-gpio";
+ gpios = <&gpio 22 0 /* sda */
+ &gpio 12 0 /* scl */>;
+ i2c-gpio,delay-us = <1>;
+ i2c-gpio,timeout-ms = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@50 {
+ compatible = "atmel,24c128";
+ reg = <0x50>;
+ pagesize = <64>;
+ };
+ };
+
+ w1: w1-gpio {
+ compatible = "w1-gpio";
+ gpios = <&gpio 83 0>;
+ };
+ };
+
+ extbus {
+ flash@0 {
+ compatible = "cfi-flash";
+ reg = <0 0 0x02000000>;
+ bank-width = <4>;
+ device-width = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ fs@0 {
+ label = "u-boot";
+ reg = <0 0x40000>;
+ };
+ fs@40000 {
+ label = "settings";
+ reg = <0x40000 0x40000>;
+ };
+ fs@80000 {
+ label = "device_tree";
+ reg = <0x80000 0x40000>;
+ };
+ fs@c0000 {
+ label = "kernel";
+ reg = <0xc0000 0x240000>;
+ };
+ fs@300000 {
+ label = "root_fs";
+ reg = <0x300000 0x1d00000>;
+ };
+ };
+
+ flash@1 {
+ compatible = "cfi-flash";
+ reg = <1 0x0 0x01000000>;
+ bank-width = <2>;
+ device-width = <1>;
+ };
+
+ netio@3 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 3 0 0x02000000>;
+ interrupt-parent = <&gpio>;
+
+ eth0: eth@0 {
+ compatible = "davicom,dm9000";
+ reg = <0x0 0x2
+ 0x4000 0x2>;
+ interrupts = <9 IRQ_TYPE_EDGE_RISING>;
+ status = "okay";
+ };
+
+ eth1: eth@1000000 {
+ compatible = "davicom,dm9000";
+ reg = <0x1000000 0x2
+ 0x1004000 0x2>;
+ interrupts = <82 IRQ_TYPE_EDGE_RISING>;
+ status = "okay";
+ };
+ };
+
+ fpga@5 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 5 0x3000000 0x10000>;
+ interrupt-parent = <&fpgairq>;
+
+ rtc@901c {
+ compatible = "dallas,rtc-ds1302";
+ reg = <0x901c 0x1>;
+ status = "okay";
+ };
+
+ sram@a000 {
+ compatible = "icpdas,sram-lp8x4x";
+ reg = <0xa000 0x1000
+ 0x901e 0x1>;
+ };
+
+ fpgairq: irq@9006 {
+ compatible = "icpdas,irq-lp8x4x";
+ reg = <0x9006 0x16>;
+ interrupt-parent = <&gpio>;
+ interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ status = "okay";
+ };
+
+ uart@9050 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9050 0x10
+ 0x9030 0x02>;
+ interrupts = <13>;
+ status = "okay";
+ };
+
+ uart@9060 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9060 0x10
+ 0x9032 0x02>;
+ interrupts = <14>;
+ status = "okay";
+ };
+
+ uart@9070 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9070 0x10
+ 0x9034 0x02>;
+ interrupts = <15>;
+ status = "okay";
+ };
+
+ backplane {
+ compatible = "icpdas,backplane-lp8x4x";
+ reg = <0x0 0x2
+ 0x1000 0x10
+ 0x2000 0x10
+ 0x3000 0x10
+ 0x4000 0x10
+ 0x5000 0x10
+ 0x6000 0x10
+ 0x7000 0x10
+ 0x8000 0x10
+ 0x9002 0x2
+ 0x9004 0x2
+ 0x9046 0x2>;
+ eeprom-gpios = <&gpio 4 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi
index 0150531..1e1c98c 100644
--- a/arch/arm/boot/dts/pxa27x.dtsi
+++ b/arch/arm/boot/dts/pxa27x.dtsi
@@ -55,4 +55,21 @@
dma-names = "rx", "tx";
};
};
+
+ extbus {
+ /*
+ * PXA27x synchrous, static and
+ * variable-latency IO interfaces
+ */
+ compatible = "simple-bus";
+
+ #address-cells = <2>; /* first cell is nCS, second is address */
+ #size-cells = <1>;
+ ranges = <0 0 0 0x04000000
+ 1 0 0x04000000 0x04000000
+ 2 0 0x08000000 0x04000000
+ 3 0 0x0c000000 0x04000000
+ 4 0 0x10000000 0x04000000
+ 5 0 0x14000000 0x04000000>;
+ };
};
diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
new file mode 100644
index 0000000..9f1efb6
--- /dev/null
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -0,0 +1,168 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_RCU_BOOST=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_UID16 is not set
+# CONFIG_SHMEM is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLOB=y
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_LBDAF is not set
+CONFIG_BLK_CMDLINE_PARSER=y
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_PXA=y
+CONFIG_MACH_PXA27X_DT=y
+# CONFIG_ARM_THUMB is not set
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_COMPACTION is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="init=/sbin/init root=/dev/mmcblk0p1 rw rootfstype=ext4 console=ttyS0,115200 mem=128M rootwait"
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_VLAN_8021Q_MVRP=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER is not set
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=2
+CONFIG_EEPROM_AT24=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_TUN=m
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+CONFIG_DM9000=y
+CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL=y
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPPOE=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=600
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=40
+CONFIG_SERIAL_8250_RUNTIME_UARTS=40
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_PXA=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=m
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_GPIO=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_SMEM=m
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_SA1100_WATCHDOG=m
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_FB=y
+CONFIG_FB_PXA=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+CONFIG_USB=m
+CONFIG_USB_OHCI_HCD=m
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+CONFIG_USB_STORAGE=m
+CONFIG_USB_SERIAL=m
+CONFIG_MMC=y
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PXA=m
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=m
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_REISERFS_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=m
+CONFIG_CIFS_STATS=y
+CONFIG_CODA_FS=m
+CONFIG_NLS_DEFAULT="cp855"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_855=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index e6690a4..fc2e634 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -4,6 +4,21 @@ menu "Intel PXA2xx/PXA3xx Implementations"

comment "Intel/Marvell Dev Platforms (sorted by hardware release time)"

+config MACH_PXA27X_DT
+ bool "Support PXA27x platforms from device tree"
+ select PXA27x
+ select USE_OF
+ help
+ Include support for Marvell PXA27x based platforms using
+ the device tree.
+
+ While MACH_PXA27X_DT when enabled should boot any PXA27x
+ compatible machine, it still makes sense to select specific
+ machines. Those options will select required features and
+ provide per-machine errata workarounds.
+
+ If unsure, say Y.
+
config MACH_PXA3XX_DT
bool "Support PXA3xx platforms from device tree"
select CPU_PXA300
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 648867a..adaa72a 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_CPU_PXA930) += pxa930.o
# NOTE: keep the order of boards in accordance to their order in Kconfig

# Device Tree support
+obj-$(CONFIG_MACH_PXA27X_DT) += pxa27x-dt.o
obj-$(CONFIG_MACH_PXA3XX_DT) += pxa-dt.o

# Intel/Marvell Dev Platforms
diff --git a/arch/arm/mach-pxa/pxa27x-dt.c b/arch/arm/mach-pxa/pxa27x-dt.c
new file mode 100644
index 0000000..865cf46
--- /dev/null
+++ b/arch/arm/mach-pxa/pxa27x-dt.c
@@ -0,0 +1,64 @@
+/*
+ * linux/arch/arm/mach-pxa/pxa27x-dt.c
+ *
+ * Copyright (C) 2013 Sergei Ianovich
+ *
+ * based mostly on linux/arch/arm/mach-pxa/pxa-dt.c by Daniel Mack
+ *
+ * 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 <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/irqs.h>
+#include <mach/pxa27x.h>
+
+#include "generic.h"
+
+#ifdef CONFIG_PXA27x
+static const struct of_dev_auxdata pxa27x_auxdata_lookup[] __initconst = {
+ OF_DEV_AUXDATA("mrvl,pxa-uart", 0x40100000, "pxa2xx-uart.0", NULL),
+ OF_DEV_AUXDATA("mrvl,pxa-uart", 0x40200000, "pxa2xx-uart.1", NULL),
+ OF_DEV_AUXDATA("mrvl,pxa-uart", 0x40700000, "pxa2xx-uart.2", NULL),
+ OF_DEV_AUXDATA("mrvl,pxa-uart", 0x41600000, "pxa2xx-uart.3", NULL),
+ OF_DEV_AUXDATA("marvell,pxa-mmc", 0x41100000, "pxa2xx-mci.0", NULL),
+ OF_DEV_AUXDATA("intel,pxa27x-gpio", 0x40e00000, "pxa27x-gpio", NULL),
+ OF_DEV_AUXDATA("marvell,pxa-ohci", 0x4c000000, "pxa27x-ohci", NULL),
+ OF_DEV_AUXDATA("mrvl,pxa-i2c", 0x40301680, "pxa2xx-i2c.0", NULL),
+ {}
+};
+
+static void __init pxa27x_dt_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ pxa27x_auxdata_lookup, NULL);
+}
+
+static const char * const pxa27x_dt_board_compat[] __initconst = {
+ "marvell,pxa270",
+ "marvell,pxa271",
+ "marvell,pxa272",
+ NULL,
+};
+
+#endif
+
+#ifdef CONFIG_PXA27x
+DT_MACHINE_START(PXA27X_DT, "Marvell PXA27x (Device Tree Support)")
+ .map_io = pxa27x_map_io,
+ .init_irq = pxa27x_dt_init_irq,
+ .handle_irq = pxa27x_handle_irq,
+ .init_time = pxa_timer_init,
+ .restart = pxa_restart,
+ .init_machine = pxa27x_dt_init,
+ .dt_compat = pxa27x_dt_board_compat,
+MACHINE_END
+
+#endif
--
1.8.4.2

2014-04-16 17:18:46

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 09/21] rtc: support DS1302 RTC on ICP DAS LP-8x4x

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS bindings to a different patch (8/21)

v2..v3
* use usleep_range instead of custom nsleep
* number change (07/16 -> 09/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/rtc/rtc-ds1302.txt | 14 +++
arch/arm/configs/lp8x4x_defconfig | 1 +
drivers/rtc/Kconfig | 2 +-
drivers/rtc/rtc-ds1302.c | 100 ++++++++++++++++++++-
4 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rtc/rtc-ds1302.txt

diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt
new file mode 100644
index 0000000..810613b
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt
@@ -0,0 +1,14 @@
+* Dallas Semiconductor DS-1302 RTC
+
+Simple device which could be used to store date/time between reboots.
+
+Required properties:
+- compatible : Should be "dallas,rtc-ds1302"
+- reg : Should be address and size of IO memory region
+
+Examples:
+
+rtc@40900000 {
+ compatible = "dallas,rtc-ds1302";
+ reg = <0x1700901c 0x1>;
+};
diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
index 9f1efb6..d60e37a 100644
--- a/arch/arm/configs/lp8x4x_defconfig
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -141,6 +141,7 @@ CONFIG_LEDS_GPIO=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1302=y
CONFIG_RTC_DRV_PXA=m
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_EXT2_FS=m
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2e565f8..80aaaa1 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -710,7 +710,7 @@ config RTC_DRV_DS1286

config RTC_DRV_DS1302
tristate "Dallas DS1302"
- depends on SH_SECUREEDGE5410
+ depends on SH_SECUREEDGE5410 || (ARCH_PXA && HIGH_RES_TIMERS)
help
If you say yes here you get support for the Dallas DS1302 RTC chips.

diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 07e8d79..3c49023 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -50,7 +50,7 @@
#define ds1302_set_tx()
#define ds1302_set_rx()

-static inline int ds1302_hw_init(void)
+static inline int ds1302_hw_init(struct platform_device *pdev)
{
return 0;
}
@@ -86,6 +86,101 @@ static inline int ds1302_rxbit(void)
return !!(get_dp() & RTC_IODATA);
}

+#elif defined(CONFIG_ARCH_PXA) && defined(CONFIG_HIGH_RES_TIMERS)
+
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#define RTC_CE 0x01
+#define RTC_CLK 0x02
+#define RTC_nWE 0x04
+#define RTC_IODATA 0x08
+
+static unsigned long ds1302_state;
+
+static void *mem;
+
+static inline int ds1302_hw_init(struct platform_device *pdev)
+{
+ struct resource *r;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -ENODEV;
+
+ mem = devm_ioremap_resource(&pdev->dev, r);
+ if (!mem)
+ return -EFAULT;
+
+ return 0;
+}
+
+static inline void ds1302_reset(void)
+{
+ ds1302_state = 0;
+ iowrite8(ds1302_state, mem);
+ usleep_range(4, 5);
+}
+
+static inline void ds1302_clock(void)
+{
+ usleep_range(1, 2);
+ ds1302_state |= RTC_CLK;
+ iowrite8(ds1302_state, mem);
+ usleep_range(1, 2);
+ ds1302_state &= ~RTC_CLK;
+ iowrite8(ds1302_state, mem);
+}
+
+static inline void ds1302_start(void)
+{
+ ds1302_state &= ~RTC_CLK;
+ ds1302_state |= RTC_CE;
+ iowrite8(ds1302_state, mem);
+ usleep_range(3, 4);
+}
+
+static inline void ds1302_stop(void)
+{
+ ds1302_state &= ~RTC_CE;
+ iowrite8(ds1302_state, mem);
+}
+
+static inline void ds1302_set_tx(void)
+{
+ ds1302_state &= ~RTC_nWE;
+ iowrite8(ds1302_state, mem);
+}
+
+static inline void ds1302_set_rx(void)
+{
+ ds1302_state |= RTC_nWE;
+ iowrite8(ds1302_state, mem);
+}
+
+static inline void ds1302_txbit(int bit)
+{
+ if (bit)
+ ds1302_state |= RTC_IODATA;
+ else
+ ds1302_state &= ~RTC_IODATA;
+ iowrite8(ds1302_state, mem);
+}
+
+static inline int ds1302_rxbit(void)
+{
+ return ioread8(mem) & 0x1;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1302_dt_ids[] = {
+ { .compatible = "dallas,rtc-ds1302" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
+#endif
+
#else
#error "Add support for your platform"
#endif
@@ -216,7 +311,7 @@ static int __init ds1302_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;

- if (ds1302_hw_init()) {
+ if (ds1302_hw_init(pdev)) {
dev_err(&pdev->dev, "Failed to init communication channel");
return -EINVAL;
}
@@ -245,6 +340,7 @@ static struct platform_driver ds1302_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ds1302_dt_ids),
},
};

--
1.8.4.2

2014-04-16 17:19:00

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 10/21] mtd: support BB SRAM on ICP DAS LP-8x4x

This provides an MTD device driver for 512kB of battery backed up SRAM
on ICPDAS LP-8X4X programmable automation controllers.

SRAM chip is connected via FPGA and is not accessible without a driver,
unlike flash memory which is wired to CPU MMU.

This SRAM becomes an excellent persisent storage of volatile process
data like counter values and sensor statuses. Storing those data in
flash or mmc card is not a viable solution.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Brian Norris <[email protected]>
---
v3..v4 for Brian Norris 'Reviewed-by'
* add doc file for DT binding
* move DTS binding to a different patch (8/21)
* drop unused include directive
* drop safely unused callback
* drop non-default partion probe types
* drop duplicate error checks
* drop duplicate error reporting
* fixed error message on MTD registeration
* fixed module removal routine

v2..v3
* no changes (except number 08/16 -> 10/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/mtd/sram-lp8x4x.txt | 22 +++
arch/arm/configs/lp8x4x_defconfig | 1 +
drivers/mtd/devices/Kconfig | 14 ++
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/sram_lp8x4x.c | 204 +++++++++++++++++++++
5 files changed, 242 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
create mode 100644 drivers/mtd/devices/sram_lp8x4x.c

diff --git a/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
new file mode 100644
index 0000000..8b1e864
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
@@ -0,0 +1,22 @@
+512kB battery backed up SRAM on LP-8x4x industrial computers
+
+Required properties:
+- compatible : should be "icpdas,sram-lp8x4x"
+
+- reg: physical base addresses and region lengths of
+ * IO memory range
+ * SRAM page selector
+
+- eeprom-gpios : should point to active-low write enable GPIO
+
+SRAM chip is connected via FPGA and is not accessible without a driver,
+unlike flash memory which is wired to CPU MMU. Driver is essentially
+an address translation routine.
+
+Example:
+
+ sram@a000 {
+ compatible = "icpdas,sram-lp8x4x";
+ reg = <0xa000 0x1000
+ 0x901e 0x1>;
+ };
diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
index d60e37a..17a4e6f 100644
--- a/arch/arm/configs/lp8x4x_defconfig
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -57,6 +57,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_SRAM_LP8X4X=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_LOOP_MIN_COUNT=2
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 1210bc2..fc8552b 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -225,4 +225,18 @@ config BCH_CONST_T
default 4
endif

+config MTD_SRAM_LP8X4X
+ tristate "SRAM on ICPDAS LP-8X4X"
+ depends on OF && ARCH_PXA
+ ---help---
+ This provides an MTD device driver for 512kiB of battery backed up SRAM
+ on ICPDAS LP-8X4X programmable automation controllers.
+
+ SRAM chip is connected via FPGA and is not accessible without a driver,
+ unlike flash memory which is wired to CPU MMU.
+
+ Say N, unless you plan to run this kernel on LP-8X4X.
+
+ If you say M, the module will be called sram_lp8x4x.
+
endmenu
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index c68868f..a7d86e2 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
+obj-$(CONFIG_MTD_SRAM_LP8X4X) += sram_lp8x4x.o


CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/sram_lp8x4x.c b/drivers/mtd/devices/sram_lp8x4x.c
new file mode 100644
index 0000000..4cfa70b
--- /dev/null
+++ b/drivers/mtd/devices/sram_lp8x4x.c
@@ -0,0 +1,204 @@
+/*
+ * linux/drivers/mtd/devices/lp8x4x_sram.c
+ *
+ * MTD Driver for SRAM on ICPDAS LP-8x4x
+ * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+
+struct lp8x4x_sram_info {
+ void __iomem *bank;
+ void __iomem *virt;
+ struct mutex lock;
+ unsigned active_bank;
+ struct mtd_info mtd;
+};
+
+static int
+lp8x4x_sram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = instr->addr >> 11;
+ unsigned offset = (instr->addr & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < instr->len; i++) {
+ iowrite8(0xff, info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+}
+
+static int
+lp8x4x_sram_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *b)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = to >> 11;
+ unsigned offset = (to & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < len; i++) {
+ iowrite8(b[i], info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ *retlen = len;
+ return 0;
+}
+
+static int
+lp8x4x_sram_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *b)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = from >> 11;
+ unsigned offset = (from & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < len; i++) {
+ b[i] = ioread8(info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ *retlen = len;
+ return 0;
+}
+
+static struct of_device_id of_flash_match[] = {
+ {
+ .compatible = "icpdas,sram-lp8x4x",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_flash_match);
+
+static int
+lp8x4x_sram_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct lp8x4x_sram_info *info;
+ struct resource *res_virt, *res_bank;
+ char sz_str[16];
+ struct mtd_part_parser_data ppdata;
+ int err = 0;
+
+ match = of_match_device(of_flash_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ res_virt = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->virt = devm_ioremap_resource(&pdev->dev, res_virt);
+ if (IS_ERR(info->virt))
+ return PTR_ERR(info->virt);
+
+ res_bank = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ info->bank = devm_ioremap_resource(&pdev->dev, res_bank);
+ if (IS_ERR(info->bank))
+ return PTR_ERR(info->bank);
+
+ info->mtd.priv = info;
+ info->mtd.name = "SRAM";
+ info->mtd.type = MTD_RAM;
+ info->mtd.flags = MTD_CAP_RAM;
+ info->mtd.size = resource_size(res_virt) << 7;
+ info->mtd.erasesize = 512;
+ info->mtd.writesize = 4;
+ info->mtd._erase = lp8x4x_sram_erase;
+ info->mtd._write = lp8x4x_sram_write;
+ info->mtd._read = lp8x4x_sram_read;
+ info->mtd.owner = THIS_MODULE;
+
+ mutex_init(&info->lock);
+ iowrite8(info->active_bank, info->bank);
+ platform_set_drvdata(pdev, info);
+
+ ppdata.of_node = pdev->dev.of_node;
+ err = mtd_device_parse_register(&info->mtd, NULL, &ppdata,
+ NULL, 0);
+
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register MTD\n");
+ return err;
+ }
+
+ string_get_size(info->mtd.size, STRING_UNITS_2, sz_str,
+ sizeof(sz_str));
+ dev_info(&pdev->dev, "using %s SRAM on LP-8X4X as %s\n", sz_str,
+ dev_name(&info->mtd.dev));
+ return 0;
+}
+
+static int
+lp8x4x_sram_remove(struct platform_device *dev)
+{
+ struct lp8x4x_sram_info *info = platform_get_drvdata(dev);
+ return mtd_device_unregister(&info->mtd);
+}
+
+static struct platform_driver lp8x4x_sram_driver = {
+ .driver = {
+ .name = "sram-lp8x4x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_flash_match,
+ },
+ .probe = lp8x4x_sram_probe,
+ .remove = lp8x4x_sram_remove,
+};
+
+module_platform_driver(lp8x4x_sram_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
+MODULE_DESCRIPTION("MTD driver for SRAM on ICPDAS LP-8x4x");
--
1.8.4.2

2014-04-16 17:19:07

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 11/21] ARM: pxa: support ICP DAS LP-8x4x FPGA irq

ICP DAS LP-8x4x contains FPGA chip. The chip functions as an interrupt
source providing 16 additional interrupts among other things. The
interrupt lines are muxed to a GPIO pin of a 2nd level PXA-GPIO
interrupt controller. GPIO pins of the 2nd level controller are in turn
muxed to a CPU interrupt line.

Until pxa is completely converted to device tree, it is impossible
to use IRQCHIP_DECLARE() and the irqdomain needs to added manually.
Drivers for the on-CPU IRQs and GPIO-IRQs are loaded using
postcore_initcall(). We need to have all irq domain drivers loaded prior
to DT parsing in order to allow normal initialization of IRQ resources
with DT.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v3.2..v4
* move DTS binding to a different patch (8/21)

v3.1..v3.2
fixes to apply Linus Walleij's "Reviewed-by":
* add kerneldoc comment for state container struct
* rename irq -> hwirq for clarity
* drop overzealous error checks from the hotpaths

v3..v3.1
fixes according to Linus Walleij review comments:
* update commit message
* use state container instead of global variables
* get hardware irq nums from irq_data, don't calculate them
* use BIT() macro
* add defines for system irq register masks
* replace cycle control variable with break
* use better names for resource variables
* add a linear domain instead of a legacy one
* use irq_create_mapping() instead of irq_alloc_desc()

v2..v3
* no changes (except number 09/16 -> 11/21)

v0..v2
* extract irqchip and move to drivers/irqchip/
* use device tree
* use devm helpers where possible

.../bindings/interrupt-controller/irq-lp8x4x.txt | 49 +++++
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-lp8x4x.c | 227 +++++++++++++++++++++
4 files changed, 282 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
create mode 100644 drivers/irqchip/irq-lp8x4x.c

diff --git a/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
new file mode 100644
index 0000000..c8940d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
@@ -0,0 +1,49 @@
+ICP DAS LP-8x4x FPGA Interrupt Controller
+
+ICP DAS LP-8x4x contains FPGA chip. The chip functions as a interrupt
+source providing 16 additional interrupts among other things.
+
+Required properties:
+- compatible : should be "icpdas,irq-lp8x4x"
+
+- reg: physical base address of the controller and length of memory mapped
+ region.
+
+- interrupt-controller : identifies the node as an interrupt controller
+
+- #interrupt-cells : should be 1
+
+- interrupts : should provide interrupt
+
+- interrupt-parent : should provide a link to interrupt controller either
+ explicitly and implicitly from a parent node
+
+Example:
+
+ fpga: fpga@17000006 {
+ compatible = "icpdas,irq-lp8x4x";
+ reg = <0x17000006 0x16>;
+ interrupt-parent = <&gpio>;
+ interrupts = <3 IRQ_TYPE_EDGE_RISING>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ status = "okay";
+ };
+
+ uart@17009050 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x17009050 0x10
+ 0x17009030 0x02>;
+ interrupt-parent = <&fpga>;
+ interrupts = <13>;
+ status = "okay";
+ };
+
+ uart@17009060 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x17009060 0x10
+ 0x17009032 0x02>;
+ interrupt-parent = <&fpga>;
+ interrupts = <14>;
+ status = "okay";
+ };
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d770f74..ede3237 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -47,6 +47,11 @@ config CLPS711X_IRQCHIP
select SPARSE_IRQ
default y

+config LP8X4X_IRQ
+ bool
+ depends on OF && ARCH_PXA
+ select IRQ_DOMAIN
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f180f8d..94f82c7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
+obj-$(CONFIG_LP8X4X_IRQ) += irq-lp8x4x.o
diff --git a/drivers/irqchip/irq-lp8x4x.c b/drivers/irqchip/irq-lp8x4x.c
new file mode 100644
index 0000000..12ccbb03
--- /dev/null
+++ b/drivers/irqchip/irq-lp8x4x.c
@@ -0,0 +1,227 @@
+/*
+ * linux/drivers/irqchip/irq-lp8x4x.c
+ *
+ * Support for ICP DAS LP-8x4x FPGA irq
+ * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define EOI 0x00000000
+#define INSINT 0x00000002
+#define ENSYSINT 0x00000004
+#define PRIMINT 0x00000006
+#define PRIMINT_MASK 0xe0
+#define SECOINT 0x00000008
+#define SECOINT_MASK 0x1f
+#define ENRISEINT 0x0000000A
+#define CLRRISEINT 0x0000000C
+#define ENHILVINT 0x0000000E
+#define CLRHILVINT 0x00000010
+#define ENFALLINT 0x00000012
+#define CLRFALLINT 0x00000014
+#define IRQ_MEM_SIZE 0x00000016
+#define LP8X4X_NUM_IRQ_DEFAULT 16
+
+/**
+ * struct lp8x4x_irq_data - LP8X4X custom irq controller state container
+ * @base: base IO memory address
+ * @irq_domain: Interrupt translation domain; responsible for mapping
+ * between hwirq number and linux irq number
+ * @irq_sys_enabled: mask keeping track of interrupts enabled in the
+ * register which vendor calls 'system'
+ * @irq_high_enabled: mask keeping track of interrupts enabled in the
+ * register which vendor calls 'high'
+ *
+ * The structure implements State Container from
+ * Documentation/driver-model/design-patterns.txt
+ */
+
+struct lp8x4x_irq_data {
+ void *base;
+ struct irq_domain *domain;
+ unsigned char irq_sys_enabled;
+ unsigned char irq_high_enabled;
+};
+
+static void lp8x4x_mask_irq(struct irq_data *d)
+{
+ unsigned mask;
+ unsigned long hwirq = d->hwirq;
+ struct lp8x4x_irq_data *host = irq_data_get_irq_chip_data(d);
+
+ if (hwirq < 8) {
+ host->irq_high_enabled &= ~BIT(hwirq);
+
+ mask = ioread8(host->base + ENHILVINT);
+ mask &= ~BIT(hwirq);
+ iowrite8(mask, host->base + ENHILVINT);
+ } else {
+ hwirq -= 8;
+ host->irq_sys_enabled &= ~BIT(hwirq);
+
+ mask = ioread8(host->base + ENSYSINT);
+ mask &= ~BIT(hwirq);
+ iowrite8(mask, host->base + ENSYSINT);
+ }
+}
+
+static void lp8x4x_unmask_irq(struct irq_data *d)
+{
+ unsigned mask;
+ unsigned long hwirq = d->hwirq;
+ struct lp8x4x_irq_data *host = irq_data_get_irq_chip_data(d);
+
+ if (hwirq < 8) {
+ host->irq_high_enabled |= BIT(hwirq);
+ mask = ioread8(host->base + CLRHILVINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + CLRHILVINT);
+
+ mask = ioread8(host->base + ENHILVINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + ENHILVINT);
+ } else {
+ hwirq -= 8;
+ host->irq_sys_enabled |= BIT(hwirq);
+
+ mask = ioread8(host->base + SECOINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + SECOINT);
+
+ mask = ioread8(host->base + ENSYSINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + ENSYSINT);
+ }
+}
+
+static struct irq_chip lp8x4x_irq_chip = {
+ .name = "FPGA",
+ .irq_ack = lp8x4x_mask_irq,
+ .irq_mask = lp8x4x_mask_irq,
+ .irq_mask_ack = lp8x4x_mask_irq,
+ .irq_unmask = lp8x4x_unmask_irq,
+};
+
+static void lp8x4x_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int n;
+ unsigned long mask;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct lp8x4x_irq_data *host = irq_desc_get_handler_data(desc);
+
+ chained_irq_enter(chip, desc);
+
+ for (;;) {
+ mask = ioread8(host->base + CLRHILVINT) & 0xff;
+ mask |= (ioread8(host->base + SECOINT) & SECOINT_MASK) << 8;
+ mask |= (ioread8(host->base + PRIMINT) & PRIMINT_MASK) << 8;
+ mask &= host->irq_high_enabled | (host->irq_sys_enabled << 8);
+ if (mask == 0)
+ break;
+ for_each_set_bit(n, &mask, BITS_PER_LONG)
+ generic_handle_irq(irq_find_mapping(host->domain, n));
+ }
+
+ iowrite8(0, host->base + EOI);
+ chained_irq_exit(chip, desc);
+}
+
+static int lp8x4x_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct lp8x4x_irq_data *host = d->host_data;
+ int err;
+
+ err = irq_set_chip_data(irq, host);
+ if (err < 0)
+ return err;
+
+ irq_set_chip_and_handler(irq, &lp8x4x_irq_chip, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ return 0;
+}
+
+const struct irq_domain_ops lp8x4x_irq_domain_ops = {
+ .map = lp8x4x_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static struct of_device_id lp8x4x_irq_dt_ids[] = {
+ { .compatible = "icpdas,irq-lp8x4x", },
+ {}
+};
+
+static int lp8x4x_irq_probe(struct platform_device *pdev)
+{
+ struct resource *res_mem, *res_irq;
+ struct device_node *np = pdev->dev.of_node;
+ struct lp8x4x_irq_data *host;
+ int i, err;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_mem || !res_irq || resource_size(res_mem) < IRQ_MEM_SIZE)
+ return -ENODEV;
+
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENODEV;
+
+ host->base = devm_ioremap_resource(&pdev->dev, res_mem);
+ if (!host->base) {
+ dev_err(&pdev->dev, "Failed to ioremap %p\n", host->base);
+ return -EFAULT;
+ }
+
+ host->domain = irq_domain_add_linear(np, LP8X4X_NUM_IRQ_DEFAULT,
+ &lp8x4x_irq_domain_ops, host);
+ if (!host->domain) {
+ dev_err(&pdev->dev, "Failed to add IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < LP8X4X_NUM_IRQ_DEFAULT; i++) {
+ err = irq_create_mapping(host->domain, i);
+ if (err < 0)
+ dev_err(&pdev->dev, "Failed to map IRQ %i\n", i);
+ }
+
+ /* Initialize chip registers */
+ iowrite8(0, host->base + CLRRISEINT);
+ iowrite8(0, host->base + ENRISEINT);
+ iowrite8(0, host->base + CLRFALLINT);
+ iowrite8(0, host->base + ENFALLINT);
+ iowrite8(0, host->base + CLRHILVINT);
+ iowrite8(0, host->base + ENHILVINT);
+ iowrite8(0, host->base + ENSYSINT);
+ iowrite8(0, host->base + SECOINT);
+
+ irq_set_handler_data(res_irq->start, host);
+ irq_set_chained_handler(res_irq->start, lp8x4x_irq_handler);
+
+ return 0;
+}
+
+static struct platform_driver lp8x4x_irq_driver = {
+ .probe = lp8x4x_irq_probe,
+ .driver = {
+ .name = "irq-lp8x4x",
+ .of_match_table = lp8x4x_irq_dt_ids,
+ },
+};
+
+static int __init lp8x4x_irq_init(void)
+{
+ return platform_driver_register(&lp8x4x_irq_driver);
+}
+postcore_initcall(lp8x4x_irq_init);
--
1.8.4.2

2014-04-16 17:19:25

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 12/21] serial: support for 16550A serial ports on LP-8x4x

The patch adds support for 3 additional LP-8x4x built-in serial
ports.

The device can also host up to 8 extension cards with 4 serial ports
on each card for a total of 35 ports. However, I don't have
the hardware to test extension cards, so they are not supported, yet.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Heikki Krogerus <[email protected]>
---
v3..v4
* move DTS bindings to a different patch (8/21) as suggested by
Heikki Krogerus

v2..v3
* no changes (except number 10/16 -> 12/21)

v0..v2
* register platform driver instead of platform device
* use device tree
* use devm helpers where possible

.../devicetree/bindings/serial/lp8x4x-serial.txt | 35 +++++
arch/arm/configs/lp8x4x_defconfig | 1 +
drivers/tty/serial/8250/8250_lp8x4x.c | 169 +++++++++++++++++++++
drivers/tty/serial/8250/Kconfig | 12 ++
drivers/tty/serial/8250/Makefile | 1 +
5 files changed, 218 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
create mode 100644 drivers/tty/serial/8250/8250_lp8x4x.c

diff --git a/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
new file mode 100644
index 0000000..5f9a4c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
@@ -0,0 +1,35 @@
+UART ports on ICP DAS LP-8x4x
+
+ICP DAS LP-8x4x contains three additional serial ports interfaced via
+Analog Devices ADM213EA chips in addition to 3 serial ports on PXA CPU.
+
+Required properties:
+- compatible : should be "icpdas,uart-lp8x4x"
+
+- reg : should provide 16 byte man IO memory region and 1 byte region for
+ termios
+
+- interrupts : should provide interrupt
+
+- interrupt-parent : should provide a link to interrupt controller either
+ explicitly or implicitly from a parent node
+
+Examples (from pxa27x-lp8x4x.dts):
+
+ uart@9050 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9050 0x10
+ 0x9030 0x02>;
+ interrupt-parent = <&fpgairg>;
+ interrupts = <13>;
+ status = "okay";
+ };
+
+ uart@9060 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9060 0x10
+ 0x9032 0x02>;
+ interrupt-parent = <&fpgairg>;
+ interrupts = <14>;
+ status = "okay";
+ };
diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
index 17a4e6f..9116ce1 100644
--- a/arch/arm/configs/lp8x4x_defconfig
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -112,6 +112,7 @@ CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_8250_LP8X4X=m
CONFIG_HW_RANDOM=y
CONFIG_I2C=m
# CONFIG_I2C_COMPAT is not set
diff --git a/drivers/tty/serial/8250/8250_lp8x4x.c b/drivers/tty/serial/8250/8250_lp8x4x.c
new file mode 100644
index 0000000..93e0f59
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_lp8x4x.c
@@ -0,0 +1,169 @@
+/* linux/drivers/tty/serial/8250/8250_lp8x4x.c
+ *
+ * Support for 16550A serial ports on ICP DAS LP-8x4x
+ *
+ * Copyright (C) 2013 Sergei Ianovich <[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/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+struct lp8x4x_serial_data {
+ int line;
+ void *ios_mem;
+};
+
+static void lp8x4x_serial_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int len;
+ unsigned int baud;
+ struct lp8x4x_serial_data *data = port->private_data;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ len = 7;
+ break;
+ case CS6:
+ len = 8;
+ break;
+ case CS7:
+ len = 9;
+ break;
+ default:
+ case CS8:
+ len = 10;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ len++;
+ if (termios->c_cflag & PARENB)
+ len++;
+ if (!(termios->c_cflag & PARODD))
+ len++;
+#ifdef CMSPAR
+ if (termios->c_cflag & CMSPAR)
+ len++;
+#endif
+
+ len -= 9;
+ len &= 3;
+ len <<= 3;
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 16);
+ switch (baud) {
+ case 2400:
+ len |= 1;
+ break;
+ case 4800:
+ len |= 2;
+ break;
+ case 19200:
+ len |= 4;
+ break;
+ case 38400:
+ len |= 5;
+ break;
+ case 57600:
+ len |= 6;
+ break;
+ case 115200:
+ len |= 7;
+ break;
+ case 9600:
+ default:
+ len |= 3;
+ break;
+ };
+ iowrite8(len, data->ios_mem);
+
+ serial8250_do_set_termios(port, termios, old);
+}
+
+static struct of_device_id lp8x4x_serial_dt_ids[] = {
+ { .compatible = "icpdas,uart-lp8x4x", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lp8x4x_serial_dt_ids);
+
+static int lp8x4x_serial_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port uart = {};
+ struct lp8x4x_serial_data *data;
+ struct resource *mmres, *mires, *irqres;
+ int ret;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mires = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mmres || !mires || !irqres)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ios_mem = devm_ioremap_resource(&pdev->dev, mires);
+ if (!data->ios_mem)
+ return -EFAULT;
+
+ uart.port.iotype = UPIO_MEM;
+ uart.port.mapbase = mmres->start;
+ uart.port.iobase = mmres->start;
+ uart.port.regshift = 1;
+ uart.port.irq = irqres->start;
+ uart.port.flags = UPF_IOREMAP;
+ uart.port.dev = &pdev->dev;
+ uart.port.uartclk = 14745600;
+ uart.port.set_termios = lp8x4x_serial_set_termios;
+ uart.port.private_data = data;
+
+ ret = serial8250_register_8250_port(&uart);
+ if (ret < 0)
+ return ret;
+
+ data->line = ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int lp8x4x_serial_remove(struct platform_device *pdev)
+{
+ struct lp8x4x_serial_data *data = platform_get_drvdata(pdev);
+
+ serial8250_unregister_port(data->line);
+
+ return 0;
+}
+
+static struct platform_driver lp8x4x_serial_driver = {
+ .probe = lp8x4x_serial_probe,
+ .remove = lp8x4x_serial_remove,
+
+ .driver = {
+ .name = "uart-lp8x4x",
+ .owner = THIS_MODULE,
+ .of_match_table = lp8x4x_serial_dt_ids,
+ },
+};
+
+module_platform_driver(lp8x4x_serial_driver);
+
+MODULE_AUTHOR("Sergei Ianovich");
+MODULE_DESCRIPTION("8250 serial port module for LP-8x4x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 81bd7c9..9fb0fbb 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -311,3 +311,15 @@ config SERIAL_PXA
can enable its onboard serial ports by enabling this option.

If you choose M here, the module name will be 8250_pxa.
+
+config SERIAL_8250_LP8X4X
+ tristate "Support 16550A ports on ICP DAS LP-8x4x"
+ depends on OF && SERIAL_8250 && SERIAL_8250_MANY_PORTS && ARCH_PXA
+ select LP8X4X_IRQ
+ help
+ In addition to serial ports on PXA270 SoC, LP-8x4x has 1 dual
+ RS232/RS485 port, 1 RS485 port and 1 RS232 port.
+
+ Say N here, unless you plan to run this kernel on a LP-8x4x system.
+
+ If you choose M here, the module name will be 8250_lp8x4x.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index b7d1b61..7370bfb 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
+obj-$(CONFIG_SERIAL_8250_LP8X4X) += 8250_lp8x4x.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
--
1.8.4.2

2014-04-16 17:19:35

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 14/21] misc: support for LP-8x4x rotary switch

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* new patch

.../devicetree/bindings/misc/lp8x4x-bus.txt | 8 ++--
Documentation/misc-devices/lp8x4x_bus.txt | 3 ++
drivers/misc/lp8x4x_bus.c | 43 ++++++++++++++++++----
3 files changed, 43 insertions(+), 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
index 1c87a29..8a55020 100644
--- a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -5,12 +5,14 @@ See Documentation/misc-devices/lp8x4x_bus.txt for details.
Required properties:
- compatible : should be "icpdas,backplane-lp8x4x"

-- reg: physical base address of the slot count register and the length
- of the memory mapped region.
+- reg: physical base addresses and region lengths of
+ * the rotary switch
+ * the slot count register

Example:

backplane {
compatible = "icpdas,backplane-lp8x4x";
- reg = <0x17009046 0x2>;
+ reg = <0x0 0x2
+ 0x9046 0x2>;
};
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index f5392b3..bea435b 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -26,5 +26,8 @@ SYSFS

/sys/bus/icpdas/devices/backplane:

+rotary
+ RO - shows position of LP-8x4x rotary switch (0-9)
+
slot_count
RO - shows total number of expansion slots on the device
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 7aa55cf..18ca8f8 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -25,6 +25,7 @@ MODULE_DESCRIPTION("ICP DAS LP-8x4x parallel bus driver");
struct lp8x4x_master {
unsigned int slot_count;
void *count_addr;
+ void *rotary_addr;
struct device dev;
};

@@ -56,8 +57,19 @@ static ssize_t slot_count_show(struct device *dev,

static DEVICE_ATTR_RO(slot_count);

+static ssize_t rotary_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+
+ return sprintf(buf, "%u\n", (ioread8(m->rotary_addr) ^ 0xf) & 0xf);
+}
+
+static DEVICE_ATTR_RO(rotary);
+
static struct attribute *master_dev_attrs[] = {
&dev_attr_slot_count.attr,
+ &dev_attr_rotary.attr,
NULL,
};
ATTRIBUTE_GROUPS(master_dev);
@@ -76,6 +88,7 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
{
struct lp8x4x_master *m, **p;
struct resource *res;
+ int r = 0;
int err = 0;

m = kzalloc(sizeof(*m), GFP_KERNEL);
@@ -89,18 +102,32 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
}
*p = m;

- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get rotary address\n");
+ err = -ENODEV;
+ goto err_free;
+ }
+
+ m->rotary_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(m->rotary_addr)) {
+ dev_err(&pdev->dev, "Failed to ioremap rotary address\n");
+ err = PTR_ERR(m->rotary_addr);
+ goto err_free;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
if (!res) {
dev_err(&pdev->dev, "could not get slot count address\n");
err = -ENODEV;
- goto err2;
+ goto err_free;
}

m->count_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(m->count_addr)) {
dev_err(&pdev->dev, "Failed to ioremap slot count address\n");
err = PTR_ERR(m->count_addr);
- goto err2;
+ goto err_free;
}

m->slot_count = ioread8(m->count_addr);
@@ -115,7 +142,7 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "unexpected slot number(%u)",
m->slot_count);
err = -ENODEV;
- goto err2;
+ goto err_free;
};

dev_info(&pdev->dev, "found bus with up to %u slots\n", m->slot_count);
@@ -123,7 +150,7 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
err = bus_register(&lp8x4x_bus_type);
if (err < 0) {
dev_err(&pdev->dev, "failed to register bus type\n");
- goto err2;
+ goto err_free;
}

m->dev.bus = &lp8x4x_bus_type;
@@ -135,15 +162,15 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
err = device_register(&m->dev);
if (err < 0) {
dev_err(&pdev->dev, "failed to register backplane device\n");
- goto err3;
+ goto err_bus;
}

devres_add(&pdev->dev, p);
return 0;

-err3:
+err_bus:
bus_unregister(&lp8x4x_bus_type);
-err2:
+err_free:
devres_free(p);
err1:
kfree(m);
--
1.8.4.2

2014-04-16 17:19:53

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 17/21] misc: support for serial slots in LP-8x4x

Serial modules (I-870xxW series) implement DCON protocol which
allows one-master-many-slaves configuration over RS-485. When
these modules are installed into the device, they could be
accessed using the 2nd PXA built-in UART port (/dev/ttyS1).
However, it seems that addresses are not processed by the modules.
So the parallel bus needs to select which slot is connected.

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* no changes (except number 12/16 -> 17/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/misc/lp8x4x-bus.txt | 2 +
Documentation/misc-devices/lp8x4x_bus.txt | 15 +++++-
drivers/misc/lp8x4x_bus.c | 63 ++++++++++++++++++++++
3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
index b0fb145..24a8c62 100644
--- a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -8,6 +8,7 @@ Required properties:
- reg: physical base addresses and region lengths of
* the rotary switch
* the 8bit DIP switch
+ * the serial slot select register
* the slot count register

- eeprom-gpios : should point to active-low write enable GPIO
@@ -18,6 +19,7 @@ Example:
compatible = "icpdas,backplane-lp8x4x";
reg = <0x0 0x2
0x9002 0x2
+ 0x9004 0x2
0x9046 0x2>;
eeprom-gpios = <&gpio 4 0>;
};
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 78ea0a89..7b86797 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -19,13 +19,26 @@ LP-8x4x is an ARM-based industrial computer with a custom parallel bus to
connect expansion modules with digital input/output, analog input/output,
serial, CAN and other types of ports.

-The bus is implemented by a FPGA.
+The bus is implemented by a FPGA. There are two major groups of expansion
+modules: serial and parallel.
+
+Serial modules (I-870xxW series) implement DCON protocol which allows one-
+master-many-slaves configuration over RS-485. When these modules are installed
+into the device, they could be accessed using the 2nd PXA built-in UART port
+(/dev/ttyS1). However, it seems that addresses are not processed by
+the modules. So the parallel bus needs to select which slot is connected.

SYSFS
-----

/sys/bus/icpdas/devices/backplane:

+active_slot
+ RW - connects the select slot for serial communications. If there
+ is a parallel module in the selected slot, it simply ignores
+ incoming packets. So it is safe to activate any available
+ slot.
+
dip
RO - shows status of LP-8x4x 8bit DIP switch

diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 567fe078..e805640 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -29,6 +29,8 @@ struct lp8x4x_master {
void *rotary_addr;
void *dip_addr;
struct gpio_desc *eeprom_nWE;
+ unsigned int active_slot;
+ void *switch_addr;
struct device dev;
};

@@ -47,6 +49,9 @@ static void lp8x4x_master_release(struct device *dev)
struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
WARN_ON(!dev);

+ /* Disable serial communications */
+ iowrite8(0xff, m->switch_addr);
+
kfree(m);
}

@@ -114,11 +119,52 @@ static ssize_t dip_show(struct device *dev,

static DEVICE_ATTR_RO(dip);

+static ssize_t active_slot_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+
+ return sprintf(buf, "%u\n", m->active_slot);
+}
+
+static ssize_t active_slot_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+ unsigned int active_slot = 0;
+ int err;
+
+ if (!buf)
+ return count;
+ if (0 == count)
+ return count;
+
+ err = kstrtouint(buf, 10, &active_slot);
+ if (err != 0 || active_slot > m->slot_count) {
+ dev_err(dev, "slot number is out of range 1..%u\n",
+ m->slot_count);
+ return count;
+ }
+
+ if (!active_slot) {
+ m->active_slot = 0;
+ iowrite8(0xff, m->switch_addr);
+ return count;
+ }
+
+ m->active_slot = active_slot;
+ iowrite8((1 << (m->active_slot - 1)) ^ 0xff, m->switch_addr);
+ return count;
+}
+
+static DEVICE_ATTR_RW(active_slot);
+
static struct attribute *master_dev_attrs[] = {
&dev_attr_slot_count.attr,
&dev_attr_rotary.attr,
&dev_attr_eeprom_write_enable.attr,
&dev_attr_dip.attr,
+ &dev_attr_active_slot.attr,
NULL,
};
ATTRIBUTE_GROUPS(master_dev);
@@ -181,6 +227,20 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)

res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
if (!res) {
+ dev_err(&pdev->dev, "Failed to get slot switch address\n");
+ err = -ENODEV;
+ goto err_free;
+ }
+
+ m->switch_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(m->switch_addr)) {
+ dev_err(&pdev->dev, "Failed to ioremap switch address\n");
+ err = PTR_ERR(m->switch_addr);
+ goto err_free;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
+ if (!res) {
dev_err(&pdev->dev, "could not get slot count address\n");
err = -ENODEV;
goto err_free;
@@ -223,6 +283,9 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)

dev_info(&pdev->dev, "found bus with up to %u slots\n", m->slot_count);

+ /* Disable serial communications until explicitly enabled */
+ iowrite8(0xff, m->switch_addr);
+
err = bus_register(&lp8x4x_bus_type);
if (err < 0) {
dev_err(&pdev->dev, "failed to register bus type\n");
--
1.8.4.2

2014-04-16 17:19:59

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 20/21] misc: support for I-8042 in LP-8x4x

Status of I-8042 16 digital output channels can be managed via
sysfs.

Status of I-8042 16 digital input channels can be read via sysfs.

http://www.icpdas.com/products/Remote_IO/i-8ke/i-8042w.htm

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
v2..v3
v0..v2
* no changes (except number 15/16 -> 20/21)

Documentation/misc-devices/lp8x4x_bus.txt | 4 ++
drivers/misc/lp8x4x_bus.c | 76 ++++++++++++++++++++++++++++++-
2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 68fefde..3ea257d 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -59,6 +59,10 @@ slot_count
model
RO - shows expansion module model number

+input_status
+ RO - get status of digital input channels on the module in
+ the expansion slot.
+
output_status
RW - set status of digital output channels on the module in
the expansion slot. Value can be read back.
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 5af788d..3fb227d 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -29,6 +29,8 @@ struct lp8x4x_slot {
struct mutex lock;
unsigned int DO_len;
u32 DO;
+ unsigned int DI_len;
+ u32 DI;
struct device dev;
};

@@ -94,6 +96,21 @@ static void lp8x4x_slot_release(struct device *dev)
{
}

+static void lp8x4x_slot_get_DI(struct lp8x4x_slot *s)
+{
+ int i;
+ u32 b;
+
+ mutex_lock(&s->lock);
+ s->DI = 0;
+ for (i = 0; i < s->DI_len; i++) {
+ b = ioread8(s->data_addr + 2 * (i + 1));
+ b ^= 0xff;
+ s->DI += b << (i * 8);
+ }
+ mutex_unlock(&s->lock);
+}
+
static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
{
int i;
@@ -103,29 +120,70 @@ static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
mutex_unlock(&s->lock);
}

+static ssize_t input_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+
+ lp8x4x_slot_get_DI(s);
+ switch (s->DI_len) {
+ case 4:
+ return sprintf(buf, "0x%08x\n", s->DI);
+ case 2:
+ return sprintf(buf, "0x%04x\n", s->DI);
+ case 1:
+ return sprintf(buf, "0x%02x\n", s->DI);
+ default:
+ break;
+ }
+ return sprintf(buf, "0x%x\n", s->DI);
+}
+
+static DEVICE_ATTR_RO(input_status);
+
static ssize_t output_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);

- return sprintf(buf, "0x%08x\n", s->DO);
+ switch (s->DO_len) {
+ case 4:
+ return sprintf(buf, "0x%08x\n", s->DO);
+ case 2:
+ return sprintf(buf, "0x%04x\n", s->DO);
+ case 1:
+ return sprintf(buf, "0x%02x\n", s->DO);
+ default:
+ break;
+ }
+ return sprintf(buf, "0x%x\n", s->DO);
}

static ssize_t output_status_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+ u32 DO;
+ u8 *b = (void *) &DO;
+ int i;

if (!buf)
return count;
if (0 == count)
return count;

- if (kstrtouint(buf, 16, &s->DO) != 0) {
+ if (kstrtouint(buf, 16, &DO) != 0) {
dev_err(dev, "bad input\n");
return count;
}

+ for (i = 4; i > s->DO_len; i--)
+ if (b[i - 1] != 0) {
+ dev_err(dev, "bad input\n");
+ return count;
+ }
+
+ s->DO = DO;
lp8x4x_slot_set_DO(s);

return count;
@@ -156,6 +214,14 @@ static struct attribute *do_slot_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(do_slot_dev);

+static struct attribute *dio_slot_dev_attrs[] = {
+ &dev_attr_model.attr,
+ &dev_attr_output_status.attr,
+ &dev_attr_input_status.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(dio_slot_dev);
+
static void lp8x4x_master_release(struct device *dev)
{
struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
@@ -321,6 +387,12 @@ static void __init lp8x4x_bus_probe_slot(struct lp8x4x_master *m, int i,
lp8x4x_slot_set_DO(s);
s->dev.groups = do_slot_dev_groups;
break;
+ case 42:
+ s->DI_len = 2;
+ s->DO_len = 2;
+ lp8x4x_slot_set_DO(s);
+ s->dev.groups = dio_slot_dev_groups;
+ break;
default:
s->dev.groups = slot_dev_groups;
break;
--
1.8.4.2

2014-04-16 17:20:35

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 21/21] misc: support for I-8024 in LP-8x4x

Status of I-8042 4 analog output channels can be managed via
sysfs.

http://www.icpdas.com/root/product/solutions/remote_io/rs-485/i-8k_i-87k/i-8024w.html

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* no changes

v2..v3
* use usleep_range instead of custom nsleep
* number change (16/16 -> 21/21)

v0..v2
* no changes

Documentation/misc-devices/lp8x4x_bus.txt | 15 ++++++
drivers/misc/lp8x4x_bus.c | 78 +++++++++++++++++++++++++++++++
2 files changed, 93 insertions(+)

diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 3ea257d..6076e01 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -66,3 +66,18 @@ input_status
output_status
RW - set status of digital output channels on the module in
the expansion slot. Value can be read back.
+
+analog_output
+ RW - set status of analog output channels on the module in
+ the expansion slot. Tested with voltage output. Bits 0-13:
+ 0x00c0 is -10.0V
+ 0x2000 is 0.0V
+ 0x3f40 is +10.0V
+
+ So 1 unit of output is 1.25 mV.
+
+ Bits 15 and 14 determine which channel to apply the value:
+ 0x0000 is channel 1
+ 0x4000 is channel 2
+ 0x8000 is channel 3
+ 0xc000 is channel 4
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 3fb227d..72cd0b2 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -8,6 +8,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation or any later version.
*/
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
@@ -23,6 +24,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
MODULE_DESCRIPTION("ICP DAS LP-8x4x parallel bus driver");

+#define LP8X4X_MAX_AO_CHANNELS 4
struct lp8x4x_slot {
void *data_addr;
unsigned int model;
@@ -31,6 +33,8 @@ struct lp8x4x_slot {
u32 DO;
unsigned int DI_len;
u32 DI;
+ unsigned int AO_len;
+ u32 AO[LP8X4X_MAX_AO_CHANNELS];
struct device dev;
};

@@ -120,6 +124,26 @@ static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
mutex_unlock(&s->lock);
}

+static void lp8x4x_slot_reset_AO(struct lp8x4x_slot *s)
+{
+ int i;
+ mutex_lock(&s->lock);
+ for (i = 0; i < s->AO_len; i++)
+ s->AO[i] = 0x2000;
+ iowrite8(0x00, s->data_addr);
+ usleep_range(1, 2);
+ iowrite8(0xff, s->data_addr);
+ mutex_unlock(&s->lock);
+}
+
+static void lp8x4x_slot_set_AO(struct lp8x4x_slot *s, u32 val)
+{
+ mutex_lock(&s->lock);
+ iowrite8(val & 0xff, s->data_addr + 2);
+ iowrite8((val >> 8) & 0xff, s->data_addr + 4);
+ mutex_unlock(&s->lock);
+}
+
static ssize_t input_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -191,6 +215,48 @@ static ssize_t output_status_store(struct device *dev,

static DEVICE_ATTR_RW(output_status);

+static ssize_t analog_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+ int i, c = 0;
+
+ for (i = 0; i < s->AO_len; i++)
+ c += sprintf(&buf[c], "0x%04x\n", s->AO[i]);
+ return c;
+}
+
+static ssize_t analog_output_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+ u32 AO;
+ int i;
+
+ if (!buf)
+ return count;
+ if (0 == count)
+ return count;
+
+ if (kstrtouint(buf, 16, &AO) != 0) {
+ dev_err(dev, "bad input\n");
+ return count;
+ }
+
+ if (AO & 0xffff0000) {
+ dev_err(dev, "bad input\n");
+ return count;
+ }
+
+ i = AO >> 14;
+ s->AO[i] = AO & 0x3fff;
+ lp8x4x_slot_set_AO(s, AO);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(analog_output);
+
static ssize_t model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -222,6 +288,13 @@ static struct attribute *dio_slot_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(dio_slot_dev);

+static struct attribute *ao_slot_dev_attrs[] = {
+ &dev_attr_model.attr,
+ &dev_attr_analog_output.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ao_slot_dev);
+
static void lp8x4x_master_release(struct device *dev)
{
struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
@@ -382,6 +455,11 @@ static void __init lp8x4x_bus_probe_slot(struct lp8x4x_master *m, int i,
mutex_init(&s->lock);

switch (model) {
+ case 24:
+ s->AO_len = 4;
+ lp8x4x_slot_reset_AO(s);
+ s->dev.groups = ao_slot_dev_groups;
+ break;
case 41:
s->DO_len = 4;
lp8x4x_slot_set_DO(s);
--
1.8.4.2

2014-04-16 17:21:31

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 19/21] misc: support for I-8041 in LP-8x4x

Status of I-8041 32 digital output channels can be managed via
sysfs now.

http://www.icpdas.com/products/Remote_IO/i-8ke/i-8041w.htm

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
v2..v3
v0..v2
* no changes (except number 14/16 -> 19/21)

Documentation/misc-devices/lp8x4x_bus.txt | 4 ++
drivers/misc/lp8x4x_bus.c | 67 ++++++++++++++++++++++++++++++-
2 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 09380fd..68fefde 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -58,3 +58,7 @@ slot_count

model
RO - shows expansion module model number
+
+output_status
+ RW - set status of digital output channels on the module in
+ the expansion slot. Value can be read back.
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 59dc767..5af788d 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -26,6 +26,9 @@ MODULE_DESCRIPTION("ICP DAS LP-8x4x parallel bus driver");
struct lp8x4x_slot {
void *data_addr;
unsigned int model;
+ struct mutex lock;
+ unsigned int DO_len;
+ u32 DO;
struct device dev;
};

@@ -91,6 +94,45 @@ static void lp8x4x_slot_release(struct device *dev)
{
}

+static void lp8x4x_slot_set_DO(struct lp8x4x_slot *s)
+{
+ int i;
+ mutex_lock(&s->lock);
+ for (i = 0; i < s->DO_len; i++)
+ iowrite8((s->DO >> (i * 8)) & 0xff, s->data_addr + 2 * i);
+ mutex_unlock(&s->lock);
+}
+
+static ssize_t output_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+
+ return sprintf(buf, "0x%08x\n", s->DO);
+}
+
+static ssize_t output_status_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+
+ if (!buf)
+ return count;
+ if (0 == count)
+ return count;
+
+ if (kstrtouint(buf, 16, &s->DO) != 0) {
+ dev_err(dev, "bad input\n");
+ return count;
+ }
+
+ lp8x4x_slot_set_DO(s);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(output_status);
+
static ssize_t model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -107,6 +149,13 @@ static struct attribute *slot_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(slot_dev);

+static struct attribute *do_slot_dev_attrs[] = {
+ &dev_attr_model.attr,
+ &dev_attr_output_status.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(do_slot_dev);
+
static void lp8x4x_master_release(struct device *dev)
{
struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
@@ -242,8 +291,10 @@ static void devm_lp8x4x_bus_release(struct device *dev, void *res)
dev_dbg(dev, "releasing devices\n");
for (i = 0; i < LP8X4X_MAX_SLOT_COUNT; i++) {
s = &m->slot[i];
- if (s->model)
+ if (s->model) {
device_unregister(&s->dev);
+ mutex_destroy(&s->lock);
+ }
}
device_unregister(&m->dev);
bus_unregister(&lp8x4x_bus_type);
@@ -261,13 +312,25 @@ static void __init lp8x4x_bus_probe_slot(struct lp8x4x_master *m, int i,
dev_set_name(&s->dev, "slot%02i", i + 1);
s->dev.parent = &m->dev;
s->dev.release = lp8x4x_slot_release;
- s->dev.groups = slot_dev_groups;
s->model = model;
+ mutex_init(&s->lock);
+
+ switch (model) {
+ case 41:
+ s->DO_len = 4;
+ lp8x4x_slot_set_DO(s);
+ s->dev.groups = do_slot_dev_groups;
+ break;
+ default:
+ s->dev.groups = slot_dev_groups;
+ break;
+ };

err = device_register(&s->dev);
if (err < 0) {
dev_err(&s->dev, "failed to register device\n");
s->model = 0;
+ mutex_destroy(&s->lock);
return;
}
}
--
1.8.4.2

2014-04-16 17:21:53

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 18/21] misc: support for parallel slots in LP-8x4x

This patch enumerates parallel modules in expansion slots and exposes
model numbers via sysfs.

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* no changes (except number 13/16 -> 18/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/misc/lp8x4x-bus.txt | 9 ++
Documentation/misc-devices/lp8x4x_bus.txt | 8 ++
drivers/misc/lp8x4x_bus.c | 119 +++++++++++++++++++++
3 files changed, 136 insertions(+)

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
index 24a8c62..75ac3b2 100644
--- a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -7,6 +7,7 @@ Required properties:

- reg: physical base addresses and region lengths of
* the rotary switch
+ * 8 plugable industrial IO slots
* the 8bit DIP switch
* the serial slot select register
* the slot count register
@@ -18,6 +19,14 @@ Example:
backplane {
compatible = "icpdas,backplane-lp8x4x";
reg = <0x0 0x2
+ 0x1000 0x10
+ 0x2000 0x10
+ 0x3000 0x10
+ 0x4000 0x10
+ 0x5000 0x10
+ 0x6000 0x10
+ 0x7000 0x10
+ 0x8000 0x10
0x9002 0x2
0x9004 0x2
0x9046 0x2>;
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 7b86797..09380fd 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -28,6 +28,9 @@ into the device, they could be accessed using the 2nd PXA built-in UART port
(/dev/ttyS1). However, it seems that addresses are not processed by
the modules. So the parallel bus needs to select which slot is connected.

+Parallel modules allow much faster communication. There are accessed using
+IO memory through the FPGA. Their ports are exposed via sysfs.
+
SYSFS
-----

@@ -50,3 +53,8 @@ rotary

slot_count
RO - shows total number of expansion slots on the device
+
+/sys/bus/icpdas/devices/slot%02i:
+
+model
+ RO - shows expansion module model number
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index e805640..59dc767 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -23,6 +23,13 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
MODULE_DESCRIPTION("ICP DAS LP-8x4x parallel bus driver");

+struct lp8x4x_slot {
+ void *data_addr;
+ unsigned int model;
+ struct device dev;
+};
+
+#define LP8X4X_MAX_SLOT_COUNT 8
struct lp8x4x_master {
unsigned int slot_count;
void *count_addr;
@@ -31,9 +38,45 @@ struct lp8x4x_master {
struct gpio_desc *eeprom_nWE;
unsigned int active_slot;
void *switch_addr;
+ struct lp8x4x_slot slot[LP8X4X_MAX_SLOT_COUNT];
struct device dev;
};

+static unsigned char lp8x4x_model[256] = {
+ 0, 0, 0, 0x11, 0, 0x18, 0x13, 0x11,
+ 0x0e, 0x11, 0, 0, 0, 0x5a, 0x5b, 0x5c,
+ 0x3c, 0x44, 0x34, 0x3a, 0x39, 0x36, 0x37, 0x33,
+ 0x35, 0x40, 0x41, 0x42, 0x38, 0x3f, 0x32, 0x45,
+ 0xac, 0x70, 0x8e, 0x8e, 0x1e, 0x72, 0x90, 0x29,
+ 0x4a, 0x22, 0xd3, 0xd2, 0x28, 0x25, 0x2a, 0x29,
+ 0x48, 0x49, 0x5d, 0x1f, 0x20, 0x23, 0x24, 0x4d,
+ 0x3d, 0x3e, 0, 0, 0, 0, 0, 0,
+ 0, 0x78, 0x72, 0x2b, 0x5e, 0x5e, 0x36, 0xae,
+ 0x30, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0x5c, 0x5e, 0, 0x5e, 0, 0,
+ 0, 0x3b, 0, 0, 0, 0, 0, 0,
+ 0, 0x50, 0x2e, 0, 0x58, 0, 0, 0x43,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x54, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
static int lp8x4x_match(struct device *dev, struct device_driver *drv)
{
return 1;
@@ -44,6 +87,26 @@ static struct bus_type lp8x4x_bus_type = {
.match = lp8x4x_match,
};

+static void lp8x4x_slot_release(struct device *dev)
+{
+}
+
+static ssize_t model_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_slot *s = container_of(dev, struct lp8x4x_slot, dev);
+
+ return sprintf(buf, "%u\n", s->model + 8000);
+}
+
+static DEVICE_ATTR_RO(model);
+
+static struct attribute *slot_dev_attrs[] = {
+ &dev_attr_model.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(slot_dev);
+
static void lp8x4x_master_release(struct device *dev)
{
struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
@@ -173,18 +236,50 @@ ATTRIBUTE_GROUPS(master_dev);
static void devm_lp8x4x_bus_release(struct device *dev, void *res)
{
struct lp8x4x_master *m = *(struct lp8x4x_master **)res;
+ struct lp8x4x_slot *s;
+ int i;

dev_dbg(dev, "releasing devices\n");
+ for (i = 0; i < LP8X4X_MAX_SLOT_COUNT; i++) {
+ s = &m->slot[i];
+ if (s->model)
+ device_unregister(&s->dev);
+ }
device_unregister(&m->dev);
bus_unregister(&lp8x4x_bus_type);
}

+static void __init lp8x4x_bus_probe_slot(struct lp8x4x_master *m, int i,
+ unsigned char model)
+{
+ struct lp8x4x_slot *s = &m->slot[i];
+ int err;
+
+ dev_info(&m->dev, "found %u in slot %i\n", 8000 + model, i + 1);
+
+ s->dev.bus = &lp8x4x_bus_type;
+ dev_set_name(&s->dev, "slot%02i", i + 1);
+ s->dev.parent = &m->dev;
+ s->dev.release = lp8x4x_slot_release;
+ s->dev.groups = slot_dev_groups;
+ s->model = model;
+
+ err = device_register(&s->dev);
+ if (err < 0) {
+ dev_err(&s->dev, "failed to register device\n");
+ s->model = 0;
+ return;
+ }
+}
+
static int __init lp8x4x_bus_probe(struct platform_device *pdev)
{
struct lp8x4x_master *m, **p;
struct resource *res;
int r = 0;
+ int i;
int err = 0;
+ unsigned int model;

m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
@@ -211,6 +306,23 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
goto err_free;
}

+ for (i = 0; i < LP8X4X_MAX_SLOT_COUNT; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get slot %i address\n",
+ i);
+ err = -ENODEV;
+ goto err_free;
+ }
+
+ m->slot[i].data_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(m->slot[i].data_addr)) {
+ dev_err(&pdev->dev, "Failed to ioremap slot %i\n", i);
+ err = PTR_ERR(m->slot[i].data_addr);
+ goto err_free;
+ }
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
if (!res) {
dev_err(&pdev->dev, "Failed to get DIP switch address\n");
@@ -305,6 +417,13 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
}

devres_add(&pdev->dev, p);
+ for (i = 0; i < LP8X4X_MAX_SLOT_COUNT; i++) {
+ model = lp8x4x_model[ioread8(m->slot[i].data_addr)];
+ if (!model)
+ continue;
+
+ lp8x4x_bus_probe_slot(m, i, model);
+ }
return 0;

err_bus:
--
1.8.4.2

2014-04-16 17:19:48

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 16/21] misc: support for writing to LP-8x4x EEPROM

at24c128 write protection is implemented by a separate GPIO line.
EEPROM driver doesn't provide this option, so we implement it
in the board-specific device.

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* new patch

.../devicetree/bindings/misc/lp8x4x-bus.txt | 3 ++
Documentation/misc-devices/lp8x4x_bus.txt | 3 ++
drivers/misc/lp8x4x_bus.c | 50 ++++++++++++++++++++++
3 files changed, 56 insertions(+)

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
index 1b1776a..b0fb145 100644
--- a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -10,6 +10,8 @@ Required properties:
* the 8bit DIP switch
* the slot count register

+- eeprom-gpios : should point to active-low write enable GPIO
+
Example:

backplane {
@@ -17,4 +19,5 @@ Example:
reg = <0x0 0x2
0x9002 0x2
0x9046 0x2>;
+ eeprom-gpios = <&gpio 4 0>;
};
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index 829781b..78ea0a89 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -29,6 +29,9 @@ SYSFS
dip
RO - shows status of LP-8x4x 8bit DIP switch

+eeprom_write_enable
+ RW - controls write access to board's EEPROM (1 - enable)
+
rotary
RO - shows position of LP-8x4x rotary switch (0-9)

diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index dfbc8c4..567fe078 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation or any later version.
*/
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/list.h>
@@ -27,6 +28,7 @@ struct lp8x4x_master {
void *count_addr;
void *rotary_addr;
void *dip_addr;
+ struct gpio_desc *eeprom_nWE;
struct device dev;
};

@@ -68,6 +70,40 @@ static ssize_t rotary_show(struct device *dev,

static DEVICE_ATTR_RO(rotary);

+static ssize_t eeprom_write_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+
+ return sprintf(buf, "%u\n", !gpiod_get_value(m->eeprom_nWE));
+}
+
+static ssize_t eeprom_write_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+ unsigned int val = 0;
+ int err;
+
+ if (!buf)
+ return count;
+
+ if (0 == count)
+ return count;
+
+ err = kstrtouint(buf, 10, &val);
+ if (err != 0) {
+ dev_err(dev, "Bad input %s\n", buf);
+ return count;
+ }
+
+ gpiod_set_value(m->eeprom_nWE, !val);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(eeprom_write_enable);
+
static ssize_t dip_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -81,6 +117,7 @@ static DEVICE_ATTR_RO(dip);
static struct attribute *master_dev_attrs[] = {
&dev_attr_slot_count.attr,
&dev_attr_rotary.attr,
+ &dev_attr_eeprom_write_enable.attr,
&dev_attr_dip.attr,
NULL,
};
@@ -156,6 +193,19 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)
goto err_free;
}

+ m->eeprom_nWE = devm_gpiod_get(&pdev->dev, "eeprom");
+ if (IS_ERR(m->eeprom_nWE)) {
+ err = PTR_ERR(m->eeprom_nWE);
+ dev_err(&pdev->dev, "Failed to get eeprom GPIO\n");
+ goto err_free;
+ }
+
+ err = gpiod_direction_output(m->eeprom_nWE, 1);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Failed to set eeprom GPIO output\n");
+ goto err_free;
+ }
+
m->slot_count = ioread8(m->count_addr);
switch (m->slot_count) {
case 1:
--
1.8.4.2

2014-04-16 17:23:03

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 15/21] misc: support for LP-8x4x DIP switch

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* new patch

.../devicetree/bindings/misc/lp8x4x-bus.txt | 2 ++
Documentation/misc-devices/lp8x4x_bus.txt | 3 +++
drivers/misc/lp8x4x_bus.c | 26 ++++++++++++++++++++++
3 files changed, 31 insertions(+)

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
index 8a55020..1b1776a 100644
--- a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -7,6 +7,7 @@ Required properties:

- reg: physical base addresses and region lengths of
* the rotary switch
+ * the 8bit DIP switch
* the slot count register

Example:
@@ -14,5 +15,6 @@ Example:
backplane {
compatible = "icpdas,backplane-lp8x4x";
reg = <0x0 0x2
+ 0x9002 0x2
0x9046 0x2>;
};
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
index bea435b..829781b 100644
--- a/Documentation/misc-devices/lp8x4x_bus.txt
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -26,6 +26,9 @@ SYSFS

/sys/bus/icpdas/devices/backplane:

+dip
+ RO - shows status of LP-8x4x 8bit DIP switch
+
rotary
RO - shows position of LP-8x4x rotary switch (0-9)

diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
index 18ca8f8..dfbc8c4 100644
--- a/drivers/misc/lp8x4x_bus.c
+++ b/drivers/misc/lp8x4x_bus.c
@@ -26,6 +26,7 @@ struct lp8x4x_master {
unsigned int slot_count;
void *count_addr;
void *rotary_addr;
+ void *dip_addr;
struct device dev;
};

@@ -67,9 +68,20 @@ static ssize_t rotary_show(struct device *dev,

static DEVICE_ATTR_RO(rotary);

+static ssize_t dip_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+
+ return sprintf(buf, "0x%02x\n", ioread8(m->dip_addr) ^ 0xff);
+}
+
+static DEVICE_ATTR_RO(dip);
+
static struct attribute *master_dev_attrs[] = {
&dev_attr_slot_count.attr,
&dev_attr_rotary.attr,
+ &dev_attr_dip.attr,
NULL,
};
ATTRIBUTE_GROUPS(master_dev);
@@ -118,6 +130,20 @@ static int __init lp8x4x_bus_probe(struct platform_device *pdev)

res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
if (!res) {
+ dev_err(&pdev->dev, "Failed to get DIP switch address\n");
+ err = -ENODEV;
+ goto err_free;
+ }
+
+ m->dip_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(m->dip_addr)) {
+ dev_err(&pdev->dev, "Failed to ioremap DIP switch address\n");
+ err = PTR_ERR(m->dip_addr);
+ goto err_free;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, r++);
+ if (!res) {
dev_err(&pdev->dev, "could not get slot count address\n");
err = -ENODEV;
goto err_free;
--
1.8.4.2

2014-04-16 17:23:47

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v4 13/21] misc: support for LP-8x4x custom parallel bus

This patch implements probing for the bus and reporting the number
of available expansion slots.

Signed-off-by: Sergei Ianovich <[email protected]>
---
v3..v4
* move DTS binding to a different patch (8/21)

v2..v3
* fixed goto after bus_register
* number change (11/16 -> 13/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/misc/lp8x4x-bus.txt | 16 ++
Documentation/misc-devices/lp8x4x_bus.txt | 30 ++++
arch/arm/configs/lp8x4x_defconfig | 1 +
drivers/misc/Kconfig | 13 ++
drivers/misc/Makefile | 1 +
drivers/misc/lp8x4x_bus.c | 167 +++++++++++++++++++++
6 files changed, 228 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
create mode 100644 Documentation/misc-devices/lp8x4x_bus.txt
create mode 100644 drivers/misc/lp8x4x_bus.c

diff --git a/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
new file mode 100644
index 0000000..1c87a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/lp8x4x-bus.txt
@@ -0,0 +1,16 @@
+Custom parallel bus on ICP DAS LP-8x4x industrial computers
+
+See Documentation/misc-devices/lp8x4x_bus.txt for details.
+
+Required properties:
+- compatible : should be "icpdas,backplane-lp8x4x"
+
+- reg: physical base address of the slot count register and the length
+ of the memory mapped region.
+
+Example:
+
+ backplane {
+ compatible = "icpdas,backplane-lp8x4x";
+ reg = <0x17009046 0x2>;
+ };
diff --git a/Documentation/misc-devices/lp8x4x_bus.txt b/Documentation/misc-devices/lp8x4x_bus.txt
new file mode 100644
index 0000000..f5392b3
--- /dev/null
+++ b/Documentation/misc-devices/lp8x4x_bus.txt
@@ -0,0 +1,30 @@
+Kernel driver lpx8x4x_bus
+======================
+
+Supported hardare:
+Custom parallel bus on ICP DAS LP-8x4x industrial computers
+
+Data sheet:
+Not freely available
+
+Author:
+Sergei Ianovich <[email protected]>
+
+Description
+-----------
+
+http://www.icpdas.com/root/product/solutions/pac/linpac/lp-8x4x_hardware.html
+
+LP-8x4x is an ARM-based industrial computer with a custom parallel bus to
+connect expansion modules with digital input/output, analog input/output,
+serial, CAN and other types of ports.
+
+The bus is implemented by a FPGA.
+
+SYSFS
+-----
+
+/sys/bus/icpdas/devices/backplane:
+
+slot_count
+ RO - shows total number of expansion slots on the device
diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
index 9116ce1..c34eb2a 100644
--- a/arch/arm/configs/lp8x4x_defconfig
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -62,6 +62,7 @@ CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_LOOP_MIN_COUNT=2
CONFIG_EEPROM_AT24=m
+CONFIG_LP8X4X_BUS=m
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1cb7408..08ffe63 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,19 @@ config SRAM
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.

+config LP8X4X_BUS
+ tristate "ICP DAS LP-8x4x industrial IO bus"
+ depends on OF && ARCH_PXA
+ select SYSFS
+ ---help---
+ This is a driver for ICP DAS LP-8x4x programmable automation
+ controller. It exposes a custom parallel bus. The bus services
+ data acquisition and control modules.
+
+ Say N, unless you plan to run this kernel on a LP-8x4x system.
+
+ If you say M here, the module will be called lp8x4x_bus.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7eb4b69..2bfe25d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
+obj-$(CONFIG_LP8X4X_BUS) += lp8x4x_bus.o
diff --git a/drivers/misc/lp8x4x_bus.c b/drivers/misc/lp8x4x_bus.c
new file mode 100644
index 0000000..7aa55cf
--- /dev/null
+++ b/drivers/misc/lp8x4x_bus.c
@@ -0,0 +1,167 @@
+/*
+ * linux/misc/lp8x4x_bus.c
+ *
+ * Support for ICP DAS LP-8x4x programmable automation controller bus
+ * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
+ */
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define MODULE_NAME "lp8x4x-bus"
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
+MODULE_DESCRIPTION("ICP DAS LP-8x4x parallel bus driver");
+
+struct lp8x4x_master {
+ unsigned int slot_count;
+ void *count_addr;
+ struct device dev;
+};
+
+static int lp8x4x_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static struct bus_type lp8x4x_bus_type = {
+ .name = "icpdas",
+ .match = lp8x4x_match,
+};
+
+static void lp8x4x_master_release(struct device *dev)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+ WARN_ON(!dev);
+
+ kfree(m);
+}
+
+static ssize_t slot_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp8x4x_master *m = container_of(dev, struct lp8x4x_master, dev);
+
+ return sprintf(buf, "%u\n", m->slot_count);
+}
+
+static DEVICE_ATTR_RO(slot_count);
+
+static struct attribute *master_dev_attrs[] = {
+ &dev_attr_slot_count.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(master_dev);
+
+
+static void devm_lp8x4x_bus_release(struct device *dev, void *res)
+{
+ struct lp8x4x_master *m = *(struct lp8x4x_master **)res;
+
+ dev_dbg(dev, "releasing devices\n");
+ device_unregister(&m->dev);
+ bus_unregister(&lp8x4x_bus_type);
+}
+
+static int __init lp8x4x_bus_probe(struct platform_device *pdev)
+{
+ struct lp8x4x_master *m, **p;
+ struct resource *res;
+ int err = 0;
+
+ m = kzalloc(sizeof(*m), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ p = devres_alloc(devm_lp8x4x_bus_release, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ err = -ENOMEM;
+ goto err1;
+ }
+ *p = m;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "could not get slot count address\n");
+ err = -ENODEV;
+ goto err2;
+ }
+
+ m->count_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(m->count_addr)) {
+ dev_err(&pdev->dev, "Failed to ioremap slot count address\n");
+ err = PTR_ERR(m->count_addr);
+ goto err2;
+ }
+
+ m->slot_count = ioread8(m->count_addr);
+ switch (m->slot_count) {
+ case 1:
+ case 4:
+ break;
+ case 7:
+ m->slot_count = 8;
+ break;
+ default:
+ dev_err(&pdev->dev, "unexpected slot number(%u)",
+ m->slot_count);
+ err = -ENODEV;
+ goto err2;
+ };
+
+ dev_info(&pdev->dev, "found bus with up to %u slots\n", m->slot_count);
+
+ err = bus_register(&lp8x4x_bus_type);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register bus type\n");
+ goto err2;
+ }
+
+ m->dev.bus = &lp8x4x_bus_type;
+ dev_set_name(&m->dev, "backplane");
+ m->dev.parent = &pdev->dev;
+ m->dev.release = lp8x4x_master_release;
+ m->dev.groups = master_dev_groups;
+
+ err = device_register(&m->dev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register backplane device\n");
+ goto err3;
+ }
+
+ devres_add(&pdev->dev, p);
+ return 0;
+
+err3:
+ bus_unregister(&lp8x4x_bus_type);
+err2:
+ devres_free(p);
+err1:
+ kfree(m);
+ return err;
+}
+
+static const struct of_device_id lp8x4x_bus_dt_ids[] = {
+ { .compatible = "icpdas,backplane-lp8x4x" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp8x4x_bus_dt_ids);
+
+static struct platform_driver lp8x4x_bus_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = lp8x4x_bus_dt_ids,
+ },
+};
+
+module_platform_driver_probe(lp8x4x_bus_driver, lp8x4x_bus_probe);
--
1.8.4.2

2014-04-16 17:39:32

by Daniel Mack

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

Hi,

On 04/16/2014 07:13 PM, Sergei Ianovich wrote:
> We had an intensive discussion of the series in the beginning of
> December 2013 [1]. The discussion resulted in 3 versions of the series
> in less than 3 weeks. Then there was a decision to block this series
> until Daniel Mack's DMA-engine-for-PXA series is ready.

For whatever reason, I only received patches 1-8 out of 21 in this series.

> Unfortunately Daniel neigther agrees to review my trivial temporary
> solution to the DMA-in-device-tree (patch 7 of 21), nor he is active
> in DMA series development. There is no progress for 3 months.

Well, IIRC, I asked you to look into the MMC performance regressions
that you reported and see what we can do about them. I currently don't
have hardware to reproduce this, so I can't do it myself.

My impression is that the transisiton will only be painless if the
mmp_pdma driver provides comparable performance to the existing
implementation. I also still think that adding hacks to the drivers to
manually parse the dma properties (as in #8) brings us further away from
a proper solution, not closer.

> As with any big out of tree series, its support is painful. It requires
> extensive merging when doing bisects, on each step.

I know that, and I'm not violently against getting your patches in. I
was just hoping for some help around here in order to sort out things
and implement it properly. The reason why I haven't been working on
these drivers lately is simply the sheer lack of time :(


Thanks,
Daniel

2014-04-16 18:38:26

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 12/21] serial: support for 16550A serial ports on LP-8x4x

> + baud = uart_get_baud_rate(port, termios, old,
> + port->uartclk / 16 / 0xffff,
> + port->uartclk / 16);
> + switch (baud) {
> + case 2400:
> + len |= 1;
> + break;
> + case 4800:
> + len |= 2;
> + break;
> + case 19200:
> + len |= 4;
> + break;
> + case 38400:
> + len |= 5;
> + break;
> + case 57600:
> + len |= 6;
> + break;
> + case 115200:
> + len |= 7;
> + break;
> + case 9600:
> + default:
> + len |= 3;
> + break;
> + };

Some explanation of this would be useful - eg why is it set to 7 for
115200 baud and 3 for 115201 baud ?

2014-04-16 18:40:38

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 21/21] misc: support for I-8024 in LP-8x4x

On Wed, 16 Apr 2014 21:17:26 +0400
Sergei Ianovich <[email protected]> wrote:

> Status of I-8042 4 analog output channels can be managed via
> sysfs.

Surely this is an iio interface and should follow the same API as
everyone else ?

Alan

2014-04-16 18:42:52

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 13/21] misc: support for LP-8x4x custom parallel bus

On Wed, 16 Apr 2014 21:17:18 +0400
Sergei Ianovich <[email protected]> wrote:

> This patch implements probing for the bus and reporting the number
> of available expansion slots.

This appears to be a bus not a misc device. I don't think it belongs in
misc. As you've got devices on this bus (or nailed into the 'bus' driver)
perhaps this belongs as drivers/platform/lp8x4x or similar with any
specific drivers split out - or even drivers/lp8x4x if there will be more
of them and more than one platform using it ?

Alan

2014-04-16 18:43:23

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH v4 13/21] misc: support for LP-8x4x custom parallel bus

On Wednesday 16 April 2014 19:41:09 One Thousand Gnomes wrote:
> On Wed, 16 Apr 2014 21:17:18 +0400
> Sergei Ianovich <[email protected]> wrote:
>
> > This patch implements probing for the bus and reporting the number
> > of available expansion slots.
>
> This appears to be a bus not a misc device. I don't think it belongs in
> misc. As you've got devices on this bus (or nailed into the 'bus' driver)
> perhaps this belongs as drivers/platform/lp8x4x or similar with any
> specific drivers split out - or even drivers/lp8x4x if there will be more
> of them and more than one platform using it ?

We have drivers/bus now, I think it could fit well in there.

Arnd

2014-04-16 19:00:01

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 12/21] serial: support for 16550A serial ports on LP-8x4x

One Thousand Gnomes <[email protected]> wrote:
>> + baud = uart_get_baud_rate(port, termios, old,
>> + port->uartclk / 16 / 0xffff,
>> + port->uartclk / 16);
>> + switch (baud) {
>> + case 2400:
>> + len |= 1;
>> + break;
>> + case 4800:
>> + len |= 2;
>> + break;
>> + case 19200:
>> + len |= 4;
>> + break;
>> + case 38400:
>> + len |= 5;
>> + break;
>> + case 57600:
>> + len |= 6;
>> + break;
>> + case 115200:
>> + len |= 7;
>> + break;
>> + case 9600:
>> + default:
>> + len |= 3;
>> + break;
>> + };
>
>Some explanation of this would be useful - eg why is it set to 7 for
>115200 baud and 3 for 115201 baud ?

I am not related to the device vendor in any way, so please take my answers for what they are worth.

It seems that there is not enough pins to properly connect the chips to the memory bus and just you the standard 8250 UART driver. Instead, clock divisor is set using this register.

So, if you know you're asking for (115200) you get it. If you don't or guess (115201), you get the default 9600.

This is a policy, it may not be the right way to write a driver, but it is cheap and it works.

2014-04-16 19:26:53

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 21/21] misc: support for I-8024 in LP-8x4x

One Thousand Gnomes <[email protected]> wrote:
>On Wed, 16 Apr 2014 21:17:26 +0400
>Sergei Ianovich <[email protected]> wrote:
>
>> Status of I-8042 4 analog output channels can be managed via
>> sysfs.
>
>Surely this is an iio interface and should follow the same API as
>everyone else ?

Thanks for the feedback. It's the first response to this particular driver ever.

I've looked around the tree and found tons of industrial io device drivers in different places: drivers/iio, drivers/misc, drivers/staging/comedi to name a few.

I've closely examined a dozen or two of drivers, but of them was close enough for I-8024 in terms of speed. Its channel takes 0.1 us to set up from kernel. Real user space programs can setup all for channels in 1 us with interface provided by this patch. That's said I don't have data on time that it takes for output to stabilize at the desired levels.

I've chosen drivers/misc as a location that looked to provide the shortest path for my driver to land. The driver needs support for the bus, and here it can be in one driver.

It would be great to have some more detailed input on this issue.

2014-04-16 19:52:08

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 13/21] misc: support for LP-8x4x custom parallel bus

One Thousand Gnomes <[email protected]> wrote:
>On Wed, 16 Apr 2014 21:17:18 +0400
>Sergei Ianovich <[email protected]> wrote:
>
>> This patch implements probing for the bus and reporting the number
>> of available expansion slots.
>
>This appears to be a bus not a misc device. I don't think it belongs in
>misc. As you've got devices on this bus (or nailed into the 'bus'
>driver)
>perhaps this belongs as drivers/platform/lp8x4x or similar with any
>specific drivers split out - or even drivers/lp8x4x if there will be
>more
>of them and more than one platform using it ?

Thanks again for responding. I don't have a clear idea about proper code organization concering this bus.

The total number of possible bus devices is quite large, around 30. Half of them would take less than 10 extra lines to get support. The rest is more complicated.

LP-8x4x has several features that would require a misc device anyway. It has a rotary switch which isn't an input device, because it produces no interrupts. The device also has a similar DIP switch. In addition, it acts as a kind of RS-485-port-to-multi-RS-232-ports logical bridge. It has a multiplexer which allows to talk to several serial modules which support a custom protocol designed for RS-485 using a RS-232 port.



2014-04-16 19:57:32

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 21/21] misc: support for I-8024 in LP-8x4x

> I've closely examined a dozen or two of drivers, but of them was close enough for I-8024 in terms of speed. Its channel takes 0.1 us to set up from kernel. Real user space programs can setup all for channels in 1 us with interface provided by this patch. That's said I don't have data on time that it takes for output to stabilize at the desired levels.
>
> I've chosen drivers/misc as a location that looked to provide the shortest path for my driver to land. The driver needs support for the bus, and here it can be in one driver.
>
> It would be great to have some more detailed input on this issue.

IIO is where we are trying to standardise all the industrial control
devices. There is stuff in places like misc for historical reasons.

2014-04-16 20:03:31

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 12/21] serial: support for 16550A serial ports on LP-8x4x

On Wed, 16 Apr 2014 23:01:47 +0400
Sergei Ianovich <[email protected]> wrote:

> One Thousand Gnomes <[email protected]> wrote:
> >> + baud = uart_get_baud_rate(port, termios, old,
> >> + port->uartclk / 16 / 0xffff,
> >> + port->uartclk / 16);
> >> + switch (baud) {
> >> + case 2400:
> >> + len |= 1;
> >> + break;
> >> + case 4800:
> >> + len |= 2;
> >> + break;
> >> + case 19200:
> >> + len |= 4;
> >> + break;
> >> + case 38400:
> >> + len |= 5;
> >> + break;
> >> + case 57600:
> >> + len |= 6;
> >> + break;
> >> + case 115200:
> >> + len |= 7;
> >> + break;
> >> + case 9600:
> >> + default:
> >> + len |= 3;
> >> + break;
> >> + };
> >
> >Some explanation of this would be useful - eg why is it set to 7 for
> >115200 baud and 3 for 115201 baud ?
>
> I am not related to the device vendor in any way, so please take my answers for what they are worth.
>
> It seems that there is not enough pins to properly connect the chips to the memory bus and just you the standard 8250 UART driver. Instead, clock divisor is set using this register.
>
> So, if you know you're asking for (115200) you get it. If you don't or guess (115201), you get the default 9600.

Thats not quite what the code actually implements though.

> This is a policy, it may not be the right way to write a driver, but it is cheap and it works.

If thats how the hardware works and only supports a few fixed rates then
fine, but for default: you need to actually update he baudrate in the
passed termios struct so that the userspace knows its request was not
matched.

Take a look how the standard 8250 termios does it.

Also looking at this and some of the other serial bits I see no
dependencies on the problematic DMA driver, so does that mean you've got
a set of pieces that can be submitted anyway while the DMA driver
discussion continues ?

Alan

2014-04-16 20:04:49

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 21/21] misc: support for I-8024 in LP-8x4x

One Thousand Gnomes <[email protected]> wrote:
>> I've closely examined a dozen or two of drivers, but of them was
>close enough for I-8024 in terms of speed. Its channel takes 0.1 us to
>set up from kernel. Real user space programs can setup all for channels
>in 1 us with interface provided by this patch. That's said I don't have
>data on time that it takes for output to stabilize at the desired
>levels.
>>
>> I've chosen drivers/misc as a location that looked to provide the
>shortest path for my driver to land. The driver needs support for the
>bus, and here it can be in one driver.
>>
>> It would be great to have some more detailed input on this issue.
>
>IIO is where we are trying to standardise all the industrial control
>devices. There is stuff in places like misc for historical reasons.

Great. I'll rewrite the driver using IIO, but the bus issue needs to be settled first.

2014-04-16 20:30:24

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 12/21] serial: support for 16550A serial ports on LP-8x4x

One Thousand Gnomes <[email protected]> wrote:
>On Wed, 16 Apr 2014 23:01:47 +0400
>Sergei Ianovich <[email protected]> wrote:
>
>> One Thousand Gnomes <[email protected]> wrote:
>> >> + baud = uart_get_baud_rate(port, termios, old,
>> >> + port->uartclk / 16 / 0xffff,
>> >> + port->uartclk / 16);
>> >> + switch (baud) {
>> >> + case 2400:
>> >> + len |= 1;
>> >> + break;
>> >> + case 4800:
>> >> + len |= 2;
>> >> + break;
>> >> + case 19200:
>> >> + len |= 4;
>> >> + break;
>> >> + case 38400:
>> >> + len |= 5;
>> >> + break;
>> >> + case 57600:
>> >> + len |= 6;
>> >> + break;
>> >> + case 115200:
>> >> + len |= 7;
>> >> + break;
>> >> + case 9600:
>> >> + default:
>> >> + len |= 3;
>> >> + break;
>> >> + };
>> >
>> >Some explanation of this would be useful - eg why is it set to 7 for
>> >115200 baud and 3 for 115201 baud ?
>>
>> I am not related to the device vendor in any way, so please take my
>answers for what they are worth.
>>
>> It seems that there is not enough pins to properly connect the chips
>to the memory bus and just you the standard 8250 UART driver. Instead,
>clock divisor is set using this register.
>>
>> So, if you know you're asking for (115200) you get it. If you don't
>or guess (115201), you get the default 9600.
>
>Thats not quite what the code actually implements though.

>> This is a policy, it may not be the right way to write a driver, but
>it is cheap and it works.

>If thats how the hardware works and only supports a few fixed rates
>then
>fine, but for default: you need to actually update he baudrate in the
>passed termios struct so that the userspace knows its request was not
>matched.
>
>Take a look how the standard 8250 termios does it.

I will. I've tested the driver by setting termios from userspace. There was no chance to ask for arbitrary rate, so I didn't think much about this case.

>Also looking at this and some of the other serial bits I see no
>dependencies on the problematic DMA driver, so does that mean you've
>got
>a set of pieces that can be submitted anyway while the DMA driver
>discussion continues ?

Yes, getting each peace in will reduce the support burden.

The degree of interdependence varies from feature to feature. rtc, mtd, irq and serial could be taken independently.

The main patch is #8. It requires #1-7. The is problem with DMA is one of policy, not technical one. It is explained in #6-7 in details.

Everything in the misc depends on bus. But the bus is very simple. There is even no interrupts.

2014-04-16 20:30:52

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH v4 13/21] misc: support for LP-8x4x custom parallel bus

On Wed, 16 Apr 2014 20:42:39 +0200
Arnd Bergmann <[email protected]> wrote:

> On Wednesday 16 April 2014 19:41:09 One Thousand Gnomes wrote:
> > On Wed, 16 Apr 2014 21:17:18 +0400
> > Sergei Ianovich <[email protected]> wrote:
> >
> > > This patch implements probing for the bus and reporting the number
> > > of available expansion slots.
> >
> > This appears to be a bus not a misc device. I don't think it belongs in
> > misc. As you've got devices on this bus (or nailed into the 'bus' driver)
> > perhaps this belongs as drivers/platform/lp8x4x or similar with any
> > specific drivers split out - or even drivers/lp8x4x if there will be more
> > of them and more than one platform using it ?
>
> We have drivers/bus now, I think it could fit well in there.

Agreed

2014-04-16 20:57:16

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

Daniel Mack <[email protected]> wrote:
>Hi,
>
>On 04/16/2014 07:13 PM, Sergei Ianovich wrote:
>> We had an intensive discussion of the series in the beginning of
>> December 2013 [1]. The discussion resulted in 3 versions of the
>series
>> in less than 3 weeks. Then there was a decision to block this series
>> until Daniel Mack's DMA-engine-for-PXA series is ready.
>
>For whatever reason, I only received patches 1-8 out of 21 in this
>series.

The rest is related to exotic parts of the tree and 'scripts/get_maintainer.pl' decided not to spam you. The whole series is sent to both lkml and arm lists. I can add you to cc by hand if you are interested.

>> Unfortunately Daniel neigther agrees to review my trivial temporary
>> solution to the DMA-in-device-tree (patch 7 of 21), nor he is active
>> in DMA series development. There is no progress for 3 months.
>
>Well, IIRC, I asked you to look into the MMC performance regressions
>that you reported and see what we can do about them. I currently don't
>have hardware to reproduce this, so I can't do it myself.

I hate to point fingers, but you promissed to refresh the code on several occasions. It is from 3.8 tree at the moment. The regression may be introduced by an error in my merging of your patches. I published the one I tested in one piece with the results.

>My impression is that the transisiton will only be painless if the
>mmp_pdma driver provides comparable performance to the existing
>implementation. I also still think that adding hacks to the drivers to
>manually parse the dma properties (as in #8) brings us further away
>from
>a proper solution, not closer

No doubt about that. But the regression is not the only problem. Driver for video capture requires an extensive rewrite as well. The result is that a working pxa DT device is kept out of the tree and there is no supported DT devices in tree. So no proper examples for new devices and no drive-by improvements. All of this prevents rather than facilitates eventual DT migration of tha PXA platform.

2014-04-17 10:46:03

by Daniel Mack

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

On 04/16/2014 10:59 PM, Sergei Ianovich wrote:
>> Well, IIRC, I asked you to look into the MMC performance
>> regressions that you reported and see what we can do about them. I
>> currently don't have hardware to reproduce this, so I can't do it
>> myself.
>
> I hate to point fingers, but you promissed to refresh the code on
> several occasions. It is from 3.8 tree at the moment. The regression
> may be introduced by an error in my merging of your patches. I
> published the one I tested in one piece with the results.

Ok, well. I didn't see this as a potential reason, sorry. Point taken.

I spent some hours on this topic again now, and rebased and tested my
tree to 3.15-rc1. Pushed it here:

https://github.com/zonque/linux/tree/pxa-dma-3.15

There's some overlap to the patches you sent, which I'll comment on
directly soon.

I'm booting my board with pxa[23]xx.dtsi taken from my tree.

Please, if you find some time, test this tree and see if you still see
the performance regression.

>> My impression is that the transisiton will only be painless if the
>> mmp_pdma driver provides comparable performance to the existing
>> implementation. I also still think that adding hacks to the drivers
>> to manually parse the dma properties (as in #8) brings us further
>> away from a proper solution, not closer
>
> No doubt about that. But the regression is not the only problem.
> Driver for video capture requires an extensive rewrite as well.

Jup, I totally see your point. I was hoping for more support from
someone with access to hardware to work on this, and possibly add the
missing bits to the DMA framework to make the hot-linking possible. This
didn't happen, which is unfortunate, so we in fact might need to
recondider on how to move forward here.

> The
> result is that a working pxa DT device is kept out of the tree and
> there is no supported DT devices in tree. So no proper examples for
> new devices and no drive-by improvements. All of this prevents rather
> than facilitates eventual DT migration of tha PXA platform.

Ok, so how about this:

1. We keep the old API around, along with compat wrappers for existing
drivers until someone finally finds time to at least test the patches
that I can only compile-test myself.

2. For platforms that don't need those exotic drivers for devices that
nobody seems to be interested in, use DT and the pxa-mmp-dma driver, and
make sure it performs as well as the old implementation.

3. Do not add hacks for DT-compatability of existing drivers to make
them work with the old DMA implementation (like your patch #7).

4. For new drivers, don't add any compat code for the old DMA
implementation but soley rely on the new DMA framework.



Does this sound suitable for you?


Thanks,
Daniel

2014-04-17 12:12:42

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

On Thu, 2014-04-17 at 12:38 +0200, Daniel Mack wrote:
> I spent some hours on this topic again now, and rebased and tested my
> tree to 3.15-rc1. Pushed it here:
>
> https://github.com/zonque/linux/tree/pxa-dma-3.15

Great.

> There's some overlap to the patches you sent, which I'll comment on
> directly soon.
>
> I'm booting my board with pxa[23]xx.dtsi taken from my tree.
>
> Please, if you find some time, test this tree and see if you still see
> the performance regression.

Sure.

> Ok, so how about this:
>
> 1. We keep the old API around, along with compat wrappers for existing
> drivers until someone finally finds time to at least test the patches
> that I can only compile-test myself.
>
> 2. For platforms that don't need those exotic drivers for devices that
> nobody seems to be interested in, use DT and the pxa-mmp-dma driver, and
> make sure it performs as well as the old implementation.
>
> 3. Do not add hacks for DT-compatability of existing drivers to make
> them work with the old DMA implementation (like your patch #7).
>
> 4. For new drivers, don't add any compat code for the old DMA
> implementation but soley rely on the new DMA framework.
>
>
>
> Does this sound suitable for you?

No. I see no value in #3. There are obvious reasons to use DT whenever
possible. #3 effectively blocks DT usage for new devices. I have all the
reasons to believe, that LP-8x4x support would already have be merged,
if I didn't try to use DT.

My plan:
A. We need to know whether the new DMA implementation performs on par
with the old one. (I'm starting to check).

if so
B. We need to thinks whether it's acceptable to kill support for video
capture.

In short:

if (A && B)
we drop old DMA
else
we take my patch #7

2014-04-17 12:34:41

by Daniel Mack

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

On 04/17/2014 02:12 PM, Sergei Ianovich wrote:
> On Thu, 2014-04-17 at 12:38 +0200, Daniel Mack wrote:

>> 1. We keep the old API around, along with compat wrappers for existing
>> drivers until someone finally finds time to at least test the patches
>> that I can only compile-test myself.
>>
>> 2. For platforms that don't need those exotic drivers for devices that
>> nobody seems to be interested in, use DT and the pxa-mmp-dma driver, and
>> make sure it performs as well as the old implementation.
>>
>> 3. Do not add hacks for DT-compatability of existing drivers to make
>> them work with the old DMA implementation (like your patch #7).
>>
>> 4. For new drivers, don't add any compat code for the old DMA
>> implementation but soley rely on the new DMA framework.
>>
>> Does this sound suitable for you?
>
> No. I see no value in #3. There are obvious reasons to use DT whenever
> possible.

Of course. But if you do, you should really use the mmp-pdma driver, and
make sure it works. That way, that driver gets more test coverage.
Please, let's *not* introduce new hacks that lead to more users of the
old DMA API instead.

> #3 effectively blocks DT usage for new devices.

No, it doesn't. It just makes sure those new boards use the new dma
implementation, and obtain their DMA runtime information from the common
APIs. After all, the problem here is the lack of users who are willing
to dig into the DMA bits of the drivers they're using. By making it a
requirement to use the new pdma driver, we can possibly change that.

> I have all the
> reasons to believe, that LP-8x4x support would already have be merged,
> if I didn't try to use DT.

That might be, but that's not the point. We want progress here, and that
means we occasionally have to get rid of legacy.

> My plan:
> A. We need to know whether the new DMA implementation performs on par
> with the old one. (I'm starting to check).

Good, thanks!

> if so
> B. We need to thinks whether it's acceptable to kill support for video
> capture.

We can't. As I said, for this particular driver, we can keep the old API
around. We can even make it depend on !CONFIG_DMA_ENGINE, so if anyone
actually wants to use it with DT-enabled boards, we finally have a user
and things can be fixed up. Similar for other drivers we can't test
ourselves.

> In short:
>
> if (A && B)
> we drop old DMA
> else
> we take my patch #7

If A works, there's no need to for patch #7, right? If A doesn't work,
we have to check why and fix it.

Arnd, any oppinion on this?


Thanks,
Daniel

2014-04-19 11:59:47

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH v4 00/21] ARM: support for ICP DAS LP-8x4x (with dts)

On Thursday 17 April 2014, Daniel Mack wrote:
> On 04/17/2014 02:12 PM, Sergei Ianovich wrote:
> > On Thu, 2014-04-17 at 12:38 +0200, Daniel Mack wrote
> > I have all the
> > reasons to believe, that LP-8x4x support would already have be merged,
> > if I didn't try to use DT.
>
> That might be, but that's not the point. We want progress here, and that
> means we occasionally have to get rid of legacy.

In most cases, I would strongly support that statement. However, for PXA
in particular, my opinion is that progress is not the highest priority
as I see no realistic hope of converting all the existing machines over
to use DT and change the platform to "multiplatform" support. Anything
more modern than PXA I hope we can eventually get at least done for
multiplatform, same for a few of the older and simpler platforms.

Then again, I'm certainly not stopping you from trying to use add
modern platforms to PXA.

One of the ideas I had earlier was to extend mach-mmp enough to
run any fully DT-enabled PXA machines and leave mach-pxa for the
old ATAGS support and stuff like the legacy DMA support.
However, I don't think we should try that as long as mach-mmp is
lacking some essential DT support, e.g. for the clocks that were
only partially converted to use the common clock framework.

> > if so
> > B. We need to thinks whether it's acceptable to kill support for video
> > capture.
>
> We can't. As I said, for this particular driver, we can keep the old API
> around. We can even make it depend on !CONFIG_DMA_ENGINE, so if anyone
> actually wants to use it with DT-enabled boards, we finally have a user
> and things can be fixed up. Similar for other drivers we can't test
> ourselves.

Sounds good to me.

> > In short:
> >
> > if (A && B)
> > we drop old DMA
> > else
> > we take my patch #7
>
> If A works, there's no need to for patch #7, right? If A doesn't work,
> we have to check why and fix it.
>
> Arnd, any oppinion on this?

No strong opinion, I wouldn't object patch #7 if there is a strong reason
to not use the dmaengine driver for PXA like I would object doing it for
MMP. Then again, I see that you and recently also Laurent are driving a
lot of good work on PXA, and if neither the arm-soc maintainers nor the
three maintainers listed for mach-pxa have a strong opinion, I'd rather
leave it up to your judgement.

Arnd

2014-04-30 17:21:52

by Brian Norris

[permalink] [raw]
Subject: Re: [PATCH v4 10/21] mtd: support BB SRAM on ICP DAS LP-8x4x

Hi Sergei,

A few more small comments.

On Wed, Apr 16, 2014 at 09:17:15PM +0400, Sergei Ianovich wrote:
> This provides an MTD device driver for 512kB of battery backed up SRAM
> on ICPDAS LP-8X4X programmable automation controllers.
>
> SRAM chip is connected via FPGA and is not accessible without a driver,
> unlike flash memory which is wired to CPU MMU.
>
> This SRAM becomes an excellent persisent storage of volatile process
> data like counter values and sensor statuses. Storing those data in
> flash or mmc card is not a viable solution.
>
> Signed-off-by: Sergei Ianovich <[email protected]>
> Reviewed-by: Brian Norris <[email protected]>
> ---
> v3..v4 for Brian Norris 'Reviewed-by'
> * add doc file for DT binding
> * move DTS binding to a different patch (8/21)
> * drop unused include directive
> * drop safely unused callback
> * drop non-default partion probe types
> * drop duplicate error checks
> * drop duplicate error reporting
> * fixed error message on MTD registeration
> * fixed module removal routine

Thanks for the updates. This patch looks pretty good to me.

> v2..v3
> * no changes (except number 08/16 -> 10/21)
>
> v0..v2
> * use device tree
> * use devm helpers where possible
>
> .../devicetree/bindings/mtd/sram-lp8x4x.txt | 22 +++
> arch/arm/configs/lp8x4x_defconfig | 1 +
> drivers/mtd/devices/Kconfig | 14 ++
> drivers/mtd/devices/Makefile | 1 +
> drivers/mtd/devices/sram_lp8x4x.c | 204 +++++++++++++++++++++
> 5 files changed, 242 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> create mode 100644 drivers/mtd/devices/sram_lp8x4x.c
>
> diff --git a/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> new file mode 100644
> index 0000000..8b1e864
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> @@ -0,0 +1,22 @@
> +512kB battery backed up SRAM on LP-8x4x industrial computers
> +
> +Required properties:
> +- compatible : should be "icpdas,sram-lp8x4x"
> +
> +- reg: physical base addresses and region lengths of
> + * IO memory range
> + * SRAM page selector

Are these region types pretty static for this type of hardware? If not,
it helps to have a reg-names property in the DT, when there are 2 or
more register resources.

> +- eeprom-gpios : should point to active-low write enable GPIO

I'm curious: your driver doesn't actually utilize this binding. Is this
intentional? Is it actually optional? (I note that the example DT below
doesn't have this property...)

> +
> +SRAM chip is connected via FPGA and is not accessible without a driver,
> +unlike flash memory which is wired to CPU MMU. Driver is essentially
> +an address translation routine.
> +
> +Example:
> +
> + sram@a000 {
> + compatible = "icpdas,sram-lp8x4x";
> + reg = <0xa000 0x1000
> + 0x901e 0x1>;
> + };
> diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
> index d60e37a..17a4e6f 100644
> --- a/arch/arm/configs/lp8x4x_defconfig
> +++ b/arch/arm/configs/lp8x4x_defconfig
> @@ -57,6 +57,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
> CONFIG_MTD_CFI_GEOMETRY=y
> CONFIG_MTD_CFI_INTELEXT=y
> CONFIG_MTD_PHYSMAP_OF=y
> +CONFIG_MTD_SRAM_LP8X4X=y
> CONFIG_PROC_DEVICETREE=y
> CONFIG_BLK_DEV_LOOP=y
> CONFIG_BLK_DEV_LOOP_MIN_COUNT=2

I can't take the defconfig update via MTD; it will need to go via the
appropriate ARM tree (arm-soc?). So this hunk needs to move to another
patch.

> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index 1210bc2..fc8552b 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -225,4 +225,18 @@ config BCH_CONST_T
> default 4
> endif
>
> +config MTD_SRAM_LP8X4X
> + tristate "SRAM on ICPDAS LP-8X4X"
> + depends on OF && ARCH_PXA
> + ---help---
> + This provides an MTD device driver for 512kiB of battery backed up SRAM
> + on ICPDAS LP-8X4X programmable automation controllers.
> +
> + SRAM chip is connected via FPGA and is not accessible without a driver,
> + unlike flash memory which is wired to CPU MMU.
> +
> + Say N, unless you plan to run this kernel on LP-8X4X.
> +
> + If you say M, the module will be called sram_lp8x4x.
> +
> endmenu
> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
> index c68868f..a7d86e2 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
> obj-$(CONFIG_MTD_SST25L) += sst25l.o
> obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
> obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
> +obj-$(CONFIG_MTD_SRAM_LP8X4X) += sram_lp8x4x.o
>
>
> CFLAGS_docg3.o += -I$(src)
> diff --git a/drivers/mtd/devices/sram_lp8x4x.c b/drivers/mtd/devices/sram_lp8x4x.c
> new file mode 100644
> index 0000000..4cfa70b
> --- /dev/null
> +++ b/drivers/mtd/devices/sram_lp8x4x.c
> @@ -0,0 +1,204 @@
> +/*
> + * linux/drivers/mtd/devices/lp8x4x_sram.c
> + *
> + * MTD Driver for SRAM on ICPDAS LP-8x4x
> + * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mtd/map.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/string_helpers.h>
> +#include <linux/types.h>
> +
> +struct lp8x4x_sram_info {
> + void __iomem *bank;
> + void __iomem *virt;
> + struct mutex lock;
> + unsigned active_bank;
> + struct mtd_info mtd;
> +};
> +
> +static int
> +lp8x4x_sram_erase(struct mtd_info *mtd, struct erase_info *instr)
> +{
> + struct lp8x4x_sram_info *info = mtd->priv;
> + unsigned bank = instr->addr >> 11;
> + unsigned offset = (instr->addr & 0x7ff) << 1;
> + loff_t i;
> +
> + mutex_lock(&info->lock);
> + if (unlikely(bank != info->active_bank)) {
> + info->active_bank = bank;
> + iowrite8(bank, info->bank);
> + }
> + for (i = 0; i < instr->len; i++) {
> + iowrite8(0xff, info->virt + offset);
> + offset += 2;
> + if (unlikely(offset == 0)) {
> + info->active_bank++;
> + iowrite8(info->active_bank, info->bank);
> + }
> + }
> + mutex_unlock(&info->lock);
> + instr->state = MTD_ERASE_DONE;
> + mtd_erase_callback(instr);
> +
> + return 0;
> +}
> +
> +static int
> +lp8x4x_sram_write(struct mtd_info *mtd, loff_t to, size_t len,
> + size_t *retlen, const u_char *b)
> +{
> + struct lp8x4x_sram_info *info = mtd->priv;
> + unsigned bank = to >> 11;
> + unsigned offset = (to & 0x7ff) << 1;
> + loff_t i;
> +
> + mutex_lock(&info->lock);
> + if (unlikely(bank != info->active_bank)) {
> + info->active_bank = bank;
> + iowrite8(bank, info->bank);
> + }
> + for (i = 0; i < len; i++) {
> + iowrite8(b[i], info->virt + offset);
> + offset += 2;
> + if (unlikely(offset == 0)) {
> + info->active_bank++;
> + iowrite8(info->active_bank, info->bank);
> + }
> + }
> + mutex_unlock(&info->lock);
> + *retlen = len;
> + return 0;
> +}
> +
> +static int
> +lp8x4x_sram_read(struct mtd_info *mtd, loff_t from, size_t len,
> + size_t *retlen, u_char *b)
> +{
> + struct lp8x4x_sram_info *info = mtd->priv;
> + unsigned bank = from >> 11;
> + unsigned offset = (from & 0x7ff) << 1;
> + loff_t i;
> +
> + mutex_lock(&info->lock);
> + if (unlikely(bank != info->active_bank)) {
> + info->active_bank = bank;
> + iowrite8(bank, info->bank);
> + }
> + for (i = 0; i < len; i++) {
> + b[i] = ioread8(info->virt + offset);
> + offset += 2;
> + if (unlikely(offset == 0)) {
> + info->active_bank++;
> + iowrite8(info->active_bank, info->bank);
> + }
> + }
> + mutex_unlock(&info->lock);
> + *retlen = len;
> + return 0;
> +}
> +
> +static struct of_device_id of_flash_match[] = {
> + {
> + .compatible = "icpdas,sram-lp8x4x",
> + },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, of_flash_match);
> +
> +static int
> +lp8x4x_sram_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *match;
> + struct lp8x4x_sram_info *info;
> + struct resource *res_virt, *res_bank;
> + char sz_str[16];
> + struct mtd_part_parser_data ppdata;
> + int err = 0;
> +
> + match = of_match_device(of_flash_match, &pdev->dev);
> + if (!match)
> + return -EINVAL;

Does this of_match_device() serve any particular purpose? Your driver
already matches against these IDs, and you're not actually retrieving
any of-data from the match, so this looks redundant.

> +
> + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> +
> + res_virt = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + info->virt = devm_ioremap_resource(&pdev->dev, res_virt);
> + if (IS_ERR(info->virt))
> + return PTR_ERR(info->virt);
> +
> + res_bank = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + info->bank = devm_ioremap_resource(&pdev->dev, res_bank);
> + if (IS_ERR(info->bank))
> + return PTR_ERR(info->bank);
> +
> + info->mtd.priv = info;
> + info->mtd.name = "SRAM";

Are you absolutely sure there is only ever a single SRAM device on a
given system? Because otherwise, you will get redundantly-named MTD's.
If the answer is no, you might consider a unique naming scheme.

> + info->mtd.type = MTD_RAM;
> + info->mtd.flags = MTD_CAP_RAM;
> + info->mtd.size = resource_size(res_virt) << 7;
> + info->mtd.erasesize = 512;
> + info->mtd.writesize = 4;
> + info->mtd._erase = lp8x4x_sram_erase;
> + info->mtd._write = lp8x4x_sram_write;
> + info->mtd._read = lp8x4x_sram_read;
> + info->mtd.owner = THIS_MODULE;
> +
> + mutex_init(&info->lock);
> + iowrite8(info->active_bank, info->bank);
> + platform_set_drvdata(pdev, info);
> +
> + ppdata.of_node = pdev->dev.of_node;
> + err = mtd_device_parse_register(&info->mtd, NULL, &ppdata,
> + NULL, 0);
> +
> + if (err < 0) {
> + dev_err(&pdev->dev, "failed to register MTD\n");
> + return err;
> + }
> +
> + string_get_size(info->mtd.size, STRING_UNITS_2, sz_str,
> + sizeof(sz_str));
> + dev_info(&pdev->dev, "using %s SRAM on LP-8X4X as %s\n", sz_str,
> + dev_name(&info->mtd.dev));
> + return 0;
> +}
> +
> +static int
> +lp8x4x_sram_remove(struct platform_device *dev)
> +{
> + struct lp8x4x_sram_info *info = platform_get_drvdata(dev);
> + return mtd_device_unregister(&info->mtd);
> +}
> +
> +static struct platform_driver lp8x4x_sram_driver = {
> + .driver = {
> + .name = "sram-lp8x4x",
> + .owner = THIS_MODULE,
> + .of_match_table = of_flash_match,
> + },
> + .probe = lp8x4x_sram_probe,
> + .remove = lp8x4x_sram_remove,
> +};
> +
> +module_platform_driver(lp8x4x_sram_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
> +MODULE_DESCRIPTION("MTD driver for SRAM on ICPDAS LP-8x4x");

Thanks,
Brian

2014-04-30 17:35:28

by ЭлектроПлюс

[permalink] [raw]
Subject: Re: [PATCH v4 10/21] mtd: support BB SRAM on ICP DAS LP-8x4x

Hi Brian,

On Wed, 2014-04-30 at 10:21 -0700, Brian Norris wrote:
> A few more small comments.
>
> On Wed, Apr 16, 2014 at 09:17:15PM +0400, Sergei Ianovich wrote:
> > +++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> > @@ -0,0 +1,22 @@
> > +512kB battery backed up SRAM on LP-8x4x industrial computers
> > +
> > +Required properties:
> > +- compatible : should be "icpdas,sram-lp8x4x"
> > +
> > +- reg: physical base addresses and region lengths of
> > + * IO memory range
> > + * SRAM page selector
>
> Are these region types pretty static for this type of hardware? If not,
> it helps to have a reg-names property in the DT, when there are 2 or
> more register resources.

The regions are fixed. The addresses are hard-wired.

> > +- eeprom-gpios : should point to active-low write enable GPIO
>
> I'm curious: your driver doesn't actually utilize this binding. Is this
> intentional? Is it actually optional? (I note that the example DT below
> doesn't have this property...)

Thanks for noticing. It's an artifact of copy-paste. I'll drop this.

> > +++ b/arch/arm/configs/lp8x4x_defconfig
> > @@ -57,6 +57,7 @@ CONFIG_MTD_CFI_ADV_OPTIONS=y
> > CONFIG_MTD_CFI_GEOMETRY=y
> > CONFIG_MTD_CFI_INTELEXT=y
> > CONFIG_MTD_PHYSMAP_OF=y
> > +CONFIG_MTD_SRAM_LP8X4X=y
> > CONFIG_PROC_DEVICETREE=y
> > CONFIG_BLK_DEV_LOOP=y
> > CONFIG_BLK_DEV_LOOP_MIN_COUNT=2
>
> I can't take the defconfig update via MTD; it will need to go via the
> appropriate ARM tree (arm-soc?). So this hunk needs to move to another
> patch.

Sure. I'll remove this chunk and put it into main device patch.

> > + match = of_match_device(of_flash_match, &pdev->dev);
> > + if (!match)
> > + return -EINVAL;
>
> Does this of_match_device() serve any particular purpose? Your driver
> already matches against these IDs, and you're not actually retrieving
> any of-data from the match, so this looks redundant.

Point taken, I'll drop this.

> > +
> > + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
> > + if (!info)
> > + return -ENOMEM;
> > +
> > + res_virt = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + info->virt = devm_ioremap_resource(&pdev->dev, res_virt);
> > + if (IS_ERR(info->virt))
> > + return PTR_ERR(info->virt);
> > +
> > + res_bank = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > + info->bank = devm_ioremap_resource(&pdev->dev, res_bank);
> > + if (IS_ERR(info->bank))
> > + return PTR_ERR(info->bank);
> > +
> > + info->mtd.priv = info;
> > + info->mtd.name = "SRAM";
>
> Are you absolutely sure there is only ever a single SRAM device on a
> given system? Because otherwise, you will get redundantly-named MTD's.
> If the answer is no, you might consider a unique naming scheme.

Like .999999 sure. This one is hard-wired. There is no extension slots
to plug in any memory device.

I'll post a new version with the rest of the series. Thanks for
reviewing.

2015-06-08 12:07:44

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [v4,09/21] rtc: support DS1302 RTC on ICP DAS LP-8x4x

Hi Sergey,

Are you still interested in seeing that patch going upstream?

On 16/04/2014 at 21:17:14 +0400, Sergey Yanovich wrote :
> Signed-off-by: Sergei Ianovich <[email protected]>
> ---
> v3..v4
> * move DTS bindings to a different patch (8/21)
>
> v2..v3
> * use usleep_range instead of custom nsleep
> * number change (07/16 -> 09/21)
>
> v0..v2
> * use device tree
> * use devm helpers where possible
>
> .../devicetree/bindings/rtc/rtc-ds1302.txt | 14 +++
> arch/arm/configs/lp8x4x_defconfig | 1 +
> drivers/rtc/Kconfig | 2 +-
> drivers/rtc/rtc-ds1302.c | 100 ++++++++++++++++++++-
> 4 files changed, 114 insertions(+), 3 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/rtc/rtc-ds1302.txt
>
> diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt
> new file mode 100644
> index 0000000..810613b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/rtc-ds1302.txt
> @@ -0,0 +1,14 @@
> +* Dallas Semiconductor DS-1302 RTC
> +
> +Simple device which could be used to store date/time between reboots.
> +
> +Required properties:
> +- compatible : Should be "dallas,rtc-ds1302"
> +- reg : Should be address and size of IO memory region
> +
> +Examples:
> +
> +rtc@40900000 {
> + compatible = "dallas,rtc-ds1302";
> + reg = <0x1700901c 0x1>;
> +};
> diff --git a/arch/arm/configs/lp8x4x_defconfig b/arch/arm/configs/lp8x4x_defconfig
> index 9f1efb6..d60e37a 100644
> --- a/arch/arm/configs/lp8x4x_defconfig
> +++ b/arch/arm/configs/lp8x4x_defconfig
> @@ -141,6 +141,7 @@ CONFIG_LEDS_GPIO=y
> CONFIG_LEDS_TRIGGERS=y
> CONFIG_LEDS_TRIGGER_HEARTBEAT=y
> CONFIG_RTC_CLASS=y
> +CONFIG_RTC_DRV_DS1302=y
> CONFIG_RTC_DRV_PXA=m
> # CONFIG_IOMMU_SUPPORT is not set
> CONFIG_EXT2_FS=m
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2e565f8..80aaaa1 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -710,7 +710,7 @@ config RTC_DRV_DS1286
>
> config RTC_DRV_DS1302
> tristate "Dallas DS1302"
> - depends on SH_SECUREEDGE5410
> + depends on SH_SECUREEDGE5410 || (ARCH_PXA && HIGH_RES_TIMERS)
> help
> If you say yes here you get support for the Dallas DS1302 RTC chips.
>
> diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
> index 07e8d79..3c49023 100644
> --- a/drivers/rtc/rtc-ds1302.c
> +++ b/drivers/rtc/rtc-ds1302.c
> @@ -50,7 +50,7 @@
> #define ds1302_set_tx()
> #define ds1302_set_rx()
>
> -static inline int ds1302_hw_init(void)
> +static inline int ds1302_hw_init(struct platform_device *pdev)
> {
> return 0;
> }
> @@ -86,6 +86,101 @@ static inline int ds1302_rxbit(void)
> return !!(get_dp() & RTC_IODATA);
> }
>
> +#elif defined(CONFIG_ARCH_PXA) && defined(CONFIG_HIGH_RES_TIMERS)
> +
> +#include <linux/delay.h>
> +#include <linux/of.h>
> +
> +#define RTC_CE 0x01
> +#define RTC_CLK 0x02
> +#define RTC_nWE 0x04
> +#define RTC_IODATA 0x08
> +
> +static unsigned long ds1302_state;
> +
> +static void *mem;
> +
> +static inline int ds1302_hw_init(struct platform_device *pdev)
> +{
> + struct resource *r;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r)
> + return -ENODEV;
> +
> + mem = devm_ioremap_resource(&pdev->dev, r);
> + if (!mem)
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +static inline void ds1302_reset(void)
> +{
> + ds1302_state = 0;
> + iowrite8(ds1302_state, mem);
> + usleep_range(4, 5);
> +}
> +
> +static inline void ds1302_clock(void)
> +{
> + usleep_range(1, 2);
> + ds1302_state |= RTC_CLK;
> + iowrite8(ds1302_state, mem);
> + usleep_range(1, 2);
> + ds1302_state &= ~RTC_CLK;
> + iowrite8(ds1302_state, mem);
> +}
> +
> +static inline void ds1302_start(void)
> +{
> + ds1302_state &= ~RTC_CLK;
> + ds1302_state |= RTC_CE;
> + iowrite8(ds1302_state, mem);
> + usleep_range(3, 4);
> +}
> +
> +static inline void ds1302_stop(void)
> +{
> + ds1302_state &= ~RTC_CE;
> + iowrite8(ds1302_state, mem);
> +}
> +
> +static inline void ds1302_set_tx(void)
> +{
> + ds1302_state &= ~RTC_nWE;
> + iowrite8(ds1302_state, mem);
> +}
> +
> +static inline void ds1302_set_rx(void)
> +{
> + ds1302_state |= RTC_nWE;
> + iowrite8(ds1302_state, mem);
> +}
> +
> +static inline void ds1302_txbit(int bit)
> +{
> + if (bit)
> + ds1302_state |= RTC_IODATA;
> + else
> + ds1302_state &= ~RTC_IODATA;
> + iowrite8(ds1302_state, mem);
> +}
> +
> +static inline int ds1302_rxbit(void)
> +{
> + return ioread8(mem) & 0x1;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ds1302_dt_ids[] = {
> + { .compatible = "dallas,rtc-ds1302" },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
> +#endif
> +
> #else
> #error "Add support for your platform"
> #endif
> @@ -216,7 +311,7 @@ static int __init ds1302_rtc_probe(struct platform_device *pdev)
> {
> struct rtc_device *rtc;
>
> - if (ds1302_hw_init()) {
> + if (ds1302_hw_init(pdev)) {
> dev_err(&pdev->dev, "Failed to init communication channel");
> return -EINVAL;
> }
> @@ -245,6 +340,7 @@ static struct platform_driver ds1302_platform_driver = {
> .driver = {
> .name = DRV_NAME,
> .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(ds1302_dt_ids),
> },
> };
>

--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2015-06-08 12:12:59

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [v4,09/21] rtc: support DS1302 RTC on ICP DAS LP-8x4x

Hi Alexandre,

On Mon, 2015-06-08 at 14:07 +0200, Alexandre Belloni wrote:
> Are you still interested in seeing that patch going upstream?

Sure.

2015-12-15 18:59:14

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v5] mtd: support BB SRAM on ICP DAS LP-8x4x

This provides an MTD device driver for 512kB of battery backed up SRAM
on ICPDAS LP-8X4X programmable automation controllers.

SRAM chip is connected via FPGA and is not accessible without a driver,
unlike flash memory which is wired to CPU MMU.

This SRAM becomes an excellent persisent storage of volatile process
data like counter values and sensor statuses. Storing those data in
flash or mmc card is not a viable solution.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Brian Norris <[email protected]>
---
v4..v5
* remove .owner from struct platform_driver
* constify struct of_device_id
for further Brian Norris comments:
* drop unused property from doc file
* move defconfig update to a different file
* drop extra match w/ of_match_device()

v3..v4 for Brian Norris 'Reviewed-by'
* add doc file for DT binding
* move DTS binding to a different patch (8/21)
* drop unused include directive
* drop safely unused callback
* drop non-default partion probe types
* drop duplicate error checks
* drop duplicate error reporting
* fixed error message on MTD registeration
* fixed module removal routine

v2..v3
* no changes (except number 08/16 -> 10/21)

v0..v2
* use device tree
* use devm helpers where possible

.../devicetree/bindings/mtd/sram-lp8x4x.txt | 20 +++
drivers/mtd/devices/Kconfig | 14 ++
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/sram_lp8x4x.c | 199 +++++++++++++++++++++
4 files changed, 234 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
create mode 100644 drivers/mtd/devices/sram_lp8x4x.c

diff --git a/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
new file mode 100644
index 0000000..476934f
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
@@ -0,0 +1,20 @@
+512kB battery backed up SRAM on LP-8x4x industrial computers
+
+Required properties:
+- compatible : should be "icpdas,sram-lp8x4x"
+
+- reg: physical base addresses and region lengths of
+ * IO memory range
+ * SRAM page selector
+
+SRAM chip is connected via FPGA and is not accessible without a driver,
+unlike flash memory which is wired to CPU MMU. Driver is essentially
+an address translation routine.
+
+Example:
+
+ sram@a000 {
+ compatible = "icpdas,sram-lp8x4x";
+ reg = <0xa000 0x1000
+ 0x901e 0x1>;
+ };
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index f73c416..a4573f6 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -233,4 +233,18 @@ config BCH_CONST_T
default 4
endif

+config MTD_SRAM_LP8X4X
+ tristate "SRAM on ICPDAS LP-8X4X"
+ depends on OF && ARCH_PXA
+ ---help---
+ This provides an MTD device driver for 512kiB of battery backed up SRAM
+ on ICPDAS LP-8X4X programmable automation controllers.
+
+ SRAM chip is connected via FPGA and is not accessible without a driver,
+ unlike flash memory which is wired to CPU MMU.
+
+ Say N, unless you plan to run this kernel on LP-8X4X.
+
+ If you say M, the module will be called sram_lp8x4x.
+
endmenu
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 7912d3a..2fd5b7a 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o
+obj-$(CONFIG_MTD_SRAM_LP8X4X) += sram_lp8x4x.o


CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/sram_lp8x4x.c b/drivers/mtd/devices/sram_lp8x4x.c
new file mode 100644
index 0000000..e43c7a7
--- /dev/null
+++ b/drivers/mtd/devices/sram_lp8x4x.c
@@ -0,0 +1,199 @@
+/*
+ * linux/drivers/mtd/devices/lp8x4x_sram.c
+ *
+ * MTD Driver for SRAM on ICPDAS LP-8x4x
+ * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+
+struct lp8x4x_sram_info {
+ void __iomem *bank;
+ void __iomem *virt;
+ struct mutex lock;
+ unsigned active_bank;
+ struct mtd_info mtd;
+};
+
+static int
+lp8x4x_sram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = instr->addr >> 11;
+ unsigned offset = (instr->addr & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < instr->len; i++) {
+ iowrite8(0xff, info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+}
+
+static int
+lp8x4x_sram_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *b)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = to >> 11;
+ unsigned offset = (to & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < len; i++) {
+ iowrite8(b[i], info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ *retlen = len;
+ return 0;
+}
+
+static int
+lp8x4x_sram_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *b)
+{
+ struct lp8x4x_sram_info *info = mtd->priv;
+ unsigned bank = from >> 11;
+ unsigned offset = (from & 0x7ff) << 1;
+ loff_t i;
+
+ mutex_lock(&info->lock);
+ if (unlikely(bank != info->active_bank)) {
+ info->active_bank = bank;
+ iowrite8(bank, info->bank);
+ }
+ for (i = 0; i < len; i++) {
+ b[i] = ioread8(info->virt + offset);
+ offset += 2;
+ if (unlikely(offset == 0)) {
+ info->active_bank++;
+ iowrite8(info->active_bank, info->bank);
+ }
+ }
+ mutex_unlock(&info->lock);
+ *retlen = len;
+ return 0;
+}
+
+static const struct of_device_id of_flash_match[] = {
+ {
+ .compatible = "icpdas,sram-lp8x4x",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_flash_match);
+
+static int
+lp8x4x_sram_probe(struct platform_device *pdev)
+{
+ struct lp8x4x_sram_info *info;
+ struct resource *res_virt, *res_bank;
+ char sz_str[16];
+ struct mtd_part_parser_data ppdata;
+ int err = 0;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ res_virt = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->virt = devm_ioremap_resource(&pdev->dev, res_virt);
+ if (IS_ERR(info->virt))
+ return PTR_ERR(info->virt);
+
+ res_bank = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ info->bank = devm_ioremap_resource(&pdev->dev, res_bank);
+ if (IS_ERR(info->bank))
+ return PTR_ERR(info->bank);
+
+ info->mtd.priv = info;
+ info->mtd.name = "SRAM";
+ info->mtd.type = MTD_RAM;
+ info->mtd.flags = MTD_CAP_RAM;
+ info->mtd.size = resource_size(res_virt) << 7;
+ info->mtd.erasesize = 512;
+ info->mtd.writesize = 4;
+ info->mtd._erase = lp8x4x_sram_erase;
+ info->mtd._write = lp8x4x_sram_write;
+ info->mtd._read = lp8x4x_sram_read;
+ info->mtd.owner = THIS_MODULE;
+
+ mutex_init(&info->lock);
+ iowrite8(info->active_bank, info->bank);
+ platform_set_drvdata(pdev, info);
+
+ ppdata.of_node = pdev->dev.of_node;
+ err = mtd_device_parse_register(&info->mtd, NULL, &ppdata,
+ NULL, 0);
+
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register MTD\n");
+ return err;
+ }
+
+ string_get_size(info->mtd.size, 1, STRING_UNITS_2, sz_str,
+ sizeof(sz_str));
+ dev_info(&pdev->dev, "using %s SRAM on LP-8X4X as %s\n", sz_str,
+ dev_name(&info->mtd.dev));
+ return 0;
+}
+
+static int
+lp8x4x_sram_remove(struct platform_device *dev)
+{
+ struct lp8x4x_sram_info *info = platform_get_drvdata(dev);
+
+ return mtd_device_unregister(&info->mtd);
+}
+
+static struct platform_driver lp8x4x_sram_driver = {
+ .driver = {
+ .name = "sram-lp8x4x",
+ .of_match_table = of_flash_match,
+ },
+ .probe = lp8x4x_sram_probe,
+ .remove = lp8x4x_sram_remove,
+};
+
+module_platform_driver(lp8x4x_sram_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergei Ianovich <[email protected]>");
+MODULE_DESCRIPTION("MTD driver for SRAM on ICPDAS LP-8x4x");
--
2.6.2

2015-12-15 19:26:58

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v5] arm: pxa: support ICP DAS LP-8x4x FPGA irq

ICP DAS LP-8x4x contains FPGA chip. The chip functions as an interrupt
source providing 16 additional interrupts among other things. The
interrupt lines are muxed to a GPIO pin of a 2nd level PXA-GPIO
interrupt controller. GPIO pins of the 2nd level controller are in turn
muxed to a CPU interrupt line.

Until pxa is completely converted to device tree, it is impossible
to use IRQCHIP_DECLARE() and the irqdomain needs to added manually.
Drivers for the on-CPU IRQs and GPIO-IRQs are loaded using
postcore_initcall(). We need to have all irq domain drivers loaded prior
to DT parsing in order to allow normal initialization of IRQ resources
with DT.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
CC: Arnd Bergmann <[email protected]>
---
v4..v5
* constify struct of_device_id
* drop irq number from handler signature

v3.2..v4
* move DTS binding to a different patch (8/21)

v3.1..v3.2
fixes to apply Linus Walleij's "Reviewed-by":
* add kerneldoc comment for state container struct
* rename irq -> hwirq for clarity
* drop overzealous error checks from the hotpaths

v3..v3.1
fixes according to Linus Walleij review comments:
* update commit message
* use state container instead of global variables
* get hardware irq nums from irq_data, don't calculate them
* use BIT() macro
* add defines for system irq register masks
* replace cycle control variable with break
* use better names for resource variables
* add a linear domain instead of a legacy one
* use irq_create_mapping() instead of irq_alloc_desc()

v2..v3
* no changes (except number 09/16 -> 11/21)

v0..v2
* extract irqchip and move to drivers/irqchip/
* use device tree
* use devm helpers where possible

.../bindings/interrupt-controller/irq-lp8x4x.txt | 49 +++++
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-lp8x4x.c | 227 +++++++++++++++++++++
4 files changed, 282 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
create mode 100644 drivers/irqchip/irq-lp8x4x.c

diff --git a/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
new file mode 100644
index 0000000..c8940d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
@@ -0,0 +1,49 @@
+ICP DAS LP-8x4x FPGA Interrupt Controller
+
+ICP DAS LP-8x4x contains FPGA chip. The chip functions as a interrupt
+source providing 16 additional interrupts among other things.
+
+Required properties:
+- compatible : should be "icpdas,irq-lp8x4x"
+
+- reg: physical base address of the controller and length of memory mapped
+ region.
+
+- interrupt-controller : identifies the node as an interrupt controller
+
+- #interrupt-cells : should be 1
+
+- interrupts : should provide interrupt
+
+- interrupt-parent : should provide a link to interrupt controller either
+ explicitly and implicitly from a parent node
+
+Example:
+
+ fpga: fpga@17000006 {
+ compatible = "icpdas,irq-lp8x4x";
+ reg = <0x17000006 0x16>;
+ interrupt-parent = <&gpio>;
+ interrupts = <3 IRQ_TYPE_EDGE_RISING>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ status = "okay";
+ };
+
+ uart@17009050 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x17009050 0x10
+ 0x17009030 0x02>;
+ interrupt-parent = <&fpga>;
+ interrupts = <13>;
+ status = "okay";
+ };
+
+ uart@17009060 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x17009060 0x10
+ 0x17009032 0x02>;
+ interrupt-parent = <&fpga>;
+ interrupts = <14>;
+ status = "okay";
+ };
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e..1de7361 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -193,3 +193,8 @@ config IRQ_MXS
def_bool y if MACH_ASM9260 || ARCH_MXS
select IRQ_DOMAIN
select STMP_DEVICE
+
+config LP8X4X_IRQ
+ bool
+ depends on OF && ARCH_PXA
+ select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..ab9ca67 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
+obj-$(CONFIG_LP8X4X_IRQ) += irq-lp8x4x.o
diff --git a/drivers/irqchip/irq-lp8x4x.c b/drivers/irqchip/irq-lp8x4x.c
new file mode 100644
index 0000000..a03d925
--- /dev/null
+++ b/drivers/irqchip/irq-lp8x4x.c
@@ -0,0 +1,227 @@
+/*
+ * linux/drivers/irqchip/irq-lp8x4x.c
+ *
+ * Support for ICP DAS LP-8x4x FPGA irq
+ * Copyright (C) 2013 Sergei Ianovich <[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 or any later version.
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define EOI 0x00000000
+#define INSINT 0x00000002
+#define ENSYSINT 0x00000004
+#define PRIMINT 0x00000006
+#define PRIMINT_MASK 0xe0
+#define SECOINT 0x00000008
+#define SECOINT_MASK 0x1f
+#define ENRISEINT 0x0000000A
+#define CLRRISEINT 0x0000000C
+#define ENHILVINT 0x0000000E
+#define CLRHILVINT 0x00000010
+#define ENFALLINT 0x00000012
+#define CLRFALLINT 0x00000014
+#define IRQ_MEM_SIZE 0x00000016
+#define LP8X4X_NUM_IRQ_DEFAULT 16
+
+/**
+ * struct lp8x4x_irq_data - LP8X4X custom irq controller state container
+ * @base: base IO memory address
+ * @irq_domain: Interrupt translation domain; responsible for mapping
+ * between hwirq number and linux irq number
+ * @irq_sys_enabled: mask keeping track of interrupts enabled in the
+ * register which vendor calls 'system'
+ * @irq_high_enabled: mask keeping track of interrupts enabled in the
+ * register which vendor calls 'high'
+ *
+ * The structure implements State Container from
+ * Documentation/driver-model/design-patterns.txt
+ */
+
+struct lp8x4x_irq_data {
+ void *base;
+ struct irq_domain *domain;
+ unsigned char irq_sys_enabled;
+ unsigned char irq_high_enabled;
+};
+
+static void lp8x4x_mask_irq(struct irq_data *d)
+{
+ unsigned mask;
+ unsigned long hwirq = d->hwirq;
+ struct lp8x4x_irq_data *host = irq_data_get_irq_chip_data(d);
+
+ if (hwirq < 8) {
+ host->irq_high_enabled &= ~BIT(hwirq);
+
+ mask = ioread8(host->base + ENHILVINT);
+ mask &= ~BIT(hwirq);
+ iowrite8(mask, host->base + ENHILVINT);
+ } else {
+ hwirq -= 8;
+ host->irq_sys_enabled &= ~BIT(hwirq);
+
+ mask = ioread8(host->base + ENSYSINT);
+ mask &= ~BIT(hwirq);
+ iowrite8(mask, host->base + ENSYSINT);
+ }
+}
+
+static void lp8x4x_unmask_irq(struct irq_data *d)
+{
+ unsigned mask;
+ unsigned long hwirq = d->hwirq;
+ struct lp8x4x_irq_data *host = irq_data_get_irq_chip_data(d);
+
+ if (hwirq < 8) {
+ host->irq_high_enabled |= BIT(hwirq);
+ mask = ioread8(host->base + CLRHILVINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + CLRHILVINT);
+
+ mask = ioread8(host->base + ENHILVINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + ENHILVINT);
+ } else {
+ hwirq -= 8;
+ host->irq_sys_enabled |= BIT(hwirq);
+
+ mask = ioread8(host->base + SECOINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + SECOINT);
+
+ mask = ioread8(host->base + ENSYSINT);
+ mask |= BIT(hwirq);
+ iowrite8(mask, host->base + ENSYSINT);
+ }
+}
+
+static struct irq_chip lp8x4x_irq_chip = {
+ .name = "FPGA",
+ .irq_ack = lp8x4x_mask_irq,
+ .irq_mask = lp8x4x_mask_irq,
+ .irq_mask_ack = lp8x4x_mask_irq,
+ .irq_unmask = lp8x4x_unmask_irq,
+};
+
+static void lp8x4x_irq_handler(struct irq_desc *desc)
+{
+ int n;
+ unsigned long mask;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct lp8x4x_irq_data *host = irq_desc_get_handler_data(desc);
+
+ chained_irq_enter(chip, desc);
+
+ for (;;) {
+ mask = ioread8(host->base + CLRHILVINT) & 0xff;
+ mask |= (ioread8(host->base + SECOINT) & SECOINT_MASK) << 8;
+ mask |= (ioread8(host->base + PRIMINT) & PRIMINT_MASK) << 8;
+ mask &= host->irq_high_enabled | (host->irq_sys_enabled << 8);
+ if (mask == 0)
+ break;
+ for_each_set_bit(n, &mask, BITS_PER_LONG)
+ generic_handle_irq(irq_find_mapping(host->domain, n));
+ }
+
+ iowrite8(0, host->base + EOI);
+ chained_irq_exit(chip, desc);
+}
+
+static int lp8x4x_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct lp8x4x_irq_data *host = d->host_data;
+ int err;
+
+ err = irq_set_chip_data(irq, host);
+ if (err < 0)
+ return err;
+
+ irq_set_chip_and_handler(irq, &lp8x4x_irq_chip, handle_level_irq);
+ irq_set_probe(irq);
+ return 0;
+}
+
+const struct irq_domain_ops lp8x4x_irq_domain_ops = {
+ .map = lp8x4x_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static const struct of_device_id lp8x4x_irq_dt_ids[] = {
+ { .compatible = "icpdas,irq-lp8x4x", },
+ {}
+};
+
+static int lp8x4x_irq_probe(struct platform_device *pdev)
+{
+ struct resource *res_mem, *res_irq;
+ struct device_node *np = pdev->dev.of_node;
+ struct lp8x4x_irq_data *host;
+ int i, err;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_mem || !res_irq || resource_size(res_mem) < IRQ_MEM_SIZE)
+ return -ENODEV;
+
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENODEV;
+
+ host->base = devm_ioremap_resource(&pdev->dev, res_mem);
+ if (!host->base) {
+ dev_err(&pdev->dev, "Failed to ioremap %p\n", host->base);
+ return -EFAULT;
+ }
+
+ host->domain = irq_domain_add_linear(np, LP8X4X_NUM_IRQ_DEFAULT,
+ &lp8x4x_irq_domain_ops, host);
+ if (!host->domain) {
+ dev_err(&pdev->dev, "Failed to add IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < LP8X4X_NUM_IRQ_DEFAULT; i++) {
+ err = irq_create_mapping(host->domain, i);
+ if (err < 0)
+ dev_err(&pdev->dev, "Failed to map IRQ %i\n", i);
+ }
+
+ /* Initialize chip registers */
+ iowrite8(0, host->base + CLRRISEINT);
+ iowrite8(0, host->base + ENRISEINT);
+ iowrite8(0, host->base + CLRFALLINT);
+ iowrite8(0, host->base + ENFALLINT);
+ iowrite8(0, host->base + CLRHILVINT);
+ iowrite8(0, host->base + ENHILVINT);
+ iowrite8(0, host->base + ENSYSINT);
+ iowrite8(0, host->base + SECOINT);
+
+ irq_set_handler_data(res_irq->start, host);
+ irq_set_chained_handler(res_irq->start, lp8x4x_irq_handler);
+
+ return 0;
+}
+
+static struct platform_driver lp8x4x_irq_driver = {
+ .probe = lp8x4x_irq_probe,
+ .driver = {
+ .name = "irq-lp8x4x",
+ .of_match_table = lp8x4x_irq_dt_ids,
+ },
+};
+
+static int __init lp8x4x_irq_init(void)
+{
+ return platform_driver_register(&lp8x4x_irq_driver);
+}
+postcore_initcall(lp8x4x_irq_init);
--
2.6.2

2015-12-15 21:06:14

by Sergei Ianovich

[permalink] [raw]
Subject: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

The patch adds support for 3 additional LP-8x4x built-in serial
ports.

The device can also host up to 8 extension cards with 4 serial ports
on each card for a total of 35 ports. However, I don't have
the hardware to test extension cards, so they are not supported, yet.

Signed-off-by: Sergei Ianovich <[email protected]>
Reviewed-by: Heikki Krogerus <[email protected]>
CC: Alan Cox <[email protected]>
---
v4..v5
* constify struct of_device_id
* drop .owner from struct platform_driver
* rewrite set_termios() baud rate hadnling as suggested by Alan Cox

v3..v4
* move DTS bindings to a different patch (8/21) as suggested by
Heikki Krogerus

v2..v3
* no changes (except number 10/16 -> 12/21)

v0..v2
* register platform driver instead of platform device
* use device tree
* use devm helpers where possible

.../devicetree/bindings/serial/lp8x4x-serial.txt | 35 +++++
drivers/tty/serial/8250/8250_lp8x4x.c | 168 +++++++++++++++++++++
drivers/tty/serial/8250/Kconfig | 12 ++
drivers/tty/serial/8250/Makefile | 1 +
4 files changed, 216 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
create mode 100644 drivers/tty/serial/8250/8250_lp8x4x.c

diff --git a/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
new file mode 100644
index 0000000..5f9a4c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
@@ -0,0 +1,35 @@
+UART ports on ICP DAS LP-8x4x
+
+ICP DAS LP-8x4x contains three additional serial ports interfaced via
+Analog Devices ADM213EA chips in addition to 3 serial ports on PXA CPU.
+
+Required properties:
+- compatible : should be "icpdas,uart-lp8x4x"
+
+- reg : should provide 16 byte man IO memory region and 1 byte region for
+ termios
+
+- interrupts : should provide interrupt
+
+- interrupt-parent : should provide a link to interrupt controller either
+ explicitly or implicitly from a parent node
+
+Examples (from pxa27x-lp8x4x.dts):
+
+ uart@9050 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9050 0x10
+ 0x9030 0x02>;
+ interrupt-parent = <&fpgairg>;
+ interrupts = <13>;
+ status = "okay";
+ };
+
+ uart@9060 {
+ compatible = "icpdas,uart-lp8x4x";
+ reg = <0x9060 0x10
+ 0x9032 0x02>;
+ interrupt-parent = <&fpgairg>;
+ interrupts = <14>;
+ status = "okay";
+ };
diff --git a/drivers/tty/serial/8250/8250_lp8x4x.c b/drivers/tty/serial/8250/8250_lp8x4x.c
new file mode 100644
index 0000000..0e07220
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_lp8x4x.c
@@ -0,0 +1,168 @@
+/* linux/drivers/tty/serial/8250/8250_lp8x4x.c
+ *
+ * Support for 16550A serial ports on ICP DAS LP-8x4x
+ *
+ * Copyright (C) 2013 Sergei Ianovich <[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/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+struct lp8x4x_serial_data {
+ int line;
+ void *ios_mem;
+};
+
+static void lp8x4x_serial_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int len;
+ unsigned int baud;
+ struct lp8x4x_serial_data *data = port->private_data;
+
+ serial8250_do_set_termios(port, termios, old);
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ len = 7;
+ break;
+ case CS6:
+ len = 8;
+ break;
+ case CS7:
+ len = 9;
+ break;
+ default:
+ case CS8:
+ len = 10;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ len++;
+ if (termios->c_cflag & PARENB)
+ len++;
+ if (!(termios->c_cflag & PARODD))
+ len++;
+#ifdef CMSPAR
+ if (termios->c_cflag & CMSPAR)
+ len++;
+#endif
+
+ len -= 9;
+ len &= 3;
+ len <<= 3;
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = tty_termios_baud_rate(termios);
+
+ switch (baud) {
+ case 115200:
+ len |= 7;
+ break;
+ case 57600:
+ len |= 6;
+ break;
+ case 38400:
+ len |= 5;
+ break;
+ case 19200:
+ len |= 4;
+ break;
+ case 9600:
+ len |= 3;
+ break;
+ case 4800:
+ len |= 2;
+ break;
+ case 2400:
+ default:
+ len |= 1;
+ break;
+ };
+ iowrite8(len, data->ios_mem);
+
+}
+
+static const struct of_device_id lp8x4x_serial_dt_ids[] = {
+ { .compatible = "icpdas,uart-lp8x4x", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lp8x4x_serial_dt_ids);
+
+static int lp8x4x_serial_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port uart = {};
+ struct lp8x4x_serial_data *data;
+ struct resource *mmres, *mires, *irqres;
+ int ret;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mires = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mmres || !mires || !irqres)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ios_mem = devm_ioremap_resource(&pdev->dev, mires);
+ if (!data->ios_mem)
+ return -EFAULT;
+
+ uart.port.iotype = UPIO_MEM;
+ uart.port.mapbase = mmres->start;
+ uart.port.iobase = mmres->start;
+ uart.port.regshift = 1;
+ uart.port.irq = irqres->start;
+ uart.port.flags = UPF_IOREMAP;
+ uart.port.dev = &pdev->dev;
+ uart.port.uartclk = 14745600;
+ uart.port.set_termios = lp8x4x_serial_set_termios;
+ uart.port.private_data = data;
+
+ ret = serial8250_register_8250_port(&uart);
+ if (ret < 0)
+ return ret;
+
+ data->line = ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int lp8x4x_serial_remove(struct platform_device *pdev)
+{
+ struct lp8x4x_serial_data *data = platform_get_drvdata(pdev);
+
+ serial8250_unregister_port(data->line);
+
+ return 0;
+}
+
+static struct platform_driver lp8x4x_serial_driver = {
+ .probe = lp8x4x_serial_probe,
+ .remove = lp8x4x_serial_remove,
+
+ .driver = {
+ .name = "uart-lp8x4x",
+ .of_match_table = lp8x4x_serial_dt_ids,
+ },
+};
+
+module_platform_driver(lp8x4x_serial_driver);
+
+MODULE_AUTHOR("Sergei Ianovich");
+MODULE_DESCRIPTION("8250 serial port module for LP-8x4x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 48b6253..00eb6b0 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -387,3 +387,15 @@ config SERIAL_8250_PXA
can enable its onboard serial ports by enabling this option.

If you choose M here, the module name will be 8250_pxa.
+
+config SERIAL_8250_LP8X4X
+ tristate "Support 16550A ports on ICP DAS LP-8x4x"
+ depends on OF && SERIAL_8250 && SERIAL_8250_MANY_PORTS && ARCH_PXA
+ select LP8X4X_IRQ
+ help
+ In addition to serial ports on PXA270 SoC, LP-8x4x has 1 dual
+ RS232/RS485 port, 1 RS485 port and 1 RS232 port.
+
+ Say N here, unless you plan to run this kernel on a LP-8x4x system.
+
+ If you choose M here, the module name will be 8250_lp8x4x.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 7e54413..8bdbf40 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
+obj-$(CONFIG_SERIAL_8250_LP8X4X) += 8250_lp8x4x.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
--
2.6.2

2015-12-15 21:52:10

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Wednesday 16 December 2015 00:04:45 Sergei Ianovich wrote:
> index 0000000..5f9a4c1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
> @@ -0,0 +1,35 @@
> +UART ports on ICP DAS LP-8x4x
> +
> +ICP DAS LP-8x4x contains three additional serial ports interfaced via
> +Analog Devices ADM213EA chips in addition to 3 serial ports on PXA CPU.
> +
> +Required properties:
> +- compatible : should be "icpdas,uart-lp8x4x"

Compatible strings should not include a 'x' wildcard like this, better use
the specific chip name.

Also, it sounds like you named them after the board vendor, which sounds
wrong as the vendor part of the compatible string should be the whoever
made that part (analog?)

> +- reg : should provide 16 byte man IO memory region and 1 byte region for
> + termios
> +
> +- interrupts : should provide interrupt
> +
> +- interrupt-parent : should provide a link to interrupt controller either
> + explicitly or implicitly from a parent node

interrupt-parent should be an optional property, or you can leave it out,
as this is a standard property that can always be there when there is
interrupts.

> +Examples (from pxa27x-lp8x4x.dts):
> +
> + uart@9050 {

By convention, the name should be 'serial', not 'uart'.

Arnd.

2015-12-16 08:06:19

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Tue, 2015-12-15 at 22:51 +0100, Arnd Bergmann wrote:
> On Wednesday 16 December 2015 00:04:45 Sergei Ianovich wrote:
> > index 0000000..5f9a4c1
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
> > @@ -0,0 +1,35 @@
> > +UART ports on ICP DAS LP-8x4x
> > +
> > +ICP DAS LP-8x4x contains three additional serial ports interfaced
> > via
> > +Analog Devices ADM213EA chips in addition to 3 serial ports on PXA
> > CPU.
> > +
> > +Required properties:
> > +- compatible : should be "icpdas,uart-lp8x4x"
>
> Compatible strings should not include a 'x' wildcard like this, better
> use
> the specific chip name.
>
> Also, it sounds like you named them after the board vendor, which
> sounds
> wrong as the vendor part of the compatible string should be the
> whoever
> made that part (analog?)

The chips themselves are standard, they would work with 8250_core if
properly connected. However, they are not connected normally. Al least
some of their config pins are wired to a different address region. So
the driver is board-specific.

'x' wildcards in the name of the board seem important. There are devices
made by the same vendor without 8 or 4 in their name. Those devices
either are not shipped with linux or are base on a x86 platform.

Does this justify the choice of the compatible string?

> > +- reg : should provide 16 byte man IO memory region and 1 byte
> > region for
> > +       termios
> > +
> > +- interrupts : should provide interrupt
> > +
> > +- interrupt-parent : should provide a link to interrupt controller
> > either
> > +                    explicitly or implicitly from a parent node
>
> interrupt-parent should be an optional property, or you can leave it
> out,
> as this is a standard property that can always be there when there is
> interrupts.

ok

> > +Examples (from pxa27x-lp8x4x.dts):
> > +
> > +               uart@9050 {
>
> By convention, the name should be 'serial', not 'uart'.

ok

2015-12-16 10:27:41

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Wednesday 16 December 2015 11:04:57 Sergei Ianovich wrote:
> On Tue, 2015-12-15 at 22:51 +0100, Arnd Bergmann wrote:
> > On Wednesday 16 December 2015 00:04:45 Sergei Ianovich wrote:
> > > index 0000000..5f9a4c1
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
> > > @@ -0,0 +1,35 @@
> > > +UART ports on ICP DAS LP-8x4x
> > > +
> > > +ICP DAS LP-8x4x contains three additional serial ports interfaced
> > > via
> > > +Analog Devices ADM213EA chips in addition to 3 serial ports on PXA
> > > CPU.
> > > +
> > > +Required properties:
> > > +- compatible : should be "icpdas,uart-lp8x4x"
> >
> > Compatible strings should not include a 'x' wildcard like this, better
> > use
> > the specific chip name.
> >
> > Also, it sounds like you named them after the board vendor, which
> > sounds
> > wrong as the vendor part of the compatible string should be the
> > whoever
> > made that part (analog?)
>
> The chips themselves are standard, they would work with 8250_core if
> properly connected. However, they are not connected normally. Al least
> some of their config pins are wired to a different address region. So
> the driver is board-specific.

Ok, I see.

> 'x' wildcards in the name of the board seem important. There are devices
> made by the same vendor without 8 or 4 in their name. Those devices
> either are not shipped with linux or are base on a x86 platform.
>
> Does this justify the choice of the compatible string?

What I meant was that you should use the specific numbers of one machine,
precisely for the reason you list above.

If there is e.g. a LP-8040 and a LP-8141 today, and you use lp8x4x in
the compatible string to cover both, this will no longer work when the
vendor comes out with a LP8047 that is completely different.

Instead, what you should do is to use the compatible string to identify
one particular board (e.g. the first one that used this setup), and
then list the other ones as compatible with this. You can also add the
other board names in addition, e.g.

compatible = "icpdas,uart-lp8041", "icpdas,uart-lp8040";

for a lp8041 that is compatible with the lp8040. If it turns out later
that they are not entirely compatible, we can work around this in the
driver by checking for the lp8041 string that will be matched first, while
the lp8040 can be used by the driver to match the entire family of
compatible machines (no need to list every one in the driver).

Arnd

2015-12-17 14:52:30

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Wed, 2015-12-16 at 00:04 +0300, Sergei Ianovich wrote:
> The patch adds support for 3 additional LP-8x4x built-in serial
> ports.
>
> The device can also host up to 8 extension cards with 4 serial ports
> on each card for a total of 35 ports. However, I don't have
> the hardware to test extension cards, so they are not supported, yet.
>

Few nitpicks, though everything looks okay.

> Signed-off-by: Sergei Ianovich <[email protected]>
> Reviewed-by: Heikki Krogerus <[email protected]>
> CC: Alan Cox <[email protected]>
> ---
>    v4..v5
>    * constify struct of_device_id
>    * drop .owner from struct platform_driver
>    * rewrite set_termios() baud rate hadnling as suggested by Alan
> Cox
>
>    v3..v4
>    * move DTS bindings to a different patch (8/21) as suggested by
>      Heikki Krogerus
>
>    v2..v3
>    * no changes (except number 10/16 -> 12/21)
>
>    v0..v2
>    * register platform driver instead of platform device
>    * use device tree
>    * use devm helpers where possible
>
>  .../devicetree/bindings/serial/lp8x4x-serial.txt   |  35 +++++
>  drivers/tty/serial/8250/8250_lp8x4x.c              | 168
> +++++++++++++++++++++
>  drivers/tty/serial/8250/Kconfig                    |  12 ++
>  drivers/tty/serial/8250/Makefile                   |   1 +
>  4 files changed, 216 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/serial/lp8x4x-
> serial.txt
>  create mode 100644 drivers/tty/serial/8250/8250_lp8x4x.c
>
> diff --git a/Documentation/devicetree/bindings/serial/lp8x4x-
> serial.txt b/Documentation/devicetree/bindings/serial/lp8x4x-
> serial.txt
> new file mode 100644
> index 0000000..5f9a4c1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/lp8x4x-serial.txt
> @@ -0,0 +1,35 @@
> +UART ports on ICP DAS LP-8x4x
> +
> +ICP DAS LP-8x4x contains three additional serial ports interfaced
> via
> +Analog Devices ADM213EA chips in addition to 3 serial ports on PXA
> CPU.
> +
> +Required properties:
> +- compatible : should be "icpdas,uart-lp8x4x"
> +
> +- reg : should provide 16 byte man IO memory region and 1 byte
> region for
> + termios
> +
> +- interrupts : should provide interrupt
> +
> +- interrupt-parent : should provide a link to interrupt controller
> either
> +      explicitly or implicitly from a parent node
> +
> +Examples (from pxa27x-lp8x4x.dts):
> +
> + uart@9050 {
> + compatible = "icpdas,uart-lp8x4x";
> + reg = <0x9050 0x10
> +        0x9030 0x02>;
> + interrupt-parent = <&fpgairg>;
> + interrupts = <13>;
> + status = "okay";
> + };
> +
> + uart@9060 {
> + compatible = "icpdas,uart-lp8x4x";
> + reg = <0x9060 0x10
> +        0x9032 0x02>;
> + interrupt-parent = <&fpgairg>;
> + interrupts = <14>;
> + status = "okay";
> + };
> diff --git a/drivers/tty/serial/8250/8250_lp8x4x.c
> b/drivers/tty/serial/8250/8250_lp8x4x.c
> new file mode 100644
> index 0000000..0e07220
> --- /dev/null
> +++ b/drivers/tty/serial/8250/8250_lp8x4x.c
> @@ -0,0 +1,168 @@
> +/*  linux/drivers/tty/serial/8250/8250_lp8x4x.c
> + *
> + *  Support for 16550A serial ports on ICP DAS LP-8x4x
> + *
> + *  Copyright (C) 2013 Sergei Ianovich <[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/io.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/serial_8250.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +struct lp8x4x_serial_data {
> + int line;
> + void *ios_mem;
> +};
> +
> +static void lp8x4x_serial_set_termios(struct uart_port *port,
> + struct ktermios *termios, struct ktermios *old)
> +{
> + unsigned int len;
> + unsigned int baud;
> + struct lp8x4x_serial_data *data = port->private_data;
> +
> + serial8250_do_set_termios(port, termios, old);
> +
> + switch (termios->c_cflag & CSIZE) {
> + case CS5:
> + len = 7;
> + break;
> + case CS6:
> + len = 8;
> + break;
> + case CS7:
> + len = 9;
> + break;

> + default:
> + case CS8:

I would suggest to exchange those lines.

> + len = 10;
> + break;
> + }
> +
> + if (termios->c_cflag & CSTOPB)
> + len++;
> + if (termios->c_cflag & PARENB)
> + len++;
> + if (!(termios->c_cflag & PARODD))
> + len++;
> +#ifdef CMSPAR
> + if (termios->c_cflag & CMSPAR)
> + len++;
> +#endif
> +
> + len -= 9;
> + len &= 3;
> + len <<= 3;

> + /*
> +  * Ask the core to calculate the divisor for us.
> +  */

Can it be one line?

> + baud = tty_termios_baud_rate(termios);
> +
> + switch (baud) {
> + case 115200:
> + len |= 7;
> + break;
> + case 57600:
> + len |= 6;
> + break;
> + case 38400:
> + len |= 5;
> + break;
> + case 19200:
> + len |= 4;
> + break;
> + case 9600:
> + len |= 3;
> + break;
> + case 4800:
> + len |= 2;
> + break;
> + case 2400:
> + default:
> + len |= 1;
> + break;
> + };
> + iowrite8(len, data->ios_mem);

writeb() ?

> +
> +}
> +
> +static const struct of_device_id lp8x4x_serial_dt_ids[] = {
> + { .compatible = "icpdas,uart-lp8x4x", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, lp8x4x_serial_dt_ids);
> +
> +static int lp8x4x_serial_probe(struct platform_device *pdev)
> +{
> + struct uart_8250_port uart = {};
> + struct lp8x4x_serial_data *data;
> + struct resource *mmres, *mires, *irqres;
> + int ret;
> +
> + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + mires = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!mmres || !mires || !irqres)
> + return -ENODEV;
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->ios_mem = devm_ioremap_resource(&pdev->dev, mires);
> + if (!data->ios_mem)
> + return -EFAULT;
> +
> + uart.port.iotype = UPIO_MEM;
> + uart.port.mapbase = mmres->start;
> + uart.port.iobase = mmres->start;
> + uart.port.regshift = 1;
> + uart.port.irq = irqres->start;
> + uart.port.flags = UPF_IOREMAP;
> + uart.port.dev = &pdev->dev;
> + uart.port.uartclk = 14745600;
> + uart.port.set_termios = lp8x4x_serial_set_termios;
> + uart.port.private_data = data;
> +
> + ret = serial8250_register_8250_port(&uart);
> + if (ret < 0)
> + return ret;
> +
> + data->line = ret;
> +
> + platform_set_drvdata(pdev, data);
> +
> + return 0;
> +}
> +
> +static int lp8x4x_serial_remove(struct platform_device *pdev)
> +{
> + struct lp8x4x_serial_data *data =
> platform_get_drvdata(pdev);
> +
> + serial8250_unregister_port(data->line);
> +
> + return 0;
> +}
> +
> +static struct platform_driver lp8x4x_serial_driver = {
> + .probe          = lp8x4x_serial_probe,
> + .remove         = lp8x4x_serial_remove,
> +
> + .driver = {
> + .name = "uart-lp8x4x",
> + .of_match_table = lp8x4x_serial_dt_ids,
> + },
> +};
> +
> +module_platform_driver(lp8x4x_serial_driver);
> +
> +MODULE_AUTHOR("Sergei Ianovich");
> +MODULE_DESCRIPTION("8250 serial port module for LP-8x4x");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/tty/serial/8250/Kconfig
> b/drivers/tty/serial/8250/Kconfig
> index 48b6253..00eb6b0 100644
> --- a/drivers/tty/serial/8250/Kconfig
> +++ b/drivers/tty/serial/8250/Kconfig
> @@ -387,3 +387,15 @@ config SERIAL_8250_PXA
>     can enable its onboard serial ports by enabling this
> option.
>  
>     If you choose M here, the module name will be 8250_pxa.
> +
> +config SERIAL_8250_LP8X4X
> + tristate "Support 16550A ports on ICP DAS LP-8x4x"
> + depends on OF && SERIAL_8250 && SERIAL_8250_MANY_PORTS &&
> ARCH_PXA
> + select LP8X4X_IRQ
> + help
> +   In addition to serial ports on PXA270 SoC, LP-8x4x has 1
> dual
> +   RS232/RS485 port, 1 RS485 port and 1 RS232 port.
> +
> +   Say N here, unless you plan to run this kernel on a LP-
> 8x4x system.
> +
> +   If you choose M here, the module name will be 8250_lp8x4x.
> diff --git a/drivers/tty/serial/8250/Makefile
> b/drivers/tty/serial/8250/Makefile
> index 7e54413..8bdbf40 100644
> --- a/drivers/tty/serial/8250/Makefile
> +++ b/drivers/tty/serial/8250/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) +=
> 8250_accent.o
>  obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
>  obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) +=
> 8250_exar_st16c554.o
>  obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
> +obj-$(CONFIG_SERIAL_8250_LP8X4X) += 8250_lp8x4x.o
>  obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
>  obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
>  obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o

--
Andy Shevchenko <[email protected]>
Intel Finland Oy

2015-12-19 04:20:10

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5] arm: pxa: support ICP DAS LP-8x4x FPGA irq

On Tue, Dec 15, 2015 at 10:26:21PM +0300, Sergei Ianovich wrote:
> ICP DAS LP-8x4x contains FPGA chip. The chip functions as an interrupt
> source providing 16 additional interrupts among other things. The
> interrupt lines are muxed to a GPIO pin of a 2nd level PXA-GPIO
> interrupt controller. GPIO pins of the 2nd level controller are in turn
> muxed to a CPU interrupt line.
>
> Until pxa is completely converted to device tree, it is impossible
> to use IRQCHIP_DECLARE() and the irqdomain needs to added manually.
> Drivers for the on-CPU IRQs and GPIO-IRQs are loaded using
> postcore_initcall(). We need to have all irq domain drivers loaded prior
> to DT parsing in order to allow normal initialization of IRQ resources
> with DT.
>
> Signed-off-by: Sergei Ianovich <[email protected]>
> Reviewed-by: Linus Walleij <[email protected]>
> CC: Arnd Bergmann <[email protected]>
> ---
> v4..v5
> * constify struct of_device_id
> * drop irq number from handler signature
>
> v3.2..v4
> * move DTS binding to a different patch (8/21)
>
> v3.1..v3.2
> fixes to apply Linus Walleij's "Reviewed-by":
> * add kerneldoc comment for state container struct
> * rename irq -> hwirq for clarity
> * drop overzealous error checks from the hotpaths
>
> v3..v3.1
> fixes according to Linus Walleij review comments:
> * update commit message
> * use state container instead of global variables
> * get hardware irq nums from irq_data, don't calculate them
> * use BIT() macro
> * add defines for system irq register masks
> * replace cycle control variable with break
> * use better names for resource variables
> * add a linear domain instead of a legacy one
> * use irq_create_mapping() instead of irq_alloc_desc()
>
> v2..v3
> * no changes (except number 09/16 -> 11/21)
>
> v0..v2
> * extract irqchip and move to drivers/irqchip/
> * use device tree
> * use devm helpers where possible
>
> .../bindings/interrupt-controller/irq-lp8x4x.txt | 49 +++++
> drivers/irqchip/Kconfig | 5 +
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-lp8x4x.c | 227 +++++++++++++++++++++
> 4 files changed, 282 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
> create mode 100644 drivers/irqchip/irq-lp8x4x.c
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
> new file mode 100644
> index 0000000..c8940d2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/irq-lp8x4x.txt
> @@ -0,0 +1,49 @@
> +ICP DAS LP-8x4x FPGA Interrupt Controller
> +
> +ICP DAS LP-8x4x contains FPGA chip. The chip functions as a interrupt
> +source providing 16 additional interrupts among other things.
> +
> +Required properties:
> +- compatible : should be "icpdas,irq-lp8x4x"
> +
> +- reg: physical base address of the controller and length of memory mapped
> + region.
> +
> +- interrupt-controller : identifies the node as an interrupt controller
> +
> +- #interrupt-cells : should be 1
> +
> +- interrupts : should provide interrupt
> +
> +- interrupt-parent : should provide a link to interrupt controller either
> + explicitly and implicitly from a parent node
> +
> +Example:
> +
> + fpga: fpga@17000006 {

Nothing else in the fpga? In any case, this node should be named
interrupt-controller@17000006.

> + compatible = "icpdas,irq-lp8x4x";

As pointed out in the uart binding, don't use wildcards here.

> + reg = <0x17000006 0x16>;
> + interrupt-parent = <&gpio>;
> + interrupts = <3 IRQ_TYPE_EDGE_RISING>;
> + #interrupt-cells = <1>;
> + interrupt-controller;
> + status = "okay";
> + };
> +
> + uart@17009050 {
> + compatible = "icpdas,uart-lp8x4x";
> + reg = <0x17009050 0x10
> + 0x17009030 0x02>;
> + interrupt-parent = <&fpga>;
> + interrupts = <13>;
> + status = "okay";
> + };

2015-12-19 07:03:26

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v5] arm: pxa: support ICP DAS LP-8x4x FPGA irq

On Fri, 2015-12-18 at 21:58 -0600, Rob Herring wrote:
> On Tue, Dec 15, 2015 at 10:26:21PM +0300, Sergei Ianovich wrote:
> > +Example:
> > +
> > + fpga: fpga@17000006 {
>
> Nothing else in the fpga? In any case, this node should be named
> interrupt-controller@17000006.
>
> > + compatible = "icpdas,irq-lp8x4x";
>
> As pointed out in the uart binding, don't use wildcards here.
>
> > + reg = <0x17000006 0x16>;
> > + interrupt-parent = <&gpio>;
> > + interrupts = <3 IRQ_TYPE_EDGE_RISING>;
> > + #interrupt-cells = <1>;
> > + interrupt-controller;
> > + status = "okay";
> > + };
> > +
> > + uart@17009050 {
> > + compatible = "icpdas,uart-lp8x4x";
> > + reg = <0x17009050 0x10
> > +        0x17009030 0x02>;
> > + interrupt-parent = <&fpga>;
> > + interrupts = <13>;
> > + status = "okay";
> > + };

That was just an example. The actual binding in LP-8x4x is bigger:
                fpga@5 {
                        compatible = "simple-bus";
                        #address-cells = <1>;
                        #size-cells = <1>;
                        ranges = <0 5 0x3000000 0x10000>;
                        interrupt-parent = <&fpgairq>;

                        rtc@901c {
                                compatible = "dallas,rtc-ds1302";
                                reg = <0x901c 0x1>;
                                status = "okay";
                        };

                        sram@a000 {
                                compatible = "icpdas,sram-lp8x4x";
                                reg = <0xa000 0x1000
                                       0x901e 0x1>;

                                partitions {
                                        #address-cells = <1>;
                                        #size-cells = <1>;
                                };
                        };

                        fpgairq: irq@9006 {
                                compatible = "icpdas,irq-lp8x4x";
                                reg = <0x9006 0x16>;
                                interrupt-parent = <&gpio>;
                                interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
                                #interrupt-cells = <1>;
                                interrupt-controller;
                                status = "okay";
                        };

                        uart@9050 {
                                compatible = "icpdas,uart-lp8x4x";
                                reg = <0x9050 0x10
                                       0x9030 0x02>;
                                interrupts = <13>;
                                status = "okay";
                        };

                        uart@9060 {
                                compatible = "icpdas,uart-lp8x4x";
                                reg = <0x9060 0x10
                                       0x9032 0x02>;
                                interrupts = <14>;
                                status = "okay";
                        };

                        uart@9070 {
                                compatible = "icpdas,uart-lp8x4x";
                                reg = <0x9070 0x10
                                       0x9034 0x02>;
                                interrupts = <15>;
                                status = "okay";
                        };

                        backplane {
                                compatible = "icpdas,backplane-lp8x4x";
                                reg = <0x0 0x2
                                       0x1000 0x10
                                       0x2000 0x10
                                       0x3000 0x10
                                       0x4000 0x10
                                       0x5000 0x10
                                       0x6000 0x10
                                       0x7000 0x10
                                       0x8000 0x10
                                       0x9002 0x2
                                       0x9004 0x2
                                       0x9046 0x2>;
                                eeprom-gpios = <&gpio 4 0>;
                        };
                };

2015-12-19 08:11:31

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Wed, 2015-12-16 at 11:26 +0100, Arnd Bergmann wrote:
> On Wednesday 16 December 2015 11:04:57 Sergei Ianovich wrote:
> > On Tue, 2015-12-15 at 22:51 +0100, Arnd Bergmann wrote:
> > > 'x' wildcards in the name of the board seem important. There are
> > devices
> > made by the same vendor without 8 or 4 in their name. Those devices
> > either are not shipped with linux or are base on a x86 platform.
> >
> > Does this justify the choice of the compatible string?
>
> What I meant was that you should use the specific numbers of one
> machine,
> precisely for the reason you list above.
>
> If there is e.g. a LP-8040 and a LP-8141 today, and you use lp8x4x in
> the compatible string to cover both, this will no longer work when the
> vendor comes out with a LP8047 that is completely different.
>
> Instead, what you should do is to use the compatible string to
> identify
> one particular board (e.g. the first one that used this setup), and
> then list the other ones as compatible with this. You can also add the
> other board names in addition, e.g.
>
> compatible = "icpdas,uart-lp8041", "icpdas,uart-lp8040";
>
> for a lp8041 that is compatible with the lp8040. If it turns out later
> that they are not entirely compatible, we can work around this in the
> driver by checking for the lp8041 string that will be matched first,
> while
> the lp8040 can be used by the driver to match the entire family of
> compatible machines (no need to list every one in the driver).

I'll try to be more specific. This driver will support ports on LP-8081,
LP-8141, LP-8441, LP-8841. Last time I checked the vendor was announcing
a series with 3 as the last digit. They use lp8x4x name, eg. in
documentation like `LP-8x4x_ChangeLog.txt`. They ship their proprietary
SDK in `lp8x4x_sdk_for_linux.tar`. All of this implies that it is a
single board.

2015-12-19 21:42:22

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v5] serial: support for 16550A serial ports on LP-8x4x

On Tue, 2015-12-15 at 22:51 +0100, Arnd Bergmann wrote:
> On Wednesday 16 December 2015 00:04:45 Sergei Ianovich wrote:
> > +Examples (from pxa27x-lp8x4x.dts):
> > +
> > +               uart@9050 {
>
> By convention, the name should be 'serial', not 'uart'.
>

arch/arm/boot/dts/pxa2xx.dtsi uses 'uart'. Should I change the names in
the patch with dts file for LP-8xx?

2015-12-20 03:38:18

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5] mtd: support BB SRAM on ICP DAS LP-8x4x

On Tue, Dec 15, 2015 at 09:58:53PM +0300, Sergei Ianovich wrote:
> This provides an MTD device driver for 512kB of battery backed up SRAM
> on ICPDAS LP-8X4X programmable automation controllers.
>
> SRAM chip is connected via FPGA and is not accessible without a driver,
> unlike flash memory which is wired to CPU MMU.
>
> This SRAM becomes an excellent persisent storage of volatile process
> data like counter values and sensor statuses. Storing those data in
> flash or mmc card is not a viable solution.
>
> Signed-off-by: Sergei Ianovich <[email protected]>
> Reviewed-by: Brian Norris <[email protected]>
> ---
> v4..v5
> * remove .owner from struct platform_driver
> * constify struct of_device_id
> for further Brian Norris comments:
> * drop unused property from doc file
> * move defconfig update to a different file
> * drop extra match w/ of_match_device()
>
> v3..v4 for Brian Norris 'Reviewed-by'
> * add doc file for DT binding
> * move DTS binding to a different patch (8/21)
> * drop unused include directive
> * drop safely unused callback
> * drop non-default partion probe types
> * drop duplicate error checks
> * drop duplicate error reporting
> * fixed error message on MTD registeration
> * fixed module removal routine
>
> v2..v3
> * no changes (except number 08/16 -> 10/21)
>
> v0..v2
> * use device tree
> * use devm helpers where possible
>
> .../devicetree/bindings/mtd/sram-lp8x4x.txt | 20 +++
> drivers/mtd/devices/Kconfig | 14 ++
> drivers/mtd/devices/Makefile | 1 +
> drivers/mtd/devices/sram_lp8x4x.c | 199 +++++++++++++++++++++
> 4 files changed, 234 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> create mode 100644 drivers/mtd/devices/sram_lp8x4x.c
>
> diff --git a/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> new file mode 100644
> index 0000000..476934f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> @@ -0,0 +1,20 @@
> +512kB battery backed up SRAM on LP-8x4x industrial computers
> +
> +Required properties:
> +- compatible : should be "icpdas,sram-lp8x4x"

No wildcards please. Otherwise looks fine.

> +
> +- reg: physical base addresses and region lengths of
> + * IO memory range
> + * SRAM page selector
> +
> +SRAM chip is connected via FPGA and is not accessible without a driver,
> +unlike flash memory which is wired to CPU MMU. Driver is essentially
> +an address translation routine.
> +
> +Example:
> +
> + sram@a000 {
> + compatible = "icpdas,sram-lp8x4x";
> + reg = <0xa000 0x1000
> + 0x901e 0x1>;
> + };

2015-12-20 10:44:06

by Sergei Ianovich

[permalink] [raw]
Subject: Re: [PATCH v5] mtd: support BB SRAM on ICP DAS LP-8x4x

On Sat, 2015-12-19 at 21:38 -0600, Rob Herring wrote:
> On Tue, Dec 15, 2015 at 09:58:53PM +0300, Sergei Ianovich wrote:
> > This provides an MTD device driver for 512kB of battery backed up
> > SRAM
> > on ICPDAS LP-8X4X programmable automation controllers.
> >
> > SRAM chip is connected via FPGA and is not accessible without a
> > driver,
> > unlike flash memory which is wired to CPU MMU.
> >
> > This SRAM becomes an excellent persisent storage of volatile process
> > data like counter values and sensor statuses. Storing those data in
> > flash or mmc card is not a viable solution.
> >
> > Signed-off-by: Sergei Ianovich <[email protected]>
> > Reviewed-by: Brian Norris <[email protected]>
> > ---
> >    v4..v5
> >    * remove .owner from struct platform_driver
> >    * constify struct of_device_id
> >     for further Brian Norris comments:
> >    * drop unused property from doc file
> >    * move defconfig update to a different file
> >    * drop extra match w/ of_match_device()
> >
> >    v3..v4 for Brian Norris 'Reviewed-by'
> >    * add doc file for DT binding
> >    * move DTS binding to a different patch (8/21)
> >    * drop unused include directive
> >    * drop safely unused callback
> >    * drop non-default partion probe types
> >    * drop duplicate error checks
> >    * drop duplicate error reporting
> >    * fixed error message on MTD registeration
> >    * fixed module removal routine
> >
> >    v2..v3
> >    * no changes (except number 08/16 -> 10/21)
> >
> >    v0..v2
> >    * use device tree
> >    * use devm helpers where possible
> >
> >  .../devicetree/bindings/mtd/sram-lp8x4x.txt        |  20 +++
> >  drivers/mtd/devices/Kconfig                        |  14 ++
> >  drivers/mtd/devices/Makefile                       |   1 +
> >  drivers/mtd/devices/sram_lp8x4x.c                  | 199
> > +++++++++++++++++++++
> >  4 files changed, 234 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/mtd/sram-
> > lp8x4x.txt
> >  create mode 100644 drivers/mtd/devices/sram_lp8x4x.c
> >
> > diff --git a/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> > b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> > new file mode 100644
> > index 0000000..476934f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mtd/sram-lp8x4x.txt
> > @@ -0,0 +1,20 @@
> > +512kB battery backed up SRAM on LP-8x4x industrial computers
> > +
> > +Required properties:
> > +- compatible : should be "icpdas,sram-lp8x4x"
>
> No wildcards please. Otherwise looks fine.

There is a similar review comment from Arnd Bergmann in the discussion
of `[PATCH v5] serial: support for 16550A serial ports on LP-8x4x`.

I'll quote my latest clarification:
> ... This driver will support ports on LP-8081, 
> LP-8141, LP-8441, LP-8841. Last time I checked the vendor was announcing
> a series with 3 as the last digit. They use lp8x4x name, eg. in
> documentation like `LP-8x4x_ChangeLog.txt`. They ship their proprietary
> SDK in `lp8x4x_sdk_for_linux.tar`. All of this implies that it is a
> single board.

I think the solution should be the same for all LP-8x4x drivers (IRQ,
SRAM, SERIAL, IIO).