All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/10] Support for Cortex-M Prototyping System
@ 2015-12-02  9:33 ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

Hi,

This patch series provide the basic support for running ucLinux on V2M-MPS2
platform.

With these patches applied ucLinux can be run on both HW and FVP models
with Cortex-M3/M4/M7 configurations.

Board description:

http://infocenter.arm.com/help/topic/com.arm.doc.100112_0100_03_en/arm_versatile_express_cortex_m_prototyping_system_(v2m_mps2)_technical_reference_manual_100112_0100_03_en.pdf

Application notes (cover Cortex-M3/M4/M7):

http://infocenter.arm.com/help/topic/com.arm.doc.dai0385a/DAI0385A_cortex_m3_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0386a/DAI0386A_cortex_m4_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0399a/DAI0399A_cortex_m7_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0400a/DAI0400A_cortex_m7_on_v2m_mps2.pdf

Cortex-M System Design Kit (referenced as CMDK from documents above):

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0479c/DDI0479C_cortex_m_system_design_kit_r1p0_trm.pdf

I'd be happy to hear any feedback/comments on this series (especially on uart
since I'm not sure I got it right)!

Changelog:

  RFC -> v1:
      - dropped RFC tag
      - rebased on 4.4-rc3
      - added Acks from Rob
      - updated mps2-timer per Daniel
      - fixed build failures reported by 0-DAY kernel test infrastructure

Thanks!

Vladimir Murzin (10):
  dt-bindings: document the MPS2 timer bindings
  clockevents/drivers: add MPS2 Timer driver
  dt-bindings: document the MPS2 UART bindings
  serial: mps2-uart: add MPS2 UART driver
  serial: mps2-uart: add support for early console
  ARM: mps2: introduce MPS2 platform
  ARM: mps2: add low-level debug support
  ARM: configs: add MPS2 defconfig
  ARM: dts: introduce MPS2 AN385/AN386
  ARM: dts: introduce MPS2 AN399/AN400

 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 +
 .../devicetree/bindings/timer/arm,mps2-timer.txt   |   28 +
 arch/arm/Kconfig                                   |    8 +
 arch/arm/Kconfig.debug                             |   12 +-
 arch/arm/Makefile                                  |    1 +
 arch/arm/boot/dts/Makefile                         |    3 +
 arch/arm/boot/dts/mps2-an385.dts                   |   90 +++
 arch/arm/boot/dts/mps2-an399.dts                   |   92 +++
 arch/arm/boot/dts/mps2.dtsi                        |  227 +++++++
 arch/arm/configs/mps2_defconfig                    |  112 ++++
 arch/arm/include/debug/mps2.S                      |   27 +
 arch/arm/mach-mps2/Makefile                        |    1 +
 arch/arm/mach-mps2/Makefile.boot                   |    3 +
 arch/arm/mach-mps2/dtmachine.c                     |   21 +
 drivers/clocksource/Kconfig                        |    5 +
 drivers/clocksource/Makefile                       |    1 +
 drivers/clocksource/mps2-timer.c                   |  277 +++++++++
 drivers/tty/serial/Kconfig                         |   13 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/mps2-uart.c                     |  626 ++++++++++++++++++++
 include/uapi/linux/serial_core.h                   |    3 +
 21 files changed, 1572 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
 create mode 100644 Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
 create mode 100644 arch/arm/boot/dts/mps2-an385.dts
 create mode 100644 arch/arm/boot/dts/mps2-an399.dts
 create mode 100644 arch/arm/boot/dts/mps2.dtsi
 create mode 100644 arch/arm/configs/mps2_defconfig
 create mode 100644 arch/arm/include/debug/mps2.S
 create mode 100644 arch/arm/mach-mps2/Makefile
 create mode 100644 arch/arm/mach-mps2/Makefile.boot
 create mode 100644 arch/arm/mach-mps2/dtmachine.c
 create mode 100644 drivers/clocksource/mps2-timer.c
 create mode 100644 drivers/tty/serial/mps2-uart.c

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 00/10] Support for Cortex-M Prototyping System
@ 2015-12-02  9:33 ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, Pawel.Moll-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Hi,

This patch series provide the basic support for running ucLinux on V2M-MPS2
platform.

With these patches applied ucLinux can be run on both HW and FVP models
with Cortex-M3/M4/M7 configurations.

Board description:

http://infocenter.arm.com/help/topic/com.arm.doc.100112_0100_03_en/arm_versatile_express_cortex_m_prototyping_system_(v2m_mps2)_technical_reference_manual_100112_0100_03_en.pdf

Application notes (cover Cortex-M3/M4/M7):

http://infocenter.arm.com/help/topic/com.arm.doc.dai0385a/DAI0385A_cortex_m3_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0386a/DAI0386A_cortex_m4_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0399a/DAI0399A_cortex_m7_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0400a/DAI0400A_cortex_m7_on_v2m_mps2.pdf

Cortex-M System Design Kit (referenced as CMDK from documents above):

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0479c/DDI0479C_cortex_m_system_design_kit_r1p0_trm.pdf

I'd be happy to hear any feedback/comments on this series (especially on uart
since I'm not sure I got it right)!

Changelog:

  RFC -> v1:
      - dropped RFC tag
      - rebased on 4.4-rc3
      - added Acks from Rob
      - updated mps2-timer per Daniel
      - fixed build failures reported by 0-DAY kernel test infrastructure

Thanks!

Vladimir Murzin (10):
  dt-bindings: document the MPS2 timer bindings
  clockevents/drivers: add MPS2 Timer driver
  dt-bindings: document the MPS2 UART bindings
  serial: mps2-uart: add MPS2 UART driver
  serial: mps2-uart: add support for early console
  ARM: mps2: introduce MPS2 platform
  ARM: mps2: add low-level debug support
  ARM: configs: add MPS2 defconfig
  ARM: dts: introduce MPS2 AN385/AN386
  ARM: dts: introduce MPS2 AN399/AN400

 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 +
 .../devicetree/bindings/timer/arm,mps2-timer.txt   |   28 +
 arch/arm/Kconfig                                   |    8 +
 arch/arm/Kconfig.debug                             |   12 +-
 arch/arm/Makefile                                  |    1 +
 arch/arm/boot/dts/Makefile                         |    3 +
 arch/arm/boot/dts/mps2-an385.dts                   |   90 +++
 arch/arm/boot/dts/mps2-an399.dts                   |   92 +++
 arch/arm/boot/dts/mps2.dtsi                        |  227 +++++++
 arch/arm/configs/mps2_defconfig                    |  112 ++++
 arch/arm/include/debug/mps2.S                      |   27 +
 arch/arm/mach-mps2/Makefile                        |    1 +
 arch/arm/mach-mps2/Makefile.boot                   |    3 +
 arch/arm/mach-mps2/dtmachine.c                     |   21 +
 drivers/clocksource/Kconfig                        |    5 +
 drivers/clocksource/Makefile                       |    1 +
 drivers/clocksource/mps2-timer.c                   |  277 +++++++++
 drivers/tty/serial/Kconfig                         |   13 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/mps2-uart.c                     |  626 ++++++++++++++++++++
 include/uapi/linux/serial_core.h                   |    3 +
 21 files changed, 1572 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
 create mode 100644 Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
 create mode 100644 arch/arm/boot/dts/mps2-an385.dts
 create mode 100644 arch/arm/boot/dts/mps2-an399.dts
 create mode 100644 arch/arm/boot/dts/mps2.dtsi
 create mode 100644 arch/arm/configs/mps2_defconfig
 create mode 100644 arch/arm/include/debug/mps2.S
 create mode 100644 arch/arm/mach-mps2/Makefile
 create mode 100644 arch/arm/mach-mps2/Makefile.boot
 create mode 100644 arch/arm/mach-mps2/dtmachine.c
 create mode 100644 drivers/clocksource/mps2-timer.c
 create mode 100644 drivers/tty/serial/mps2-uart.c

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 00/10] Support for Cortex-M Prototyping System
@ 2015-12-02  9:33 ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patch series provide the basic support for running ucLinux on V2M-MPS2
platform.

With these patches applied ucLinux can be run on both HW and FVP models
with Cortex-M3/M4/M7 configurations.

Board description:

http://infocenter.arm.com/help/topic/com.arm.doc.100112_0100_03_en/arm_versatile_express_cortex_m_prototyping_system_(v2m_mps2)_technical_reference_manual_100112_0100_03_en.pdf

Application notes (cover Cortex-M3/M4/M7):

http://infocenter.arm.com/help/topic/com.arm.doc.dai0385a/DAI0385A_cortex_m3_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0386a/DAI0386A_cortex_m4_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0399a/DAI0399A_cortex_m7_on_v2m_mps2.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.dai0400a/DAI0400A_cortex_m7_on_v2m_mps2.pdf

Cortex-M System Design Kit (referenced as CMDK from documents above):

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0479c/DDI0479C_cortex_m_system_design_kit_r1p0_trm.pdf

I'd be happy to hear any feedback/comments on this series (especially on uart
since I'm not sure I got it right)!

Changelog:

  RFC -> v1:
      - dropped RFC tag
      - rebased on 4.4-rc3
      - added Acks from Rob
      - updated mps2-timer per Daniel
      - fixed build failures reported by 0-DAY kernel test infrastructure

Thanks!

Vladimir Murzin (10):
  dt-bindings: document the MPS2 timer bindings
  clockevents/drivers: add MPS2 Timer driver
  dt-bindings: document the MPS2 UART bindings
  serial: mps2-uart: add MPS2 UART driver
  serial: mps2-uart: add support for early console
  ARM: mps2: introduce MPS2 platform
  ARM: mps2: add low-level debug support
  ARM: configs: add MPS2 defconfig
  ARM: dts: introduce MPS2 AN385/AN386
  ARM: dts: introduce MPS2 AN399/AN400

 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 +
 .../devicetree/bindings/timer/arm,mps2-timer.txt   |   28 +
 arch/arm/Kconfig                                   |    8 +
 arch/arm/Kconfig.debug                             |   12 +-
 arch/arm/Makefile                                  |    1 +
 arch/arm/boot/dts/Makefile                         |    3 +
 arch/arm/boot/dts/mps2-an385.dts                   |   90 +++
 arch/arm/boot/dts/mps2-an399.dts                   |   92 +++
 arch/arm/boot/dts/mps2.dtsi                        |  227 +++++++
 arch/arm/configs/mps2_defconfig                    |  112 ++++
 arch/arm/include/debug/mps2.S                      |   27 +
 arch/arm/mach-mps2/Makefile                        |    1 +
 arch/arm/mach-mps2/Makefile.boot                   |    3 +
 arch/arm/mach-mps2/dtmachine.c                     |   21 +
 drivers/clocksource/Kconfig                        |    5 +
 drivers/clocksource/Makefile                       |    1 +
 drivers/clocksource/mps2-timer.c                   |  277 +++++++++
 drivers/tty/serial/Kconfig                         |   13 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/mps2-uart.c                     |  626 ++++++++++++++++++++
 include/uapi/linux/serial_core.h                   |    3 +
 21 files changed, 1572 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
 create mode 100644 Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
 create mode 100644 arch/arm/boot/dts/mps2-an385.dts
 create mode 100644 arch/arm/boot/dts/mps2-an399.dts
 create mode 100644 arch/arm/boot/dts/mps2.dtsi
 create mode 100644 arch/arm/configs/mps2_defconfig
 create mode 100644 arch/arm/include/debug/mps2.S
 create mode 100644 arch/arm/mach-mps2/Makefile
 create mode 100644 arch/arm/mach-mps2/Makefile.boot
 create mode 100644 arch/arm/mach-mps2/dtmachine.c
 create mode 100644 drivers/clocksource/mps2-timer.c
 create mode 100644 drivers/tty/serial/mps2-uart.c

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 01/10] dt-bindings: document the MPS2 timer bindings
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

This adds documentation of device tree bindings for the
timers found on ARM MPS2 platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/timer/arm,mps2-timer.txt   |   28 ++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/arm,mps2-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt b/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
new file mode 100644
index 0000000..48f84d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
@@ -0,0 +1,28 @@
+ARM MPS2 timer
+
+The MPS2 platform has simple general-purpose 32 bits timers.
+
+Required properties:
+- compatible	: Should be "arm,mps2-timer"
+- reg		: Address and length of the register set
+- interrupts	: Reference to the timer interrupt
+
+Required clocking property, have to be one of:
+- clocks	  : The input clock of the timer
+- clock-frequency : The rate in HZ in input of the ARM MPS2 timer
+
+Examples:
+
+timer1: mps2-timer@40000000 {
+	compatible = "arm,mps2-timer";
+	reg = <0x40000000 0x1000>;
+	interrupts = <8>;
+	clocks = <&sysclk>;
+};
+
+timer2: mps2-timer@40001000 {
+	compatible = "arm,mps2-timer";
+	reg = <0x40001000 0x1000>;
+	interrupts = <9>;
+	clock-frequency = <25000000>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 01/10] dt-bindings: document the MPS2 timer bindings
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

This adds documentation of device tree bindings for the
timers found on ARM MPS2 platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/timer/arm,mps2-timer.txt   |   28 ++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/arm,mps2-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt b/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
new file mode 100644
index 0000000..48f84d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/arm,mps2-timer.txt
@@ -0,0 +1,28 @@
+ARM MPS2 timer
+
+The MPS2 platform has simple general-purpose 32 bits timers.
+
+Required properties:
+- compatible	: Should be "arm,mps2-timer"
+- reg		: Address and length of the register set
+- interrupts	: Reference to the timer interrupt
+
+Required clocking property, have to be one of:
+- clocks	  : The input clock of the timer
+- clock-frequency : The rate in HZ in input of the ARM MPS2 timer
+
+Examples:
+
+timer1: mps2-timer at 40000000 {
+	compatible = "arm,mps2-timer";
+	reg = <0x40000000 0x1000>;
+	interrupts = <8>;
+	clocks = <&sysclk>;
+};
+
+timer2: mps2-timer at 40001000 {
+	compatible = "arm,mps2-timer";
+	reg = <0x40001000 0x1000>;
+	interrupts = <9>;
+	clock-frequency = <25000000>;
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
  2015-12-02  9:33 ` Vladimir Murzin
  (?)
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

MPS2 platform has simple 32 bits general purpose countdown timers.

The driver uses the first detected timer as a clocksource and the rest
of the timers as a clockevent

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/clocksource/Kconfig      |    5 +
 drivers/clocksource/Makefile     |    1 +
 drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/clocksource/mps2-timer.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 2eb5f0e..8bca09c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -137,6 +137,11 @@ config CLKSRC_STM32
 	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
 	select CLKSRC_MMIO
 
+config CLKSRC_MPS2
+	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
+	depends on OF && ARM
+	select CLKSRC_MMIO
+
 config ARM_ARCH_TIMER
 	bool
 	select CLKSRC_OF if OF
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 56bd16e..7033b9c 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
 obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
 obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
 obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
+obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
 obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
 obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
 obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
new file mode 100644
index 0000000..3e19af5
--- /dev/null
+++ b/drivers/clocksource/mps2-timer.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#define TIMER_CTRL		0x0
+#define TIMER_CTRL_ENABLE	BIT(0)
+#define TIMER_CTRL_IE		BIT(3)
+
+#define TIMER_VALUE		0x4
+#define TIMER_RELOAD		0x8
+#define TIMER_INT		0xc
+
+struct clockevent_mps2 {
+	void __iomem *reg;
+	u32 clock_count_per_tick;
+	struct clock_event_device clkevt;
+};
+
+static void __iomem *sched_clock_base;
+
+static u64 notrace mps2_sched_read(void)
+{
+        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
+}
+
+static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
+{
+	return container_of(c, struct clockevent_mps2, clkevt);
+}
+
+static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
+{
+	writel(val, to_mps2_clkevt(c)->reg + offset);
+}
+
+static int mps2_timer_shutdown(struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(0, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(next, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_periodic(struct clock_event_device *ce)
+{
+	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
+
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
+{
+	struct clockevent_mps2 *ce = dev_id;
+	u32 status = readl(ce->reg + TIMER_INT);
+
+	if (!status) {
+		pr_warn("spuirous interrupt\n");
+		return IRQ_NONE;
+	}
+
+	writel(1, ce->reg + TIMER_INT);
+
+	ce->clkevt.event_handler(&ce->clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int __init mps2_clockevent_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	struct clockevent_mps2 *ce;
+	u32 rate;
+	int irq, ret;
+	const char *name = "mps2-clkevt";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clockevent: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clockevent: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clockevent: %d\n", ret);
+		goto err_iomap;
+	}
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq) {
+		ret = -ENOENT;
+		pr_err("failed to get irq for clockevent: %d\n", ret);
+		goto err_get_irq;
+	}
+
+	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
+	if (!ce) {
+		ret = -ENOMEM;
+		pr_err("failed to allocate clockevent: %d\n", ret);
+		goto err_ce_alloc;
+	}
+
+	ce->reg = base;
+	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
+	ce->clkevt.irq = irq;
+	ce->clkevt.name = name;
+	ce->clkevt.rating = 200;
+	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	ce->clkevt.cpumask = cpu_possible_mask;
+	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
+	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
+	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
+	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
+	if (ret) {
+		pr_err("failed to request irq: %d\n", ret);
+		goto err_ia_alloc;
+	}
+
+	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
+
+	return 0;
+
+err_ia_alloc:
+	kfree(ce);
+err_ce_alloc:
+err_get_irq:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static int __init mps2_clocksource_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	u32 rate;
+	int ret;
+	const char *name = "mps2-clksrc";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clocksource: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clocksource: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clocksource: %d\n", ret);
+		goto err_iomap;
+	}
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	/* ... and set it up as free-running clocksource */
+	writel(0xffffffff, base + TIMER_VALUE);
+	writel(0xffffffff, base + TIMER_RELOAD);
+
+	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
+
+	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
+				    rate, 200, 32,
+				    clocksource_mmio_readl_down);
+	if (ret) {
+		pr_err("failed to init clocksource: %d\n", ret);
+		goto err_clocksource_init;
+	}
+
+	sched_clock_base = base;
+	sched_clock_register(mps2_sched_read, 32, rate);
+
+	return 0;
+
+err_clocksource_init:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static void __init mps2_timer_init(struct device_node *np)
+{
+	static int has_clocksource, has_clockevent;
+	int ret;
+
+	if (!has_clocksource) {
+		ret = mps2_clocksource_init(np);
+		if (!ret) {
+			has_clocksource = 1;
+			return;
+		}
+	}
+
+	if (!has_clockevent) {
+		ret = mps2_clockevent_init(np);
+		if (!ret) {
+			has_clockevent = 1;
+			return;
+		}
+	}
+}
+
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

MPS2 platform has simple 32 bits general purpose countdown timers.

The driver uses the first detected timer as a clocksource and the rest
of the timers as a clockevent

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/clocksource/Kconfig      |    5 +
 drivers/clocksource/Makefile     |    1 +
 drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/clocksource/mps2-timer.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 2eb5f0e..8bca09c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -137,6 +137,11 @@ config CLKSRC_STM32
 	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
 	select CLKSRC_MMIO
 
+config CLKSRC_MPS2
+	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
+	depends on OF && ARM
+	select CLKSRC_MMIO
+
 config ARM_ARCH_TIMER
 	bool
 	select CLKSRC_OF if OF
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 56bd16e..7033b9c 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
 obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
 obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
 obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
+obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
 obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
 obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
 obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
new file mode 100644
index 0000000..3e19af5
--- /dev/null
+++ b/drivers/clocksource/mps2-timer.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#define TIMER_CTRL		0x0
+#define TIMER_CTRL_ENABLE	BIT(0)
+#define TIMER_CTRL_IE		BIT(3)
+
+#define TIMER_VALUE		0x4
+#define TIMER_RELOAD		0x8
+#define TIMER_INT		0xc
+
+struct clockevent_mps2 {
+	void __iomem *reg;
+	u32 clock_count_per_tick;
+	struct clock_event_device clkevt;
+};
+
+static void __iomem *sched_clock_base;
+
+static u64 notrace mps2_sched_read(void)
+{
+        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
+}
+
+static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
+{
+	return container_of(c, struct clockevent_mps2, clkevt);
+}
+
+static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
+{
+	writel(val, to_mps2_clkevt(c)->reg + offset);
+}
+
+static int mps2_timer_shutdown(struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(0, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(next, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_periodic(struct clock_event_device *ce)
+{
+	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
+
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
+{
+	struct clockevent_mps2 *ce = dev_id;
+	u32 status = readl(ce->reg + TIMER_INT);
+
+	if (!status) {
+		pr_warn("spuirous interrupt\n");
+		return IRQ_NONE;
+	}
+
+	writel(1, ce->reg + TIMER_INT);
+
+	ce->clkevt.event_handler(&ce->clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int __init mps2_clockevent_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	struct clockevent_mps2 *ce;
+	u32 rate;
+	int irq, ret;
+	const char *name = "mps2-clkevt";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clockevent: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clockevent: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clockevent: %d\n", ret);
+		goto err_iomap;
+	}
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq) {
+		ret = -ENOENT;
+		pr_err("failed to get irq for clockevent: %d\n", ret);
+		goto err_get_irq;
+	}
+
+	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
+	if (!ce) {
+		ret = -ENOMEM;
+		pr_err("failed to allocate clockevent: %d\n", ret);
+		goto err_ce_alloc;
+	}
+
+	ce->reg = base;
+	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
+	ce->clkevt.irq = irq;
+	ce->clkevt.name = name;
+	ce->clkevt.rating = 200;
+	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	ce->clkevt.cpumask = cpu_possible_mask;
+	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
+	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
+	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
+	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
+	if (ret) {
+		pr_err("failed to request irq: %d\n", ret);
+		goto err_ia_alloc;
+	}
+
+	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
+
+	return 0;
+
+err_ia_alloc:
+	kfree(ce);
+err_ce_alloc:
+err_get_irq:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static int __init mps2_clocksource_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	u32 rate;
+	int ret;
+	const char *name = "mps2-clksrc";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clocksource: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clocksource: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clocksource: %d\n", ret);
+		goto err_iomap;
+	}
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	/* ... and set it up as free-running clocksource */
+	writel(0xffffffff, base + TIMER_VALUE);
+	writel(0xffffffff, base + TIMER_RELOAD);
+
+	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
+
+	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
+				    rate, 200, 32,
+				    clocksource_mmio_readl_down);
+	if (ret) {
+		pr_err("failed to init clocksource: %d\n", ret);
+		goto err_clocksource_init;
+	}
+
+	sched_clock_base = base;
+	sched_clock_register(mps2_sched_read, 32, rate);
+
+	return 0;
+
+err_clocksource_init:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static void __init mps2_timer_init(struct device_node *np)
+{
+	static int has_clocksource, has_clockevent;
+	int ret;
+
+	if (!has_clocksource) {
+		ret = mps2_clocksource_init(np);
+		if (!ret) {
+			has_clocksource = 1;
+			return;
+		}
+	}
+
+	if (!has_clockevent) {
+		ret = mps2_clockevent_init(np);
+		if (!ret) {
+			has_clockevent = 1;
+			return;
+		}
+	}
+}
+
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

MPS2 platform has simple 32 bits general purpose countdown timers.

The driver uses the first detected timer as a clocksource and the rest
of the timers as a clockevent

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/clocksource/Kconfig      |    5 +
 drivers/clocksource/Makefile     |    1 +
 drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/clocksource/mps2-timer.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 2eb5f0e..8bca09c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -137,6 +137,11 @@ config CLKSRC_STM32
 	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
 	select CLKSRC_MMIO
 
+config CLKSRC_MPS2
+	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
+	depends on OF && ARM
+	select CLKSRC_MMIO
+
 config ARM_ARCH_TIMER
 	bool
 	select CLKSRC_OF if OF
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 56bd16e..7033b9c 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
 obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
 obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
 obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
+obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
 obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
 obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
 obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
new file mode 100644
index 0000000..3e19af5
--- /dev/null
+++ b/drivers/clocksource/mps2-timer.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#define TIMER_CTRL		0x0
+#define TIMER_CTRL_ENABLE	BIT(0)
+#define TIMER_CTRL_IE		BIT(3)
+
+#define TIMER_VALUE		0x4
+#define TIMER_RELOAD		0x8
+#define TIMER_INT		0xc
+
+struct clockevent_mps2 {
+	void __iomem *reg;
+	u32 clock_count_per_tick;
+	struct clock_event_device clkevt;
+};
+
+static void __iomem *sched_clock_base;
+
+static u64 notrace mps2_sched_read(void)
+{
+        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
+}
+
+static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
+{
+	return container_of(c, struct clockevent_mps2, clkevt);
+}
+
+static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
+{
+	writel(val, to_mps2_clkevt(c)->reg + offset);
+}
+
+static int mps2_timer_shutdown(struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(0, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
+{
+	clockevent_mps2_writel(next, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static int mps2_timer_set_periodic(struct clock_event_device *ce)
+{
+	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
+
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
+	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
+	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
+{
+	struct clockevent_mps2 *ce = dev_id;
+	u32 status = readl(ce->reg + TIMER_INT);
+
+	if (!status) {
+		pr_warn("spuirous interrupt\n");
+		return IRQ_NONE;
+	}
+
+	writel(1, ce->reg + TIMER_INT);
+
+	ce->clkevt.event_handler(&ce->clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int __init mps2_clockevent_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	struct clockevent_mps2 *ce;
+	u32 rate;
+	int irq, ret;
+	const char *name = "mps2-clkevt";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clockevent: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clockevent: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clockevent: %d\n", ret);
+		goto err_iomap;
+	}
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq) {
+		ret = -ENOENT;
+		pr_err("failed to get irq for clockevent: %d\n", ret);
+		goto err_get_irq;
+	}
+
+	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
+	if (!ce) {
+		ret = -ENOMEM;
+		pr_err("failed to allocate clockevent: %d\n", ret);
+		goto err_ce_alloc;
+	}
+
+	ce->reg = base;
+	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
+	ce->clkevt.irq = irq;
+	ce->clkevt.name = name;
+	ce->clkevt.rating = 200;
+	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	ce->clkevt.cpumask = cpu_possible_mask;
+	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
+	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
+	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
+	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
+	if (ret) {
+		pr_err("failed to request irq: %d\n", ret);
+		goto err_ia_alloc;
+	}
+
+	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
+
+	return 0;
+
+err_ia_alloc:
+	kfree(ce);
+err_ce_alloc:
+err_get_irq:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static int __init mps2_clocksource_init(struct device_node *np)
+{
+	void __iomem *base;
+	struct clk *clk;
+	u32 rate;
+	int ret;
+	const char *name = "mps2-clksrc";
+
+	ret = of_property_read_u32(np, "clock-frequency", &rate);
+	if (ret) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			pr_err("failed to get clock for clocksource: %d\n", ret);
+			goto err_clk_get;
+		}
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			pr_err("failed to enable clock for clocksource: %d\n", ret);
+			clk_put(clk);
+			goto err_clk_enable;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		ret = -EADDRNOTAVAIL;
+		pr_err("failed to map register for clocksource: %d\n", ret);
+		goto err_iomap;
+	}
+
+	/* Ensure timer is disabled */
+	writel(0, base + TIMER_CTRL);
+
+	/* ... and set it up as free-running clocksource */
+	writel(0xffffffff, base + TIMER_VALUE);
+	writel(0xffffffff, base + TIMER_RELOAD);
+
+	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
+
+	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
+				    rate, 200, 32,
+				    clocksource_mmio_readl_down);
+	if (ret) {
+		pr_err("failed to init clocksource: %d\n", ret);
+		goto err_clocksource_init;
+	}
+
+	sched_clock_base = base;
+	sched_clock_register(mps2_sched_read, 32, rate);
+
+	return 0;
+
+err_clocksource_init:
+	iounmap(base);
+err_iomap:
+	clk_disable_unprepare(clk);
+err_clk_enable:
+	clk_put(clk);
+err_clk_get:
+	return ret;
+}
+
+static void __init mps2_timer_init(struct device_node *np)
+{
+	static int has_clocksource, has_clockevent;
+	int ret;
+
+	if (!has_clocksource) {
+		ret = mps2_clocksource_init(np);
+		if (!ret) {
+			has_clocksource = 1;
+			return;
+		}
+	}
+
+	if (!has_clockevent) {
+		ret = mps2_clockevent_init(np);
+		if (!ret) {
+			has_clockevent = 1;
+			return;
+		}
+	}
+}
+
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 03/10] dt-bindings: document the MPS2 UART bindings
  2015-12-02  9:33 ` Vladimir Murzin
  (?)
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

This adds documentation of device tree bindings for the
UART found on ARM MPS2 platform

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
Acked-by: Rob Herring <robh@kernel.org
---
 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
new file mode 100644
index 0000000..b3f7cb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
@@ -0,0 +1,22 @@
+ARM MPS2 UART
+
+Required properties:
+- compatible	: Should be "arm,mps2-uart"
+- reg		: Address and length of the register set
+- interrupts	: Reference to the UART RX, TX and overrun interrupts
+
+Required clocking property:
+- clocks	  : The input clock of the UART
+
+
+Examples:
+
+uart0: serial@40004000 {
+	compatible = "arm,mps2-uart";
+	reg = <0x40004000 0x1000>;
+	interrupts = <0 1 12>;
+	clocks = <&sysclk>;
+};
+
+
+
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 03/10] dt-bindings: document the MPS2 UART bindings
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

