"Das U-Boot" Source Tree

Merge patch series "ti: Add support for eCAP PWM and LCD pin mux"

Sukrut Bellary <sbellary@baylibre.com> says:

This patch series adds the support for

1. In am33xx SoC[1], enhanced capture (eCAP) supports auxiliary PWM (APWM).
This series adds the PWM driver support for the APWM feature for eCAP on
AM33xx. AM335X_ECAP0_IN_PWM0_OUT is used to enable the backlight.

2. Fix build warning in ti-ehrpwm driver in dev_deb().

3. Enable eCAP0 PWM and LCD pin muxing to support splash screen on
AM335x EVM[2].

[1] AM335x TRM - https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf
[2] AM335x EVM - https://www.ti.com/tool/TMDXEVM3358

per discussion on the earlier patch series,
https://lore.kernel.org/all/20250319202516.3300444-1-sbellary@baylibre.com/
dropping the device tree changes in this series due to OF_UPSTREAM
conflict.
As we are dropping the DT patch, started with the new series instead of
V2.

This series doesn't contain defconfig changes since we don't want to
enable LCD, splash screen and PWM support by default.
Enabling splash screen and PWM support in defconfig causes u-boot crash
on AM335x based beaglebone black. This will be handled in a separate
patch.

Link: https://lore.kernel.org/r/20250530212232.1686613-1-sbellary@baylibre.com

Tom Rini 7660feee 548d9972

