diff mbox series

[20/31] pwm: ti: am33xx: add subsystem driver

Message ID 20200825092124.4284-21-dariobin@libero.it
State Changes Requested
Delegated to: Lokesh Vutla
Headers show
Series Add DM support for omap PWM backlight | expand

Commit Message

Dario Binacchi Aug. 25, 2020, 9:21 a.m. UTC
The TI PWMSS driver is a simple bus driver for providing clock and power
management for the PWM peripherals on TI AM33xx SoCs, namely eCAP,
eHRPWM and eQEP.

Based on more recent versions of the device tree inside the linux kernel,
I added the clock domain for each subsystem in am33x.dtsi so it can be
enabled before probing the peripheral child device and disabled after
its removal.

Signed-off-by: Dario Binacchi <dariobin@libero.it>
---

 arch/arm/dts/am33xx.dtsi                  |  9 +++
 doc/device-tree-bindings/pwm/ti,pwmss.txt | 58 ++++++++++++++
 drivers/pwm/Kconfig                       |  9 ++-
 drivers/pwm/Makefile                      |  1 +
 drivers/pwm/pwm-ti-pwmss.c                | 95 +++++++++++++++++++++++
 5 files changed, 171 insertions(+), 1 deletion(-)
 create mode 100644 doc/device-tree-bindings/pwm/ti,pwmss.txt
 create mode 100644 drivers/pwm/pwm-ti-pwmss.c
diff mbox series

Patch

diff --git a/arch/arm/dts/am33xx.dtsi b/arch/arm/dts/am33xx.dtsi
index d3dd6a16e7..fa7492a9b6 100644
--- a/arch/arm/dts/am33xx.dtsi
+++ b/arch/arm/dts/am33xx.dtsi
@@ -758,6 +758,9 @@ 
 				  0x48300180 0x48300180 0x80   /* EQEP */
 				  0x48300200 0x48300200 0x80>; /* EHRPWM */
 