This adds documentation of device tree bindings for the
UART found on ARM MPS2 platform

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
Acked-by: Rob Herring <robh@kernel.org
---
 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
new file mode 100644
index 0000000..b3f7cb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
@@ -0,0 +1,22 @@
+ARM MPS2 UART
+
+Required properties:
+- compatible	: Should be "arm,mps2-uart"
+- reg		: Address and length of the register set
+- interrupts	: Reference to the UART RX, TX and overrun interrupts
+
+Required clocking property:
+- clocks	  : The input clock of the UART
+
+
+Examples:
+
+uart0: serial@40004000 {
+	compatible = "arm,mps2-uart";
+	reg = <0x40004000 0x1000>;
+	interrupts = <0 1 12>;
+	clocks = <&sysclk>;
+};
+
+
+
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 03/10] dt-bindings: document the MPS2 UART bindings
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

This adds documentation of device tree bindings for the
UART found on ARM MPS2 platform

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
Acked-by: Rob Herring <robh at kernel.org
---
 .../devicetree/bindings/serial/arm,mps2-uart.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/arm,mps2-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
new file mode 100644
index 0000000..b3f7cb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/arm,mps2-uart.txt
@@ -0,0 +1,22 @@
+ARM MPS2 UART
+
+Required properties:
+- compatible	: Should be "arm,mps2-uart"
+- reg		: Address and length of the register set
+- interrupts	: Reference to the UART RX, TX and overrun interrupts
+
+Required clocking property:
+- clocks	  : The input clock of the UART
+
+
+Examples:
+
+uart0: serial@40004000 {
+	compatible = "arm,mps2-uart";
+	reg = <0x40004000 0x1000>;
+	interrupts = <0 1 12>;
+	clocks = <&sysclk>;
+};
+
+
+
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
  2015-12-02  9:33 ` Vladimir Murzin
  (?)
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

This driver adds support to the UART controller found on ARM MPS2
platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/tty/serial/Kconfig       |   12 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 612 insertions(+)
 create mode 100644 drivers/tty/serial/mps2-uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f38beb2..e98bfea 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
 	  This driver support the USART and UART ports on
 	  Energy Micro's efm32 SoCs.
 
+config SERIAL_MPS2_UART_CONSOLE
+	bool "MPS2 UART console support"
+	depends on SERIAL_MPS2_UART
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_MPS2_UART
+	bool "MPS2 UART port"
+	depends on ARM || COMPILE_TEST
+	select SERIAL_CORE
+	help
+	  This driver support the UART ports on ARM MPS2.
+
 config SERIAL_EFM32_UART_CONSOLE
 	bool "EFM32 UART/USART console support"
 	depends on SERIAL_EFM32_UART=y
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 5ab4111..7f589f5 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
 obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
+obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
new file mode 100644
index 0000000..09bac16
--- /dev/null
+++ b/drivers/tty/serial/mps2-uart.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO: support for SysRq
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define SERIAL_NAME "ttyMPS"
+#define DRIVER_NAME "mps2-uart"
+#define MAKE_NAME(x) (DRIVER_NAME # x)
+
+#define UARTn_DATA				0x0
+
+#define UARTn_STATE				0x4
+#define UARTn_STATE_TX_FULL			BIT(0)
+#define UARTn_STATE_RX_FULL			BIT(1)
+#define UARTn_STATE_TX_OVERRUN			BIT(2)
+#define UARTn_STATE_RX_OVERRUN			BIT(3)
+
+#define UARTn_CTRL				0x8
+#define UARTn_CTRL_TX_ENABLE			BIT(0)
+#define UARTn_CTRL_RX_ENABLE			BIT(1)
+#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
+#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
+#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
+#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
+
+#define UARTn_INT				0xc
+#define UARTn_INT_TX				BIT(0)
+#define UARTn_INT_RX				BIT(1)
+#define UARTn_INT_TX_OVERRUN			BIT(2)
+#define UARTn_INT_RX_OVERRUN			BIT(3)
+
+#define UARTn_BAUDDIV				0x10
+#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
+
+#define MPS2_MAX_PORTS				3
+
+struct mps2_uart_port {
+	struct uart_port port;
+	struct clk *clk;
+	unsigned int tx_irq;
+	unsigned int rx_irq;
+};
+
+static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
+{
+	return container_of(port, struct mps2_uart_port, port);
+}
+
+static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writeb(val, mps_port->port.membase + off);
+}
+
+static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	return readb(mps_port->port.membase + off);
+}
+
+static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writel_relaxed(val, mps_port->port.membase + off);
+}
+
+static unsigned int mps2_uart_tx_empty(struct uart_port *port)
+{
+	u8 status = mps2_uart_read8(port, UARTn_STATE);
+
+	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
+}
+
+static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void mps2_uart_stop_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
+		if (port->x_char) {
+			mps2_uart_write8(port, port->x_char, UARTn_DATA);
+			port->x_char = 0;
+			port->icount.tx++;
+			continue;
+		}
+
+		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+			break;
+
+		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		mps2_uart_stop_tx(port);
+}
+
+static void mps2_uart_start_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control |= (UARTn_CTRL_TX_ENABLE		|
+		    UARTn_CTRL_TX_INT_ENABLE		|
+		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+	mps2_uart_tx_chars(port);
+}
+
+static void mps2_uart_stop_rx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
+{
+}
+
+static void mps2_uart_rx_chars(struct uart_port *port)
+{
+	struct tty_port *tport = &port->state->port;
+
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
+		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
+
+		port->icount.rx++;
+		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
+	}
+
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
+}
+
+static irqreturn_t mps2_uart_rxirq(int irq, void *data)
+{
+
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_RX)))
+		return IRQ_NONE;
+
+	spin_lock(&port->lock);
+
+	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
+	mps2_uart_rx_chars(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_txirq(int irq, void *data)
+{
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_TX)))
+		return IRQ_NONE;
+
+	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
+	mps2_uart_tx_chars(port);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
+{
+	irqreturn_t handled = IRQ_NONE;
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	spin_lock(&port->lock);
+
+	if (irqflag & UARTn_INT_RX_OVERRUN) {
+		struct tty_port *tport = &port->state->port;
+
+		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
+		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+		port->icount.overrun++;
+		handled = IRQ_HANDLED;
+	}
+
+	/* XXX: this shouldn't happen? */
+	if (irqflag & UARTn_INT_TX_OVERRUN) {
+		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
+		handled = IRQ_HANDLED;
+	}
+
+	spin_unlock(&port->lock);
+
+	return handled;
+}
+
+static int mps2_uart_startup(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+	int ret;
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
+			  MAKE_NAME(-rx), mps_port);
+	if (ret) {
+		pr_info("failed to register rxirq (%d)\n", ret);
+		goto err_no_rxirq;
+	}
+
+	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
+			  MAKE_NAME(-tx), mps_port);
+	if (ret) {
+		pr_info("failed to register txirq (%d)\n", ret);
+		goto err_no_txirq;
+	}
+
+	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
+			  MAKE_NAME(-overrun), mps_port);
+
+	if (ret)
+		pr_info("failed to register oerrirq (%d)\n", ret);
+	else {
+		control |= UARTn_CTRL_RX_ENABLE			|
+			   UARTn_CTRL_TX_ENABLE			|
+			   UARTn_CTRL_RX_INT_ENABLE		|
+			   UARTn_CTRL_TX_INT_ENABLE		|
+			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
+
+		mps2_uart_write8(port, control, UARTn_CTRL);
+
+		return 0;
+	}
+
+	free_irq(mps_port->tx_irq, mps_port);
+err_no_txirq:
+	free_irq(mps_port->rx_irq, mps_port);
+err_no_rxirq:
+	return ret;
+}
+
+static void mps2_uart_shutdown(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	free_irq(mps_port->rx_irq, mps_port);
+	free_irq(mps_port->tx_irq, mps_port);
+	free_irq(port->irq, mps_port);
+}
+
+static void mps2_uart_set_termios(struct uart_port *port,
+	struct ktermios *new, struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, bauddiv;
+
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+	new->c_cflag &= ~CSIZE;
+	new->c_cflag |= CS8;
+	new->c_cflag &= ~PARENB;
+	new->c_cflag &= ~CSTOPB;
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uart_update_timeout(port, new->c_cflag, baud);
+	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *mps2_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
+}
+
+static void mps2_uart_release_port(struct uart_port *port)
+{
+}
+
+static int mps2_uart_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void mps2_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
+		port->type = PORT_MPS2UART;
+}
+
+static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
+{
+	return -EINVAL;
+}
+
+static const struct uart_ops mps2_uart_pops = {
+	.tx_empty = mps2_uart_tx_empty,
+	.set_mctrl = mps2_uart_set_mctrl,
+	.get_mctrl = mps2_uart_get_mctrl,
+	.stop_tx = mps2_uart_stop_tx,
+	.start_tx = mps2_uart_start_tx,
+	.stop_rx = mps2_uart_stop_rx,
+	.enable_ms = mps2_uart_enable_ms,
+	.break_ctl = mps2_uart_break_ctl,
+	.startup = mps2_uart_startup,
+	.shutdown = mps2_uart_shutdown,
+	.set_termios = mps2_uart_set_termios,
+	.type = mps2_uart_type,
+	.release_port = mps2_uart_release_port,
+	.request_port = mps2_uart_request_port,
+	.config_port = mps2_uart_config_port,
+	.verify_port = mps2_uart_verify_port,
+};
+
+static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
+
+#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
+static void mps2_uart_console_putchar(struct uart_port *port, int ch)
+{
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
+		cpu_relax();
+
+	mps2_uart_write8(port, ch, UARTn_DATA);
+}
+
+static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
+{
+	struct uart_port *port = &mps2_uart_ports[co->index].port;
+
+	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
+}
+
+static int mps2_uart_console_setup(struct console *co, char *options)
+{
+	struct mps2_uart_port *mps_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
+		return -ENODEV;
+
+	mps_port = &mps2_uart_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mps2_uart_driver;
+
+static struct console mps2_uart_console = {
+	.name = SERIAL_NAME,
+	.device = uart_console_device,
+	.write = mps2_uart_console_write,
+	.setup = mps2_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &mps2_uart_driver,
+};
+
+#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
+
+#else
+#define MPS2_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver mps2_uart_driver = {
+	.driver_name = DRIVER_NAME,
+	.dev_name = SERIAL_NAME,
+	.nr = MPS2_MAX_PORTS,
+	.cons = MPS2_SERIAL_CONSOLE,
+};
+
+static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int id;
+
+	if (!np)
+		return NULL;
+
+	id = of_alias_get_id(np, "serial");
+	if (id < 0)
+		id = 0;
+
+	if (WARN_ON(id >= MPS2_MAX_PORTS))
+		return NULL;
+
+	mps2_uart_ports[id].port.line = id;
+	return &mps2_uart_ports[id];
+}
+
+static int mps2_init_port(struct mps2_uart_port *mps_port,
+			struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mps_port->port.membase))
+		return PTR_ERR(mps_port->port.membase);
+
+	mps_port->port.mapbase = res->start;
+	mps_port->port.mapsize = resource_size(res);
+
+	mps_port->rx_irq = platform_get_irq(pdev, 0);
+	mps_port->tx_irq = platform_get_irq(pdev, 1);
+	mps_port->port.irq = platform_get_irq(pdev, 2);
+
+	mps_port->port.iotype = UPIO_MEM;
+	mps_port->port.flags = UPF_BOOT_AUTOCONF;
+	mps_port->port.fifosize = 1;
+	mps_port->port.ops = &mps2_uart_pops;
+	mps_port->port.dev = &pdev->dev;
+
+	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mps_port->clk))
+		return PTR_ERR(mps_port->clk);
+
+	ret = clk_prepare_enable(mps_port->clk);
+	if (ret)
+		return ret;
+
+	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
+	if (!mps_port->port.uartclk)
+		ret = -EINVAL;
+
+	clk_disable_unprepare(mps_port->clk);
+
+	return ret;
+}
+
+static int mps2_serial_probe(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port;
+	int ret;
+
+	mps_port = mps2_of_get_port(pdev);
+	if (!mps_port)
+		return -ENODEV;
+
+	ret = mps2_init_port(mps_port, pdev);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, mps_port);
+
+	return 0;
+}
+
+static int mps2_serial_remove(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
+
+	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mps2_match[] = {
+	{ .compatible = "arm,mps2-uart", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mps2_match);
+#endif
+
+static struct platform_driver mps2_serial_driver = {
+	.probe = mps2_serial_probe,
+	.remove = mps2_serial_remove,
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = of_match_ptr(mps2_match),
+	},
+};
+
+static int __init mps2_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&mps2_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&mps2_serial_driver);
+	if (ret)
+		uart_unregister_driver(&mps2_uart_driver);
+
+	pr_info("MPS2 UART driver initialized\n");
+
+	return ret;
+}
+module_init(mps2_uart_init);
+
+static void __exit mps2_uart_exit(void)
+{
+	platform_driver_unregister(&mps2_serial_driver);
+	uart_unregister_driver(&mps2_uart_driver);
+}
+module_exit(mps2_uart_exit);
+
+MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
+MODULE_DESCRIPTION("MPS2 UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148..f097fe9 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -261,4 +261,7 @@
 /* STM32 USART */
 #define PORT_STM32	113
 
+/* MPS2 UART */
+#define PORT_MPS2UART	114
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

This driver adds support to the UART controller found on ARM MPS2
platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/tty/serial/Kconfig       |   12 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 612 insertions(+)
 create mode 100644 drivers/tty/serial/mps2-uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f38beb2..e98bfea 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
 	  This driver support the USART and UART ports on
 	  Energy Micro's efm32 SoCs.
 
+config SERIAL_MPS2_UART_CONSOLE
+	bool "MPS2 UART console support"
+	depends on SERIAL_MPS2_UART
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_MPS2_UART
+	bool "MPS2 UART port"
+	depends on ARM || COMPILE_TEST
+	select SERIAL_CORE
+	help
+	  This driver support the UART ports on ARM MPS2.
+
 config SERIAL_EFM32_UART_CONSOLE
 	bool "EFM32 UART/USART console support"
 	depends on SERIAL_EFM32_UART=y
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 5ab4111..7f589f5 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
 obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
+obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
new file mode 100644
index 0000000..09bac16
--- /dev/null
+++ b/drivers/tty/serial/mps2-uart.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO: support for SysRq
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define SERIAL_NAME "ttyMPS"
+#define DRIVER_NAME "mps2-uart"
+#define MAKE_NAME(x) (DRIVER_NAME # x)
+
+#define UARTn_DATA				0x0
+
+#define UARTn_STATE				0x4
+#define UARTn_STATE_TX_FULL			BIT(0)
+#define UARTn_STATE_RX_FULL			BIT(1)
+#define UARTn_STATE_TX_OVERRUN			BIT(2)
+#define UARTn_STATE_RX_OVERRUN			BIT(3)
+
+#define UARTn_CTRL				0x8
+#define UARTn_CTRL_TX_ENABLE			BIT(0)
+#define UARTn_CTRL_RX_ENABLE			BIT(1)
+#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
+#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
+#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
+#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
+
+#define UARTn_INT				0xc
+#define UARTn_INT_TX				BIT(0)
+#define UARTn_INT_RX				BIT(1)
+#define UARTn_INT_TX_OVERRUN			BIT(2)
+#define UARTn_INT_RX_OVERRUN			BIT(3)
+
+#define UARTn_BAUDDIV				0x10
+#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
+
+#define MPS2_MAX_PORTS				3
+
+struct mps2_uart_port {
+	struct uart_port port;
+	struct clk *clk;
+	unsigned int tx_irq;
+	unsigned int rx_irq;
+};
+
+static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
+{
+	return container_of(port, struct mps2_uart_port, port);
+}
+
+static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writeb(val, mps_port->port.membase + off);
+}
+
+static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	return readb(mps_port->port.membase + off);
+}
+
+static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writel_relaxed(val, mps_port->port.membase + off);
+}
+
+static unsigned int mps2_uart_tx_empty(struct uart_port *port)
+{
+	u8 status = mps2_uart_read8(port, UARTn_STATE);
+
+	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
+}
+
+static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void mps2_uart_stop_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
+		if (port->x_char) {
+			mps2_uart_write8(port, port->x_char, UARTn_DATA);
+			port->x_char = 0;
+			port->icount.tx++;
+			continue;
+		}
+
+		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+			break;
+
+		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		mps2_uart_stop_tx(port);
+}
+
+static void mps2_uart_start_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control |= (UARTn_CTRL_TX_ENABLE		|
+		    UARTn_CTRL_TX_INT_ENABLE		|
+		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+	mps2_uart_tx_chars(port);
+}
+
+static void mps2_uart_stop_rx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
+{
+}
+
+static void mps2_uart_rx_chars(struct uart_port *port)
+{
+	struct tty_port *tport = &port->state->port;
+
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
+		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
+
+		port->icount.rx++;
+		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
+	}
+
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
+}
+
+static irqreturn_t mps2_uart_rxirq(int irq, void *data)
+{
+
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_RX)))
+		return IRQ_NONE;
+
+	spin_lock(&port->lock);
+
+	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
+	mps2_uart_rx_chars(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_txirq(int irq, void *data)
+{
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_TX)))
+		return IRQ_NONE;
+
+	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
+	mps2_uart_tx_chars(port);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
+{
+	irqreturn_t handled = IRQ_NONE;
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	spin_lock(&port->lock);
+
+	if (irqflag & UARTn_INT_RX_OVERRUN) {
+		struct tty_port *tport = &port->state->port;
+
+		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
+		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+		port->icount.overrun++;
+		handled = IRQ_HANDLED;
+	}
+
+	/* XXX: this shouldn't happen? */
+	if (irqflag & UARTn_INT_TX_OVERRUN) {
+		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
+		handled = IRQ_HANDLED;
+	}
+
+	spin_unlock(&port->lock);
+
+	return handled;
+}
+
+static int mps2_uart_startup(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+	int ret;
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
+			  MAKE_NAME(-rx), mps_port);
+	if (ret) {
+		pr_info("failed to register rxirq (%d)\n", ret);
+		goto err_no_rxirq;
+	}
+
+	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
+			  MAKE_NAME(-tx), mps_port);
+	if (ret) {
+		pr_info("failed to register txirq (%d)\n", ret);
+		goto err_no_txirq;
+	}
+
+	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
+			  MAKE_NAME(-overrun), mps_port);
+
+	if (ret)
+		pr_info("failed to register oerrirq (%d)\n", ret);
+	else {
+		control |= UARTn_CTRL_RX_ENABLE			|
+			   UARTn_CTRL_TX_ENABLE			|
+			   UARTn_CTRL_RX_INT_ENABLE		|
+			   UARTn_CTRL_TX_INT_ENABLE		|
+			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
+
+		mps2_uart_write8(port, control, UARTn_CTRL);
+
+		return 0;
+	}
+
+	free_irq(mps_port->tx_irq, mps_port);
+err_no_txirq:
+	free_irq(mps_port->rx_irq, mps_port);
+err_no_rxirq:
+	return ret;
+}
+
+static void mps2_uart_shutdown(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	free_irq(mps_port->rx_irq, mps_port);
+	free_irq(mps_port->tx_irq, mps_port);
+	free_irq(port->irq, mps_port);
+}
+
+static void mps2_uart_set_termios(struct uart_port *port,
+	struct ktermios *new, struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, bauddiv;
+
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+	new->c_cflag &= ~CSIZE;
+	new->c_cflag |= CS8;
+	new->c_cflag &= ~PARENB;
+	new->c_cflag &= ~CSTOPB;
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uart_update_timeout(port, new->c_cflag, baud);
+	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *mps2_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
+}
+
+static void mps2_uart_release_port(struct uart_port *port)
+{
+}
+
+static int mps2_uart_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void mps2_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
+		port->type = PORT_MPS2UART;
+}
+
+static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
+{
+	return -EINVAL;
+}
+
+static const struct uart_ops mps2_uart_pops = {
+	.tx_empty = mps2_uart_tx_empty,
+	.set_mctrl = mps2_uart_set_mctrl,
+	.get_mctrl = mps2_uart_get_mctrl,
+	.stop_tx = mps2_uart_stop_tx,
+	.start_tx = mps2_uart_start_tx,
+	.stop_rx = mps2_uart_stop_rx,
+	.enable_ms = mps2_uart_enable_ms,
+	.break_ctl = mps2_uart_break_ctl,
+	.startup = mps2_uart_startup,
+	.shutdown = mps2_uart_shutdown,
+	.set_termios = mps2_uart_set_termios,
+	.type = mps2_uart_type,
+	.release_port = mps2_uart_release_port,
+	.request_port = mps2_uart_request_port,
+	.config_port = mps2_uart_config_port,
+	.verify_port = mps2_uart_verify_port,
+};
+
+static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
+
+#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
+static void mps2_uart_console_putchar(struct uart_port *port, int ch)
+{
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
+		cpu_relax();
+
+	mps2_uart_write8(port, ch, UARTn_DATA);
+}
+
+static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
+{
+	struct uart_port *port = &mps2_uart_ports[co->index].port;
+
+	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
+}
+
+static int mps2_uart_console_setup(struct console *co, char *options)
+{
+	struct mps2_uart_port *mps_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
+		return -ENODEV;
+
+	mps_port = &mps2_uart_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mps2_uart_driver;
+
+static struct console mps2_uart_console = {
+	.name = SERIAL_NAME,
+	.device = uart_console_device,
+	.write = mps2_uart_console_write,
+	.setup = mps2_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &mps2_uart_driver,
+};
+
+#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
+
+#else
+#define MPS2_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver mps2_uart_driver = {
+	.driver_name = DRIVER_NAME,
+	.dev_name = SERIAL_NAME,
+	.nr = MPS2_MAX_PORTS,
+	.cons = MPS2_SERIAL_CONSOLE,
+};
+
+static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int id;
+
+	if (!np)
+		return NULL;
+
+	id = of_alias_get_id(np, "serial");
+	if (id < 0)
+		id = 0;
+
+	if (WARN_ON(id >= MPS2_MAX_PORTS))
+		return NULL;
+
+	mps2_uart_ports[id].port.line = id;
+	return &mps2_uart_ports[id];
+}
+
+static int mps2_init_port(struct mps2_uart_port *mps_port,
+			struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mps_port->port.membase))
+		return PTR_ERR(mps_port->port.membase);
+
+	mps_port->port.mapbase = res->start;
+	mps_port->port.mapsize = resource_size(res);
+
+	mps_port->rx_irq = platform_get_irq(pdev, 0);
+	mps_port->tx_irq = platform_get_irq(pdev, 1);
+	mps_port->port.irq = platform_get_irq(pdev, 2);
+
+	mps_port->port.iotype = UPIO_MEM;
+	mps_port->port.flags = UPF_BOOT_AUTOCONF;
+	mps_port->port.fifosize = 1;
+	mps_port->port.ops = &mps2_uart_pops;
+	mps_port->port.dev = &pdev->dev;
+
+	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mps_port->clk))
+		return PTR_ERR(mps_port->clk);
+
+	ret = clk_prepare_enable(mps_port->clk);
+	if (ret)
+		return ret;
+
+	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
+	if (!mps_port->port.uartclk)
+		ret = -EINVAL;
+
+	clk_disable_unprepare(mps_port->clk);
+
+	return ret;
+}
+
+static int mps2_serial_probe(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port;
+	int ret;
+
+	mps_port = mps2_of_get_port(pdev);
+	if (!mps_port)
+		return -ENODEV;
+
+	ret = mps2_init_port(mps_port, pdev);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, mps_port);
+
+	return 0;
+}
+
+static int mps2_serial_remove(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
+
+	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mps2_match[] = {
+	{ .compatible = "arm,mps2-uart", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mps2_match);
+#endif
+
+static struct platform_driver mps2_serial_driver = {
+	.probe = mps2_serial_probe,
+	.remove = mps2_serial_remove,
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = of_match_ptr(mps2_match),
+	},
+};
+
+static int __init mps2_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&mps2_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&mps2_serial_driver);
+	if (ret)
+		uart_unregister_driver(&mps2_uart_driver);
+
+	pr_info("MPS2 UART driver initialized\n");
+
+	return ret;
+}
+module_init(mps2_uart_init);
+
+static void __exit mps2_uart_exit(void)
+{
+	platform_driver_unregister(&mps2_serial_driver);
+	uart_unregister_driver(&mps2_uart_driver);
+}
+module_exit(mps2_uart_exit);
+
+MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
+MODULE_DESCRIPTION("MPS2 UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148..f097fe9 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -261,4 +261,7 @@
 /* STM32 USART */
 #define PORT_STM32	113
 
+/* MPS2 UART */
+#define PORT_MPS2UART	114
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

This driver adds support to the UART controller found on ARM MPS2
platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/tty/serial/Kconfig       |   12 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 612 insertions(+)
 create mode 100644 drivers/tty/serial/mps2-uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f38beb2..e98bfea 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
 	  This driver support the USART and UART ports on
 	  Energy Micro's efm32 SoCs.
 
+config SERIAL_MPS2_UART_CONSOLE
+	bool "MPS2 UART console support"
+	depends on SERIAL_MPS2_UART
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_MPS2_UART
+	bool "MPS2 UART port"
+	depends on ARM || COMPILE_TEST
+	select SERIAL_CORE
+	help
+	  This driver support the UART ports on ARM MPS2.
+
 config SERIAL_EFM32_UART_CONSOLE
 	bool "EFM32 UART/USART console support"
 	depends on SERIAL_EFM32_UART=y
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 5ab4111..7f589f5 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
 obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
+obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
new file mode 100644
index 0000000..09bac16
--- /dev/null
+++ b/drivers/tty/serial/mps2-uart.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO: support for SysRq
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define SERIAL_NAME "ttyMPS"
+#define DRIVER_NAME "mps2-uart"
+#define MAKE_NAME(x) (DRIVER_NAME # x)
+
+#define UARTn_DATA				0x0
+
+#define UARTn_STATE				0x4
+#define UARTn_STATE_TX_FULL			BIT(0)
+#define UARTn_STATE_RX_FULL			BIT(1)
+#define UARTn_STATE_TX_OVERRUN			BIT(2)
+#define UARTn_STATE_RX_OVERRUN			BIT(3)
+
+#define UARTn_CTRL				0x8
+#define UARTn_CTRL_TX_ENABLE			BIT(0)
+#define UARTn_CTRL_RX_ENABLE			BIT(1)
+#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
+#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
+#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
+#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
+
+#define UARTn_INT				0xc
+#define UARTn_INT_TX				BIT(0)
+#define UARTn_INT_RX				BIT(1)
+#define UARTn_INT_TX_OVERRUN			BIT(2)
+#define UARTn_INT_RX_OVERRUN			BIT(3)
+
+#define UARTn_BAUDDIV				0x10
+#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
+
+#define MPS2_MAX_PORTS				3
+
+struct mps2_uart_port {
+	struct uart_port port;
+	struct clk *clk;
+	unsigned int tx_irq;
+	unsigned int rx_irq;
+};
+
+static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
+{
+	return container_of(port, struct mps2_uart_port, port);
+}
+
+static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writeb(val, mps_port->port.membase + off);
+}
+
+static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	return readb(mps_port->port.membase + off);
+}
+
+static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+
+	writel_relaxed(val, mps_port->port.membase + off);
+}
+
+static unsigned int mps2_uart_tx_empty(struct uart_port *port)
+{
+	u8 status = mps2_uart_read8(port, UARTn_STATE);
+
+	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
+}
+
+static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void mps2_uart_stop_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
+		if (port->x_char) {
+			mps2_uart_write8(port, port->x_char, UARTn_DATA);
+			port->x_char = 0;
+			port->icount.tx++;
+			continue;
+		}
+
+		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+			break;
+
+		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		mps2_uart_stop_tx(port);
+}
+
+static void mps2_uart_start_tx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control |= (UARTn_CTRL_TX_ENABLE		|
+		    UARTn_CTRL_TX_INT_ENABLE		|
+		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+	mps2_uart_tx_chars(port);
+}
+
+static void mps2_uart_stop_rx(struct uart_port *port)
+{
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+}
+
+static void mps2_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
+{
+}
+
+static void mps2_uart_rx_chars(struct uart_port *port)
+{
+	struct tty_port *tport = &port->state->port;
+
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
+		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
+
+		port->icount.rx++;
+		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
+	}
+
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
+}
+
+static irqreturn_t mps2_uart_rxirq(int irq, void *data)
+{
+
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_RX)))
+		return IRQ_NONE;
+
+	spin_lock(&port->lock);
+
+	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
+	mps2_uart_rx_chars(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_txirq(int irq, void *data)
+{
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	if (unlikely(!(irqflag & UARTn_INT_TX)))
+		return IRQ_NONE;
+
+	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
+	mps2_uart_tx_chars(port);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
+{
+	irqreturn_t handled = IRQ_NONE;
+	struct uart_port *port = data;
+	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
+
+	spin_lock(&port->lock);
+
+	if (irqflag & UARTn_INT_RX_OVERRUN) {
+		struct tty_port *tport = &port->state->port;
+
+		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
+		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+		port->icount.overrun++;
+		handled = IRQ_HANDLED;
+	}
+
+	/* XXX: this shouldn't happen? */
+	if (irqflag & UARTn_INT_TX_OVERRUN) {
+		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
+		handled = IRQ_HANDLED;
+	}
+
+	spin_unlock(&port->lock);
+
+	return handled;
+}
+
+static int mps2_uart_startup(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+	int ret;
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
+			  MAKE_NAME(-rx), mps_port);
+	if (ret) {
+		pr_info("failed to register rxirq (%d)\n", ret);
+		goto err_no_rxirq;
+	}
+
+	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
+			  MAKE_NAME(-tx), mps_port);
+	if (ret) {
+		pr_info("failed to register txirq (%d)\n", ret);
+		goto err_no_txirq;
+	}
+
+	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
+			  MAKE_NAME(-overrun), mps_port);
+
+	if (ret)
+		pr_info("failed to register oerrirq (%d)\n", ret);
+	else {
+		control |= UARTn_CTRL_RX_ENABLE			|
+			   UARTn_CTRL_TX_ENABLE			|
+			   UARTn_CTRL_RX_INT_ENABLE		|
+			   UARTn_CTRL_TX_INT_ENABLE		|
+			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
+
+		mps2_uart_write8(port, control, UARTn_CTRL);
+
+		return 0;
+	}
+
+	free_irq(mps_port->tx_irq, mps_port);
+err_no_txirq:
+	free_irq(mps_port->rx_irq, mps_port);
+err_no_rxirq:
+	return ret;
+}
+
+static void mps2_uart_shutdown(struct uart_port *port)
+{
+	struct mps2_uart_port *mps_port = to_mps2_port(port);
+	u8 control = mps2_uart_read8(port, UARTn_CTRL);
+
+	control &= ~(UARTn_CTRL_RX_ENABLE		|
+		     UARTn_CTRL_TX_ENABLE		|
+		     UARTn_CTRL_RX_INT_ENABLE		|
+		     UARTn_CTRL_TX_INT_ENABLE		|
+		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
+		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
+
+	mps2_uart_write8(port, control, UARTn_CTRL);
+
+	free_irq(mps_port->rx_irq, mps_port);
+	free_irq(mps_port->tx_irq, mps_port);
+	free_irq(port->irq, mps_port);
+}
+
+static void mps2_uart_set_termios(struct uart_port *port,
+	struct ktermios *new, struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, bauddiv;
+
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+	new->c_cflag &= ~CSIZE;
+	new->c_cflag |= CS8;
+	new->c_cflag &= ~PARENB;
+	new->c_cflag &= ~CSTOPB;
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uart_update_timeout(port, new->c_cflag, baud);
+	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *mps2_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
+}
+
+static void mps2_uart_release_port(struct uart_port *port)
+{
+}
+
+static int mps2_uart_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void mps2_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
+		port->type = PORT_MPS2UART;
+}
+
+static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
+{
+	return -EINVAL;
+}
+
+static const struct uart_ops mps2_uart_pops = {
+	.tx_empty = mps2_uart_tx_empty,
+	.set_mctrl = mps2_uart_set_mctrl,
+	.get_mctrl = mps2_uart_get_mctrl,
+	.stop_tx = mps2_uart_stop_tx,
+	.start_tx = mps2_uart_start_tx,
+	.stop_rx = mps2_uart_stop_rx,
+	.enable_ms = mps2_uart_enable_ms,
+	.break_ctl = mps2_uart_break_ctl,
+	.startup = mps2_uart_startup,
+	.shutdown = mps2_uart_shutdown,
+	.set_termios = mps2_uart_set_termios,
+	.type = mps2_uart_type,
+	.release_port = mps2_uart_release_port,
+	.request_port = mps2_uart_request_port,
+	.config_port = mps2_uart_config_port,
+	.verify_port = mps2_uart_verify_port,
+};
+
+static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
+
+#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
+static void mps2_uart_console_putchar(struct uart_port *port, int ch)
+{
+	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
+		cpu_relax();
+
+	mps2_uart_write8(port, ch, UARTn_DATA);
+}
+
+static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
+{
+	struct uart_port *port = &mps2_uart_ports[co->index].port;
+
+	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
+}
+
+static int mps2_uart_console_setup(struct console *co, char *options)
+{
+	struct mps2_uart_port *mps_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
+		return -ENODEV;
+
+	mps_port = &mps2_uart_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mps2_uart_driver;
+
+static struct console mps2_uart_console = {
+	.name = SERIAL_NAME,
+	.device = uart_console_device,
+	.write = mps2_uart_console_write,
+	.setup = mps2_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &mps2_uart_driver,
+};
+
+#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
+
+#else
+#define MPS2_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver mps2_uart_driver = {
+	.driver_name = DRIVER_NAME,
+	.dev_name = SERIAL_NAME,
+	.nr = MPS2_MAX_PORTS,
+	.cons = MPS2_SERIAL_CONSOLE,
+};
+
+static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int id;
+
+	if (!np)
+		return NULL;
+
+	id = of_alias_get_id(np, "serial");
+	if (id < 0)
+		id = 0;
+
+	if (WARN_ON(id >= MPS2_MAX_PORTS))
+		return NULL;
+
+	mps2_uart_ports[id].port.line = id;
+	return &mps2_uart_ports[id];
+}
+
+static int mps2_init_port(struct mps2_uart_port *mps_port,
+			struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mps_port->port.membase))
+		return PTR_ERR(mps_port->port.membase);
+
+	mps_port->port.mapbase = res->start;
+	mps_port->port.mapsize = resource_size(res);
+
+	mps_port->rx_irq = platform_get_irq(pdev, 0);
+	mps_port->tx_irq = platform_get_irq(pdev, 1);
+	mps_port->port.irq = platform_get_irq(pdev, 2);
+
+	mps_port->port.iotype = UPIO_MEM;
+	mps_port->port.flags = UPF_BOOT_AUTOCONF;
+	mps_port->port.fifosize = 1;
+	mps_port->port.ops = &mps2_uart_pops;
+	mps_port->port.dev = &pdev->dev;
+
+	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mps_port->clk))
+		return PTR_ERR(mps_port->clk);
+
+	ret = clk_prepare_enable(mps_port->clk);
+	if (ret)
+		return ret;
+
+	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
+	if (!mps_port->port.uartclk)
+		ret = -EINVAL;
+
+	clk_disable_unprepare(mps_port->clk);
+
+	return ret;
+}
+
+static int mps2_serial_probe(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port;
+	int ret;
+
+	mps_port = mps2_of_get_port(pdev);
+	if (!mps_port)
+		return -ENODEV;
+
+	ret = mps2_init_port(mps_port, pdev);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, mps_port);
+
+	return 0;
+}
+
+static int mps2_serial_remove(struct platform_device *pdev)
+{
+	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
+
+	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mps2_match[] = {
+	{ .compatible = "arm,mps2-uart", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mps2_match);
+#endif
+
+static struct platform_driver mps2_serial_driver = {
+	.probe = mps2_serial_probe,
+	.remove = mps2_serial_remove,
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = of_match_ptr(mps2_match),
+	},
+};
+
+static int __init mps2_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&mps2_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&mps2_serial_driver);
+	if (ret)
+		uart_unregister_driver(&mps2_uart_driver);
+
+	pr_info("MPS2 UART driver initialized\n");
+
+	return ret;
+}
+module_init(mps2_uart_init);
+
+static void __exit mps2_uart_exit(void)
+{
+	platform_driver_unregister(&mps2_serial_driver);
+	uart_unregister_driver(&mps2_uart_driver);
+}
+module_exit(mps2_uart_exit);
+
+MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
+MODULE_DESCRIPTION("MPS2 UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148..f097fe9 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -261,4 +261,7 @@
 /* STM32 USART */
 #define PORT_STM32	113
 
+/* MPS2 UART */
+#define PORT_MPS2UART	114
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 05/10] serial: mps2-uart: add support for early console
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

This adds support early console for MPS2 UART which can be enabled via
earlycon=mps2,0x40004000

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/tty/serial/Kconfig     |    1 +
 drivers/tty/serial/mps2-uart.c |   30 ++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index e98bfea..33d1365 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1477,6 +1477,7 @@ config SERIAL_MPS2_UART_CONSOLE
 	bool "MPS2 UART console support"
 	depends on SERIAL_MPS2_UART
 	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
 
 config SERIAL_MPS2_UART
 	bool "MPS2 UART port"
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
index 09bac16..5f3252e 100644
--- a/drivers/tty/serial/mps2-uart.c
+++ b/drivers/tty/serial/mps2-uart.c
@@ -445,6 +445,36 @@ static struct console mps2_uart_console = {
 
 #define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
 
+static void mps2_early_putchar(struct uart_port *port, int ch)
+{
+
+	while (readb(port->membase + UARTn_STATE) & UARTn_STATE_TX_FULL)
+		cpu_relax();
+
+	writeb((unsigned char)ch, port->membase + UARTn_DATA);
+}
+
+
+static void mps2_early_write(struct console *con, const char *s, unsigned n)
+{
+	struct earlycon_device *dev = con->data;
+
+	uart_console_write(&dev->port, s, n, mps2_early_putchar);
+}
+
+static int __init mps2_early_console_setup(struct earlycon_device *device,
+					   const char *opt)
+{
+	if (!device->port.membase)
+		return -ENODEV;
+
+	device->con->write = mps2_early_write;
+
+	return 0;
+}
+EARLYCON_DECLARE(mps2, mps2_early_console_setup);
+OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup);
+
 #else
 #define MPS2_SERIAL_CONSOLE NULL
 #endif
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 05/10] serial: mps2-uart: add support for early console
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

This adds support early console for MPS2 UART which can be enabled via
earlycon=mps2,0x40004000

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 drivers/tty/serial/Kconfig     |    1 +
 drivers/tty/serial/mps2-uart.c |   30 ++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index e98bfea..33d1365 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1477,6 +1477,7 @@ config SERIAL_MPS2_UART_CONSOLE
 	bool "MPS2 UART console support"
 	depends on SERIAL_MPS2_UART
 	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
 
 config SERIAL_MPS2_UART
 	bool "MPS2 UART port"
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
index 09bac16..5f3252e 100644
--- a/drivers/tty/serial/mps2-uart.c
+++ b/drivers/tty/serial/mps2-uart.c
@@ -445,6 +445,36 @@ static struct console mps2_uart_console = {
 
 #define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
 
+static void mps2_early_putchar(struct uart_port *port, int ch)
+{
+
+	while (readb(port->membase + UARTn_STATE) & UARTn_STATE_TX_FULL)
+		cpu_relax();
+
+	writeb((unsigned char)ch, port->membase + UARTn_DATA);
+}
+
+
+static void mps2_early_write(struct console *con, const char *s, unsigned n)
+{
+	struct earlycon_device *dev = con->data;
+
+	uart_console_write(&dev->port, s, n, mps2_early_putchar);
+}
+
+static int __init mps2_early_console_setup(struct earlycon_device *device,
+					   const char *opt)
+{
+	if (!device->port.membase)
+		return -ENODEV;
+
+	device->con->write = mps2_early_write;
+
+	return 0;
+}
+EARLYCON_DECLARE(mps2, mps2_early_console_setup);
+OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup);
+
 #else
 #define MPS2_SERIAL_CONSOLE NULL
 #endif
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 06/10] ARM: mps2: introduce MPS2 platform
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

The Cortex-M Prototyping System (or V2M-MPS2) is designed for
prototyping and evaluation Cortex-M family of processors including the
latest Cortex-M7

It comes with a range of useful peripherals including 8MB single cycle
SRAM, 16MB PSRAM, Ethernet, QSVGA touch screen panel, 4bit RGB VGA
connector, Audio, SPI and GPIO.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/Kconfig                 |    8 ++++++++
 arch/arm/Makefile                |    1 +
 arch/arm/mach-mps2/Makefile      |    1 +
 arch/arm/mach-mps2/Makefile.boot |    3 +++
 arch/arm/mach-mps2/dtmachine.c   |   21 +++++++++++++++++++++
 5 files changed, 34 insertions(+)
 create mode 100644 arch/arm/mach-mps2/Makefile
 create mode 100644 arch/arm/mach-mps2/Makefile.boot
 create mode 100644 arch/arm/mach-mps2/dtmachine.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 34e1569..f9857e7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -969,6 +969,14 @@ config ARCH_STM32
 	help
 	  Support for STMicroelectronics STM32 processors.
 
+config ARCH_MPS2
+	bool "ARM MPS2 paltform"
+	depends on ARM_SINGLE_ARMV7M
+	select ARM_AMBA
+	select CLKSRC_MPS2
+	help
+	  Support for ARM MPS2 Cortex-M platform.
+
 # Definitions to make life easier
 config ARCH_ACORN
 	bool
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 2c2b28e..dbe06fd 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -183,6 +183,7 @@ machine-$(CONFIG_ARCH_LPC18XX)		+= lpc18xx
 machine-$(CONFIG_ARCH_LPC32XX)		+= lpc32xx
 machine-$(CONFIG_ARCH_MESON)		+= meson
 machine-$(CONFIG_ARCH_MMP)		+= mmp
+machine-$(CONFIG_ARCH_MPS2)		+= mps2
 machine-$(CONFIG_ARCH_MOXART)		+= moxart
 machine-$(CONFIG_ARCH_MV78XX0)		+= mv78xx0
 machine-$(CONFIG_ARCH_MVEBU)		+= mvebu
diff --git a/arch/arm/mach-mps2/Makefile b/arch/arm/mach-mps2/Makefile
new file mode 100644
index 0000000..3a74af7
--- /dev/null
+++ b/arch/arm/mach-mps2/Makefile
@@ -0,0 +1 @@
+obj-y += dtmachine.o
diff --git a/arch/arm/mach-mps2/Makefile.boot b/arch/arm/mach-mps2/Makefile.boot
new file mode 100644
index 0000000..eacfc3f
--- /dev/null
+++ b/arch/arm/mach-mps2/Makefile.boot
@@ -0,0 +1,3 @@
+# Empty file waiting for deletion once Makefile.boot isn't needed any more.
+# Patch waits for application at
+# http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=7889/1 .
diff --git a/arch/arm/mach-mps2/dtmachine.c b/arch/arm/mach-mps2/dtmachine.c
new file mode 100644
index 0000000..e7ad9c2
--- /dev/null
+++ b/arch/arm/mach-mps2/dtmachine.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <asm/mach/arch.h>
+
+static const char *const mps2_compat[] __initconst = {
+	"arm,mps2",
+	NULL
+};
+
+DT_MACHINE_START(MPS2DT, "MPS2 (Device Tree Support)")
+	.dt_compat = mps2_compat,
+MACHINE_END
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 06/10] ARM: mps2: introduce MPS2 platform
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

The Cortex-M Prototyping System (or V2M-MPS2) is designed for
prototyping and evaluation Cortex-M family of processors including the
latest Cortex-M7

It comes with a range of useful peripherals including 8MB single cycle
SRAM, 16MB PSRAM, Ethernet, QSVGA touch screen panel, 4bit RGB VGA
connector, Audio, SPI and GPIO.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/Kconfig                 |    8 ++++++++
 arch/arm/Makefile                |    1 +
 arch/arm/mach-mps2/Makefile      |    1 +
 arch/arm/mach-mps2/Makefile.boot |    3 +++
 arch/arm/mach-mps2/dtmachine.c   |   21 +++++++++++++++++++++
 5 files changed, 34 insertions(+)
 create mode 100644 arch/arm/mach-mps2/Makefile
 create mode 100644 arch/arm/mach-mps2/Makefile.boot
 create mode 100644 arch/arm/mach-mps2/dtmachine.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 34e1569..f9857e7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -969,6 +969,14 @@ config ARCH_STM32
 	help
 	  Support for STMicroelectronics STM32 processors.
 
+config ARCH_MPS2
+	bool "ARM MPS2 paltform"
+	depends on ARM_SINGLE_ARMV7M
+	select ARM_AMBA
+	select CLKSRC_MPS2
+	help
+	  Support for ARM MPS2 Cortex-M platform.
+
 # Definitions to make life easier
 config ARCH_ACORN
 	bool
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 2c2b28e..dbe06fd 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -183,6 +183,7 @@ machine-$(CONFIG_ARCH_LPC18XX)		+= lpc18xx
 machine-$(CONFIG_ARCH_LPC32XX)		+= lpc32xx
 machine-$(CONFIG_ARCH_MESON)		+= meson
 machine-$(CONFIG_ARCH_MMP)		+= mmp
+machine-$(CONFIG_ARCH_MPS2)		+= mps2
 machine-$(CONFIG_ARCH_MOXART)		+= moxart
 machine-$(CONFIG_ARCH_MV78XX0)		+= mv78xx0
 machine-$(CONFIG_ARCH_MVEBU)		+= mvebu
diff --git a/arch/arm/mach-mps2/Makefile b/arch/arm/mach-mps2/Makefile
new file mode 100644
index 0000000..3a74af7
--- /dev/null
+++ b/arch/arm/mach-mps2/Makefile
@@ -0,0 +1 @@
+obj-y += dtmachine.o
diff --git a/arch/arm/mach-mps2/Makefile.boot b/arch/arm/mach-mps2/Makefile.boot
new file mode 100644
index 0000000..eacfc3f
--- /dev/null
+++ b/arch/arm/mach-mps2/Makefile.boot
@@ -0,0 +1,3 @@
+# Empty file waiting for deletion once Makefile.boot isn't needed any more.
+# Patch waits for application at
+# http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=7889/1 .
diff --git a/arch/arm/mach-mps2/dtmachine.c b/arch/arm/mach-mps2/dtmachine.c
new file mode 100644
index 0000000..e7ad9c2
--- /dev/null
+++ b/arch/arm/mach-mps2/dtmachine.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <asm/mach/arch.h>
+
+static const char *const mps2_compat[] __initconst = {
+	"arm,mps2",
+	NULL
+};
+
+DT_MACHINE_START(MPS2DT, "MPS2 (Device Tree Support)")
+	.dt_compat = mps2_compat,
+MACHINE_END
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 07/10] ARM: mps2: add low-level debug support
  2015-12-02  9:33 ` Vladimir Murzin
  (?)
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