+254 -1
+48
board/ti/am335x/mux.c
··· 274 274 {-1}, 275 275 }; 276 276 277 + #if (IS_ENABLED(CONFIG_AM335X_LCD)) 278 + static struct module_pin_mux lcd_pin_mux[] = { 279 + {OFFSET(lcd_data0), (MODE(0))}, /* LCD-Data(0) */ 280 + {OFFSET(lcd_data1), (MODE(0))}, /* LCD-Data(1) */ 281 + {OFFSET(lcd_data2), (MODE(0))}, /* LCD-Data(2) */ 282 + {OFFSET(lcd_data3), (MODE(0))}, /* LCD-Data(3) */ 283 + {OFFSET(lcd_data4), (MODE(0))}, /* LCD-Data(4) */ 284 + {OFFSET(lcd_data5), (MODE(0))}, /* LCD-Data(5) */ 285 + {OFFSET(lcd_data6), (MODE(0))}, /* LCD-Data(6) */ 286 + {OFFSET(lcd_data7), (MODE(0))}, /* LCD-Data(7) */ 287 + {OFFSET(lcd_data8), (MODE(0))}, /* LCD-Data(8) */ 288 + {OFFSET(lcd_data9), (MODE(0))}, /* LCD-Data(9) */ 289 + {OFFSET(lcd_data10), (MODE(0))}, /* LCD-Data(10) */ 290 + {OFFSET(lcd_data11), (MODE(0))}, /* LCD-Data(11) */ 291 + {OFFSET(lcd_data12), (MODE(0))}, /* LCD-Data(12) */ 292 + {OFFSET(lcd_data13), (MODE(0))}, /* LCD-Data(13) */ 293 + {OFFSET(lcd_data14), (MODE(0))}, /* LCD-Data(14) */ 294 + {OFFSET(lcd_data15), (MODE(0))}, /* LCD-Data(15) */ 295 + {OFFSET(gpmc_ad15), (MODE(1))}, /* LCD-Data(16) */ 296 + {OFFSET(gpmc_ad14), (MODE(1))}, /* LCD-Data(17) */ 297 + {OFFSET(gpmc_ad13), (MODE(1))}, /* LCD-Data(18) */ 298 + {OFFSET(gpmc_ad12), (MODE(1))}, /* LCD-Data(19) */ 299 + {OFFSET(gpmc_ad11), (MODE(1))}, /* LCD-Data(20) */ 300 + {OFFSET(gpmc_ad10), (MODE(1))}, /* LCD-Data(21) */ 301 + {OFFSET(gpmc_ad9), (MODE(1))}, /* LCD-Data(22) */ 302 + {OFFSET(gpmc_ad8), (MODE(1))}, /* LCD-Data(23) */ 303 + {OFFSET(lcd_vsync), (MODE(0))}, /* LCD-VSync */ 304 + {OFFSET(lcd_hsync), (MODE(0))}, /* LCD-HSync */ 305 + {OFFSET(lcd_ac_bias_en), (MODE(0))}, /* LCD-DE */ 306 + {OFFSET(lcd_pclk), (MODE(0))}, /* LCD-CLK */ 307 + {-1}, 308 + }; 309 + #endif 310 + 311 + #if (IS_ENABLED(CONFIG_PWM_TI_ECAP)) 312 + static struct module_pin_mux ecap_pin_mux[] = { 313 + {OFFSET(ecap0_in_pwm0_out), (MODE(0))}, /* ecap0_in_pwm0_out */ 314 + {-1}, 315 + }; 316 + #endif 317 + 277 318 #if defined(CONFIG_NOR_BOOT) 278 319 void enable_norboot_pin_mux(void) 279 320 { ··· 389 430 configure_module_pin_mux(mmc1_pin_mux); 390 431 configure_module_pin_mux(spi0_pin_mux); 391 432 } 433 + #if IS_ENABLED(CONFIG_AM335X_LCD) 434 + configure_module_pin_mux(lcd_pin_mux); 435 + #endif 436 + 437 + #if IS_ENABLED(CONFIG_PWM_TI_ECAP) 438 + configure_module_pin_mux(ecap_pin_mux); 439 + #endif 392 440 } else if (board_is_idk()) { 393 441 /* Industrial Motor Control (IDK) */ 394 442 configure_module_pin_mux(mii1_pin_mux);
+6
drivers/pwm/Kconfig
··· 126 126 default y 127 127 help 128 128 PWM driver support for the EHRPWM controller found on TI SOCs. 129 + 130 + config PWM_TI_ECAP 131 + bool "Enable support for ECAP PWM" 132 + depends on DM_PWM && ARCH_OMAP2PLUS 133 + help 134 + PWM driver support for the ECAP controller found on TI SOCs.
+1
drivers/pwm/Makefile
··· 25 25 obj-$(CONFIG_PWM_STM32) += pwm-stm32.o 26 26 obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o 27 27 obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o 28 + obj-$(CONFIG_PWM_TI_ECAP) += pwm-tiecap.o
+1 -1
drivers/pwm/pwm-ti-ehrpwm.c
··· 399 399 return -EINVAL; 400 400 } 401 401 402 - dev_dbg(dev, "regs=0x%08lx\n", priv->regs); 402 + dev_dbg(dev, "regs=0x%08x\n", priv->regs); 403 403 return 0; 404 404 } 405 405
+198
drivers/pwm/pwm-tiecap.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * ECAP PWM driver 4 + * 5 + * Copyright (C) 2025 BayLibre, SAS 6 + * Author: Sukrut Bellary <sbellary@baylibre.com> 7 + */ 8 + 9 + #include <clk.h> 10 + #include <div64.h> 11 + #include <dm.h> 12 + #include <dm/device_compat.h> 13 + #include <pwm.h> 14 + #include <asm/io.h> 15 + 16 + /* eCAP module registers */ 17 + #define ECAP_PWM_CAP1 0x08 18 + #define ECAP_PWM_CAP2 0x0C 19 + #define ECAP_PWM_CAP3 0x10 20 + #define ECAP_PWM_CAP4 0x14 21 + 22 + #define ECAP_PWM_ECCTL2 0x2A 23 + #define ECAP_PWM_ECCTL2_APWM_POL_LOW BIT(10) 24 + #define ECAP_PWM_ECCTL2_APWM_MODE BIT(9) 25 + #define ECAP_PWM_ECCTL2_TSCTR_FREERUN BIT(4) 26 + #define ECAP_PWM_ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) 27 + 28 + #define NSEC_PER_SEC 1000000000L 29 + 30 + enum tiecap_pwm_polarity { 31 + TIECAP_PWM_POLARITY_NORMAL, 32 + TIECAP_PWM_POLARITY_INVERSED 33 + }; 34 + 35 + enum tiecap_pwm_state { 36 + TIECAP_APWM_DISABLED, 37 + TIECAP_APWM_ENABLED 38 + }; 39 + 40 + struct tiecap_pwm_priv { 41 + fdt_addr_t regs; 42 + u32 clk_rate; 43 + enum tiecap_pwm_state pwm_state; 44 + }; 45 + 46 + static int tiecap_pwm_set_config(struct udevice *dev, uint channel, 47 + uint period_ns, uint duty_ns) 48 + { 49 + struct tiecap_pwm_priv *priv = dev_get_priv(dev); 50 + u32 period_cycles, duty_cycles; 51 + unsigned long long c; 52 + u16 value; 53 + 54 + c = priv->clk_rate; 55 + c = c * period_ns; 56 + do_div(c, NSEC_PER_SEC); 57 + period_cycles = (u32)c; 58 + 59 + if (period_cycles < 1) { 60 + period_cycles = 1; 61 + duty_cycles = 1; 62 + } else { 63 + c = priv->clk_rate; 64 + c = c * duty_ns; 65 + do_div(c, NSEC_PER_SEC); 66 + duty_cycles = (u32)c; 67 + } 68 + 69 + value = readw(priv->regs + ECAP_PWM_ECCTL2); 70 + 71 + /* Configure APWM mode & disable sync option */ 72 + value |= ECAP_PWM_ECCTL2_APWM_MODE | ECAP_PWM_ECCTL2_SYNC_SEL_DISA; 73 + 74 + writew(value, priv->regs + ECAP_PWM_ECCTL2); 75 + 76 + if (priv->pwm_state == TIECAP_APWM_DISABLED) { 77 + /* Update active registers */ 78 + writel(duty_cycles, priv->regs + ECAP_PWM_CAP2); 79 + writel(period_cycles, priv->regs + ECAP_PWM_CAP1); 80 + } else { 81 + /* Update shadow registers to configure period and 82 + * compare values. This helps current pwm period to 83 + * complete on reconfiguring. 84 + */ 85 + writel(duty_cycles, priv->regs + ECAP_PWM_CAP4); 86 + writel(period_cycles, priv->regs + ECAP_PWM_CAP3); 87 + } 88 + 89 + return 0; 90 + } 91 + 92 + static int tiecap_pwm_set_enable(struct udevice *dev, uint channel, bool enable) 93 + { 94 + struct tiecap_pwm_priv *priv = dev_get_priv(dev); 95 + u16 value; 96 + 97 + value = readw(priv->regs + ECAP_PWM_ECCTL2); 98 + 99 + if (enable) { 100 + /* 101 + * Enable 'Free run Time stamp counter mode' to start counter 102 + * and 'APWM mode' to enable APWM output 103 + */ 104 + value |= ECAP_PWM_ECCTL2_TSCTR_FREERUN | ECAP_PWM_ECCTL2_APWM_MODE; 105 + priv->pwm_state = TIECAP_APWM_ENABLED; 106 + } else { 107 + /* Disable 'Free run Time stamp counter mode' to stop counter 108 + * and 'APWM mode' to put APWM output to low 109 + */ 110 + value &= ~(ECAP_PWM_ECCTL2_TSCTR_FREERUN | ECAP_PWM_ECCTL2_APWM_MODE); 111 + priv->pwm_state = TIECAP_APWM_DISABLED; 112 + } 113 + 114 + writew(value, priv->regs + ECAP_PWM_ECCTL2); 115 + 116 + return 0; 117 + } 118 + 119 + static int tiecap_pwm_set_invert(struct udevice *dev, uint channel, 120 + bool polarity) 121 + { 122 + struct tiecap_pwm_priv *priv = dev_get_priv(dev); 123 + u16 value; 124 + 125 + value = readw(priv->regs + ECAP_PWM_ECCTL2); 126 + 127 + if (polarity == TIECAP_PWM_POLARITY_INVERSED) 128 + /* Duty cycle defines LOW period of PWM */ 129 + value |= ECAP_PWM_ECCTL2_APWM_POL_LOW; 130 + else 131 + /* Duty cycle defines HIGH period of PWM */ 132 + value &= ~ECAP_PWM_ECCTL2_APWM_POL_LOW; 133 + 134 + writew(value, priv->regs + ECAP_PWM_ECCTL2); 135 + 136 + return 0; 137 + } 138 + 139 + static int tiecap_pwm_of_to_plat(struct udevice *dev) 140 + { 141 + struct tiecap_pwm_priv *priv = dev_get_priv(dev); 142 + 143 + priv->regs = dev_read_addr(dev); 144 + if (priv->regs == FDT_ADDR_T_NONE) { 145 + dev_err(dev, "invalid address\n"); 146 + return -EINVAL; 147 + } 148 + 149 + dev_dbg(dev, "regs=0x%08x\n", priv->regs); 150 + 151 + return 0; 152 + } 153 + 154 + static int tiecap_pwm_probe(struct udevice *dev) 155 + { 156 + struct tiecap_pwm_priv *priv = dev_get_priv(dev); 157 + struct clk clk; 158 + int err; 159 + 160 + err = clk_get_by_name(dev, "fck", &clk); 161 + if (err) { 162 + dev_err(dev, "failed to get clock\n"); 163 + return err; 164 + } 165 + 166 + priv->clk_rate = clk_get_rate(&clk); 167 + if (IS_ERR_VALUE(priv->clk_rate) || !priv->clk_rate) { 168 + dev_err(dev, "failed to get clock rate\n"); 169 + if (IS_ERR_VALUE(priv->clk_rate)) 170 + return priv->clk_rate; 171 + 172 + return -EINVAL; 173 + } 174 + 175 + return 0; 176 + } 177 + 178 + static const struct pwm_ops tiecap_pwm_ops = { 179 + .set_config = tiecap_pwm_set_config, 180 + .set_enable = tiecap_pwm_set_enable, 181 + .set_invert = tiecap_pwm_set_invert, 182 + }; 183 + 184 + static const struct udevice_id tiecap_pwm_ids[] = { 185 + { .compatible = "ti,am3352-ecap" }, 186 + { .compatible = "ti,am33xx-ecap" }, 187 + { } 188 + }; 189 + 190 + U_BOOT_DRIVER(tiecap_pwm) = { 191 + .name = "tiecap_pwm", 192 + .id = UCLASS_PWM, 193 + .of_match = tiecap_pwm_ids, 194 + .ops = &tiecap_pwm_ops, 195 + .probe = tiecap_pwm_probe, 196 + .of_to_plat = tiecap_pwm_of_to_plat, 197 + .priv_auto = sizeof(struct tiecap_pwm_priv), 198 + };