summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/ti/am335x/mux.c48
-rw-r--r--drivers/pwm/Kconfig6
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-ti-ehrpwm.c2
-rw-r--r--drivers/pwm/pwm-tiecap.c198
5 files changed, 254 insertions, 1 deletions
diff --git a/board/ti/am335x/mux.c b/board/ti/am335x/mux.c
index 960de15398f..d416d88c1a1 100644
--- a/board/ti/am335x/mux.c
+++ b/board/ti/am335x/mux.c
@@ -274,6 +274,47 @@ static struct module_pin_mux uart3_icev2_pin_mux[] = {
{-1},
};
+#if (IS_ENABLED(CONFIG_AM335X_LCD))
+static struct module_pin_mux lcd_pin_mux[] = {
+ {OFFSET(lcd_data0), (MODE(0))}, /* LCD-Data(0) */
+ {OFFSET(lcd_data1), (MODE(0))}, /* LCD-Data(1) */
+ {OFFSET(lcd_data2), (MODE(0))}, /* LCD-Data(2) */
+ {OFFSET(lcd_data3), (MODE(0))}, /* LCD-Data(3) */
+ {OFFSET(lcd_data4), (MODE(0))}, /* LCD-Data(4) */
+ {OFFSET(lcd_data5), (MODE(0))}, /* LCD-Data(5) */
+ {OFFSET(lcd_data6), (MODE(0))}, /* LCD-Data(6) */
+ {OFFSET(lcd_data7), (MODE(0))}, /* LCD-Data(7) */
+ {OFFSET(lcd_data8), (MODE(0))}, /* LCD-Data(8) */
+ {OFFSET(lcd_data9), (MODE(0))}, /* LCD-Data(9) */
+ {OFFSET(lcd_data10), (MODE(0))}, /* LCD-Data(10) */
+ {OFFSET(lcd_data11), (MODE(0))}, /* LCD-Data(11) */
+ {OFFSET(lcd_data12), (MODE(0))}, /* LCD-Data(12) */
+ {OFFSET(lcd_data13), (MODE(0))}, /* LCD-Data(13) */
+ {OFFSET(lcd_data14), (MODE(0))}, /* LCD-Data(14) */
+ {OFFSET(lcd_data15), (MODE(0))}, /* LCD-Data(15) */
+ {OFFSET(gpmc_ad15), (MODE(1))}, /* LCD-Data(16) */
+ {OFFSET(gpmc_ad14), (MODE(1))}, /* LCD-Data(17) */
+ {OFFSET(gpmc_ad13), (MODE(1))}, /* LCD-Data(18) */
+ {OFFSET(gpmc_ad12), (MODE(1))}, /* LCD-Data(19) */
+ {OFFSET(gpmc_ad11), (MODE(1))}, /* LCD-Data(20) */
+ {OFFSET(gpmc_ad10), (MODE(1))}, /* LCD-Data(21) */
+ {OFFSET(gpmc_ad9), (MODE(1))}, /* LCD-Data(22) */
+ {OFFSET(gpmc_ad8), (MODE(1))}, /* LCD-Data(23) */
+ {OFFSET(lcd_vsync), (MODE(0))}, /* LCD-VSync */
+ {OFFSET(lcd_hsync), (MODE(0))}, /* LCD-HSync */
+ {OFFSET(lcd_ac_bias_en), (MODE(0))}, /* LCD-DE */
+ {OFFSET(lcd_pclk), (MODE(0))}, /* LCD-CLK */
+ {-1},
+};
+#endif
+
+#if (IS_ENABLED(CONFIG_PWM_TI_ECAP))
+static struct module_pin_mux ecap_pin_mux[] = {
+ {OFFSET(ecap0_in_pwm0_out), (MODE(0))}, /* ecap0_in_pwm0_out */
+ {-1},
+};
+#endif
+
#if defined(CONFIG_NOR_BOOT)
void enable_norboot_pin_mux(void)
{
@@ -389,6 +430,13 @@ void enable_board_pin_mux(void)
configure_module_pin_mux(mmc1_pin_mux);
configure_module_pin_mux(spi0_pin_mux);
}
+ #if IS_ENABLED(CONFIG_AM335X_LCD)
+ configure_module_pin_mux(lcd_pin_mux);
+ #endif
+
+ #if IS_ENABLED(CONFIG_PWM_TI_ECAP)
+ configure_module_pin_mux(ecap_pin_mux);
+ #endif
} else if (board_is_idk()) {
/* Industrial Motor Control (IDK) */
configure_module_pin_mux(mii1_pin_mux);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index de312656746..e4c676d75c2 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -126,3 +126,9 @@ config PWM_TI_EHRPWM
default y
help
PWM driver support for the EHRPWM controller found on TI SOCs.
+
+config PWM_TI_ECAP
+ bool "Enable support for ECAP PWM"
+ depends on DM_PWM && ARCH_OMAP2PLUS
+ help
+ PWM driver support for the ECAP controller found on TI SOCs.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 76305b93bc9..2682c536c6f 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o
+obj-$(CONFIG_PWM_TI_ECAP) += pwm-tiecap.o
diff --git a/drivers/pwm/pwm-ti-ehrpwm.c b/drivers/pwm/pwm-ti-ehrpwm.c
index 563109ef0f8..135ea3b4321 100644
--- a/drivers/pwm/pwm-ti-ehrpwm.c
+++ b/drivers/pwm/pwm-ti-ehrpwm.c
@@ -399,7 +399,7 @@ static int ti_ehrpwm_of_to_plat(struct udevice *dev)
return -EINVAL;
}
- dev_dbg(dev, "regs=0x%08lx\n", priv->regs);
+ dev_dbg(dev, "regs=0x%08x\n", priv->regs);
return 0;
}
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
new file mode 100644
index 00000000000..cfd6c871b57
--- /dev/null
+++ b/drivers/pwm/pwm-tiecap.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2025 BayLibre, SAS
+ * Author: Sukrut Bellary <sbellary@baylibre.com>
+ */
+
+#include <clk.h>
+#include <div64.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <pwm.h>
+#include <asm/io.h>
+
+/* eCAP module registers */
+#define ECAP_PWM_CAP1 0x08
+#define ECAP_PWM_CAP2 0x0C
+#define ECAP_PWM_CAP3 0x10
+#define ECAP_PWM_CAP4 0x14
+
+#define ECAP_PWM_ECCTL2 0x2A
+#define ECAP_PWM_ECCTL2_APWM_POL_LOW BIT(10)
+#define ECAP_PWM_ECCTL2_APWM_MODE BIT(9)
+#define ECAP_PWM_ECCTL2_TSCTR_FREERUN BIT(4)
+#define ECAP_PWM_ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
+
+#define NSEC_PER_SEC 1000000000L
+
+enum tiecap_pwm_polarity {
+ TIECAP_PWM_POLARITY_NORMAL,
+ TIECAP_PWM_POLARITY_INVERSED
+};
+
+enum tiecap_pwm_state {
+ TIECAP_APWM_DISABLED,
+ TIECAP_APWM_ENABLED
+};
+
+struct tiecap_pwm_priv {
+ fdt_addr_t regs;
+ u32 clk_rate;
+ enum tiecap_pwm_state pwm_state;
+};
+
+static int tiecap_pwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct tiecap_pwm_priv *priv = dev_get_priv(dev);
+ u32 period_cycles, duty_cycles;
+ unsigned long long c;
+ u16 value;
+
+ c = priv->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (u32)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = priv->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (u32)c;
+ }
+
+ value = readw(priv->regs + ECAP_PWM_ECCTL2);
+
+ /* Configure APWM mode & disable sync option */
+ value |= ECAP_PWM_ECCTL2_APWM_MODE | ECAP_PWM_ECCTL2_SYNC_SEL_DISA;
+
+ writew(value, priv->regs + ECAP_PWM_ECCTL2);
+
+ if (priv->pwm_state == TIECAP_APWM_DISABLED) {
+ /* Update active registers */
+ writel(duty_cycles, priv->regs + ECAP_PWM_CAP2);
+ writel(period_cycles, priv->regs + ECAP_PWM_CAP1);
+ } else {
+ /* Update shadow registers to configure period and
+ * compare values. This helps current pwm period to
+ * complete on reconfiguring.
+ */
+ writel(duty_cycles, priv->regs + ECAP_PWM_CAP4);
+ writel(period_cycles, priv->regs + ECAP_PWM_CAP3);
+ }
+
+ return 0;
+}
+
+static int tiecap_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
+{
+ struct tiecap_pwm_priv *priv = dev_get_priv(dev);
+ u16 value;
+
+ value = readw(priv->regs + ECAP_PWM_ECCTL2);
+
+ if (enable) {
+ /*
+ * Enable 'Free run Time stamp counter mode' to start counter
+ * and 'APWM mode' to enable APWM output
+ */
+ value |= ECAP_PWM_ECCTL2_TSCTR_FREERUN | ECAP_PWM_ECCTL2_APWM_MODE;
+ priv->pwm_state = TIECAP_APWM_ENABLED;
+ } else {
+ /* Disable 'Free run Time stamp counter mode' to stop counter
+ * and 'APWM mode' to put APWM output to low
+ */
+ value &= ~(ECAP_PWM_ECCTL2_TSCTR_FREERUN | ECAP_PWM_ECCTL2_APWM_MODE);
+ priv->pwm_state = TIECAP_APWM_DISABLED;
+ }
+
+ writew(value, priv->regs + ECAP_PWM_ECCTL2);
+
+ return 0;
+}
+
+static int tiecap_pwm_set_invert(struct udevice *dev, uint channel,
+ bool polarity)
+{
+ struct tiecap_pwm_priv *priv = dev_get_priv(dev);
+ u16 value;
+
+ value = readw(priv->regs + ECAP_PWM_ECCTL2);
+
+ if (polarity == TIECAP_PWM_POLARITY_INVERSED)
+ /* Duty cycle defines LOW period of PWM */
+ value |= ECAP_PWM_ECCTL2_APWM_POL_LOW;
+ else
+ /* Duty cycle defines HIGH period of PWM */
+ value &= ~ECAP_PWM_ECCTL2_APWM_POL_LOW;
+
+ writew(value, priv->regs + ECAP_PWM_ECCTL2);
+
+ return 0;
+}
+
+static int tiecap_pwm_of_to_plat(struct udevice *dev)
+{
+ struct tiecap_pwm_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr(dev);
+ if (priv->regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "invalid address\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "regs=0x%08x\n", priv->regs);
+
+ return 0;
+}
+
+static int tiecap_pwm_probe(struct udevice *dev)
+{
+ struct tiecap_pwm_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int err;
+
+ err = clk_get_by_name(dev, "fck", &clk);
+ if (err) {
+ dev_err(dev, "failed to get clock\n");
+ return err;
+ }
+
+ priv->clk_rate = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(priv->clk_rate) || !priv->clk_rate) {
+ dev_err(dev, "failed to get clock rate\n");
+ if (IS_ERR_VALUE(priv->clk_rate))
+ return priv->clk_rate;
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops tiecap_pwm_ops = {
+ .set_config = tiecap_pwm_set_config,
+ .set_enable = tiecap_pwm_set_enable,
+ .set_invert = tiecap_pwm_set_invert,
+};
+
+static const struct udevice_id tiecap_pwm_ids[] = {
+ { .compatible = "ti,am3352-ecap" },
+ { .compatible = "ti,am33xx-ecap" },
+ { }
+};
+
+U_BOOT_DRIVER(tiecap_pwm) = {
+ .name = "tiecap_pwm",
+ .id = UCLASS_PWM,
+ .of_match = tiecap_pwm_ids,
+ .ops = &tiecap_pwm_ops,
+ .probe = tiecap_pwm_probe,
+ .of_to_plat = tiecap_pwm_of_to_plat,
+ .priv_auto = sizeof(struct tiecap_pwm_priv),
+};