Add low-level debug support for MPS2, so that earlyprintk can be enabled
for debugging early boot issues.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/Kconfig.debug        |   12 +++++++++++-
 arch/arm/include/debug/mps2.S |   27 +++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/debug/mps2.S

diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 259c0ca..cfbf09a 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -479,6 +479,13 @@ choice
 		  Say Y here if you want kernel low-level debugging support
 		  on MMP UART3.
 
+	config DEBUG_MPS2_UART
+		bool "Kernel low-level debugging message via MPS2 UART0"
+		depends on ARCH_MPS2
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on MPS2 based platforms.
+
 	config DEBUG_QCOM_UARTDM
 		bool "Kernel low-level debugging messages via QCOM UARTDM"
 		depends on ARCH_QCOM
@@ -1350,6 +1357,7 @@ config DEBUG_LL_INCLUDE
 	default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1
 	default "debug/bcm63xx.S" if DEBUG_UART_BCM63XX
 	default "debug/digicolor.S" if DEBUG_DIGICOLOR_UA0
+	default "debug/mps2.S" if DEBUG_MPS2_UART
 	default "mach/debug-macro.S"
 
 # Compatibility options for PL01x
@@ -1403,6 +1411,7 @@ config DEBUG_UART_PHYS
 	default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3
 	default 0x20201000 if DEBUG_BCM2835
 	default 0x3e000000 if DEBUG_BCM_KONA_UART
+	default 0x40004000 if DEBUG_MPS2_UART
 	default 0x4000e400 if DEBUG_LL_UART_EFM32
 	default 0x40081000 if DEBUG_LPC18XX_UART0
 	default 0x40090000 if ARCH_LPC32XX
@@ -1476,7 +1485,8 @@ config DEBUG_UART_PHYS
 		DEBUG_RMOBILE_SCIFA4 || DEBUG_S3C24XX_UART || \
 		DEBUG_UART_BCM63XX || DEBUG_ASM9260_UART || \
 		DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \
-		DEBUG_AT91_UART
+		DEBUG_AT91_UART || \
+		DEBUG_MPS2_UART
 
 config DEBUG_UART_VIRT
 	hex "Virtual base address of debug UART"
diff --git a/arch/arm/include/debug/mps2.S b/arch/arm/include/debug/mps2.S
new file mode 100644
index 0000000..0ced0ce
--- /dev/null
+++ b/arch/arm/include/debug/mps2.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+	.macro	addruart, rp, tmp1, tmp2
+	ldr	\rp, =CONFIG_DEBUG_UART_PHYS
+	.endm
+
+	.macro	senduart, rd, rx
+	strb	\rd, [\rx]		@ Data Register
+	.endm
+
+	.macro	busyuart, rd, rx
+1001:	ldrb	\rd, [\rx, #0x4]	@ State Register
+	tst	\rd, #1			@ busy
+	bne	1001b			@ wait until transmit done
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 07/10] ARM: mps2: add low-level debug support
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

Add low-level debug support for MPS2, so that earlyprintk can be enabled
for debugging early boot issues.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/Kconfig.debug        |   12 +++++++++++-
 arch/arm/include/debug/mps2.S |   27 +++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/debug/mps2.S

diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 259c0ca..cfbf09a 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -479,6 +479,13 @@ choice
 		  Say Y here if you want kernel low-level debugging support
 		  on MMP UART3.
 
+	config DEBUG_MPS2_UART
+		bool "Kernel low-level debugging message via MPS2 UART0"
+		depends on ARCH_MPS2
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on MPS2 based platforms.
+
 	config DEBUG_QCOM_UARTDM
 		bool "Kernel low-level debugging messages via QCOM UARTDM"
 		depends on ARCH_QCOM
@@ -1350,6 +1357,7 @@ config DEBUG_LL_INCLUDE
 	default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1
 	default "debug/bcm63xx.S" if DEBUG_UART_BCM63XX
 	default "debug/digicolor.S" if DEBUG_DIGICOLOR_UA0
+	default "debug/mps2.S" if DEBUG_MPS2_UART
 	default "mach/debug-macro.S"
 
 # Compatibility options for PL01x
@@ -1403,6 +1411,7 @@ config DEBUG_UART_PHYS
 	default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3
 	default 0x20201000 if DEBUG_BCM2835
 	default 0x3e000000 if DEBUG_BCM_KONA_UART
+	default 0x40004000 if DEBUG_MPS2_UART
 	default 0x4000e400 if DEBUG_LL_UART_EFM32
 	default 0x40081000 if DEBUG_LPC18XX_UART0
 	default 0x40090000 if ARCH_LPC32XX
@@ -1476,7 +1485,8 @@ config DEBUG_UART_PHYS
 		DEBUG_RMOBILE_SCIFA4 || DEBUG_S3C24XX_UART || \
 		DEBUG_UART_BCM63XX || DEBUG_ASM9260_UART || \
 		DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \
-		DEBUG_AT91_UART
+		DEBUG_AT91_UART || \
+		DEBUG_MPS2_UART
 
 config DEBUG_UART_VIRT
 	hex "Virtual base address of debug UART"
diff --git a/arch/arm/include/debug/mps2.S b/arch/arm/include/debug/mps2.S
new file mode 100644
index 0000000..0ced0ce
--- /dev/null
+++ b/arch/arm/include/debug/mps2.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+	.macro	addruart, rp, tmp1, tmp2
+	ldr	\rp, =CONFIG_DEBUG_UART_PHYS
+	.endm
+
+	.macro	senduart, rd, rx
+	strb	\rd, [\rx]		@ Data Register
+	.endm
+
+	.macro	busyuart, rd, rx
+1001:	ldrb	\rd, [\rx, #0x4]	@ State Register
+	tst	\rd, #1			@ busy
+	bne	1001b			@ wait until transmit done
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 07/10] ARM: mps2: add low-level debug support
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Add low-level debug support for MPS2, so that earlyprintk can be enabled
for debugging early boot issues.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/Kconfig.debug        |   12 +++++++++++-
 arch/arm/include/debug/mps2.S |   27 +++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/debug/mps2.S

diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 259c0ca..cfbf09a 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -479,6 +479,13 @@ choice
 		  Say Y here if you want kernel low-level debugging support
 		  on MMP UART3.
 
+	config DEBUG_MPS2_UART
+		bool "Kernel low-level debugging message via MPS2 UART0"
+		depends on ARCH_MPS2
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on MPS2 based platforms.
+
 	config DEBUG_QCOM_UARTDM
 		bool "Kernel low-level debugging messages via QCOM UARTDM"
 		depends on ARCH_QCOM
@@ -1350,6 +1357,7 @@ config DEBUG_LL_INCLUDE
 	default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1
 	default "debug/bcm63xx.S" if DEBUG_UART_BCM63XX
 	default "debug/digicolor.S" if DEBUG_DIGICOLOR_UA0
+	default "debug/mps2.S" if DEBUG_MPS2_UART
 	default "mach/debug-macro.S"
 
 # Compatibility options for PL01x
@@ -1403,6 +1411,7 @@ config DEBUG_UART_PHYS
 	default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3
 	default 0x20201000 if DEBUG_BCM2835
 	default 0x3e000000 if DEBUG_BCM_KONA_UART
+	default 0x40004000 if DEBUG_MPS2_UART
 	default 0x4000e400 if DEBUG_LL_UART_EFM32
 	default 0x40081000 if DEBUG_LPC18XX_UART0
 	default 0x40090000 if ARCH_LPC32XX
@@ -1476,7 +1485,8 @@ config DEBUG_UART_PHYS
 		DEBUG_RMOBILE_SCIFA4 || DEBUG_S3C24XX_UART || \
 		DEBUG_UART_BCM63XX || DEBUG_ASM9260_UART || \
 		DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \
-		DEBUG_AT91_UART
+		DEBUG_AT91_UART || \
+		DEBUG_MPS2_UART
 
 config DEBUG_UART_VIRT
 	hex "Virtual base address of debug UART"
diff --git a/arch/arm/include/debug/mps2.S b/arch/arm/include/debug/mps2.S
new file mode 100644
index 0000000..0ced0ce
--- /dev/null
+++ b/arch/arm/include/debug/mps2.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+	.macro	addruart, rp, tmp1, tmp2
+	ldr	\rp, =CONFIG_DEBUG_UART_PHYS
+	.endm
+
+	.macro	senduart, rd, rx
+	strb	\rd, [\rx]		@ Data Register
+	.endm
+
+	.macro	busyuart, rd, rx
+1001:	ldrb	\rd, [\rx, #0x4]	@ State Register
+	tst	\rd, #1			@ busy
+	bne	1001b			@ wait until transmit done
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 08/10] ARM: configs: add MPS2 defconfig
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

This patch adds a new config for MPS2 platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/configs/mps2_defconfig |  112 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 arch/arm/configs/mps2_defconfig

diff --git a/arch/arm/configs/mps2_defconfig b/arch/arm/configs/mps2_defconfig
new file mode 100644
index 0000000..c36c519
--- /dev/null
+++ b/arch/arm/configs/mps2_defconfig
@@ -0,0 +1,112 @@
+# CONFIG_USELIB is not set
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_UID16 is not set
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_BLOCK is not set
+# CONFIG_MMU is not set
+CONFIG_ARM_SINGLE_ARMV7M=y
+CONFIG_ARCH_MPS2=y
+CONFIG_SET_MEM_PARAM=y
+CONFIG_DRAM_BASE=0x21000000
+CONFIG_DRAM_SIZE=0x1000000
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_ATAGS is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_FLAT=y
+CONFIG_BINFMT_SHARED_FLAT=y
+# CONFIG_COREDUMP is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_CORE is not set
+# 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_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HISILICON 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_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MPS2_UART_CONSOLE=y
+CONFIG_SERIAL_MPS2_UART=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_ARM_SP805_WATCHDOG=y
+CONFIG_MFD_SYSCON=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_SYSCON=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_ARM_TIMER_SP804=y
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_MEMTEST=y
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 08/10] ARM: configs: add MPS2 defconfig
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds a new config for MPS2 platform.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/configs/mps2_defconfig |  112 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 arch/arm/configs/mps2_defconfig