+			clocks = <&l4_per_clkctrl AM3_EPWMSS0_CLKCTRL 0>;
+			clock-names = "fck";
+
 			ecap0: ecap@48300100 {
 				compatible = "ti,am3352-ecap",
 					     "ti,am33xx-ecap";
@@ -792,6 +795,9 @@ 
 				  0x48302180 0x48302180 0x80   /* EQEP */
 				  0x48302200 0x48302200 0x80>; /* EHRPWM */
 
+			clocks = <&l4_per_clkctrl AM3_EPWMSS1_CLKCTRL 0>;
+			clock-names = "fck";
+
 			ecap1: ecap@48302100 {
 				compatible = "ti,am3352-ecap",
 					     "ti,am33xx-ecap";
@@ -826,6 +832,9 @@ 
 				  0x48304180 0x48304180 0x80   /* EQEP */
 				  0x48304200 0x48304200 0x80>; /* EHRPWM */
 
+			clocks = <&l4_per_clkctrl AM3_EPWMSS2_CLKCTRL 0>;
+			clock-names = "fck";
+
 			ecap2: ecap@48304100 {
 				compatible = "ti,am3352-ecap",
 					     "ti,am33xx-ecap";
diff --git a/doc/device-tree-bindings/pwm/ti,pwmss.txt b/doc/device-tree-bindings/pwm/ti,pwmss.txt
new file mode 100644
index 0000000000..4633697fbd
--- /dev/null
+++ b/doc/device-tree-bindings/pwm/ti,pwmss.txt
@@ -0,0 +1,58 @@ 
+TI SOC based PWM Subsystem
+
+Required properties:
+- compatible: Must be "ti,<soc>-pwmss".
+  for am33xx  - compatible = "ti,am33xx-pwmss";
+  for am4372  - compatible = "ti,am4372-pwmss","ti,am33xx-pwmss";
+  for dra746 - compatible = "ti,dra746-pwmss", "ti,am33xx-pwmss"
+
+- reg: physical base address and size of the registers map.
+- address-cells: Specify the number of u32 entries needed in child nodes.
+		  Should set to 1.
+- size-cells: specify number of u32 entries needed to specify child nodes size
+		in reg property. Should set to 1.
+- ranges: describes the address mapping of a memory-mapped bus. Should set to
+	   physical address map of child's base address, physical address within
+	   parent's address  space and length of the address map. For am33xx,
+	   3 set of child register maps present, ECAP register space, EQEP
+	   register space, EHRPWM register space.
+
+Also child nodes should also populated under PWMSS DT node.
+
+Example:
+epwmss0: epwmss@48300000 { /* PWMSS for am33xx */
+	compatible = "ti,am33xx-pwmss";
+	reg = <0x48300000 0x10>;
+	ti,hwmods = "epwmss0";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0x48300100 0x48300100 0x80   /* ECAP */
+		  0x48300180 0x48300180 0x80   /* EQEP */
+		  0x48300200 0x48300200 0x80>; /* EHRPWM */
+
+	/* child nodes go here */
+};
+
+epwmss0: epwmss@48300000 { /* PWMSS for am4372 */
+	compatible = "ti,am4372-pwmss","ti,am33xx-pwmss"
+	reg = <0x48300000 0x10>;
+	ti,hwmods = "epwmss0";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0x48300100 0x48300100 0x80   /* ECAP */
+		  0x48300180 0x48300180 0x80   /* EQEP */
+		  0x48300200 0x48300200 0x80>; /* EHRPWM */
+
+	/* child nodes go here */
+};
+
+epwmss0: epwmss@4843e000 { /* PWMSS for DRA7xx */
+	compatible = "ti,dra746-pwmss", "ti,am33xx-pwmss";
+	reg = <0x4843e000 0x30>;
+	ti,hwmods = "epwmss0";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+
+	/* child nodes go here */
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e4ba8b3d80..129f5d77ca 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -69,8 +69,15 @@  config PWM_SUNXI
 	  This PWM is found on H3, A64 and other Allwinner SoCs. It supports a
 	  programmable period and duty cycle. A 16-bit counter is used.
 
+config PWM_TI_PWMSS
+	bool "Enable support for the AM335x PWM Subsystem"
+	depends on DM_PWM
+	help
+	  This enables the AM335x PWM subsystem driver support on TI's SOCs.
+
 config PWM_TI_EHRPWM
 	bool "Enable support for the AM335x EHRPWM"
-	depends on DM_PWM
+	depends on PWM_TI_PWMSS
+	default y
 	help
 	  This enables the AM335x EHRPWM driver support on TI's SoCs.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index c5f88f7501..df9c54e764 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -19,3 +19,4 @@  obj-$(CONFIG_PWM_SIFIVE)	+= pwm-sifive.o
 obj-$(CONFIG_PWM_TEGRA)		+= tegra_pwm.o
 obj-$(CONFIG_PWM_SUNXI)		+= sunxi_pwm.o
 obj-$(CONFIG_PWM_TI_EHRPWM)	+= pwm-ti-ehrpwm.o
+obj-$(CONFIG_PWM_TI_PWMSS)	+= pwm-ti-pwmss.o
diff --git a/drivers/pwm/pwm-ti-pwmss.c b/drivers/pwm/pwm-ti-pwmss.c
new file mode 100644
index 0000000000..57f86512d6
--- /dev/null
+++ b/drivers/pwm/pwm-ti-pwmss.c
@@ -0,0 +1,95 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Pulse-Width Modulation Subsystem (pwmss)
+ *
+ * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
+ */
+#define DEBUG
+#undef CONFIG_LOGLEVEL
+#define CONFIG_LOGLEVEL 8
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+
+struct ti_pwmss_priv {
+	int child_count;
+	struct clk clkdm;
+};
+
+static const struct udevice_id ti_pwmss_ids[] = {
+	{.compatible = "ti,am33xx-pwmss"},
+	{}
+};
+
+static int ti_pwmss_child_post_remove(struct udevice *dev)
+{
+	struct ti_pwmss_priv *priv = dev_get_priv(dev->parent);
+	int err;
+
+	if (--priv->child_count > 0)
+		return 0;
+
+	err = clk_disable(&priv->clkdm);
+	if (err) {
+		dev_err(dev->parent, "%s: failed to disable clock domain\n",
+			__func__);
+		goto clkdm_disable_err;
+	}
+
+	err = clk_release_all(&priv->clkdm, 1);
+	if (err) {
+		dev_err(dev->parent, "%s: failed to release clock domain\n",
+			__func__);
+		goto clkdm_release_err;
+	}
+
+	return 0;
+
+clkdm_release_err:
+	clk_enable(&priv->clkdm);
+clkdm_disable_err:
+	priv->child_count++;
+	return err;
+}
+
+static int ti_pwmss_child_pre_probe(struct udevice *dev)
+{
+	struct ti_pwmss_priv *priv = dev_get_priv(dev->parent);
+	int err;
+
+	if (++priv->child_count > 1)
+		return 0;
+
+	err = clk_get_by_name(dev->parent, "fck", &priv->clkdm);
+	if (err) {
+		dev_err(dev->parent, "%s: failed to get clock domain\n",
+			__func__);
+		goto clkdm_get_err;
+	}
+
+	err = clk_enable(&priv->clkdm);
+	if (err) {
+		dev_err(dev->parent, "%s: failed to enable clock domain\n",
+			__func__);
+		goto clkdm_enable_err;
+	}
+
+	return 0;
+
+clkdm_enable_err:
+	clk_release_all(&priv->clkdm, 1);
+clkdm_get_err:
+	priv->child_count--;
+	return err;
+}
+
+U_BOOT_DRIVER(ti_pwmss) = {
+	.name = "ti_pwmss",
+	.id = UCLASS_SIMPLE_BUS,
+	.of_match = ti_pwmss_ids,
+	.bind = dm_scan_fdt_dev,
+	.child_pre_probe = ti_pwmss_child_pre_probe,
+	.child_post_remove = ti_pwmss_child_post_remove,
+	.priv_auto_alloc_size = sizeof(struct ti_pwmss_priv),
+};