diff --git a/arch/arm/configs/mps2_defconfig b/arch/arm/configs/mps2_defconfig
new file mode 100644
index 0000000..c36c519
--- /dev/null
+++ b/arch/arm/configs/mps2_defconfig
@@ -0,0 +1,112 @@
+# CONFIG_USELIB is not set
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_UID16 is not set
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_BLOCK is not set
+# CONFIG_MMU is not set
+CONFIG_ARM_SINGLE_ARMV7M=y
+CONFIG_ARCH_MPS2=y
+CONFIG_SET_MEM_PARAM=y
+CONFIG_DRAM_BASE=0x21000000
+CONFIG_DRAM_SIZE=0x1000000
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_ATAGS is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_FLAT=y
+CONFIG_BINFMT_SHARED_FLAT=y
+# CONFIG_COREDUMP is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_CORE is not set
+# 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_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HISILICON 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_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MPS2_UART_CONSOLE=y
+CONFIG_SERIAL_MPS2_UART=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_ARM_SP805_WATCHDOG=y
+CONFIG_MFD_SYSCON=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_SYSCON=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_ARM_TIMER_SP804=y
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_MEMTEST=y
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

Application Notes 385 and 386 shares the same memory map and features
except the CPU is used. AN385 is supplied with Cortex-M3 CPU and AN386
is supplied with Cortex-M4.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/boot/dts/Makefile       |    1 +
 arch/arm/boot/dts/mps2-an385.dts |   90 +++++++++++++++
 arch/arm/boot/dts/mps2.dtsi      |  227 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 318 insertions(+)
 create mode 100644 arch/arm/boot/dts/mps2-an385.dts
 create mode 100644 arch/arm/boot/dts/mps2.dtsi

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 30bbc37..4ef3720 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -237,6 +237,7 @@ dtb-$(CONFIG_ARCH_MMP) += \
 dtb-$(CONFIG_MACH_MESON8B) += \
 	meson8b-mxq.dtb \
 	meson8b-odroidc1.dtb
+dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
 dtb-$(CONFIG_ARCH_MOXART) += \
 	moxart-uc7112lx.dtb
 dtb-$(CONFIG_SOC_IMX1) += \
diff --git a/arch/arm/boot/dts/mps2-an385.dts b/arch/arm/boot/dts/mps2-an385.dts
new file mode 100644
index 0000000..ddb03d3
--- /dev/null
+++ b/arch/arm/boot/dts/mps2-an385.dts
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "mps2.dtsi"
+
+/ {
+	model = "ARM MPS2 Application Note 385/386";
+	compatible = "arm,mps2";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		bootargs = "init=/sbin/init earlycon";
+		stdout-path = "serial0:9600n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x21000000 0x1000000>;
+	};
+
+	ethernet@40200000 {
+		compatible = "smsc,lan9220", "smsc,lan9115";
+		reg = <0x40200000 0x10000>;
+		interrupts = <13>;
+		interrupt-parent = <&nvic>;
+		smsc,irq-active-high;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&timer0 {
+	status = "okay";
+};
+
+&timer1 {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
diff --git a/arch/arm/boot/dts/mps2.dtsi b/arch/arm/boot/dts/mps2.dtsi
new file mode 100644
index 0000000..5d2c539
--- /dev/null
+++ b/arch/arm/boot/dts/mps2.dtsi
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "armv7-m.dtsi"
+
+/ {
+	oscclk0: clk-osc0 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <50000000>;
+	};
+
+	oscclk1: clk-osc1 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <24576000>;
+	};
+
+	oscclk2: clk-osc2 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <25000000>;
+	};
+
+	cfgclk: clk-cfg {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <5000000>;
+	};
+
+	spicfgclk: clk-spicfg {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <75000000>;
+	};
+
+	sysclk: clk-sys {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	audmclk: clk-audm {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk1>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	audsclk: clk-auds {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk1>;
+		#clock-cells = <0>;
+		clock-div = <8>;
+		clock-mult = <1>;
+	};
+
+	spiclcd: clk-cpiclcd {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	spicon: clk-spicon {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	i2cclcd: clk-i2cclcd {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	i2caud: clk-i2caud {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		ranges;
+
+		apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0x40000000 0x10000>;
+
+			timer0: mps2-timer0@0 {
+				compatible = "arm,mps2-timer";
+				reg = <0x0 0x1000>;
+				interrupts = <8>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			timer1: mps2-timer1@1000 {
+				compatible = "arm,mps2-timer";
+				reg = <0x1000 0x1000>;
+				interrupts = <9>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			timer2: dual-timer@2000 {
+				compatible = "arm,sp804";
+				reg = <0x2000 0x1000>;
+				clocks = <&sysclk>;
+				interrupts = <10>;
+				status = "disabled";
+			};
+
+
+			uart0: serial@4000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x4000 0x1000>;
+				interrupts = <0 1 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			uart1: serial@5000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x5000 0x1000>;
+				interrupts = <2 3 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			uart2: serial@6000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x6000 0x1000>;
+				interrupts = <4 5 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			wdt: watchdog@8000 {
+				compatible = "arm,sp805", "arm,primecell";
+				arm,primecell-periphid = <0x00141805>;
+				reg = <0x8000 0x1000>;
+				interrupts = <0>;
+				clocks = <&sysclk>;
+				clock-names = "apb_pclk";
+				status = "disabled";
+			};
+		};
+
+		fpgaio {
+			compatible = "syscon", "simple-mfd";
+			reg = <0x40028000 0x10>;
+
+			led@0 {
+				compatible = "register-bit-led";
+				offset = <0x0>;
+				mask = <0x01>;
+				label = "userled:0";
+				linux,default-trigger = "heartbeat";
+				default-state = "on";
+			};
+
+			led@1 {
+				compatible = "register-bit-led";
+				offset = <0x0>;
+				mask = <0x02>;
+				label = "userled:1";
+				linux,default-trigger = "usr";
+				default-state = "off";
+			};
+		};
+	};
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Application Notes 385 and 386 shares the same memory map and features
except the CPU is used. AN385 is supplied with Cortex-M3 CPU and AN386
is supplied with Cortex-M4.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/boot/dts/Makefile       |    1 +
 arch/arm/boot/dts/mps2-an385.dts |   90 +++++++++++++++
 arch/arm/boot/dts/mps2.dtsi      |  227 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 318 insertions(+)
 create mode 100644 arch/arm/boot/dts/mps2-an385.dts
 create mode 100644 arch/arm/boot/dts/mps2.dtsi

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 30bbc37..4ef3720 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -237,6 +237,7 @@ dtb-$(CONFIG_ARCH_MMP) += \
 dtb-$(CONFIG_MACH_MESON8B) += \
 	meson8b-mxq.dtb \
 	meson8b-odroidc1.dtb
+dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
 dtb-$(CONFIG_ARCH_MOXART) += \
 	moxart-uc7112lx.dtb
 dtb-$(CONFIG_SOC_IMX1) += \
diff --git a/arch/arm/boot/dts/mps2-an385.dts b/arch/arm/boot/dts/mps2-an385.dts
new file mode 100644
index 0000000..ddb03d3
--- /dev/null
+++ b/arch/arm/boot/dts/mps2-an385.dts
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "mps2.dtsi"
+
+/ {
+	model = "ARM MPS2 Application Note 385/386";
+	compatible = "arm,mps2";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		bootargs = "init=/sbin/init earlycon";
+		stdout-path = "serial0:9600n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x21000000 0x1000000>;
+	};
+
+	ethernet at 40200000 {
+		compatible = "smsc,lan9220", "smsc,lan9115";
+		reg = <0x40200000 0x10000>;
+		interrupts = <13>;
+		interrupt-parent = <&nvic>;
+		smsc,irq-active-high;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&timer0 {
+	status = "okay";
+};
+
+&timer1 {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
diff --git a/arch/arm/boot/dts/mps2.dtsi b/arch/arm/boot/dts/mps2.dtsi
new file mode 100644
index 0000000..5d2c539
--- /dev/null
+++ b/arch/arm/boot/dts/mps2.dtsi
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "armv7-m.dtsi"
+
+/ {
+	oscclk0: clk-osc0 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <50000000>;
+	};
+
+	oscclk1: clk-osc1 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <24576000>;
+	};
+
+	oscclk2: clk-osc2 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <25000000>;
+	};
+
+	cfgclk: clk-cfg {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <5000000>;
+	};
+
+	spicfgclk: clk-spicfg {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <75000000>;
+	};
+
+	sysclk: clk-sys {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	audmclk: clk-audm {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk1>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	audsclk: clk-auds {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk1>;
+		#clock-cells = <0>;
+		clock-div = <8>;
+		clock-mult = <1>;
+	};
+
+	spiclcd: clk-cpiclcd {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	spicon: clk-spicon {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	i2cclcd: clk-i2cclcd {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	i2caud: clk-i2caud {
+		compatible = "fixed-factor-clock";
+		clocks = <&oscclk0>;
+		#clock-cells = <0>;
+		clock-div = <2>;
+		clock-mult = <1>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		ranges;
+
+		apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0x40000000 0x10000>;
+
+			timer0: mps2-timer0 at 0 {
+				compatible = "arm,mps2-timer";
+				reg = <0x0 0x1000>;
+				interrupts = <8>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			timer1: mps2-timer1 at 1000 {
+				compatible = "arm,mps2-timer";
+				reg = <0x1000 0x1000>;
+				interrupts = <9>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			timer2: dual-timer at 2000 {
+				compatible = "arm,sp804";
+				reg = <0x2000 0x1000>;
+				clocks = <&sysclk>;
+				interrupts = <10>;
+				status = "disabled";
+			};
+
+
+			uart0: serial at 4000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x4000 0x1000>;
+				interrupts = <0 1 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			uart1: serial at 5000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x5000 0x1000>;
+				interrupts = <2 3 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			uart2: serial at 6000 {
+				compatible = "arm,mps2-uart";
+				reg = <0x6000 0x1000>;
+				interrupts = <4 5 12>;
+				clocks = <&sysclk>;
+				status = "disabled";
+			};
+
+			wdt: watchdog at 8000 {
+				compatible = "arm,sp805", "arm,primecell";
+				arm,primecell-periphid = <0x00141805>;
+				reg = <0x8000 0x1000>;
+				interrupts = <0>;
+				clocks = <&sysclk>;
+				clock-names = "apb_pclk";
+				status = "disabled";
+			};
+		};
+
+		fpgaio {
+			compatible = "syscon", "simple-mfd";
+			reg = <0x40028000 0x10>;
+
+			led at 0 {
+				compatible = "register-bit-led";
+				offset = <0x0>;
+				mask = <0x01>;
+				label = "userled:0";
+				linux,default-trigger = "heartbeat";
+				default-state = "on";
+			};
+
+			led at 1 {
+				compatible = "register-bit-led";
+				offset = <0x0>;
+				mask = <0x02>;
+				label = "userled:1";
+				linux,default-trigger = "usr";
+				default-state = "off";
+			};
+		};
+	};
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 10/10] ARM: dts: introduce MPS2 AN399/AN400
  2015-12-02  9:33 ` Vladimir Murzin
@ 2015-12-02  9:33   ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

Application Notes 399 and 400 shares the same memory map and
features. Both are shipped with Cortex-M7 and have the same  peripheral
as AN385/AN386, but with different location of PSRAM and Ethernet
controller.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/boot/dts/Makefile       |    4 +-
 arch/arm/boot/dts/mps2-an399.dts |   92 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/boot/dts/mps2-an399.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 4ef3720..6603a1f 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -237,7 +237,9 @@ dtb-$(CONFIG_ARCH_MMP) += \
 dtb-$(CONFIG_MACH_MESON8B) += \
 	meson8b-mxq.dtb \
 	meson8b-odroidc1.dtb
-dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
+dtb-$(CONFIG_ARCH_MPS2) += \
+	mps2-an385.dtb \
+	mps2-an399.dtb
 dtb-$(CONFIG_ARCH_MOXART) += \
 	moxart-uc7112lx.dtb
 dtb-$(CONFIG_SOC_IMX1) += \
diff --git a/arch/arm/boot/dts/mps2-an399.dts b/arch/arm/boot/dts/mps2-an399.dts
new file mode 100644
index 0000000..e591d75
--- /dev/null
+++ b/arch/arm/boot/dts/mps2-an399.dts
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "mps2.dtsi"
+
+/ {
+	model = "ARM MPS2 Application Note 399/400";
+	compatible = "arm,mps2";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		bootargs = "rdinit=/sbin/init earlycon";
+		stdout-path = "serial0:9600n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x60000000 0x1000000>;
+	};
+
+
+	ethernet@a0000000 {
+		compatible = "smsc,lan9220", "smsc,lan9115";
+		reg = <0xa0000000 0x10000>;
+		interrupts = <13>;
+		interrupt-parent = <&nvic>;
+		smsc,irq-active-high;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+
+&timer0 {
+	status = "okay";
+};
+
+&timer1 {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v1 10/10] ARM: dts: introduce MPS2 AN399/AN400
@ 2015-12-02  9:33   ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-02  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Application Notes 399 and 400 shares the same memory map and
features. Both are shipped with Cortex-M7 and have the same  peripheral
as AN385/AN386, but with different location of PSRAM and Ethernet
controller.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
 arch/arm/boot/dts/Makefile       |    4 +-
 arch/arm/boot/dts/mps2-an399.dts |   92 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/boot/dts/mps2-an399.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 4ef3720..6603a1f 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -237,7 +237,9 @@ dtb-$(CONFIG_ARCH_MMP) += \
 dtb-$(CONFIG_MACH_MESON8B) += \
 	meson8b-mxq.dtb \
 	meson8b-odroidc1.dtb
-dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
+dtb-$(CONFIG_ARCH_MPS2) += \
+	mps2-an385.dtb \
+	mps2-an399.dtb
 dtb-$(CONFIG_ARCH_MOXART) += \
 	moxart-uc7112lx.dtb
 dtb-$(CONFIG_SOC_IMX1) += \
diff --git a/arch/arm/boot/dts/mps2-an399.dts b/arch/arm/boot/dts/mps2-an399.dts
new file mode 100644
index 0000000..e591d75
--- /dev/null
+++ b/arch/arm/boot/dts/mps2-an399.dts
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Vladimir Murzin <vladimir.murzin@arm.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file 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.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "mps2.dtsi"
+
+/ {
+	model = "ARM MPS2 Application Note 399/400";
+	compatible = "arm,mps2";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		bootargs = "rdinit=/sbin/init earlycon";
+		stdout-path = "serial0:9600n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x60000000 0x1000000>;
+	};
+
+
+	ethernet at a0000000 {
+		compatible = "smsc,lan9220", "smsc,lan9115";
+		reg = <0xa0000000 0x10000>;
+		interrupts = <13>;
+		interrupt-parent = <&nvic>;
+		smsc,irq-active-high;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+
+&timer0 {
+	status = "okay";
+};
+
+&timer1 {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-07  9:25     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:25 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

On 02/12/15 09:33, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
> 
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent

Daniel, you had concerns on the RFC version. Does this one look fine to
you or there is something I should improve?

Thanks
Vladimir

> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
> 
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>  	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>  	select CLKSRC_MMIO
>  
> +config CLKSRC_MPS2
> +	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +	depends on OF && ARM
> +	select CLKSRC_MMIO
> +
>  config ARM_ARCH_TIMER
>  	bool
>  	select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 56bd16e..7033b9c 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
>  obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
>  obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
>  obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
> +obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
>  obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
>  obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
>  obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
> diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
> new file mode 100644
> index 0000000..3e19af5
> --- /dev/null
> +++ b/drivers/clocksource/mps2-timer.c
> @@ -0,0 +1,277 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/clocksource.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/sched_clock.h>
> +#include <linux/slab.h>
> +
> +#define TIMER_CTRL		0x0
> +#define TIMER_CTRL_ENABLE	BIT(0)
> +#define TIMER_CTRL_IE		BIT(3)
> +
> +#define TIMER_VALUE		0x4
> +#define TIMER_RELOAD		0x8
> +#define TIMER_INT		0xc
> +
> +struct clockevent_mps2 {
> +	void __iomem *reg;
> +	u32 clock_count_per_tick;
> +	struct clock_event_device clkevt;
> +};
> +
> +static void __iomem *sched_clock_base;
> +
> +static u64 notrace mps2_sched_read(void)
> +{
> +        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
> +}
> +
> +static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
> +{
> +	return container_of(c, struct clockevent_mps2, clkevt);
> +}
> +
> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}
> +
> +static int mps2_timer_shutdown(struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(0, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(next, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	writel(1, ce->reg + TIMER_INT);
> +
> +	ce->clkevt.event_handler(&ce->clkevt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __init mps2_clockevent_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	struct clockevent_mps2 *ce;
> +	u32 rate;
> +	int irq, ret;
> +	const char *name = "mps2-clkevt";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clockevent: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clockevent: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clockevent: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	irq = irq_of_parse_and_map(np, 0);
> +	if (!irq) {
> +		ret = -ENOENT;
> +		pr_err("failed to get irq for clockevent: %d\n", ret);
> +		goto err_get_irq;
> +	}
> +
> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);
> +		goto err_ce_alloc;
> +	}
> +
> +	ce->reg = base;
> +	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
> +	ce->clkevt.irq = irq;
> +	ce->clkevt.name = name;
> +	ce->clkevt.rating = 200;
> +	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> +	ce->clkevt.cpumask = cpu_possible_mask;
> +	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
> +	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
> +	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
> +	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
> +	if (ret) {
> +		pr_err("failed to request irq: %d\n", ret);
> +		goto err_ia_alloc;
> +	}
> +
> +	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
> +
> +	return 0;
> +
> +err_ia_alloc:
> +	kfree(ce);
> +err_ce_alloc:
> +err_get_irq:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static int __init mps2_clocksource_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	u32 rate;
> +	int ret;
> +	const char *name = "mps2-clksrc";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clocksource: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clocksource: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clocksource: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	/* ... and set it up as free-running clocksource */
> +	writel(0xffffffff, base + TIMER_VALUE);
> +	writel(0xffffffff, base + TIMER_RELOAD);
> +
> +	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
> +
> +	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
> +				    rate, 200, 32,
> +				    clocksource_mmio_readl_down);
> +	if (ret) {
> +		pr_err("failed to init clocksource: %d\n", ret);
> +		goto err_clocksource_init;
> +	}
> +
> +	sched_clock_base = base;
> +	sched_clock_register(mps2_sched_read, 32, rate);
> +
> +	return 0;
> +
> +err_clocksource_init:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static void __init mps2_timer_init(struct device_node *np)
> +{
> +	static int has_clocksource, has_clockevent;
> +	int ret;
> +
> +	if (!has_clocksource) {
> +		ret = mps2_clocksource_init(np);
> +		if (!ret) {
> +			has_clocksource = 1;
> +			return;
> +		}
> +	}
> +
> +	if (!has_clockevent) {
> +		ret = mps2_clockevent_init(np);
> +		if (!ret) {
> +			has_clockevent = 1;
> +			return;
> +		}
> +	}
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-07  9:25     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:25 UTC (permalink / raw)
  To: arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Pawel.Moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 02/12/15 09:33, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
> 
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent

Daniel, you had concerns on the RFC version. Does this one look fine to
you or there is something I should improve?

Thanks
Vladimir

> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
> 
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>  	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>  	select CLKSRC_MMIO
>  
> +config CLKSRC_MPS2
> +	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +	depends on OF && ARM
> +	select CLKSRC_MMIO
> +
>  config ARM_ARCH_TIMER
>  	bool
>  	select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 56bd16e..7033b9c 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
>  obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
>  obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
>  obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
> +obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
>  obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
>  obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
>  obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
> diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
> new file mode 100644
> index 0000000..3e19af5
> --- /dev/null
> +++ b/drivers/clocksource/mps2-timer.c
> @@ -0,0 +1,277 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/clocksource.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/sched_clock.h>
> +#include <linux/slab.h>
> +
> +#define TIMER_CTRL		0x0
> +#define TIMER_CTRL_ENABLE	BIT(0)
> +#define TIMER_CTRL_IE		BIT(3)
> +
> +#define TIMER_VALUE		0x4
> +#define TIMER_RELOAD		0x8
> +#define TIMER_INT		0xc
> +
> +struct clockevent_mps2 {
> +	void __iomem *reg;
> +	u32 clock_count_per_tick;
> +	struct clock_event_device clkevt;
> +};
> +
> +static void __iomem *sched_clock_base;
> +
> +static u64 notrace mps2_sched_read(void)
> +{
> +        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
> +}
> +
> +static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
> +{
> +	return container_of(c, struct clockevent_mps2, clkevt);
> +}
> +
> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}
> +
> +static int mps2_timer_shutdown(struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(0, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(next, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	writel(1, ce->reg + TIMER_INT);
> +
> +	ce->clkevt.event_handler(&ce->clkevt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __init mps2_clockevent_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	struct clockevent_mps2 *ce;
> +	u32 rate;
> +	int irq, ret;
> +	const char *name = "mps2-clkevt";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clockevent: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clockevent: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clockevent: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	irq = irq_of_parse_and_map(np, 0);
> +	if (!irq) {
> +		ret = -ENOENT;
> +		pr_err("failed to get irq for clockevent: %d\n", ret);
> +		goto err_get_irq;
> +	}
> +
> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);
> +		goto err_ce_alloc;
> +	}
> +
> +	ce->reg = base;
> +	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
> +	ce->clkevt.irq = irq;
> +	ce->clkevt.name = name;
> +	ce->clkevt.rating = 200;
> +	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> +	ce->clkevt.cpumask = cpu_possible_mask;
> +	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
> +	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
> +	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
> +	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
> +	if (ret) {
> +		pr_err("failed to request irq: %d\n", ret);
> +		goto err_ia_alloc;
> +	}
> +
> +	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
> +
> +	return 0;
> +
> +err_ia_alloc:
> +	kfree(ce);
> +err_ce_alloc:
> +err_get_irq:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static int __init mps2_clocksource_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	u32 rate;
> +	int ret;
> +	const char *name = "mps2-clksrc";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clocksource: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clocksource: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clocksource: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	/* ... and set it up as free-running clocksource */
> +	writel(0xffffffff, base + TIMER_VALUE);
> +	writel(0xffffffff, base + TIMER_RELOAD);
> +
> +	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
> +
> +	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
> +				    rate, 200, 32,
> +				    clocksource_mmio_readl_down);
> +	if (ret) {
> +		pr_err("failed to init clocksource: %d\n", ret);
> +		goto err_clocksource_init;
> +	}
> +
> +	sched_clock_base = base;
> +	sched_clock_register(mps2_sched_read, 32, rate);
> +
> +	return 0;
> +
> +err_clocksource_init:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static void __init mps2_timer_init(struct device_node *np)
> +{
> +	static int has_clocksource, has_clockevent;
> +	int ret;
> +
> +	if (!has_clocksource) {
> +		ret = mps2_clocksource_init(np);
> +		if (!ret) {
> +			has_clocksource = 1;
> +			return;
> +		}
> +	}
> +
> +	if (!has_clockevent) {
> +		ret = mps2_clockevent_init(np);
> +		if (!ret) {
> +			has_clockevent = 1;
> +			return;
> +		}
> +	}
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-07  9:25     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

On 02/12/15 09:33, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
> 
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent

Daniel, you had concerns on the RFC version. Does this one look fine to
you or there is something I should improve?

Thanks
Vladimir

> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
> 
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>  	depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>  	select CLKSRC_MMIO
>  
> +config CLKSRC_MPS2
> +	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +	depends on OF && ARM
> +	select CLKSRC_MMIO
> +
>  config ARM_ARCH_TIMER
>  	bool
>  	select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 56bd16e..7033b9c 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_CLKSRC_EFM32)	+= time-efm32.o
>  obj-$(CONFIG_CLKSRC_STM32)	+= timer-stm32.o
>  obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
>  obj-$(CONFIG_CLKSRC_LPC32XX)	+= time-lpc32xx.o
> +obj-$(CONFIG_CLKSRC_MPS2)	+= mps2-timer.o
>  obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o
>  obj-$(CONFIG_FSL_FTM_TIMER)	+= fsl_ftm_timer.o
>  obj-$(CONFIG_VF_PIT_TIMER)	+= vf_pit_timer.o
> diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
> new file mode 100644
> index 0000000..3e19af5
> --- /dev/null
> +++ b/drivers/clocksource/mps2-timer.c
> @@ -0,0 +1,277 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/clocksource.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/sched_clock.h>
> +#include <linux/slab.h>
> +
> +#define TIMER_CTRL		0x0
> +#define TIMER_CTRL_ENABLE	BIT(0)
> +#define TIMER_CTRL_IE		BIT(3)
> +
> +#define TIMER_VALUE		0x4
> +#define TIMER_RELOAD		0x8
> +#define TIMER_INT		0xc
> +
> +struct clockevent_mps2 {
> +	void __iomem *reg;
> +	u32 clock_count_per_tick;
> +	struct clock_event_device clkevt;
> +};
> +
> +static void __iomem *sched_clock_base;
> +
> +static u64 notrace mps2_sched_read(void)
> +{
> +        return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
> +}
> +
> +static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
> +{
> +	return container_of(c, struct clockevent_mps2, clkevt);
> +}
> +
> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}
> +
> +static int mps2_timer_shutdown(struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(0, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(0, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
> +{
> +	clockevent_mps2_writel(next, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	writel(1, ce->reg + TIMER_INT);
> +
> +	ce->clkevt.event_handler(&ce->clkevt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __init mps2_clockevent_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	struct clockevent_mps2 *ce;
> +	u32 rate;
> +	int irq, ret;
> +	const char *name = "mps2-clkevt";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clockevent: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clockevent: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clockevent: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	irq = irq_of_parse_and_map(np, 0);
> +	if (!irq) {
> +		ret = -ENOENT;
> +		pr_err("failed to get irq for clockevent: %d\n", ret);
> +		goto err_get_irq;
> +	}
> +
> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);
> +		goto err_ce_alloc;
> +	}
> +
> +	ce->reg = base;
> +	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
> +	ce->clkevt.irq = irq;
> +	ce->clkevt.name = name;
> +	ce->clkevt.rating = 200;
> +	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> +	ce->clkevt.cpumask = cpu_possible_mask;
> +	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown,
> +	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic,
> +	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown,
> +	ce->clkevt.set_next_event	= mps2_timer_set_next_event;
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
> +	if (ret) {
> +		pr_err("failed to request irq: %d\n", ret);
> +		goto err_ia_alloc;
> +	}
> +
> +	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
> +
> +	return 0;
> +
> +err_ia_alloc:
> +	kfree(ce);
> +err_ce_alloc:
> +err_get_irq:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static int __init mps2_clocksource_init(struct device_node *np)
> +{
> +	void __iomem *base;
> +	struct clk *clk;
> +	u32 rate;
> +	int ret;
> +	const char *name = "mps2-clksrc";
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &rate);
> +	if (ret) {
> +		clk = of_clk_get(np, 0);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			pr_err("failed to get clock for clocksource: %d\n", ret);
> +			goto err_clk_get;
> +		}
> +
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			pr_err("failed to enable clock for clocksource: %d\n", ret);
> +			clk_put(clk);
> +			goto err_clk_enable;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		ret = -EADDRNOTAVAIL;
> +		pr_err("failed to map register for clocksource: %d\n", ret);
> +		goto err_iomap;
> +	}
> +
> +	/* Ensure timer is disabled */
> +	writel(0, base + TIMER_CTRL);
> +
> +	/* ... and set it up as free-running clocksource */
> +	writel(0xffffffff, base + TIMER_VALUE);
> +	writel(0xffffffff, base + TIMER_RELOAD);
> +
> +	writel(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
> +
> +	ret = clocksource_mmio_init(base + TIMER_VALUE, name,
> +				    rate, 200, 32,
> +				    clocksource_mmio_readl_down);
> +	if (ret) {
> +		pr_err("failed to init clocksource: %d\n", ret);
> +		goto err_clocksource_init;
> +	}
> +
> +	sched_clock_base = base;
> +	sched_clock_register(mps2_sched_read, 32, rate);
> +
> +	return 0;
> +
> +err_clocksource_init:
> +	iounmap(base);
> +err_iomap:
> +	clk_disable_unprepare(clk);
> +err_clk_enable:
> +	clk_put(clk);
> +err_clk_get:
> +	return ret;
> +}
> +
> +static void __init mps2_timer_init(struct device_node *np)
> +{
> +	static int has_clocksource, has_clockevent;
> +	int ret;
> +
> +	if (!has_clocksource) {
> +		ret = mps2_clocksource_init(np);
> +		if (!ret) {
> +			has_clocksource = 1;
> +			return;
> +		}
> +	}
> +
> +	if (!has_clockevent) {
> +		ret = mps2_clockevent_init(np);
> +		if (!ret) {
> +			has_clockevent = 1;
> +			return;
> +		}
> +	}
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-07  9:26     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:26 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

Gentle ping...

On 02/12/15 09:33, Vladimir Murzin wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>  	  This driver support the USART and UART ports on
>  	  Energy Micro's efm32 SoCs.
>  
> +config SERIAL_MPS2_UART_CONSOLE
> +	bool "MPS2 UART console support"
> +	depends on SERIAL_MPS2_UART
> +	select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +	bool "MPS2 UART port"
> +	depends on ARM || COMPILE_TEST
> +	select SERIAL_CORE
> +	help
> +	  This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>  	bool "EFM32 UART/USART console support"
>  	depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
>  
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"
> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA				0x0
> +
> +#define UARTn_STATE				0x4
> +#define UARTn_STATE_TX_FULL			BIT(0)
> +#define UARTn_STATE_RX_FULL			BIT(1)
> +#define UARTn_STATE_TX_OVERRUN			BIT(2)
> +#define UARTn_STATE_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_CTRL				0x8
> +#define UARTn_CTRL_TX_ENABLE			BIT(0)
> +#define UARTn_CTRL_RX_ENABLE			BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
> +
> +#define UARTn_INT				0xc
> +#define UARTn_INT_TX				BIT(0)
> +#define UARTn_INT_RX				BIT(1)
> +#define UARTn_INT_TX_OVERRUN			BIT(2)
> +#define UARTn_INT_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_BAUDDIV				0x10
> +#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS				3
> +
> +struct mps2_uart_port {
> +	struct uart_port port;
> +	struct clk *clk;
> +	unsigned int tx_irq;
> +	unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +	return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +	u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +	struct circ_buf *xmit = &port->state->xmit;
> +
> +	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +		if (port->x_char) {
> +			mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +			port->x_char = 0;
> +			port->icount.tx++;
> +			continue;
> +		}
> +
> +		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +			break;
> +
> +		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		port->icount.tx++;
> +	}
> +
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(port);
> +
> +	if (uart_circ_empty(xmit))
> +		mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control |= (UARTn_CTRL_TX_ENABLE		|
> +		    UARTn_CTRL_TX_INT_ENABLE		|
> +		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +	mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}
> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +	struct tty_port *tport = &port->state->port;
> +
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +		port->icount.rx++;
> +		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +	}
> +
> +	spin_unlock(&port->lock);
> +	tty_flip_buffer_push(tport);
> +	spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_RX)))
> +		return IRQ_NONE;
> +
> +	spin_lock(&port->lock);
> +
> +	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +	mps2_uart_rx_chars(port);
> +
> +	spin_unlock(&port->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_TX)))
> +		return IRQ_NONE;
> +
> +	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +	mps2_uart_tx_chars(port);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +	irqreturn_t handled = IRQ_NONE;
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	spin_lock(&port->lock);
> +
> +	if (irqflag & UARTn_INT_RX_OVERRUN) {
> +		struct tty_port *tport = &port->state->port;
> +
> +		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +		port->icount.overrun++;
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	/* XXX: this shouldn't happen? */
> +	if (irqflag & UARTn_INT_TX_OVERRUN) {
> +		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	spin_unlock(&port->lock);
> +
> +	return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +	int ret;
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +			  MAKE_NAME(-rx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register rxirq (%d)\n", ret);
> +		goto err_no_rxirq;
> +	}
> +
> +	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +			  MAKE_NAME(-tx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register txirq (%d)\n", ret);
> +		goto err_no_txirq;
> +	}
> +
> +	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +			  MAKE_NAME(-overrun), mps_port);
> +
> +	if (ret)
> +		pr_info("failed to register oerrirq (%d)\n", ret);
> +	else {
> +		control |= UARTn_CTRL_RX_ENABLE			|
> +			   UARTn_CTRL_TX_ENABLE			|
> +			   UARTn_CTRL_RX_INT_ENABLE		|
> +			   UARTn_CTRL_TX_INT_ENABLE		|
> +			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +		mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +		return 0;
> +	}
> +
> +	free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +	free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +	return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	free_irq(mps_port->rx_irq, mps_port);
> +	free_irq(mps_port->tx_irq, mps_port);
> +	free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +	struct ktermios *new, struct ktermios *old)
> +{
> +	unsigned long flags;
> +	unsigned int baud, bauddiv;
> +
> +	new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +	new->c_cflag &= ~CSIZE;
> +	new->c_cflag |= CS8;
> +	new->c_cflag &= ~PARENB;
> +	new->c_cflag &= ~CSTOPB;
> +
> +	baud = uart_get_baud_rate(port, new, old,
> +			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +			DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	uart_update_timeout(port, new->c_cflag, baud);
> +	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +	spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +	return 0;
> +}
> +
> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +		port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +	return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +	.tx_empty = mps2_uart_tx_empty,
> +	.set_mctrl = mps2_uart_set_mctrl,
> +	.get_mctrl = mps2_uart_get_mctrl,
> +	.stop_tx = mps2_uart_stop_tx,
> +	.start_tx = mps2_uart_start_tx,
> +	.stop_rx = mps2_uart_stop_rx,
> +	.enable_ms = mps2_uart_enable_ms,
> +	.break_ctl = mps2_uart_break_ctl,
> +	.startup = mps2_uart_startup,
> +	.shutdown = mps2_uart_shutdown,
> +	.set_termios = mps2_uart_set_termios,
> +	.type = mps2_uart_type,
> +	.release_port = mps2_uart_release_port,
> +	.request_port = mps2_uart_request_port,
> +	.config_port = mps2_uart_config_port,
> +	.verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +		cpu_relax();
> +
> +	mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +	struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int baud = 9600;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +
> +	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +		return -ENODEV;
> +
> +	mps_port = &mps2_uart_ports[co->index];
> +
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +	.name = SERIAL_NAME,
> +	.device = uart_console_device,
> +	.write = mps2_uart_console_write,
> +	.setup = mps2_uart_console_setup,
> +	.flags = CON_PRINTBUFFER,
> +	.index = -1,
> +	.data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +	.driver_name = DRIVER_NAME,
> +	.dev_name = SERIAL_NAME,
> +	.nr = MPS2_MAX_PORTS,
> +	.cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	int id;
> +
> +	if (!np)
> +		return NULL;
> +
> +	id = of_alias_get_id(np, "serial");
> +	if (id < 0)
> +		id = 0;
> +
> +	if (WARN_ON(id >= MPS2_MAX_PORTS))
> +		return NULL;
> +
> +	mps2_uart_ports[id].port.line = id;
> +	return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +			struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(mps_port->port.membase))
> +		return PTR_ERR(mps_port->port.membase);
> +
> +	mps_port->port.mapbase = res->start;
> +	mps_port->port.mapsize = resource_size(res);
> +
> +	mps_port->rx_irq = platform_get_irq(pdev, 0);
> +	mps_port->tx_irq = platform_get_irq(pdev, 1);
> +	mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +	mps_port->port.iotype = UPIO_MEM;
> +	mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +	mps_port->port.fifosize = 1;
> +	mps_port->port.ops = &mps2_uart_pops;
> +	mps_port->port.dev = &pdev->dev;
> +
> +	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(mps_port->clk))
> +		return PTR_ERR(mps_port->clk);
> +
> +	ret = clk_prepare_enable(mps_port->clk);
> +	if (ret)
> +		return ret;
> +
> +	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +	if (!mps_port->port.uartclk)
> +		ret = -EINVAL;
> +
> +	clk_disable_unprepare(mps_port->clk);
> +
> +	return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int ret;
> +
> +	mps_port = mps2_of_get_port(pdev);
> +	if (!mps_port)
> +		return -ENODEV;
> +
> +	ret = mps2_init_port(mps_port, pdev);
> +	if (ret)
> +		return ret;
> +
> +	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, mps_port);
> +
> +	return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +	{ .compatible = "arm,mps2-uart", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +	.probe = mps2_serial_probe,
> +	.remove = mps2_serial_remove,
> +
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.of_match_table = of_match_ptr(mps2_match),
> +	},
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +	int ret;
> +
> +	ret = uart_register_driver(&mps2_uart_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&mps2_serial_driver);
> +	if (ret)
> +		uart_unregister_driver(&mps2_uart_driver);
> +
> +	pr_info("MPS2 UART driver initialized\n");
> +
> +	return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +	platform_driver_unregister(&mps2_serial_driver);
> +	uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);
> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32	113
>  
> +/* MPS2 UART */
> +#define PORT_MPS2UART	114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-07  9:26     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:26 UTC (permalink / raw)
  To: arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Pawel.Moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Gentle ping...

On 02/12/15 09:33, Vladimir Murzin wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>  	  This driver support the USART and UART ports on
>  	  Energy Micro's efm32 SoCs.
>  
> +config SERIAL_MPS2_UART_CONSOLE
> +	bool "MPS2 UART console support"
> +	depends on SERIAL_MPS2_UART
> +	select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +	bool "MPS2 UART port"
> +	depends on ARM || COMPILE_TEST
> +	select SERIAL_CORE
> +	help
> +	  This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>  	bool "EFM32 UART/USART console support"
>  	depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
>  
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> + *
> + * 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.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"
> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA				0x0
> +
> +#define UARTn_STATE				0x4
> +#define UARTn_STATE_TX_FULL			BIT(0)
> +#define UARTn_STATE_RX_FULL			BIT(1)
> +#define UARTn_STATE_TX_OVERRUN			BIT(2)
> +#define UARTn_STATE_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_CTRL				0x8
> +#define UARTn_CTRL_TX_ENABLE			BIT(0)
> +#define UARTn_CTRL_RX_ENABLE			BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
> +
> +#define UARTn_INT				0xc
> +#define UARTn_INT_TX				BIT(0)
> +#define UARTn_INT_RX				BIT(1)
> +#define UARTn_INT_TX_OVERRUN			BIT(2)
> +#define UARTn_INT_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_BAUDDIV				0x10
> +#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS				3
> +
> +struct mps2_uart_port {
> +	struct uart_port port;
> +	struct clk *clk;
> +	unsigned int tx_irq;
> +	unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +	return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +	u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +	struct circ_buf *xmit = &port->state->xmit;
> +
> +	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +		if (port->x_char) {
> +			mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +			port->x_char = 0;
> +			port->icount.tx++;
> +			continue;
> +		}
> +
> +		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +			break;
> +
> +		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		port->icount.tx++;
> +	}
> +
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(port);
> +
> +	if (uart_circ_empty(xmit))
> +		mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control |= (UARTn_CTRL_TX_ENABLE		|
> +		    UARTn_CTRL_TX_INT_ENABLE		|
> +		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +	mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}
> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +	struct tty_port *tport = &port->state->port;
> +
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +		port->icount.rx++;
> +		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +	}
> +
> +	spin_unlock(&port->lock);
> +	tty_flip_buffer_push(tport);
> +	spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_RX)))
> +		return IRQ_NONE;
> +
> +	spin_lock(&port->lock);
> +
> +	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +	mps2_uart_rx_chars(port);
> +
> +	spin_unlock(&port->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_TX)))
> +		return IRQ_NONE;
> +
> +	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +	mps2_uart_tx_chars(port);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +	irqreturn_t handled = IRQ_NONE;
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	spin_lock(&port->lock);
> +
> +	if (irqflag & UARTn_INT_RX_OVERRUN) {
> +		struct tty_port *tport = &port->state->port;
> +
> +		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +		port->icount.overrun++;
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	/* XXX: this shouldn't happen? */
> +	if (irqflag & UARTn_INT_TX_OVERRUN) {
> +		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	spin_unlock(&port->lock);
> +
> +	return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +	int ret;
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +			  MAKE_NAME(-rx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register rxirq (%d)\n", ret);
> +		goto err_no_rxirq;
> +	}
> +
> +	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +			  MAKE_NAME(-tx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register txirq (%d)\n", ret);
> +		goto err_no_txirq;
> +	}
> +
> +	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +			  MAKE_NAME(-overrun), mps_port);
> +
> +	if (ret)
> +		pr_info("failed to register oerrirq (%d)\n", ret);
> +	else {
> +		control |= UARTn_CTRL_RX_ENABLE			|
> +			   UARTn_CTRL_TX_ENABLE			|
> +			   UARTn_CTRL_RX_INT_ENABLE		|
> +			   UARTn_CTRL_TX_INT_ENABLE		|
> +			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +		mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +		return 0;
> +	}
> +
> +	free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +	free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +	return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	free_irq(mps_port->rx_irq, mps_port);
> +	free_irq(mps_port->tx_irq, mps_port);
> +	free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +	struct ktermios *new, struct ktermios *old)
> +{
> +	unsigned long flags;
> +	unsigned int baud, bauddiv;
> +
> +	new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +	new->c_cflag &= ~CSIZE;
> +	new->c_cflag |= CS8;
> +	new->c_cflag &= ~PARENB;
> +	new->c_cflag &= ~CSTOPB;
> +
> +	baud = uart_get_baud_rate(port, new, old,
> +			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +			DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	uart_update_timeout(port, new->c_cflag, baud);
> +	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +	spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +	return 0;
> +}
> +
> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +		port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +	return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +	.tx_empty = mps2_uart_tx_empty,
> +	.set_mctrl = mps2_uart_set_mctrl,
> +	.get_mctrl = mps2_uart_get_mctrl,
> +	.stop_tx = mps2_uart_stop_tx,
> +	.start_tx = mps2_uart_start_tx,
> +	.stop_rx = mps2_uart_stop_rx,
> +	.enable_ms = mps2_uart_enable_ms,
> +	.break_ctl = mps2_uart_break_ctl,
> +	.startup = mps2_uart_startup,
> +	.shutdown = mps2_uart_shutdown,
> +	.set_termios = mps2_uart_set_termios,
> +	.type = mps2_uart_type,
> +	.release_port = mps2_uart_release_port,
> +	.request_port = mps2_uart_request_port,
> +	.config_port = mps2_uart_config_port,
> +	.verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +		cpu_relax();
> +
> +	mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +	struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int baud = 9600;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +
> +	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +		return -ENODEV;
> +
> +	mps_port = &mps2_uart_ports[co->index];
> +
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +	.name = SERIAL_NAME,
> +	.device = uart_console_device,
> +	.write = mps2_uart_console_write,
> +	.setup = mps2_uart_console_setup,
> +	.flags = CON_PRINTBUFFER,
> +	.index = -1,
> +	.data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +	.driver_name = DRIVER_NAME,
> +	.dev_name = SERIAL_NAME,
> +	.nr = MPS2_MAX_PORTS,
> +	.cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	int id;
> +
> +	if (!np)
> +		return NULL;
> +
> +	id = of_alias_get_id(np, "serial");
> +	if (id < 0)
> +		id = 0;
> +
> +	if (WARN_ON(id >= MPS2_MAX_PORTS))
> +		return NULL;
> +
> +	mps2_uart_ports[id].port.line = id;
> +	return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +			struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(mps_port->port.membase))
> +		return PTR_ERR(mps_port->port.membase);
> +
> +	mps_port->port.mapbase = res->start;
> +	mps_port->port.mapsize = resource_size(res);
> +
> +	mps_port->rx_irq = platform_get_irq(pdev, 0);
> +	mps_port->tx_irq = platform_get_irq(pdev, 1);
> +	mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +	mps_port->port.iotype = UPIO_MEM;
> +	mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +	mps_port->port.fifosize = 1;
> +	mps_port->port.ops = &mps2_uart_pops;
> +	mps_port->port.dev = &pdev->dev;
> +
> +	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(mps_port->clk))
> +		return PTR_ERR(mps_port->clk);
> +
> +	ret = clk_prepare_enable(mps_port->clk);
> +	if (ret)
> +		return ret;
> +
> +	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +	if (!mps_port->port.uartclk)
> +		ret = -EINVAL;
> +
> +	clk_disable_unprepare(mps_port->clk);
> +
> +	return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int ret;
> +
> +	mps_port = mps2_of_get_port(pdev);
> +	if (!mps_port)
> +		return -ENODEV;
> +
> +	ret = mps2_init_port(mps_port, pdev);
> +	if (ret)
> +		return ret;
> +
> +	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, mps_port);
> +
> +	return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +	{ .compatible = "arm,mps2-uart", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +	.probe = mps2_serial_probe,
> +	.remove = mps2_serial_remove,
> +
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.of_match_table = of_match_ptr(mps2_match),
> +	},
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +	int ret;
> +
> +	ret = uart_register_driver(&mps2_uart_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&mps2_serial_driver);
> +	if (ret)
> +		uart_unregister_driver(&mps2_uart_driver);
> +
> +	pr_info("MPS2 UART driver initialized\n");
> +
> +	return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +	platform_driver_unregister(&mps2_serial_driver);
> +	uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);
> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32	113
>  
> +/* MPS2 UART */
> +#define PORT_MPS2UART	114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-07  9:26     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-07  9:26 UTC (permalink / raw)
  To: linux-arm-kernel

Gentle ping...

On 02/12/15 09:33, Vladimir Murzin wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>  	  This driver support the USART and UART ports on
>  	  Energy Micro's efm32 SoCs.
>  
> +config SERIAL_MPS2_UART_CONSOLE
> +	bool "MPS2 UART console support"
> +	depends on SERIAL_MPS2_UART
> +	select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +	bool "MPS2 UART port"
> +	depends on ARM || COMPILE_TEST
> +	select SERIAL_CORE
> +	help
> +	  This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>  	bool "EFM32 UART/USART console support"
>  	depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o
>  
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"
> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA				0x0
> +
> +#define UARTn_STATE				0x4
> +#define UARTn_STATE_TX_FULL			BIT(0)
> +#define UARTn_STATE_RX_FULL			BIT(1)
> +#define UARTn_STATE_TX_OVERRUN			BIT(2)
> +#define UARTn_STATE_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_CTRL				0x8
> +#define UARTn_CTRL_TX_ENABLE			BIT(0)
> +#define UARTn_CTRL_RX_ENABLE			BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE		BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE		BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE	BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE	BIT(5)
> +
> +#define UARTn_INT				0xc
> +#define UARTn_INT_TX				BIT(0)
> +#define UARTn_INT_RX				BIT(1)
> +#define UARTn_INT_TX_OVERRUN			BIT(2)
> +#define UARTn_INT_RX_OVERRUN			BIT(3)
> +
> +#define UARTn_BAUDDIV				0x10
> +#define UARTn_BAUDDIV_MASK			GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS				3
> +
> +struct mps2_uart_port {
> +	struct uart_port port;
> +	struct clk *clk;
> +	unsigned int tx_irq;
> +	unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +	return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +	writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +	u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +	return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +	struct circ_buf *xmit = &port->state->xmit;
> +
> +	while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +		if (port->x_char) {
> +			mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +			port->x_char = 0;
> +			port->icount.tx++;
> +			continue;
> +		}
> +
> +		if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +			break;
> +
> +		mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		port->icount.tx++;
> +	}
> +
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(port);
> +
> +	if (uart_circ_empty(xmit))
> +		mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control |= (UARTn_CTRL_TX_ENABLE		|
> +		    UARTn_CTRL_TX_INT_ENABLE		|
> +		    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +	mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}
> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +	struct tty_port *tport = &port->state->port;
> +
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +		u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +		port->icount.rx++;
> +		tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +	}
> +
> +	spin_unlock(&port->lock);
> +	tty_flip_buffer_push(tport);
> +	spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_RX)))
> +		return IRQ_NONE;
> +
> +	spin_lock(&port->lock);
> +
> +	mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +	mps2_uart_rx_chars(port);
> +
> +	spin_unlock(&port->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	if (unlikely(!(irqflag & UARTn_INT_TX)))
> +		return IRQ_NONE;
> +
> +	mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +	mps2_uart_tx_chars(port);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +	irqreturn_t handled = IRQ_NONE;
> +	struct uart_port *port = data;
> +	u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +	spin_lock(&port->lock);
> +
> +	if (irqflag & UARTn_INT_RX_OVERRUN) {
> +		struct tty_port *tport = &port->state->port;
> +
> +		mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +		port->icount.overrun++;
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	/* XXX: this shouldn't happen? */
> +	if (irqflag & UARTn_INT_TX_OVERRUN) {
> +		mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +		handled = IRQ_HANDLED;
> +	}
> +
> +	spin_unlock(&port->lock);
> +
> +	return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +	int ret;
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +			  MAKE_NAME(-rx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register rxirq (%d)\n", ret);
> +		goto err_no_rxirq;
> +	}
> +
> +	ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +			  MAKE_NAME(-tx), mps_port);
> +	if (ret) {
> +		pr_info("failed to register txirq (%d)\n", ret);
> +		goto err_no_txirq;
> +	}
> +
> +	ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +			  MAKE_NAME(-overrun), mps_port);
> +
> +	if (ret)
> +		pr_info("failed to register oerrirq (%d)\n", ret);
> +	else {
> +		control |= UARTn_CTRL_RX_ENABLE			|
> +			   UARTn_CTRL_TX_ENABLE			|
> +			   UARTn_CTRL_RX_INT_ENABLE		|
> +			   UARTn_CTRL_TX_INT_ENABLE		|
> +			   UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +			   UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +		mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +		return 0;
> +	}
> +
> +	free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +	free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +	return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +	struct mps2_uart_port *mps_port = to_mps2_port(port);
> +	u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +	control &= ~(UARTn_CTRL_RX_ENABLE		|
> +		     UARTn_CTRL_TX_ENABLE		|
> +		     UARTn_CTRL_RX_INT_ENABLE		|
> +		     UARTn_CTRL_TX_INT_ENABLE		|
> +		     UARTn_CTRL_RX_OVERRUN_INT_ENABLE	|
> +		     UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +	mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +	free_irq(mps_port->rx_irq, mps_port);
> +	free_irq(mps_port->tx_irq, mps_port);
> +	free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +	struct ktermios *new, struct ktermios *old)
> +{
> +	unsigned long flags;
> +	unsigned int baud, bauddiv;
> +
> +	new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +	new->c_cflag &= ~CSIZE;
> +	new->c_cflag |= CS8;
> +	new->c_cflag &= ~PARENB;
> +	new->c_cflag &= ~CSTOPB;
> +
> +	baud = uart_get_baud_rate(port, new, old,
> +			DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +			DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +	bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +
> +	uart_update_timeout(port, new->c_cflag, baud);
> +	mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +	spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +	return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +	return 0;
> +}
> +
> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +	if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +		port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +	return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +	.tx_empty = mps2_uart_tx_empty,
> +	.set_mctrl = mps2_uart_set_mctrl,
> +	.get_mctrl = mps2_uart_get_mctrl,
> +	.stop_tx = mps2_uart_stop_tx,
> +	.start_tx = mps2_uart_start_tx,
> +	.stop_rx = mps2_uart_stop_rx,
> +	.enable_ms = mps2_uart_enable_ms,
> +	.break_ctl = mps2_uart_break_ctl,
> +	.startup = mps2_uart_startup,
> +	.shutdown = mps2_uart_shutdown,
> +	.set_termios = mps2_uart_set_termios,
> +	.type = mps2_uart_type,
> +	.release_port = mps2_uart_release_port,
> +	.request_port = mps2_uart_request_port,
> +	.config_port = mps2_uart_config_port,
> +	.verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +	while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +		cpu_relax();
> +
> +	mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +	struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +	uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int baud = 9600;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +
> +	if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +		return -ENODEV;
> +
> +	mps_port = &mps2_uart_ports[co->index];
> +
> +	if (options)
> +		uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +	return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +	.name = SERIAL_NAME,
> +	.device = uart_console_device,
> +	.write = mps2_uart_console_write,
> +	.setup = mps2_uart_console_setup,
> +	.flags = CON_PRINTBUFFER,
> +	.index = -1,
> +	.data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +	.driver_name = DRIVER_NAME,
> +	.dev_name = SERIAL_NAME,
> +	.nr = MPS2_MAX_PORTS,
> +	.cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	int id;
> +
> +	if (!np)
> +		return NULL;
> +
> +	id = of_alias_get_id(np, "serial");
> +	if (id < 0)
> +		id = 0;
> +
> +	if (WARN_ON(id >= MPS2_MAX_PORTS))
> +		return NULL;
> +
> +	mps2_uart_ports[id].port.line = id;
> +	return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +			struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(mps_port->port.membase))
> +		return PTR_ERR(mps_port->port.membase);
> +
> +	mps_port->port.mapbase = res->start;
> +	mps_port->port.mapsize = resource_size(res);
> +
> +	mps_port->rx_irq = platform_get_irq(pdev, 0);
> +	mps_port->tx_irq = platform_get_irq(pdev, 1);
> +	mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +	mps_port->port.iotype = UPIO_MEM;
> +	mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +	mps_port->port.fifosize = 1;
> +	mps_port->port.ops = &mps2_uart_pops;
> +	mps_port->port.dev = &pdev->dev;
> +
> +	mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(mps_port->clk))
> +		return PTR_ERR(mps_port->clk);
> +
> +	ret = clk_prepare_enable(mps_port->clk);
> +	if (ret)
> +		return ret;
> +
> +	mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +	if (!mps_port->port.uartclk)
> +		ret = -EINVAL;
> +
> +	clk_disable_unprepare(mps_port->clk);
> +
> +	return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port;
> +	int ret;
> +
> +	mps_port = mps2_of_get_port(pdev);
> +	if (!mps_port)
> +		return -ENODEV;
> +
> +	ret = mps2_init_port(mps_port, pdev);
> +	if (ret)
> +		return ret;
> +
> +	ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, mps_port);
> +
> +	return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +	struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +	uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +	{ .compatible = "arm,mps2-uart", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +	.probe = mps2_serial_probe,
> +	.remove = mps2_serial_remove,
> +
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.of_match_table = of_match_ptr(mps2_match),
> +	},
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +	int ret;
> +
> +	ret = uart_register_driver(&mps2_uart_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = platform_driver_register(&mps2_serial_driver);
> +	if (ret)
> +		uart_unregister_driver(&mps2_uart_driver);
> +
> +	pr_info("MPS2 UART driver initialized\n");
> +
> +	return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +	platform_driver_unregister(&mps2_serial_driver);
> +	uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);
> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32	113
>  
> +/* MPS2 UART */
> +#define PORT_MPS2UART	114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-12 23:39     ` Andy Shevchenko
  0 siblings, 0 replies; 57+ messages in thread
From: Andy Shevchenko @ 2015-12-12 23:39 UTC (permalink / raw)
  To: Vladimir Murzin
  Cc: Arnd Bergmann, Russell King, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Uwe Kleine-König, Andreas Färber,
	Maxime Coquelin, Mark Rutland, Pawel Moll, ijc+devicetree,
	Kumar Gala, Jiri Slaby, Rob Herring, devicetree, linux-serial,
	linux-api, linux-arm Mailing List, linux-kernel

On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
<vladimir.murzin@arm.com> wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.

Just few comments (have neither time not big desire to do full review).

>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>           This driver support the USART and UART ports on
>           Energy Micro's efm32 SoCs.
>
> +config SERIAL_MPS2_UART_CONSOLE
> +       bool "MPS2 UART console support"
> +       depends on SERIAL_MPS2_UART
> +       select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +       bool "MPS2 UART port"
> +       depends on ARM || COMPILE_TEST
> +       select SERIAL_CORE
> +       help
> +         This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>         bool "EFM32 UART/USART console support"
>         depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
>
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"

Can it be ttyS?

> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA                             0x0

I think it makes sense to have same width for all offsets, i.e. 0x00
here and so on below.

> +
> +#define UARTn_STATE                            0x4
> +#define UARTn_STATE_TX_FULL                    BIT(0)
> +#define UARTn_STATE_RX_FULL                    BIT(1)
> +#define UARTn_STATE_TX_OVERRUN                 BIT(2)
> +#define UARTn_STATE_RX_OVERRUN                 BIT(3)
> +
> +#define UARTn_CTRL                             0x8
> +#define UARTn_CTRL_TX_ENABLE                   BIT(0)
> +#define UARTn_CTRL_RX_ENABLE                   BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE               BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE               BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE       BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE       BIT(5)
> +
> +#define UARTn_INT                              0xc
> +#define UARTn_INT_TX                           BIT(0)
> +#define UARTn_INT_RX                           BIT(1)
> +#define UARTn_INT_TX_OVERRUN                   BIT(2)
> +#define UARTn_INT_RX_OVERRUN                   BIT(3)
> +
> +#define UARTn_BAUDDIV                          0x10
> +#define UARTn_BAUDDIV_MASK                     GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS                         3
> +
> +struct mps2_uart_port {
> +       struct uart_port port;
> +       struct clk *clk;
> +       unsigned int tx_irq;
> +       unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +       return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +       u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +       return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +       return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +       struct circ_buf *xmit = &port->state->xmit;
> +
> +       while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +               if (port->x_char) {
> +                       mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +                       port->x_char = 0;
> +                       port->icount.tx++;
> +                       continue;
> +               }
> +
> +               if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +                       break;
> +
> +               mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);

More flexible here to use % UART_XMIT_SIZE. I believe compiler makes it optimal.

> +               port->icount.tx++;
> +       }
> +
> +       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +               uart_write_wakeup(port);
> +
> +       if (uart_circ_empty(xmit))
> +               mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control |= (UARTn_CTRL_TX_ENABLE                |
> +                   UARTn_CTRL_TX_INT_ENABLE            |
> +                   UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +       mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}

Are those required to be present? If not, remove them until you have
alive code there.

> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +       struct tty_port *tport = &port->state->port;
> +
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +               u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +               port->icount.rx++;
> +               tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +       }
> +
> +       spin_unlock(&port->lock);
> +       tty_flip_buffer_push(tport);
> +       spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_RX)))
> +               return IRQ_NONE;
> +
> +       spin_lock(&port->lock);
> +
> +       mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +       mps2_uart_rx_chars(port);
> +
> +       spin_unlock(&port->lock);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_TX)))
> +               return IRQ_NONE;
> +
> +       mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +       mps2_uart_tx_chars(port);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +       irqreturn_t handled = IRQ_NONE;
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       spin_lock(&port->lock);
> +
> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
> +               struct tty_port *tport = &port->state->port;
> +
> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +               port->icount.overrun++;
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       /* XXX: this shouldn't happen? */

If shouldn't why it's there? Otherwise better to explain which
conditions may lead to this.

> +       if (irqflag & UARTn_INT_TX_OVERRUN) {
> +               mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       spin_unlock(&port->lock);
> +
> +       return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +       int ret;
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);


If you unifyf both RX and TX groups you may use them here, and above.

control &= ~(UART…RX | UART…TX);

Above case just RX. Maybe something alike below.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +                         MAKE_NAME(-rx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register rxirq (%d)\n", ret);

And why not _err() and even dev_err()? Same for stuff below.

> +               goto err_no_rxirq;
> +       }
> +
> +       ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +                         MAKE_NAME(-tx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register txirq (%d)\n", ret);
> +               goto err_no_txirq;
> +       }
> +
> +       ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +                         MAKE_NAME(-overrun), mps_port);
> +
> +       if (ret)
> +               pr_info("failed to register oerrirq (%d)\n", ret);
> +       else {

Keep style according to requirement.
} else {

> +               control |= UARTn_CTRL_RX_ENABLE                 |
> +                          UARTn_CTRL_TX_ENABLE                 |
> +                          UARTn_CTRL_RX_INT_ENABLE             |
> +                          UARTn_CTRL_TX_INT_ENABLE             |
> +                          UARTn_CTRL_RX_OVERRUN_INT_ENABLE     |
> +                          UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +               mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +               return 0;
> +       }
> +
> +       free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +       free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +       return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);

TX group + RX group, see above.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       free_irq(mps_port->rx_irq, mps_port);
> +       free_irq(mps_port->tx_irq, mps_port);
> +       free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +       struct ktermios *new, struct ktermios *old)
> +{
> +       unsigned long flags;
> +       unsigned int baud, bauddiv;
> +
> +       new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +       new->c_cflag &= ~CSIZE;
> +       new->c_cflag |= CS8;
> +       new->c_cflag &= ~PARENB;
> +       new->c_cflag &= ~CSTOPB;
> +
> +       baud = uart_get_baud_rate(port, new, old,
> +                       DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +                       DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +       bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +       spin_lock_irqsave(&port->lock, flags);
> +
> +       uart_update_timeout(port, new->c_cflag, baud);
> +       mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +       spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +       return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +       return 0;
> +}
> +

Same question about empty stubs.

> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +       if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +               port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +       return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +       .tx_empty = mps2_uart_tx_empty,
> +       .set_mctrl = mps2_uart_set_mctrl,
> +       .get_mctrl = mps2_uart_get_mctrl,
> +       .stop_tx = mps2_uart_stop_tx,
> +       .start_tx = mps2_uart_start_tx,
> +       .stop_rx = mps2_uart_stop_rx,
> +       .enable_ms = mps2_uart_enable_ms,
> +       .break_ctl = mps2_uart_break_ctl,
> +       .startup = mps2_uart_startup,
> +       .shutdown = mps2_uart_shutdown,
> +       .set_termios = mps2_uart_set_termios,
> +       .type = mps2_uart_type,
> +       .release_port = mps2_uart_release_port,
> +       .request_port = mps2_uart_request_port,
> +       .config_port = mps2_uart_config_port,
> +       .verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +               cpu_relax();
> +
> +       mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +       struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +       uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int baud = 9600;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +
> +       if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +               return -ENODEV;
> +
> +       mps_port = &mps2_uart_ports[co->index];
> +
> +       if (options)
> +               uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +       return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +       .name = SERIAL_NAME,
> +       .device = uart_console_device,
> +       .write = mps2_uart_console_write,
> +       .setup = mps2_uart_console_setup,
> +       .flags = CON_PRINTBUFFER,
> +       .index = -1,
> +       .data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +       .driver_name = DRIVER_NAME,
> +       .dev_name = SERIAL_NAME,
> +       .nr = MPS2_MAX_PORTS,
> +       .cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       int id;
> +
> +       if (!np)
> +               return NULL;
> +
> +       id = of_alias_get_id(np, "serial");
> +       if (id < 0)
> +               id = 0;
> +
> +       if (WARN_ON(id >= MPS2_MAX_PORTS))
> +               return NULL;
> +
> +       mps2_uart_ports[id].port.line = id;
> +       return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +                       struct platform_device *pdev)
> +{
> +       int ret = 0;

Redundant assignment.

> +       struct resource *res;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(mps_port->port.membase))
> +               return PTR_ERR(mps_port->port.membase);
> +
> +       mps_port->port.mapbase = res->start;
> +       mps_port->port.mapsize = resource_size(res);
> +
> +       mps_port->rx_irq = platform_get_irq(pdev, 0);
> +       mps_port->tx_irq = platform_get_irq(pdev, 1);
> +       mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +       mps_port->port.iotype = UPIO_MEM;
> +       mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +       mps_port->port.fifosize = 1;
> +       mps_port->port.ops = &mps2_uart_pops;
> +       mps_port->port.dev = &pdev->dev;
> +
> +       mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(mps_port->clk))
> +               return PTR_ERR(mps_port->clk);
> +
> +       ret = clk_prepare_enable(mps_port->clk);
> +       if (ret)
> +               return ret;
> +
> +       mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +       if (!mps_port->port.uartclk)
> +               ret = -EINVAL;

So, 1 is OK, 0 is not. I think 1 is too low to be provided by hardware.

> +
> +       clk_disable_unprepare(mps_port->clk);
> +
> +       return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int ret;
> +
> +       mps_port = mps2_of_get_port(pdev);
> +       if (!mps_port)
> +               return -ENODEV;
> +
> +       ret = mps2_init_port(mps_port, pdev);
> +       if (ret)
> +               return ret;
> +
> +       ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, mps_port);
> +
> +       return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +       uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +       { .compatible = "arm,mps2-uart", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +       .probe = mps2_serial_probe,
> +       .remove = mps2_serial_remove,
> +
> +       .driver = {
> +               .name = DRIVER_NAME,
> +               .of_match_table = of_match_ptr(mps2_match),
> +       },
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +       int ret;
> +
> +       ret = uart_register_driver(&mps2_uart_driver);
> +       if (ret)
> +               return ret;
> +
> +       ret = platform_driver_register(&mps2_serial_driver);
> +       if (ret)
> +               uart_unregister_driver(&mps2_uart_driver);
> +
> +       pr_info("MPS2 UART driver initialized\n");
> +
> +       return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +       platform_driver_unregister(&mps2_serial_driver);
> +       uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);

module_platform_driver();
And move uart_*register calls to probe/remove.

> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32     113
>
> +/* MPS2 UART */
> +#define PORT_MPS2UART  114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-12 23:39     ` Andy Shevchenko
  0 siblings, 0 replies; 57+ messages in thread
From: Andy Shevchenko @ 2015-12-12 23:39 UTC (permalink / raw)
  To: Vladimir Murzin
  Cc: Arnd Bergmann, Russell King, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Uwe Kleine-König, Andreas Färber,
	Maxime Coquelin, Mark Rutland, Pawel Moll, ijc+devicetree,
	Kumar Gala, Jiri Slaby, Rob Herring, devicetree,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, linux-arm Mailing List,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
<vladimir.murzin-5wv7dgnIgG8@public.gmane.org> wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.

Just few comments (have neither time not big desire to do full review).

>
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>           This driver support the USART and UART ports on
>           Energy Micro's efm32 SoCs.
>
> +config SERIAL_MPS2_UART_CONSOLE
> +       bool "MPS2 UART console support"
> +       depends on SERIAL_MPS2_UART
> +       select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +       bool "MPS2 UART port"
> +       depends on ARM || COMPILE_TEST
> +       select SERIAL_CORE
> +       help
> +         This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>         bool "EFM32 UART/USART console support"
>         depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
>
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> + *
> + * 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.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"

Can it be ttyS?

> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA                             0x0

I think it makes sense to have same width for all offsets, i.e. 0x00
here and so on below.

> +
> +#define UARTn_STATE                            0x4
> +#define UARTn_STATE_TX_FULL                    BIT(0)
> +#define UARTn_STATE_RX_FULL                    BIT(1)
> +#define UARTn_STATE_TX_OVERRUN                 BIT(2)
> +#define UARTn_STATE_RX_OVERRUN                 BIT(3)
> +
> +#define UARTn_CTRL                             0x8
> +#define UARTn_CTRL_TX_ENABLE                   BIT(0)
> +#define UARTn_CTRL_RX_ENABLE                   BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE               BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE               BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE       BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE       BIT(5)
> +
> +#define UARTn_INT                              0xc
> +#define UARTn_INT_TX                           BIT(0)
> +#define UARTn_INT_RX                           BIT(1)
> +#define UARTn_INT_TX_OVERRUN                   BIT(2)
> +#define UARTn_INT_RX_OVERRUN                   BIT(3)
> +
> +#define UARTn_BAUDDIV                          0x10
> +#define UARTn_BAUDDIV_MASK                     GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS                         3
> +
> +struct mps2_uart_port {
> +       struct uart_port port;
> +       struct clk *clk;
> +       unsigned int tx_irq;
> +       unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +       return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +       u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +       return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +       return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +       struct circ_buf *xmit = &port->state->xmit;
> +
> +       while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +               if (port->x_char) {
> +                       mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +                       port->x_char = 0;
> +                       port->icount.tx++;
> +                       continue;
> +               }
> +
> +               if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +                       break;
> +
> +               mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);

More flexible here to use % UART_XMIT_SIZE. I believe compiler makes it optimal.

> +               port->icount.tx++;
> +       }
> +
> +       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +               uart_write_wakeup(port);
> +
> +       if (uart_circ_empty(xmit))
> +               mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control |= (UARTn_CTRL_TX_ENABLE                |
> +                   UARTn_CTRL_TX_INT_ENABLE            |
> +                   UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +       mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}

Are those required to be present? If not, remove them until you have
alive code there.

> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +       struct tty_port *tport = &port->state->port;
> +
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +               u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +               port->icount.rx++;
> +               tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +       }
> +
> +       spin_unlock(&port->lock);
> +       tty_flip_buffer_push(tport);
> +       spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_RX)))
> +               return IRQ_NONE;
> +
> +       spin_lock(&port->lock);
> +
> +       mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +       mps2_uart_rx_chars(port);
> +
> +       spin_unlock(&port->lock);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_TX)))
> +               return IRQ_NONE;
> +
> +       mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +       mps2_uart_tx_chars(port);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +       irqreturn_t handled = IRQ_NONE;
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       spin_lock(&port->lock);
> +
> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
> +               struct tty_port *tport = &port->state->port;
> +
> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +               port->icount.overrun++;
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       /* XXX: this shouldn't happen? */

If shouldn't why it's there? Otherwise better to explain which
conditions may lead to this.

> +       if (irqflag & UARTn_INT_TX_OVERRUN) {
> +               mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       spin_unlock(&port->lock);
> +
> +       return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +       int ret;
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);


If you unifyf both RX and TX groups you may use them here, and above.

control &= ~(UART…RX | UART…TX);

Above case just RX. Maybe something alike below.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +                         MAKE_NAME(-rx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register rxirq (%d)\n", ret);

And why not _err() and even dev_err()? Same for stuff below.

> +               goto err_no_rxirq;
> +       }
> +
> +       ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +                         MAKE_NAME(-tx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register txirq (%d)\n", ret);
> +               goto err_no_txirq;
> +       }
> +
> +       ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +                         MAKE_NAME(-overrun), mps_port);
> +
> +       if (ret)
> +               pr_info("failed to register oerrirq (%d)\n", ret);
> +       else {

Keep style according to requirement.
} else {

> +               control |= UARTn_CTRL_RX_ENABLE                 |
> +                          UARTn_CTRL_TX_ENABLE                 |
> +                          UARTn_CTRL_RX_INT_ENABLE             |
> +                          UARTn_CTRL_TX_INT_ENABLE             |
> +                          UARTn_CTRL_RX_OVERRUN_INT_ENABLE     |
> +                          UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +               mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +               return 0;
> +       }
> +
> +       free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +       free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +       return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);

TX group + RX group, see above.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       free_irq(mps_port->rx_irq, mps_port);
> +       free_irq(mps_port->tx_irq, mps_port);
> +       free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +       struct ktermios *new, struct ktermios *old)
> +{
> +       unsigned long flags;
> +       unsigned int baud, bauddiv;
> +
> +       new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +       new->c_cflag &= ~CSIZE;
> +       new->c_cflag |= CS8;
> +       new->c_cflag &= ~PARENB;
> +       new->c_cflag &= ~CSTOPB;
> +
> +       baud = uart_get_baud_rate(port, new, old,
> +                       DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +                       DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +       bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +       spin_lock_irqsave(&port->lock, flags);
> +
> +       uart_update_timeout(port, new->c_cflag, baud);
> +       mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +       spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +       return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +       return 0;
> +}
> +

Same question about empty stubs.

> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +       if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +               port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +       return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +       .tx_empty = mps2_uart_tx_empty,
> +       .set_mctrl = mps2_uart_set_mctrl,
> +       .get_mctrl = mps2_uart_get_mctrl,
> +       .stop_tx = mps2_uart_stop_tx,
> +       .start_tx = mps2_uart_start_tx,
> +       .stop_rx = mps2_uart_stop_rx,
> +       .enable_ms = mps2_uart_enable_ms,
> +       .break_ctl = mps2_uart_break_ctl,
> +       .startup = mps2_uart_startup,
> +       .shutdown = mps2_uart_shutdown,
> +       .set_termios = mps2_uart_set_termios,
> +       .type = mps2_uart_type,
> +       .release_port = mps2_uart_release_port,
> +       .request_port = mps2_uart_request_port,
> +       .config_port = mps2_uart_config_port,
> +       .verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +               cpu_relax();
> +
> +       mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +       struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +       uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int baud = 9600;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +
> +       if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +               return -ENODEV;
> +
> +       mps_port = &mps2_uart_ports[co->index];
> +
> +       if (options)
> +               uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +       return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +       .name = SERIAL_NAME,
> +       .device = uart_console_device,
> +       .write = mps2_uart_console_write,
> +       .setup = mps2_uart_console_setup,
> +       .flags = CON_PRINTBUFFER,
> +       .index = -1,
> +       .data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +       .driver_name = DRIVER_NAME,
> +       .dev_name = SERIAL_NAME,
> +       .nr = MPS2_MAX_PORTS,
> +       .cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       int id;
> +
> +       if (!np)
> +               return NULL;
> +
> +       id = of_alias_get_id(np, "serial");
> +       if (id < 0)
> +               id = 0;
> +
> +       if (WARN_ON(id >= MPS2_MAX_PORTS))
> +               return NULL;
> +
> +       mps2_uart_ports[id].port.line = id;
> +       return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +                       struct platform_device *pdev)
> +{
> +       int ret = 0;

Redundant assignment.

> +       struct resource *res;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(mps_port->port.membase))
> +               return PTR_ERR(mps_port->port.membase);
> +
> +       mps_port->port.mapbase = res->start;
> +       mps_port->port.mapsize = resource_size(res);
> +
> +       mps_port->rx_irq = platform_get_irq(pdev, 0);
> +       mps_port->tx_irq = platform_get_irq(pdev, 1);
> +       mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +       mps_port->port.iotype = UPIO_MEM;
> +       mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +       mps_port->port.fifosize = 1;
> +       mps_port->port.ops = &mps2_uart_pops;
> +       mps_port->port.dev = &pdev->dev;
> +
> +       mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(mps_port->clk))
> +               return PTR_ERR(mps_port->clk);
> +
> +       ret = clk_prepare_enable(mps_port->clk);
> +       if (ret)
> +               return ret;
> +
> +       mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +       if (!mps_port->port.uartclk)
> +               ret = -EINVAL;

So, 1 is OK, 0 is not. I think 1 is too low to be provided by hardware.

> +
> +       clk_disable_unprepare(mps_port->clk);
> +
> +       return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int ret;
> +
> +       mps_port = mps2_of_get_port(pdev);
> +       if (!mps_port)
> +               return -ENODEV;
> +
> +       ret = mps2_init_port(mps_port, pdev);
> +       if (ret)
> +               return ret;
> +
> +       ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, mps_port);
> +
> +       return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +       uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +       { .compatible = "arm,mps2-uart", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +       .probe = mps2_serial_probe,
> +       .remove = mps2_serial_remove,
> +
> +       .driver = {
> +               .name = DRIVER_NAME,
> +               .of_match_table = of_match_ptr(mps2_match),
> +       },
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +       int ret;
> +
> +       ret = uart_register_driver(&mps2_uart_driver);
> +       if (ret)
> +               return ret;
> +
> +       ret = platform_driver_register(&mps2_serial_driver);
> +       if (ret)
> +               uart_unregister_driver(&mps2_uart_driver);
> +
> +       pr_info("MPS2 UART driver initialized\n");
> +
> +       return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +       platform_driver_unregister(&mps2_serial_driver);
> +       uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);

module_platform_driver();
And move uart_*register calls to probe/remove.

> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32     113
>
> +/* MPS2 UART */
> +#define PORT_MPS2UART  114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-12 23:39     ` Andy Shevchenko
  0 siblings, 0 replies; 57+ messages in thread
From: Andy Shevchenko @ 2015-12-12 23:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
<vladimir.murzin@arm.com> wrote:
> This driver adds support to the UART controller found on ARM MPS2
> platform.

Just few comments (have neither time not big desire to do full review).

>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/tty/serial/Kconfig       |   12 +
>  drivers/tty/serial/Makefile      |    1 +
>  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h |    3 +
>  4 files changed, 612 insertions(+)
>  create mode 100644 drivers/tty/serial/mps2-uart.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index f38beb2..e98bfea 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
>           This driver support the USART and UART ports on
>           Energy Micro's efm32 SoCs.
>
> +config SERIAL_MPS2_UART_CONSOLE
> +       bool "MPS2 UART console support"
> +       depends on SERIAL_MPS2_UART
> +       select SERIAL_CORE_CONSOLE
> +
> +config SERIAL_MPS2_UART
> +       bool "MPS2 UART port"
> +       depends on ARM || COMPILE_TEST
> +       select SERIAL_CORE
> +       help
> +         This driver support the UART ports on ARM MPS2.
> +
>  config SERIAL_EFM32_UART_CONSOLE
>         bool "EFM32 UART/USART console support"
>         depends on SERIAL_EFM32_UART=y
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 5ab4111..7f589f5 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
>  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
>  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
>
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> new file mode 100644
> index 0000000..09bac16
> --- /dev/null
> +++ b/drivers/tty/serial/mps2-uart.c
> @@ -0,0 +1,596 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * TODO: support for SysRq
> + */
> +
> +#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty_flip.h>
> +#include <linux/types.h>
> +
> +#define SERIAL_NAME "ttyMPS"

Can it be ttyS?

> +#define DRIVER_NAME "mps2-uart"
> +#define MAKE_NAME(x) (DRIVER_NAME # x)
> +
> +#define UARTn_DATA                             0x0

I think it makes sense to have same width for all offsets, i.e. 0x00
here and so on below.

> +
> +#define UARTn_STATE                            0x4
> +#define UARTn_STATE_TX_FULL                    BIT(0)
> +#define UARTn_STATE_RX_FULL                    BIT(1)
> +#define UARTn_STATE_TX_OVERRUN                 BIT(2)
> +#define UARTn_STATE_RX_OVERRUN                 BIT(3)
> +
> +#define UARTn_CTRL                             0x8
> +#define UARTn_CTRL_TX_ENABLE                   BIT(0)
> +#define UARTn_CTRL_RX_ENABLE                   BIT(1)
> +#define UARTn_CTRL_TX_INT_ENABLE               BIT(2)
> +#define UARTn_CTRL_RX_INT_ENABLE               BIT(3)
> +#define UARTn_CTRL_TX_OVERRUN_INT_ENABLE       BIT(4)
> +#define UARTn_CTRL_RX_OVERRUN_INT_ENABLE       BIT(5)
> +
> +#define UARTn_INT                              0xc
> +#define UARTn_INT_TX                           BIT(0)
> +#define UARTn_INT_RX                           BIT(1)
> +#define UARTn_INT_TX_OVERRUN                   BIT(2)
> +#define UARTn_INT_RX_OVERRUN                   BIT(3)
> +
> +#define UARTn_BAUDDIV                          0x10
> +#define UARTn_BAUDDIV_MASK                     GENMASK(20, 0)
> +
> +#define MPS2_MAX_PORTS                         3
> +
> +struct mps2_uart_port {
> +       struct uart_port port;
> +       struct clk *clk;
> +       unsigned int tx_irq;
> +       unsigned int rx_irq;
> +};
> +
> +static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
> +{
> +       return container_of(port, struct mps2_uart_port, port);
> +}
> +
> +static void mps2_uart_write8(struct uart_port *port, u8 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writeb(val, mps_port->port.membase + off);
> +}
> +
> +static u8 mps2_uart_read8(struct uart_port *port, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       return readb(mps_port->port.membase + off);
> +}
> +
> +static void mps2_uart_write32(struct uart_port *port, u32 val, unsigned off)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +
> +       writel_relaxed(val, mps_port->port.membase + off);
> +}
> +
> +static unsigned int mps2_uart_tx_empty(struct uart_port *port)
> +{
> +       u8 status = mps2_uart_read8(port, UARTn_STATE);
> +
> +       return (status & UARTn_STATE_TX_FULL) ? 0 : TIOCSER_TEMT;
> +}
> +
> +static void mps2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +}
> +
> +static unsigned int mps2_uart_get_mctrl(struct uart_port *port)
> +{
> +       return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
> +}
> +
> +static void mps2_uart_stop_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_tx_chars(struct uart_port *port)
> +{
> +       struct circ_buf *xmit = &port->state->xmit;
> +
> +       while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
> +               if (port->x_char) {
> +                       mps2_uart_write8(port, port->x_char, UARTn_DATA);
> +                       port->x_char = 0;
> +                       port->icount.tx++;
> +                       continue;
> +               }
> +
> +               if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> +                       break;
> +
> +               mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
> +               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);

More flexible here to use % UART_XMIT_SIZE. I believe compiler makes it optimal.

> +               port->icount.tx++;
> +       }
> +
> +       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +               uart_write_wakeup(port);
> +
> +       if (uart_circ_empty(xmit))
> +               mps2_uart_stop_tx(port);
> +}
> +
> +static void mps2_uart_start_tx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control |= (UARTn_CTRL_TX_ENABLE                |
> +                   UARTn_CTRL_TX_INT_ENABLE            |
> +                   UARTn_CTRL_TX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +       mps2_uart_tx_chars(port);
> +}
> +
> +static void mps2_uart_stop_rx(struct uart_port *port)
> +{
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE);
> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +}
> +
> +static void mps2_uart_enable_ms(struct uart_port *port)
> +{
> +}
> +
> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
> +{
> +}

Are those required to be present? If not, remove them until you have
alive code there.

> +
> +static void mps2_uart_rx_chars(struct uart_port *port)
> +{
> +       struct tty_port *tport = &port->state->port;
> +
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_RX_FULL) {
> +               u8 rxdata = mps2_uart_read8(port, UARTn_DATA);
> +
> +               port->icount.rx++;
> +               tty_insert_flip_char(&port->state->port, rxdata, TTY_NORMAL);
> +       }
> +
> +       spin_unlock(&port->lock);
> +       tty_flip_buffer_push(tport);
> +       spin_lock(&port->lock);
> +}
> +
> +static irqreturn_t mps2_uart_rxirq(int irq, void *data)
> +{
> +
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_RX)))
> +               return IRQ_NONE;
> +
> +       spin_lock(&port->lock);
> +
> +       mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT);
> +       mps2_uart_rx_chars(port);
> +
> +       spin_unlock(&port->lock);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_txirq(int irq, void *data)
> +{
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       if (unlikely(!(irqflag & UARTn_INT_TX)))
> +               return IRQ_NONE;
> +
> +       mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT);
> +       mps2_uart_tx_chars(port);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
> +{
> +       irqreturn_t handled = IRQ_NONE;
> +       struct uart_port *port = data;
> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
> +
> +       spin_lock(&port->lock);
> +
> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
> +               struct tty_port *tport = &port->state->port;
> +
> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
> +               port->icount.overrun++;
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       /* XXX: this shouldn't happen? */

If shouldn't why it's there? Otherwise better to explain which
conditions may lead to this.

> +       if (irqflag & UARTn_INT_TX_OVERRUN) {
> +               mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
> +               handled = IRQ_HANDLED;
> +       }
> +
> +       spin_unlock(&port->lock);
> +
> +       return handled;
> +}
> +
> +static int mps2_uart_startup(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +       int ret;
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);


If you unifyf both RX and TX groups you may use them here, and above.

control &= ~(UART?RX | UART?TX);

Above case just RX. Maybe something alike below.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
> +                         MAKE_NAME(-rx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register rxirq (%d)\n", ret);

And why not _err() and even dev_err()? Same for stuff below.

> +               goto err_no_rxirq;
> +       }
> +
> +       ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
> +                         MAKE_NAME(-tx), mps_port);
> +       if (ret) {
> +               pr_info("failed to register txirq (%d)\n", ret);
> +               goto err_no_txirq;
> +       }
> +
> +       ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
> +                         MAKE_NAME(-overrun), mps_port);
> +
> +       if (ret)
> +               pr_info("failed to register oerrirq (%d)\n", ret);
> +       else {

Keep style according to requirement.
} else {

> +               control |= UARTn_CTRL_RX_ENABLE                 |
> +                          UARTn_CTRL_TX_ENABLE                 |
> +                          UARTn_CTRL_RX_INT_ENABLE             |
> +                          UARTn_CTRL_TX_INT_ENABLE             |
> +                          UARTn_CTRL_RX_OVERRUN_INT_ENABLE     |
> +                          UARTn_CTRL_TX_OVERRUN_INT_ENABLE;
> +
> +               mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +               return 0;
> +       }
> +
> +       free_irq(mps_port->tx_irq, mps_port);
> +err_no_txirq:
> +       free_irq(mps_port->rx_irq, mps_port);
> +err_no_rxirq:
> +       return ret;
> +}
> +
> +static void mps2_uart_shutdown(struct uart_port *port)
> +{
> +       struct mps2_uart_port *mps_port = to_mps2_port(port);
> +       u8 control = mps2_uart_read8(port, UARTn_CTRL);
> +
> +       control &= ~(UARTn_CTRL_RX_ENABLE               |
> +                    UARTn_CTRL_TX_ENABLE               |
> +                    UARTn_CTRL_RX_INT_ENABLE           |
> +                    UARTn_CTRL_TX_INT_ENABLE           |
> +                    UARTn_CTRL_RX_OVERRUN_INT_ENABLE   |
> +                    UARTn_CTRL_TX_OVERRUN_INT_ENABLE);

TX group + RX group, see above.

> +
> +       mps2_uart_write8(port, control, UARTn_CTRL);
> +
> +       free_irq(mps_port->rx_irq, mps_port);
> +       free_irq(mps_port->tx_irq, mps_port);
> +       free_irq(port->irq, mps_port);
> +}
> +
> +static void mps2_uart_set_termios(struct uart_port *port,
> +       struct ktermios *new, struct ktermios *old)
> +{
> +       unsigned long flags;
> +       unsigned int baud, bauddiv;
> +
> +       new->c_cflag &= ~(CRTSCTS | CMSPAR);
> +       new->c_cflag &= ~CSIZE;
> +       new->c_cflag |= CS8;
> +       new->c_cflag &= ~PARENB;
> +       new->c_cflag &= ~CSTOPB;
> +
> +       baud = uart_get_baud_rate(port, new, old,
> +                       DIV_ROUND_CLOSEST(port->uartclk, UARTn_BAUDDIV_MASK),
> +                       DIV_ROUND_CLOSEST(port->uartclk, 16));
> +
> +       bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
> +
> +       spin_lock_irqsave(&port->lock, flags);
> +
> +       uart_update_timeout(port, new->c_cflag, baud);
> +       mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV);
> +
> +       spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *mps2_uart_type(struct uart_port *port)
> +{
> +       return (port->type == PORT_MPS2UART) ? DRIVER_NAME : NULL;
> +}
> +
> +static void mps2_uart_release_port(struct uart_port *port)
> +{
> +}
> +
> +static int mps2_uart_request_port(struct uart_port *port)
> +{
> +       return 0;
> +}
> +

Same question about empty stubs.

> +static void mps2_uart_config_port(struct uart_port *port, int type)
> +{
> +       if (type & UART_CONFIG_TYPE && !mps2_uart_request_port(port))
> +               port->type = PORT_MPS2UART;
> +}
> +
> +static int mps2_uart_verify_port(struct uart_port *port, struct serial_struct *serinfo)
> +{
> +       return -EINVAL;
> +}
> +
> +static const struct uart_ops mps2_uart_pops = {
> +       .tx_empty = mps2_uart_tx_empty,
> +       .set_mctrl = mps2_uart_set_mctrl,
> +       .get_mctrl = mps2_uart_get_mctrl,
> +       .stop_tx = mps2_uart_stop_tx,
> +       .start_tx = mps2_uart_start_tx,
> +       .stop_rx = mps2_uart_stop_rx,
> +       .enable_ms = mps2_uart_enable_ms,
> +       .break_ctl = mps2_uart_break_ctl,
> +       .startup = mps2_uart_startup,
> +       .shutdown = mps2_uart_shutdown,
> +       .set_termios = mps2_uart_set_termios,
> +       .type = mps2_uart_type,
> +       .release_port = mps2_uart_release_port,
> +       .request_port = mps2_uart_request_port,
> +       .config_port = mps2_uart_config_port,
> +       .verify_port = mps2_uart_verify_port,
> +};
> +
> +static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
> +
> +#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
> +static void mps2_uart_console_putchar(struct uart_port *port, int ch)
> +{
> +       while (mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)
> +               cpu_relax();
> +
> +       mps2_uart_write8(port, ch, UARTn_DATA);
> +}
> +
> +static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
> +{
> +       struct uart_port *port = &mps2_uart_ports[co->index].port;
> +
> +       uart_console_write(port, s, cnt, mps2_uart_console_putchar);
> +}
> +
> +static int mps2_uart_console_setup(struct console *co, char *options)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int baud = 9600;
> +       int bits = 8;
> +       int parity = 'n';
> +       int flow = 'n';
> +
> +       if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
> +               return -ENODEV;
> +
> +       mps_port = &mps2_uart_ports[co->index];
> +
> +       if (options)
> +               uart_parse_options(options, &baud, &parity, &bits, &flow);
> +
> +       return uart_set_options(&mps_port->port, co, baud, parity, bits, flow);
> +}
> +
> +static struct uart_driver mps2_uart_driver;
> +
> +static struct console mps2_uart_console = {
> +       .name = SERIAL_NAME,
> +       .device = uart_console_device,
> +       .write = mps2_uart_console_write,
> +       .setup = mps2_uart_console_setup,
> +       .flags = CON_PRINTBUFFER,
> +       .index = -1,
> +       .data = &mps2_uart_driver,
> +};
> +
> +#define MPS2_SERIAL_CONSOLE (&mps2_uart_console)
> +
> +#else
> +#define MPS2_SERIAL_CONSOLE NULL
> +#endif
> +
> +static struct uart_driver mps2_uart_driver = {
> +       .driver_name = DRIVER_NAME,
> +       .dev_name = SERIAL_NAME,
> +       .nr = MPS2_MAX_PORTS,
> +       .cons = MPS2_SERIAL_CONSOLE,
> +};
> +
> +static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       int id;
> +
> +       if (!np)
> +               return NULL;
> +
> +       id = of_alias_get_id(np, "serial");
> +       if (id < 0)
> +               id = 0;
> +
> +       if (WARN_ON(id >= MPS2_MAX_PORTS))
> +               return NULL;
> +
> +       mps2_uart_ports[id].port.line = id;
> +       return &mps2_uart_ports[id];
> +}
> +
> +static int mps2_init_port(struct mps2_uart_port *mps_port,
> +                       struct platform_device *pdev)
> +{
> +       int ret = 0;

Redundant assignment.

> +       struct resource *res;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       mps_port->port.membase = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(mps_port->port.membase))
> +               return PTR_ERR(mps_port->port.membase);
> +
> +       mps_port->port.mapbase = res->start;
> +       mps_port->port.mapsize = resource_size(res);
> +
> +       mps_port->rx_irq = platform_get_irq(pdev, 0);
> +       mps_port->tx_irq = platform_get_irq(pdev, 1);
> +       mps_port->port.irq = platform_get_irq(pdev, 2);
> +
> +       mps_port->port.iotype = UPIO_MEM;
> +       mps_port->port.flags = UPF_BOOT_AUTOCONF;
> +       mps_port->port.fifosize = 1;
> +       mps_port->port.ops = &mps2_uart_pops;
> +       mps_port->port.dev = &pdev->dev;
> +
> +       mps_port->clk = devm_clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(mps_port->clk))
> +               return PTR_ERR(mps_port->clk);
> +
> +       ret = clk_prepare_enable(mps_port->clk);
> +       if (ret)
> +               return ret;
> +
> +       mps_port->port.uartclk = clk_get_rate(mps_port->clk);
> +       if (!mps_port->port.uartclk)
> +               ret = -EINVAL;

So, 1 is OK, 0 is not. I think 1 is too low to be provided by hardware.

> +
> +       clk_disable_unprepare(mps_port->clk);
> +
> +       return ret;
> +}
> +
> +static int mps2_serial_probe(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port;
> +       int ret;
> +
> +       mps_port = mps2_of_get_port(pdev);
> +       if (!mps_port)
> +               return -ENODEV;
> +
> +       ret = mps2_init_port(mps_port, pdev);
> +       if (ret)
> +               return ret;
> +
> +       ret = uart_add_one_port(&mps2_uart_driver, &mps_port->port);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, mps_port);
> +
> +       return 0;
> +}
> +
> +static int mps2_serial_remove(struct platform_device *pdev)
> +{
> +       struct mps2_uart_port *mps_port = platform_get_drvdata(pdev);
> +
> +       uart_remove_one_port(&mps2_uart_driver, &mps_port->port);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id mps2_match[] = {
> +       { .compatible = "arm,mps2-uart", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mps2_match);
> +#endif
> +
> +static struct platform_driver mps2_serial_driver = {
> +       .probe = mps2_serial_probe,
> +       .remove = mps2_serial_remove,
> +
> +       .driver = {
> +               .name = DRIVER_NAME,
> +               .of_match_table = of_match_ptr(mps2_match),
> +       },
> +};
> +
> +static int __init mps2_uart_init(void)
> +{
> +       int ret;
> +
> +       ret = uart_register_driver(&mps2_uart_driver);
> +       if (ret)
> +               return ret;
> +
> +       ret = platform_driver_register(&mps2_serial_driver);
> +       if (ret)
> +               uart_unregister_driver(&mps2_uart_driver);
> +
> +       pr_info("MPS2 UART driver initialized\n");
> +
> +       return ret;
> +}
> +module_init(mps2_uart_init);
> +
> +static void __exit mps2_uart_exit(void)
> +{
> +       platform_driver_unregister(&mps2_serial_driver);
> +       uart_unregister_driver(&mps2_uart_driver);
> +}
> +module_exit(mps2_uart_exit);

module_platform_driver();
And move uart_*register calls to probe/remove.

> +
> +MODULE_AUTHOR("Vladimir Murzin <vladimir.murzin@arm.com>");
> +MODULE_DESCRIPTION("MPS2 UART driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 93ba148..f097fe9 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -261,4 +261,7 @@
>  /* STM32 USART */
>  #define PORT_STM32     113
>
> +/* MPS2 UART */
> +#define PORT_MPS2UART  114
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
  2015-12-12 23:39     ` Andy Shevchenko
@ 2015-12-13  7:00       ` Greg Kroah-Hartman
  -1 siblings, 0 replies; 57+ messages in thread
From: Greg Kroah-Hartman @ 2015-12-13  7:00 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Vladimir Murzin, Arnd Bergmann, Russell King, Daniel Lezcano,
	Thomas Gleixner, Uwe Kleine-König, Andreas Färber,
	Maxime Coquelin, Mark Rutland, Pawel Moll, ijc+devicetree,
	Kumar Gala, Jiri Slaby, Rob Herring, devicetree, linux-serial,
	linux-api, linux-arm Mailing List, linux-kernel

On Sun, Dec 13, 2015 at 01:39:26AM +0200, Andy Shevchenko wrote:
> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
> <vladimir.murzin@arm.com> wrote:
> > This driver adds support to the UART controller found on ARM MPS2
> > platform.
> 
> Just few comments (have neither time not big desire to do full review).
> 
> >
> > Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> > ---
> >  drivers/tty/serial/Kconfig       |   12 +
> >  drivers/tty/serial/Makefile      |    1 +
> >  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
> >  include/uapi/linux/serial_core.h |    3 +
> >  4 files changed, 612 insertions(+)
> >  create mode 100644 drivers/tty/serial/mps2-uart.c
> >
> > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> > index f38beb2..e98bfea 100644
> > --- a/drivers/tty/serial/Kconfig
> > +++ b/drivers/tty/serial/Kconfig
> > @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
> >           This driver support the USART and UART ports on
> >           Energy Micro's efm32 SoCs.
> >
> > +config SERIAL_MPS2_UART_CONSOLE
> > +       bool "MPS2 UART console support"
> > +       depends on SERIAL_MPS2_UART
> > +       select SERIAL_CORE_CONSOLE
> > +
> > +config SERIAL_MPS2_UART
> > +       bool "MPS2 UART port"
> > +       depends on ARM || COMPILE_TEST
> > +       select SERIAL_CORE
> > +       help
> > +         This driver support the UART ports on ARM MPS2.
> > +
> >  config SERIAL_EFM32_UART_CONSOLE
> >         bool "EFM32 UART/USART console support"
> >         depends on SERIAL_EFM32_UART=y
> > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> > index 5ab4111..7f589f5 100644
> > --- a/drivers/tty/serial/Makefile
> > +++ b/drivers/tty/serial/Makefile
> > @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
> >  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
> >  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
> >  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> > +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
> >
> >  # GPIOLIB helpers for modem control lines
> >  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> > diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> > new file mode 100644
> > index 0000000..09bac16
> > --- /dev/null
> > +++ b/drivers/tty/serial/mps2-uart.c
> > @@ -0,0 +1,596 @@
> > +/*
> > + * Copyright (C) 2015 ARM Limited
> > + *
> > + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * TODO: support for SysRq
> > + */
> > +
> > +#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/clk.h>
> > +#include <linux/console.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/serial_core.h>
> > +#include <linux/tty_flip.h>
> > +#include <linux/types.h>
> > +
> > +#define SERIAL_NAME "ttyMPS"
> 
> Can it be ttyS?

It must be ttyS, please fix this.  Don't go creating new tty device
names, it never will work out and will eventually come back to cause
problems if you do not.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-13  7:00       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 57+ messages in thread
From: Greg Kroah-Hartman @ 2015-12-13  7:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Dec 13, 2015 at 01:39:26AM +0200, Andy Shevchenko wrote:
> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
> <vladimir.murzin@arm.com> wrote:
> > This driver adds support to the UART controller found on ARM MPS2
> > platform.
> 
> Just few comments (have neither time not big desire to do full review).
> 
> >
> > Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> > ---
> >  drivers/tty/serial/Kconfig       |   12 +
> >  drivers/tty/serial/Makefile      |    1 +
> >  drivers/tty/serial/mps2-uart.c   |  596 ++++++++++++++++++++++++++++++++++++++
> >  include/uapi/linux/serial_core.h |    3 +
> >  4 files changed, 612 insertions(+)
> >  create mode 100644 drivers/tty/serial/mps2-uart.c
> >
> > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> > index f38beb2..e98bfea 100644
> > --- a/drivers/tty/serial/Kconfig
> > +++ b/drivers/tty/serial/Kconfig
> > @@ -1473,6 +1473,18 @@ config SERIAL_EFM32_UART
> >           This driver support the USART and UART ports on
> >           Energy Micro's efm32 SoCs.
> >
> > +config SERIAL_MPS2_UART_CONSOLE
> > +       bool "MPS2 UART console support"
> > +       depends on SERIAL_MPS2_UART
> > +       select SERIAL_CORE_CONSOLE
> > +
> > +config SERIAL_MPS2_UART
> > +       bool "MPS2 UART port"
> > +       depends on ARM || COMPILE_TEST
> > +       select SERIAL_CORE
> > +       help
> > +         This driver support the UART ports on ARM MPS2.
> > +
> >  config SERIAL_EFM32_UART_CONSOLE
> >         bool "EFM32 UART/USART console support"
> >         depends on SERIAL_EFM32_UART=y
> > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> > index 5ab4111..7f589f5 100644
> > --- a/drivers/tty/serial/Makefile
> > +++ b/drivers/tty/serial/Makefile
> > @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)       += digicolor-usart.o
> >  obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
> >  obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
> >  obj-$(CONFIG_SERIAL_STM32)     += stm32-usart.o
> > +obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
> >
> >  # GPIOLIB helpers for modem control lines
> >  obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
> > diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
> > new file mode 100644
> > index 0000000..09bac16
> > --- /dev/null
> > +++ b/drivers/tty/serial/mps2-uart.c
> > @@ -0,0 +1,596 @@
> > +/*
> > + * Copyright (C) 2015 ARM Limited
> > + *
> > + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * TODO: support for SysRq
> > + */
> > +
> > +#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/clk.h>
> > +#include <linux/console.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/serial_core.h>
> > +#include <linux/tty_flip.h>
> > +#include <linux/types.h>
> > +
> > +#define SERIAL_NAME "ttyMPS"
> 
> Can it be ttyS?

It must be ttyS, please fix this.  Don't go creating new tty device
names, it never will work out and will eventually come back to cause
problems if you do not.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:36     ` Daniel Lezcano
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Lezcano @ 2015-12-14 13:36 UTC (permalink / raw)
  To: Vladimir Murzin, arnd, linux, gregkh, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---

[ ... ]

> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}

Is it possible to use writel_relaxed here ?

[ ... ]

> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");

typo: 'spurious'

[ ... ]

> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);

There is already a stack trace in the kernel when an allocation fails.

With the above fixed:

Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>


-- 
  <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:36     ` Daniel Lezcano
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Lezcano @ 2015-12-14 13:36 UTC (permalink / raw)
  To: Vladimir Murzin, arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, Pawel.Moll-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---

[ ... ]

> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}

Is it possible to use writel_relaxed here ?

[ ... ]

> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");

typo: 'spurious'

[ ... ]

> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);

There is already a stack trace in the kernel when an allocation fails.

With the above fixed:

Acked-by: Daniel Lezcano <daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>


-- 
  <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:36     ` Daniel Lezcano
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Lezcano @ 2015-12-14 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---

[ ... ]

> +static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
> +{
> +	writel(val, to_mps2_clkevt(c)->reg + offset);
> +}

Is it possible to use writel_relaxed here ?

[ ... ]

> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
> +{
> +	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
> +
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
> +	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
> +	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clockevent_mps2 *ce = dev_id;
> +	u32 status = readl(ce->reg + TIMER_INT);
> +
> +	if (!status) {
> +		pr_warn("spuirous interrupt\n");

typo: 'spurious'

[ ... ]

> +	ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
> +	if (!ce) {
> +		ret = -ENOMEM;
> +		pr_err("failed to allocate clockevent: %d\n", ret);

There is already a stack trace in the kernel when an allocation fails.

With the above fixed:

Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>


-- 
  <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:56     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2015-12-14 13:56 UTC (permalink / raw)
  To: Vladimir Murzin
  Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Uwe Kleine-König,
	Andreas Färber, Maxime Coquelin, Mark Rutland, Pawel Moll,
	Ian Campbell, Kumar Gala, Jiri Slaby, devicetree, linux-serial,
	linux-api, linux-arm-kernel, linux-kernel

On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin@arm.com> wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>         select CLKSRC_MMIO
>
> +config CLKSRC_MPS2
> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +       depends on OF && ARM

Does this really depend on both of these for COMPILE_TEST?

You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.

Rob

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:56     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2015-12-14 13:56 UTC (permalink / raw)
  To: Vladimir Murzin
  Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Uwe Kleine-König,
	Andreas Färber, Maxime Coquelin, Mark Rutland, Pawel Moll,
	Ian Campbell, Kumar Gala, Jiri Slaby,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org> wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>         select CLKSRC_MMIO
>
> +config CLKSRC_MPS2
> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +       depends on OF && ARM

Does this really depend on both of these for COMPILE_TEST?

You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-14 13:56     ` Rob Herring
  0 siblings, 0 replies; 57+ messages in thread
From: Rob Herring @ 2015-12-14 13:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin@arm.com> wrote:
> MPS2 platform has simple 32 bits general purpose countdown timers.
>
> The driver uses the first detected timer as a clocksource and the rest
> of the timers as a clockevent
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  drivers/clocksource/Kconfig      |    5 +
>  drivers/clocksource/Makefile     |    1 +
>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/clocksource/mps2-timer.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 2eb5f0e..8bca09c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>         select CLKSRC_MMIO
>
> +config CLKSRC_MPS2
> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
> +       depends on OF && ARM

Does this really depend on both of these for COMPILE_TEST?

You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.

Rob

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
  2015-12-12 23:39     ` Andy Shevchenko
@ 2015-12-15 12:40       ` Vladimir Murzin
  -1 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 12:40 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Arnd Bergmann, Russell King, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Uwe Kleine-König, Andreas Färber,
	Maxime Coquelin, Mark Rutland, Pawel Moll, ijc+devicetree,
	Kumar Gala, Jiri Slaby, Rob Herring, devicetree, linux-serial,
	linux-api, linux-arm Mailing List, linux-kernel

On 12/12/15 23:39, Andy Shevchenko wrote:
> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
> <vladimir.murzin@arm.com> wrote:
>> This driver adds support to the UART controller found on ARM MPS2
>> platform.
> 
> Just few comments (have neither time not big desire to do full review).
> 

Still better than nothing ;) I'm mostly agree on points you had, so I've
just left some I'm doubt about...

>> +
>> +static void mps2_uart_enable_ms(struct uart_port *port)
>> +{
>> +}
>> +
>> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
>> +{
>> +}
> 
> Are those required to be present? If not, remove them until you have
> alive code there.

A quick grep shows that core calls mps2_uart_break_ctl()
unconditionally, but, yes, it checks for presence of
mps2_uart_enable_ms() before jumping there, so it is safe to remove latter.

>> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
>> +{
>> +       irqreturn_t handled = IRQ_NONE;
>> +       struct uart_port *port = data;
>> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
>> +
>> +       spin_lock(&port->lock);
>> +
>> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
>> +               struct tty_port *tport = &port->state->port;
>> +
>> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
>> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>> +               port->icount.overrun++;
>> +               handled = IRQ_HANDLED;
>> +       }
>> +
>> +       /* XXX: this shouldn't happen? */
> 
> If shouldn't why it's there? Otherwise better to explain which
> conditions may lead to this.
> 

In practice I've never seen that happened and I think it never *should*
happen since we check if there is room in TX buffer. However, I could be
wrong here, so it is why that statement has question mark.

>> +       if (irqflag & UARTn_INT_TX_OVERRUN) {
>> +               mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
>> +               handled = IRQ_HANDLED;
>> +       }
>> +
>> +       spin_unlock(&port->lock);
>> +
>> +       return handled;
>> +}
>> +
...
>> +static void mps2_uart_release_port(struct uart_port *port)
>> +{
>> +}
>> +
>> +static int mps2_uart_request_port(struct uart_port *port)
>> +{
>> +       return 0;
>> +}
>> +
> 
> Same question about empty stubs.

Looks like they called unconditionally by the core.

>> +static int __init mps2_uart_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = uart_register_driver(&mps2_uart_driver);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = platform_driver_register(&mps2_serial_driver);
>> +       if (ret)
>> +               uart_unregister_driver(&mps2_uart_driver);
>> +
>> +       pr_info("MPS2 UART driver initialized\n");
>> +
>> +       return ret;
>> +}
>> +module_init(mps2_uart_init);
>> +
>> +static void __exit mps2_uart_exit(void)
>> +{
>> +       platform_driver_unregister(&mps2_serial_driver);
>> +       uart_unregister_driver(&mps2_uart_driver);
>> +}
>> +module_exit(mps2_uart_exit);
> 
> module_platform_driver();
> And move uart_*register calls to probe/remove.
> 

With this move we'll get uart_*register for every device probed, no?

Thanks
Vladimir


^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-15 12:40       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 12:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/12/15 23:39, Andy Shevchenko wrote:
> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
> <vladimir.murzin@arm.com> wrote:
>> This driver adds support to the UART controller found on ARM MPS2
>> platform.
> 
> Just few comments (have neither time not big desire to do full review).
> 

Still better than nothing ;) I'm mostly agree on points you had, so I've
just left some I'm doubt about...

>> +
>> +static void mps2_uart_enable_ms(struct uart_port *port)
>> +{
>> +}
>> +
>> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
>> +{
>> +}
> 
> Are those required to be present? If not, remove them until you have
> alive code there.

A quick grep shows that core calls mps2_uart_break_ctl()
unconditionally, but, yes, it checks for presence of
mps2_uart_enable_ms() before jumping there, so it is safe to remove latter.

>> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
>> +{
>> +       irqreturn_t handled = IRQ_NONE;
>> +       struct uart_port *port = data;
>> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
>> +
>> +       spin_lock(&port->lock);
>> +
>> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
>> +               struct tty_port *tport = &port->state->port;
>> +
>> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
>> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>> +               port->icount.overrun++;
>> +               handled = IRQ_HANDLED;
>> +       }
>> +
>> +       /* XXX: this shouldn't happen? */
> 
> If shouldn't why it's there? Otherwise better to explain which
> conditions may lead to this.
> 

In practice I've never seen that happened and I think it never *should*
happen since we check if there is room in TX buffer. However, I could be
wrong here, so it is why that statement has question mark.

>> +       if (irqflag & UARTn_INT_TX_OVERRUN) {
>> +               mps2_uart_write8(port, UARTn_INT_TX_OVERRUN, UARTn_INT);
>> +               handled = IRQ_HANDLED;
>> +       }
>> +
>> +       spin_unlock(&port->lock);
>> +
>> +       return handled;
>> +}
>> +
...
>> +static void mps2_uart_release_port(struct uart_port *port)
>> +{
>> +}
>> +
>> +static int mps2_uart_request_port(struct uart_port *port)
>> +{
>> +       return 0;
>> +}
>> +
> 
> Same question about empty stubs.

Looks like they called unconditionally by the core.

>> +static int __init mps2_uart_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = uart_register_driver(&mps2_uart_driver);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = platform_driver_register(&mps2_serial_driver);
>> +       if (ret)
>> +               uart_unregister_driver(&mps2_uart_driver);
>> +
>> +       pr_info("MPS2 UART driver initialized\n");
>> +
>> +       return ret;
>> +}
>> +module_init(mps2_uart_init);
>> +
>> +static void __exit mps2_uart_exit(void)
>> +{
>> +       platform_driver_unregister(&mps2_serial_driver);
>> +       uart_unregister_driver(&mps2_uart_driver);
>> +}
>> +module_exit(mps2_uart_exit);
> 
> module_platform_driver();
> And move uart_*register calls to probe/remove.
> 

With this move we'll get uart_*register for every device probed, no?

Thanks
Vladimir

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 12:47       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 12:47 UTC (permalink / raw)
  To: Daniel Lezcano, arnd, linux, gregkh, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, Pawel.Moll, ijc+devicetree, galak, jslaby, robh+dt,
	devicetree, linux-serial, linux-api, linux-arm-kernel,
	linux-kernel

On 14/12/15 13:36, Daniel Lezcano wrote:
> On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>> ---
> 
> [ ... ]
> 
>> +static void clockevent_mps2_writel(u32 val, struct clock_event_device
>> *c, u32 offset)
>> +{
>> +    writel(val, to_mps2_clkevt(c)->reg + offset);
>> +}
> 
> Is it possible to use writel_relaxed here ?

I think it is possible. I'll update that part.

> 
> [ ... ]
> 
>> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
>> +{
>> +    u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
>> +
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
>> +    clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce,
>> TIMER_CTRL);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
>> +{
>> +    struct clockevent_mps2 *ce = dev_id;
>> +    u32 status = readl(ce->reg + TIMER_INT);
>> +
>> +    if (!status) {
>> +        pr_warn("spuirous interrupt\n");
> 
> typo: 'spurious'
> 
> [ ... ]
> 
>> +    ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
>> +    if (!ce) {
>> +        ret = -ENOMEM;
>> +        pr_err("failed to allocate clockevent: %d\n", ret);
> 
> There is already a stack trace in the kernel when an allocation fails.
> 
> With the above fixed:
> 
> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> 
> 

All above fixed locally.

Thanks
Vladimir



^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 12:47       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 12:47 UTC (permalink / raw)
  To: Daniel Lezcano, arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, Pawel.Moll-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 14/12/15 13:36, Daniel Lezcano wrote:
> On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
>> ---
> 
> [ ... ]
> 
>> +static void clockevent_mps2_writel(u32 val, struct clock_event_device
>> *c, u32 offset)
>> +{
>> +    writel(val, to_mps2_clkevt(c)->reg + offset);
>> +}
> 
> Is it possible to use writel_relaxed here ?

I think it is possible. I'll update that part.

> 
> [ ... ]
> 
>> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
>> +{
>> +    u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
>> +
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
>> +    clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce,
>> TIMER_CTRL);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
>> +{
>> +    struct clockevent_mps2 *ce = dev_id;
>> +    u32 status = readl(ce->reg + TIMER_INT);
>> +
>> +    if (!status) {
>> +        pr_warn("spuirous interrupt\n");
> 
> typo: 'spurious'
> 
> [ ... ]
> 
>> +    ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
>> +    if (!ce) {
>> +        ret = -ENOMEM;
>> +        pr_err("failed to allocate clockevent: %d\n", ret);
> 
> There is already a stack trace in the kernel when an allocation fails.
> 
> With the above fixed:
> 
> Acked-by: Daniel Lezcano <daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> 
> 

All above fixed locally.

Thanks
Vladimir


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 12:47       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 12:47 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/12/15 13:36, Daniel Lezcano wrote:
> On 12/02/2015 10:33 AM, Vladimir Murzin wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>> ---
> 
> [ ... ]
> 
>> +static void clockevent_mps2_writel(u32 val, struct clock_event_device
>> *c, u32 offset)
>> +{
>> +    writel(val, to_mps2_clkevt(c)->reg + offset);
>> +}
> 
> Is it possible to use writel_relaxed here ?

I think it is possible. I'll update that part.

> 
> [ ... ]
> 
>> +static int mps2_timer_set_periodic(struct clock_event_device *ce)
>> +{
>> +    u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
>> +
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
>> +    clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
>> +    clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce,
>> TIMER_CTRL);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
>> +{
>> +    struct clockevent_mps2 *ce = dev_id;
>> +    u32 status = readl(ce->reg + TIMER_INT);
>> +
>> +    if (!status) {
>> +        pr_warn("spuirous interrupt\n");
> 
> typo: 'spurious'
> 
> [ ... ]
> 
>> +    ce = kzalloc(sizeof(struct clockevent_mps2), GFP_KERNEL);
>> +    if (!ce) {
>> +        ret = -ENOMEM;
>> +        pr_err("failed to allocate clockevent: %d\n", ret);
> 
> There is already a stack trace in the kernel when an allocation fails.
> 
> With the above fixed:
> 
> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> 
> 

All above fixed locally.

Thanks
Vladimir

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 13:16       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 13:16 UTC (permalink / raw)
  To: Rob Herring
  Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Uwe Kleine-König,
	Andreas Färber, Maxime Coquelin, Mark Rutland, Pawel Moll,
	Ian Campbell, Kumar Gala, Jiri Slaby, devicetree, linux-serial,
	linux-api, linux-arm-kernel, linux-kernel

On 14/12/15 13:56, Rob Herring wrote:
> On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin@arm.com> wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>> ---
>>  drivers/clocksource/Kconfig      |    5 +
>>  drivers/clocksource/Makefile     |    1 +
>>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 283 insertions(+)
>>  create mode 100644 drivers/clocksource/mps2-timer.c
>>
>> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
>> index 2eb5f0e..8bca09c 100644
>> --- a/drivers/clocksource/Kconfig
>> +++ b/drivers/clocksource/Kconfig
>> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>>         select CLKSRC_MMIO
>>
>> +config CLKSRC_MPS2
>> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
>> +       depends on OF && ARM
> 
> Does this really depend on both of these for COMPILE_TEST?

Not really...

> 
> You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.
> 

Does it looks better?

config CLKSRC_MPS2
	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
	depends on GENERIC_SCHED_CLOCK
	select CLKSRC_MMIO
	select CLKSRC_OF


Cheers
Vladimir

> Rob
> 
> 
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 13:16       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 13:16 UTC (permalink / raw)
  To: Rob Herring
  Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Uwe Kleine-König,
	Andreas Färber, Maxime Coquelin, Mark Rutland, Pawel Moll,
	Ian Campbell, Kumar Gala, Jiri Slaby,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

On 14/12/15 13:56, Rob Herring wrote:
> On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org> wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
>> ---
>>  drivers/clocksource/Kconfig      |    5 +
>>  drivers/clocksource/Makefile     |    1 +
>>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 283 insertions(+)
>>  create mode 100644 drivers/clocksource/mps2-timer.c
>>
>> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
>> index 2eb5f0e..8bca09c 100644
>> --- a/drivers/clocksource/Kconfig
>> +++ b/drivers/clocksource/Kconfig
>> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>>         select CLKSRC_MMIO
>>
>> +config CLKSRC_MPS2
>> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
>> +       depends on OF && ARM
> 
> Does this really depend on both of these for COMPILE_TEST?

Not really...

> 
> You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.
> 

Does it looks better?

config CLKSRC_MPS2
	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
	depends on GENERIC_SCHED_CLOCK
	select CLKSRC_MMIO
	select CLKSRC_OF


Cheers
Vladimir

> Rob
> 
> 
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver
@ 2015-12-15 13:16       ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-15 13:16 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/12/15 13:56, Rob Herring wrote:
> On Wed, Dec 2, 2015 at 3:33 AM, Vladimir Murzin <vladimir.murzin@arm.com> wrote:
>> MPS2 platform has simple 32 bits general purpose countdown timers.
>>
>> The driver uses the first detected timer as a clocksource and the rest
>> of the timers as a clockevent
>>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>> ---
>>  drivers/clocksource/Kconfig      |    5 +
>>  drivers/clocksource/Makefile     |    1 +
>>  drivers/clocksource/mps2-timer.c |  277 ++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 283 insertions(+)
>>  create mode 100644 drivers/clocksource/mps2-timer.c
>>
>> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
>> index 2eb5f0e..8bca09c 100644
>> --- a/drivers/clocksource/Kconfig
>> +++ b/drivers/clocksource/Kconfig
>> @@ -137,6 +137,11 @@ config CLKSRC_STM32
>>         depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
>>         select CLKSRC_MMIO
>>
>> +config CLKSRC_MPS2
>> +       bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
>> +       depends on OF && ARM
> 
> Does this really depend on both of these for COMPILE_TEST?

Not really...

> 
> You need to select CLKSRC_OF rather than CLKSRC_MMIO as well.
> 

Does it looks better?

config CLKSRC_MPS2
	bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
	depends on GENERIC_SCHED_CLOCK
	select CLKSRC_MMIO
	select CLKSRC_OF


Cheers
Vladimir

> Rob
> 
> 
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
  2015-12-15 12:40       ` Vladimir Murzin
@ 2015-12-17 13:15         ` Andy Shevchenko
  -1 siblings, 0 replies; 57+ messages in thread
From: Andy Shevchenko @ 2015-12-17 13:15 UTC (permalink / raw)
  To: Vladimir Murzin
  Cc: Arnd Bergmann, Russell King, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Uwe Kleine-König, Andreas Färber,
	Maxime Coquelin, Mark Rutland, Pawel Moll, ijc+devicetree,
	Kumar Gala, Jiri Slaby, Rob Herring, devicetree, linux-serial,
	linux-api, linux-arm Mailing List, linux-kernel

On Tue, Dec 15, 2015 at 2:40 PM, Vladimir Murzin
<vladimir.murzin@arm.com> wrote:
> On 12/12/15 23:39, Andy Shevchenko wrote:
>> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
>> <vladimir.murzin@arm.com> wrote:
>>> This driver adds support to the UART controller found on ARM MPS2
>>> platform.
>>
>> Just few comments (have neither time not big desire to do full review).
>>
>
> Still better than nothing ;) I'm mostly agree on points you had, so I've
> just left some I'm doubt about...
>
>>> +
>>> +static void mps2_uart_enable_ms(struct uart_port *port)
>>> +{
>>> +}
>>> +
>>> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
>>> +{
>>> +}
>>
>> Are those required to be present? If not, remove them until you have
>> alive code there.
>
> A quick grep shows that core calls mps2_uart_break_ctl()
> unconditionally, but, yes, it checks for presence of
> mps2_uart_enable_ms() before jumping there, so it is safe to remove latter.

OK.

>>> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
>>> +{
>>> +       irqreturn_t handled = IRQ_NONE;
>>> +       struct uart_port *port = data;
>>> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
>>> +
>>> +       spin_lock(&port->lock);
>>> +
>>> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
>>> +               struct tty_port *tport = &port->state->port;
>>> +
>>> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
>>> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>>> +               port->icount.overrun++;
>>> +               handled = IRQ_HANDLED;
>>> +       }
>>> +
>>> +       /* XXX: this shouldn't happen? */
>>
>> If shouldn't why it's there? Otherwise better to explain which
>> conditions may lead to this.
>>
>
> In practice I've never seen that happened and I think it never *should*
> happen since we check if there is room in TX buffer. However, I could be
> wrong here, so it is why that statement has question mark.

So, worth to have a proper comment then.

>>> +static int __init mps2_uart_init(void)
>>> +{
>>> +       int ret;
>>> +
>>> +       ret = uart_register_driver(&mps2_uart_driver);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = platform_driver_register(&mps2_serial_driver);
>>> +       if (ret)
>>> +               uart_unregister_driver(&mps2_uart_driver);
>>> +
>>> +       pr_info("MPS2 UART driver initialized\n");
>>> +
>>> +       return ret;
>>> +}
>>> +module_init(mps2_uart_init);
>>> +
>>> +static void __exit mps2_uart_exit(void)
>>> +{
>>> +       platform_driver_unregister(&mps2_serial_driver);
>>> +       uart_unregister_driver(&mps2_uart_driver);
>>> +}
>>> +module_exit(mps2_uart_exit);
>>
>> module_platform_driver();
>> And move uart_*register calls to probe/remove.
>>
>
> With this move we'll get uart_*register for every device probed, no?

Don't see a problem, maybe someone else could share an authoritive opinion.
Some of the drivers do that, though most do in __init stage. So, see above.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver
@ 2015-12-17 13:15         ` Andy Shevchenko
  0 siblings, 0 replies; 57+ messages in thread
From: Andy Shevchenko @ 2015-12-17 13:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Dec 15, 2015 at 2:40 PM, Vladimir Murzin
<vladimir.murzin@arm.com> wrote:
> On 12/12/15 23:39, Andy Shevchenko wrote:
>> On Wed, Dec 2, 2015 at 11:33 AM, Vladimir Murzin
>> <vladimir.murzin@arm.com> wrote:
>>> This driver adds support to the UART controller found on ARM MPS2
>>> platform.
>>
>> Just few comments (have neither time not big desire to do full review).
>>
>
> Still better than nothing ;) I'm mostly agree on points you had, so I've
> just left some I'm doubt about...
>
>>> +
>>> +static void mps2_uart_enable_ms(struct uart_port *port)
>>> +{
>>> +}
>>> +
>>> +static void mps2_uart_break_ctl(struct uart_port *port, int ctl)
>>> +{
>>> +}
>>
>> Are those required to be present? If not, remove them until you have
>> alive code there.
>
> A quick grep shows that core calls mps2_uart_break_ctl()
> unconditionally, but, yes, it checks for presence of
> mps2_uart_enable_ms() before jumping there, so it is safe to remove latter.

OK.

>>> +static irqreturn_t mps2_uart_oerrirq(int irq, void *data)
>>> +{
>>> +       irqreturn_t handled = IRQ_NONE;
>>> +       struct uart_port *port = data;
>>> +       u8 irqflag = mps2_uart_read8(port, UARTn_INT);
>>> +
>>> +       spin_lock(&port->lock);
>>> +
>>> +       if (irqflag & UARTn_INT_RX_OVERRUN) {
>>> +               struct tty_port *tport = &port->state->port;
>>> +
>>> +               mps2_uart_write8(port, UARTn_INT_RX_OVERRUN, UARTn_INT);
>>> +               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
>>> +               port->icount.overrun++;
>>> +               handled = IRQ_HANDLED;
>>> +       }
>>> +
>>> +       /* XXX: this shouldn't happen? */
>>
>> If shouldn't why it's there? Otherwise better to explain which
>> conditions may lead to this.
>>
>
> In practice I've never seen that happened and I think it never *should*
> happen since we check if there is room in TX buffer. However, I could be
> wrong here, so it is why that statement has question mark.

So, worth to have a proper comment then.

>>> +static int __init mps2_uart_init(void)
>>> +{
>>> +       int ret;
>>> +
>>> +       ret = uart_register_driver(&mps2_uart_driver);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = platform_driver_register(&mps2_serial_driver);
>>> +       if (ret)
>>> +               uart_unregister_driver(&mps2_uart_driver);
>>> +
>>> +       pr_info("MPS2 UART driver initialized\n");
>>> +
>>> +       return ret;
>>> +}
>>> +module_init(mps2_uart_init);
>>> +
>>> +static void __exit mps2_uart_exit(void)
>>> +{
>>> +       platform_driver_unregister(&mps2_serial_driver);
>>> +       uart_unregister_driver(&mps2_uart_driver);
>>> +}
>>> +module_exit(mps2_uart_exit);
>>
>> module_platform_driver();
>> And move uart_*register calls to probe/remove.
>>
>
> With this move we'll get uart_*register for every device probed, no?

Don't see a problem, maybe someone else could share an authoritive opinion.
Some of the drivers do that, though most do in __init stage. So, see above.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386
@ 2015-12-23  9:33     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-23  9:33 UTC (permalink / raw)
  To: arnd, linux, gregkh, daniel.lezcano, tglx, u.kleine-koenig,
	afaerber, mcoquelin.stm32
  Cc: Mark.Rutland, devicetree, Pawel.Moll, ijc+devicetree, linux-api,
	linux-kernel, robh+dt, linux-serial, galak, jslaby,
	linux-arm-kernel

I'd very appreciate some feedback on this (and following) patch from DT
camp, so I could incorporate them in the next attempt after merge window
closed.

Merry Christmas!
Vladimir

On 02/12/15 09:33, Vladimir Murzin wrote:
> Application Notes 385 and 386 shares the same memory map and features
> except the CPU is used. AN385 is supplied with Cortex-M3 CPU and AN386
> is supplied with Cortex-M4.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  arch/arm/boot/dts/Makefile       |    1 +
>  arch/arm/boot/dts/mps2-an385.dts |   90 +++++++++++++++
>  arch/arm/boot/dts/mps2.dtsi      |  227 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 318 insertions(+)
>  create mode 100644 arch/arm/boot/dts/mps2-an385.dts
>  create mode 100644 arch/arm/boot/dts/mps2.dtsi
> 
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 30bbc37..4ef3720 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -237,6 +237,7 @@ dtb-$(CONFIG_ARCH_MMP) += \
>  dtb-$(CONFIG_MACH_MESON8B) += \
>  	meson8b-mxq.dtb \
>  	meson8b-odroidc1.dtb
> +dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
>  dtb-$(CONFIG_ARCH_MOXART) += \
>  	moxart-uc7112lx.dtb
>  dtb-$(CONFIG_SOC_IMX1) += \
> diff --git a/arch/arm/boot/dts/mps2-an385.dts b/arch/arm/boot/dts/mps2-an385.dts
> new file mode 100644
> index 0000000..ddb03d3
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2-an385.dts
> @@ -0,0 +1,90 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +/dts-v1/;
> +
> +#include "mps2.dtsi"
> +
> +/ {
> +	model = "ARM MPS2 Application Note 385/386";
> +	compatible = "arm,mps2";
> +
> +	aliases {
> +		serial0 = &uart0;
> +	};
> +
> +	chosen {
> +		bootargs = "init=/sbin/init earlycon";
> +		stdout-path = "serial0:9600n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x21000000 0x1000000>;
> +	};
> +
> +	ethernet@40200000 {
> +		compatible = "smsc,lan9220", "smsc,lan9115";
> +		reg = <0x40200000 0x10000>;
> +		interrupts = <13>;
> +		interrupt-parent = <&nvic>;
> +		smsc,irq-active-high;
> +	};
> +};
> +
> +&uart0 {
> +	status = "okay";
> +};
> +
> +&timer0 {
> +	status = "okay";
> +};
> +
> +&timer1 {
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
> diff --git a/arch/arm/boot/dts/mps2.dtsi b/arch/arm/boot/dts/mps2.dtsi
> new file mode 100644
> index 0000000..5d2c539
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2.dtsi
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include "armv7-m.dtsi"
> +
> +/ {
> +	oscclk0: clk-osc0 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <50000000>;
> +	};
> +
> +	oscclk1: clk-osc1 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <24576000>;
> +	};
> +
> +	oscclk2: clk-osc2 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <25000000>;
> +	};
> +
> +	cfgclk: clk-cfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <5000000>;
> +	};
> +
> +	spicfgclk: clk-spicfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <75000000>;
> +	};
> +
> +	sysclk: clk-sys {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audmclk: clk-audm {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audsclk: clk-auds {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <8>;
> +		clock-mult = <1>;
> +	};
> +
> +	spiclcd: clk-cpiclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	spicon: clk-spicon {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2cclcd: clk-i2cclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2caud: clk-i2caud {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	soc {
> +		compatible = "simple-bus";
> +		ranges;
> +
> +		apb {
> +			compatible = "simple-bus";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0 0x40000000 0x10000>;
> +
> +			timer0: mps2-timer0@0 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x0 0x1000>;
> +				interrupts = <8>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer1: mps2-timer1@1000 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x1000 0x1000>;
> +				interrupts = <9>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer2: dual-timer@2000 {
> +				compatible = "arm,sp804";
> +				reg = <0x2000 0x1000>;
> +				clocks = <&sysclk>;
> +				interrupts = <10>;
> +				status = "disabled";
> +			};
> +
> +
> +			uart0: serial@4000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x4000 0x1000>;
> +				interrupts = <0 1 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart1: serial@5000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x5000 0x1000>;
> +				interrupts = <2 3 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart2: serial@6000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x6000 0x1000>;
> +				interrupts = <4 5 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			wdt: watchdog@8000 {
> +				compatible = "arm,sp805", "arm,primecell";
> +				arm,primecell-periphid = <0x00141805>;
> +				reg = <0x8000 0x1000>;
> +				interrupts = <0>;
> +				clocks = <&sysclk>;
> +				clock-names = "apb_pclk";
> +				status = "disabled";
> +			};
> +		};
> +
> +		fpgaio {
> +			compatible = "syscon", "simple-mfd";
> +			reg = <0x40028000 0x10>;
> +
> +			led@0 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x01>;
> +				label = "userled:0";
> +				linux,default-trigger = "heartbeat";
> +				default-state = "on";
> +			};
> +
> +			led@1 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x02>;
> +				label = "userled:1";
> +				linux,default-trigger = "usr";
> +				default-state = "off";
> +			};
> +		};
> +	};
> +};
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386
@ 2015-12-23  9:33     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-23  9:33 UTC (permalink / raw)
  To: arnd-r2nGTMty4D4, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A,
	tglx-hfZtesqFncYOwBW4kG4KsQ,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ, afaerber-l3A5Bk7waGM,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w
  Cc: Mark.Rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Pawel.Moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, jslaby-AlSwsSmVLrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

I'd very appreciate some feedback on this (and following) patch from DT
camp, so I could incorporate them in the next attempt after merge window
closed.

Merry Christmas!
Vladimir

On 02/12/15 09:33, Vladimir Murzin wrote:
> Application Notes 385 and 386 shares the same memory map and features
> except the CPU is used. AN385 is supplied with Cortex-M3 CPU and AN386
> is supplied with Cortex-M4.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> ---
>  arch/arm/boot/dts/Makefile       |    1 +
>  arch/arm/boot/dts/mps2-an385.dts |   90 +++++++++++++++
>  arch/arm/boot/dts/mps2.dtsi      |  227 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 318 insertions(+)
>  create mode 100644 arch/arm/boot/dts/mps2-an385.dts
>  create mode 100644 arch/arm/boot/dts/mps2.dtsi
> 
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 30bbc37..4ef3720 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -237,6 +237,7 @@ dtb-$(CONFIG_ARCH_MMP) += \
>  dtb-$(CONFIG_MACH_MESON8B) += \
>  	meson8b-mxq.dtb \
>  	meson8b-odroidc1.dtb
> +dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
>  dtb-$(CONFIG_ARCH_MOXART) += \
>  	moxart-uc7112lx.dtb
>  dtb-$(CONFIG_SOC_IMX1) += \
> diff --git a/arch/arm/boot/dts/mps2-an385.dts b/arch/arm/boot/dts/mps2-an385.dts
> new file mode 100644
> index 0000000..ddb03d3
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2-an385.dts
> @@ -0,0 +1,90 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +/dts-v1/;
> +
> +#include "mps2.dtsi"
> +
> +/ {
> +	model = "ARM MPS2 Application Note 385/386";
> +	compatible = "arm,mps2";
> +
> +	aliases {
> +		serial0 = &uart0;
> +	};
> +
> +	chosen {
> +		bootargs = "init=/sbin/init earlycon";
> +		stdout-path = "serial0:9600n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x21000000 0x1000000>;
> +	};
> +
> +	ethernet@40200000 {
> +		compatible = "smsc,lan9220", "smsc,lan9115";
> +		reg = <0x40200000 0x10000>;
> +		interrupts = <13>;
> +		interrupt-parent = <&nvic>;
> +		smsc,irq-active-high;
> +	};
> +};
> +
> +&uart0 {
> +	status = "okay";
> +};
> +
> +&timer0 {
> +	status = "okay";
> +};
> +
> +&timer1 {
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
> diff --git a/arch/arm/boot/dts/mps2.dtsi b/arch/arm/boot/dts/mps2.dtsi
> new file mode 100644
> index 0000000..5d2c539
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2.dtsi
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin-5wv7dgnIgG8@public.gmane.org>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include "armv7-m.dtsi"
> +
> +/ {
> +	oscclk0: clk-osc0 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <50000000>;
> +	};
> +
> +	oscclk1: clk-osc1 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <24576000>;
> +	};
> +
> +	oscclk2: clk-osc2 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <25000000>;
> +	};
> +
> +	cfgclk: clk-cfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <5000000>;
> +	};
> +
> +	spicfgclk: clk-spicfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <75000000>;
> +	};
> +
> +	sysclk: clk-sys {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audmclk: clk-audm {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audsclk: clk-auds {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <8>;
> +		clock-mult = <1>;
> +	};
> +
> +	spiclcd: clk-cpiclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	spicon: clk-spicon {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2cclcd: clk-i2cclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2caud: clk-i2caud {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	soc {
> +		compatible = "simple-bus";
> +		ranges;
> +
> +		apb {
> +			compatible = "simple-bus";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0 0x40000000 0x10000>;
> +
> +			timer0: mps2-timer0@0 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x0 0x1000>;
> +				interrupts = <8>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer1: mps2-timer1@1000 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x1000 0x1000>;
> +				interrupts = <9>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer2: dual-timer@2000 {
> +				compatible = "arm,sp804";
> +				reg = <0x2000 0x1000>;
> +				clocks = <&sysclk>;
> +				interrupts = <10>;
> +				status = "disabled";
> +			};
> +
> +
> +			uart0: serial@4000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x4000 0x1000>;
> +				interrupts = <0 1 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart1: serial@5000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x5000 0x1000>;
> +				interrupts = <2 3 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart2: serial@6000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x6000 0x1000>;
> +				interrupts = <4 5 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			wdt: watchdog@8000 {
> +				compatible = "arm,sp805", "arm,primecell";
> +				arm,primecell-periphid = <0x00141805>;
> +				reg = <0x8000 0x1000>;
> +				interrupts = <0>;
> +				clocks = <&sysclk>;
> +				clock-names = "apb_pclk";
> +				status = "disabled";
> +			};
> +		};
> +
> +		fpgaio {
> +			compatible = "syscon", "simple-mfd";
> +			reg = <0x40028000 0x10>;
> +
> +			led@0 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x01>;
> +				label = "userled:0";
> +				linux,default-trigger = "heartbeat";
> +				default-state = "on";
> +			};
> +
> +			led@1 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x02>;
> +				label = "userled:1";
> +				linux,default-trigger = "usr";
> +				default-state = "off";
> +			};
> +		};
> +	};
> +};
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386
@ 2015-12-23  9:33     ` Vladimir Murzin
  0 siblings, 0 replies; 57+ messages in thread
From: Vladimir Murzin @ 2015-12-23  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

I'd very appreciate some feedback on this (and following) patch from DT
camp, so I could incorporate them in the next attempt after merge window
closed.

Merry Christmas!
Vladimir

On 02/12/15 09:33, Vladimir Murzin wrote:
> Application Notes 385 and 386 shares the same memory map and features
> except the CPU is used. AN385 is supplied with Cortex-M3 CPU and AN386
> is supplied with Cortex-M4.
> 
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
>  arch/arm/boot/dts/Makefile       |    1 +
>  arch/arm/boot/dts/mps2-an385.dts |   90 +++++++++++++++
>  arch/arm/boot/dts/mps2.dtsi      |  227 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 318 insertions(+)
>  create mode 100644 arch/arm/boot/dts/mps2-an385.dts
>  create mode 100644 arch/arm/boot/dts/mps2.dtsi
> 
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 30bbc37..4ef3720 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -237,6 +237,7 @@ dtb-$(CONFIG_ARCH_MMP) += \
>  dtb-$(CONFIG_MACH_MESON8B) += \
>  	meson8b-mxq.dtb \
>  	meson8b-odroidc1.dtb
> +dtb-$(CONFIG_ARCH_MPS2) += mps2-an385.dtb
>  dtb-$(CONFIG_ARCH_MOXART) += \
>  	moxart-uc7112lx.dtb
>  dtb-$(CONFIG_SOC_IMX1) += \
> diff --git a/arch/arm/boot/dts/mps2-an385.dts b/arch/arm/boot/dts/mps2-an385.dts
> new file mode 100644
> index 0000000..ddb03d3
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2-an385.dts
> @@ -0,0 +1,90 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +/dts-v1/;
> +
> +#include "mps2.dtsi"
> +
> +/ {
> +	model = "ARM MPS2 Application Note 385/386";
> +	compatible = "arm,mps2";
> +
> +	aliases {
> +		serial0 = &uart0;
> +	};
> +
> +	chosen {
> +		bootargs = "init=/sbin/init earlycon";
> +		stdout-path = "serial0:9600n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x21000000 0x1000000>;
> +	};
> +
> +	ethernet at 40200000 {
> +		compatible = "smsc,lan9220", "smsc,lan9115";
> +		reg = <0x40200000 0x10000>;
> +		interrupts = <13>;
> +		interrupt-parent = <&nvic>;
> +		smsc,irq-active-high;
> +	};
> +};
> +
> +&uart0 {
> +	status = "okay";
> +};
> +
> +&timer0 {
> +	status = "okay";
> +};
> +
> +&timer1 {
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
> diff --git a/arch/arm/boot/dts/mps2.dtsi b/arch/arm/boot/dts/mps2.dtsi
> new file mode 100644
> index 0000000..5d2c539
> --- /dev/null
> +++ b/arch/arm/boot/dts/mps2.dtsi
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2015 ARM Limited
> + *
> + * Author: Vladimir Murzin <vladimir.murzin@arm.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file 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.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include "armv7-m.dtsi"
> +
> +/ {
> +	oscclk0: clk-osc0 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <50000000>;
> +	};
> +
> +	oscclk1: clk-osc1 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <24576000>;
> +	};
> +
> +	oscclk2: clk-osc2 {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <25000000>;
> +	};
> +
> +	cfgclk: clk-cfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <5000000>;
> +	};
> +
> +	spicfgclk: clk-spicfg {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <75000000>;
> +	};
> +
> +	sysclk: clk-sys {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audmclk: clk-audm {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	audsclk: clk-auds {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk1>;
> +		#clock-cells = <0>;
> +		clock-div = <8>;
> +		clock-mult = <1>;
> +	};
> +
> +	spiclcd: clk-cpiclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	spicon: clk-spicon {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2cclcd: clk-i2cclcd {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	i2caud: clk-i2caud {
> +		compatible = "fixed-factor-clock";
> +		clocks = <&oscclk0>;
> +		#clock-cells = <0>;
> +		clock-div = <2>;
> +		clock-mult = <1>;
> +	};
> +
> +	soc {
> +		compatible = "simple-bus";
> +		ranges;
> +
> +		apb {
> +			compatible = "simple-bus";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0 0x40000000 0x10000>;
> +
> +			timer0: mps2-timer0 at 0 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x0 0x1000>;
> +				interrupts = <8>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer1: mps2-timer1 at 1000 {
> +				compatible = "arm,mps2-timer";
> +				reg = <0x1000 0x1000>;
> +				interrupts = <9>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			timer2: dual-timer at 2000 {
> +				compatible = "arm,sp804";
> +				reg = <0x2000 0x1000>;
> +				clocks = <&sysclk>;
> +				interrupts = <10>;
> +				status = "disabled";
> +			};
> +
> +
> +			uart0: serial at 4000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x4000 0x1000>;
> +				interrupts = <0 1 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart1: serial at 5000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x5000 0x1000>;
> +				interrupts = <2 3 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			uart2: serial at 6000 {
> +				compatible = "arm,mps2-uart";
> +				reg = <0x6000 0x1000>;
> +				interrupts = <4 5 12>;
> +				clocks = <&sysclk>;
> +				status = "disabled";
> +			};
> +
> +			wdt: watchdog at 8000 {
> +				compatible = "arm,sp805", "arm,primecell";
> +				arm,primecell-periphid = <0x00141805>;
> +				reg = <0x8000 0x1000>;
> +				interrupts = <0>;
> +				clocks = <&sysclk>;
> +				clock-names = "apb_pclk";
> +				status = "disabled";
> +			};
> +		};
> +
> +		fpgaio {
> +			compatible = "syscon", "simple-mfd";
> +			reg = <0x40028000 0x10>;
> +
> +			led at 0 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x01>;
> +				label = "userled:0";
> +				linux,default-trigger = "heartbeat";
> +				default-state = "on";
> +			};
> +
> +			led at 1 {
> +				compatible = "register-bit-led";
> +				offset = <0x0>;
> +				mask = <0x02>;
> +				label = "userled:1";
> +				linux,default-trigger = "usr";
> +				default-state = "off";
> +			};
> +		};
> +	};
> +};
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2015-12-23  9:34 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-02  9:33 [PATCH v1 00/10] Support for Cortex-M Prototyping System Vladimir Murzin
2015-12-02  9:33 ` Vladimir Murzin
2015-12-02  9:33 ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 01/10] dt-bindings: document the MPS2 timer bindings Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 02/10] clockevents/drivers: add MPS2 Timer driver Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-07  9:25   ` Vladimir Murzin
2015-12-07  9:25     ` Vladimir Murzin
2015-12-07  9:25     ` Vladimir Murzin
2015-12-14 13:36   ` Daniel Lezcano
2015-12-14 13:36     ` Daniel Lezcano
2015-12-14 13:36     ` Daniel Lezcano
2015-12-15 12:47     ` Vladimir Murzin
2015-12-15 12:47       ` Vladimir Murzin
2015-12-15 12:47       ` Vladimir Murzin
2015-12-14 13:56   ` Rob Herring
2015-12-14 13:56     ` Rob Herring
2015-12-14 13:56     ` Rob Herring
2015-12-15 13:16     ` Vladimir Murzin
2015-12-15 13:16       ` Vladimir Murzin
2015-12-15 13:16       ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 03/10] dt-bindings: document the MPS2 UART bindings Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 04/10] serial: mps2-uart: add MPS2 UART driver Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-07  9:26   ` Vladimir Murzin
2015-12-07  9:26     ` Vladimir Murzin
2015-12-07  9:26     ` Vladimir Murzin
2015-12-12 23:39   ` Andy Shevchenko
2015-12-12 23:39     ` Andy Shevchenko
2015-12-12 23:39     ` Andy Shevchenko
2015-12-13  7:00     ` Greg Kroah-Hartman
2015-12-13  7:00       ` Greg Kroah-Hartman
2015-12-15 12:40     ` Vladimir Murzin
2015-12-15 12:40       ` Vladimir Murzin
2015-12-17 13:15       ` Andy Shevchenko
2015-12-17 13:15         ` Andy Shevchenko
2015-12-02  9:33 ` [PATCH v1 05/10] serial: mps2-uart: add support for early console Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 06/10] ARM: mps2: introduce MPS2 platform Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 07/10] ARM: mps2: add low-level debug support Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 08/10] ARM: configs: add MPS2 defconfig Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 09/10] ARM: dts: introduce MPS2 AN385/AN386 Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin
2015-12-23  9:33   ` Vladimir Murzin
2015-12-23  9:33     ` Vladimir Murzin
2015-12-23  9:33     ` Vladimir Murzin
2015-12-02  9:33 ` [PATCH v1 10/10] ARM: dts: introduce MPS2 AN399/AN400 Vladimir Murzin
2015-12-02  9:33   ` Vladimir Murzin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.