Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pwm: airoha: Add support for EN7581 SoC

Introduce driver for PWM module available on EN7581 SoC.

Limitations:
- Only 8 concurrent waveform generators are available for 8 combinations of
duty_cycle and period. Waveform generators are shared between 16 GPIO
pins and 17 SIPO GPIO pins.
- Supports only normal polarity.
- On configuration the currently running period is completed.
- Minimum supported period is 4 ms
- Maximum supported period is 1s

Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Link: https://patch.msgid.link/20251013103408.14724-1-ansuelsmth@gmail.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Benjamin Larsson and committed by
Uwe Kleine-König
b55bbc28 24ec5632

+633
+10
drivers/pwm/Kconfig
··· 63 63 This option enables support for the PWM function found in the Analog 64 64 Devices ADP5585. 65 65 66 + config PWM_AIROHA 67 + tristate "Airoha PWM support" 68 + depends on ARCH_AIROHA || COMPILE_TEST 69 + select REGMAP_MMIO 70 + help 71 + Generic PWM framework driver for Airoha SoC. 72 + 73 + To compile this driver as a module, choose M here: the module 74 + will be called pwm-airoha. 75 + 66 76 config PWM_APPLE 67 77 tristate "Apple SoC PWM support" 68 78 depends on ARCH_APPLE || COMPILE_TEST
+1
drivers/pwm/Makefile
··· 2 2 obj-$(CONFIG_PWM) += core.o 3 3 obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o 4 4 obj-$(CONFIG_PWM_ADP5585) += pwm-adp5585.o 5 + obj-$(CONFIG_PWM_AIROHA) += pwm-airoha.o 5 6 obj-$(CONFIG_PWM_APPLE) += pwm-apple.o 6 7 obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o 7 8 obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
+622
drivers/pwm/pwm-airoha.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2022 Markus Gothe <markus.gothe@genexis.eu> 4 + * Copyright 2025 Christian Marangi <ansuelsmth@gmail.com> 5 + * 6 + * Limitations: 7 + * - Only 8 concurrent waveform generators are available for 8 combinations of 8 + * duty_cycle and period. Waveform generators are shared between 16 GPIO 9 + * pins and 17 SIPO GPIO pins. 10 + * - Supports only normal polarity. 11 + * - On configuration the currently running period is completed. 12 + * - Minimum supported period is 4 ms 13 + * - Maximum supported period is 1s 14 + */ 15 + 16 + #include <linux/array_size.h> 17 + #include <linux/bitfield.h> 18 + #include <linux/bitmap.h> 19 + #include <linux/err.h> 20 + #include <linux/io.h> 21 + #include <linux/iopoll.h> 22 + #include <linux/math64.h> 23 + #include <linux/mfd/syscon.h> 24 + #include <linux/module.h> 25 + #include <linux/mod_devicetable.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/pwm.h> 28 + #include <linux/regmap.h> 29 + #include <linux/types.h> 30 + 31 + #define AIROHA_PWM_REG_SGPIO_LED_DATA 0x0024 32 + #define AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG BIT(31) 33 + #define AIROHA_PWM_SGPIO_LED_DATA_DATA GENMASK(16, 0) 34 + 35 + #define AIROHA_PWM_REG_SGPIO_CLK_DIVR 0x0028 36 + #define AIROHA_PWM_SGPIO_CLK_DIVR GENMASK(1, 0) 37 + #define AIROHA_PWM_SGPIO_CLK_DIVR_32 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 3) 38 + #define AIROHA_PWM_SGPIO_CLK_DIVR_16 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 2) 39 + #define AIROHA_PWM_SGPIO_CLK_DIVR_8 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 1) 40 + #define AIROHA_PWM_SGPIO_CLK_DIVR_4 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 0) 41 + 42 + #define AIROHA_PWM_REG_SGPIO_CLK_DLY 0x002c 43 + 44 + #define AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG 0x0030 45 + #define AIROHA_PWM_SERIAL_GPIO_FLASH_MODE BIT(1) 46 + #define AIROHA_PWM_SERIAL_GPIO_MODE_74HC164 BIT(0) 47 + 48 + #define AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(_n) (0x003c + (4 * (_n))) 49 + #define AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(_n) (16 * (_n)) 50 + #define AIROHA_PWM_GPIO_FLASH_PRD_LOW GENMASK(15, 8) 51 + #define AIROHA_PWM_GPIO_FLASH_PRD_HIGH GENMASK(7, 0) 52 + 53 + #define AIROHA_PWM_REG_GPIO_FLASH_MAP(_n) (0x004c + (4 * (_n))) 54 + #define AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(_n) (4 * (_n)) 55 + #define AIROHA_PWM_GPIO_FLASH_EN BIT(3) 56 + #define AIROHA_PWM_GPIO_FLASH_SET_ID GENMASK(2, 0) 57 + 58 + /* Register map is equal to GPIO flash map */ 59 + #define AIROHA_PWM_REG_SIPO_FLASH_MAP(_n) (0x0054 + (4 * (_n))) 60 + 61 + #define AIROHA_PWM_REG_CYCLE_CFG_VALUE(_n) (0x0098 + (4 * (_n))) 62 + #define AIROHA_PWM_REG_CYCLE_CFG_SHIFT(_n) (8 * (_n)) 63 + #define AIROHA_PWM_WAVE_GEN_CYCLE GENMASK(7, 0) 64 + 65 + /* GPIO/SIPO flash map handles 8 pins in one register */ 66 + #define AIROHA_PWM_PINS_PER_FLASH_MAP 8 67 + /* Cycle(Period) registers handles 4 generators in one 32-bit register */ 68 + #define AIROHA_PWM_BUCKET_PER_CYCLE_CFG 4 69 + /* Flash(Duty) producer handles 2 generators in one 32-bit register */ 70 + #define AIROHA_PWM_BUCKET_PER_FLASH_PROD 2 71 + 72 + #define AIROHA_PWM_NUM_BUCKETS 8 73 + /* 74 + * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15. 75 + * The SIPO GPIO pins are 17 pins which are mapped into 17 PWM channels, 16-32. 76 + * However, we've only got 8 concurrent waveform generators and can therefore 77 + * only use up to 8 different combinations of duty cycle and period at a time. 78 + */ 79 + #define AIROHA_PWM_NUM_GPIO 16 80 + #define AIROHA_PWM_NUM_SIPO 17 81 + #define AIROHA_PWM_MAX_CHANNELS (AIROHA_PWM_NUM_GPIO + AIROHA_PWM_NUM_SIPO) 82 + 83 + struct airoha_pwm_bucket { 84 + /* Concurrent access protected by PWM core */ 85 + int used; 86 + u32 period_ticks; 87 + u32 duty_ticks; 88 + }; 89 + 90 + struct airoha_pwm { 91 + struct regmap *regmap; 92 + 93 + DECLARE_BITMAP(initialized, AIROHA_PWM_MAX_CHANNELS); 94 + 95 + struct airoha_pwm_bucket buckets[AIROHA_PWM_NUM_BUCKETS]; 96 + 97 + /* Cache bucket used by each pwm channel */ 98 + u8 channel_bucket[AIROHA_PWM_MAX_CHANNELS]; 99 + }; 100 + 101 + /* The PWM hardware supports periods between 4 ms and 1 s */ 102 + #define AIROHA_PWM_PERIOD_TICK_NS (4 * NSEC_PER_MSEC) 103 + #define AIROHA_PWM_PERIOD_MAX_NS (1 * NSEC_PER_SEC) 104 + /* It is represented internally as 1/250 s between 1 and 250. Unit is ticks. */ 105 + #define AIROHA_PWM_PERIOD_MIN 1 106 + #define AIROHA_PWM_PERIOD_MAX 250 107 + /* Duty cycle is relative with 255 corresponding to 100% */ 108 + #define AIROHA_PWM_DUTY_FULL 255 109 + 110 + static void airoha_pwm_get_flash_map_addr_and_shift(unsigned int hwpwm, 111 + u32 *addr, u32 *shift) 112 + { 113 + unsigned int offset, hwpwm_bit; 114 + 115 + if (hwpwm >= AIROHA_PWM_NUM_GPIO) { 116 + unsigned int sipohwpwm = hwpwm - AIROHA_PWM_NUM_GPIO; 117 + 118 + offset = sipohwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP; 119 + hwpwm_bit = sipohwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP; 120 + 121 + /* One FLASH_MAP register handles 8 pins */ 122 + *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit); 123 + *addr = AIROHA_PWM_REG_SIPO_FLASH_MAP(offset); 124 + } else { 125 + offset = hwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP; 126 + hwpwm_bit = hwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP; 127 + 128 + /* One FLASH_MAP register handles 8 pins */ 129 + *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit); 130 + *addr = AIROHA_PWM_REG_GPIO_FLASH_MAP(offset); 131 + } 132 + } 133 + 134 + static u32 airoha_pwm_get_period_ticks_from_ns(u32 period_ns) 135 + { 136 + return period_ns / AIROHA_PWM_PERIOD_TICK_NS; 137 + } 138 + 139 + static u32 airoha_pwm_get_duty_ticks_from_ns(u32 period_ns, u32 duty_ns) 140 + { 141 + return mul_u64_u32_div(duty_ns, AIROHA_PWM_DUTY_FULL, period_ns); 142 + } 143 + 144 + static u32 airoha_pwm_get_period_ns_from_ticks(u32 period_tick) 145 + { 146 + return period_tick * AIROHA_PWM_PERIOD_TICK_NS; 147 + } 148 + 149 + static u32 airoha_pwm_get_duty_ns_from_ticks(u32 period_tick, u32 duty_tick) 150 + { 151 + u32 period_ns = period_tick * AIROHA_PWM_PERIOD_TICK_NS; 152 + 153 + /* 154 + * Overflow can't occur in multiplication as duty_tick is just 8 bit 155 + * and period_ns is clamped to AIROHA_PWM_PERIOD_MAX_NS and fit in a 156 + * u64. 157 + */ 158 + return DIV_U64_ROUND_UP(duty_tick * period_ns, AIROHA_PWM_DUTY_FULL); 159 + } 160 + 161 + static int airoha_pwm_get_bucket(struct airoha_pwm *pc, int bucket, 162 + u64 *period_ns, u64 *duty_ns) 163 + { 164 + struct regmap *map = pc->regmap; 165 + u32 period_tick, duty_tick; 166 + unsigned int offset; 167 + u32 shift, val; 168 + int ret; 169 + 170 + offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG; 171 + shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG; 172 + shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift); 173 + 174 + ret = regmap_read(map, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset), &val); 175 + if (ret) 176 + return ret; 177 + 178 + period_tick = FIELD_GET(AIROHA_PWM_WAVE_GEN_CYCLE, val >> shift); 179 + *period_ns = airoha_pwm_get_period_ns_from_ticks(period_tick); 180 + 181 + offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD; 182 + shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD; 183 + shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift); 184 + 185 + ret = regmap_read(map, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), 186 + &val); 187 + if (ret) 188 + return ret; 189 + 190 + duty_tick = FIELD_GET(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, val >> shift); 191 + *duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_tick, duty_tick); 192 + 193 + return 0; 194 + } 195 + 196 + static int airoha_pwm_get_generator(struct airoha_pwm *pc, u32 duty_ticks, 197 + u32 period_ticks) 198 + { 199 + int best = -ENOENT, unused = -ENOENT; 200 + u32 duty_ns, best_duty_ns = 0; 201 + u32 best_period_ticks = 0; 202 + unsigned int i; 203 + 204 + duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_ticks, duty_ticks); 205 + 206 + for (i = 0; i < ARRAY_SIZE(pc->buckets); i++) { 207 + struct airoha_pwm_bucket *bucket = &pc->buckets[i]; 208 + u32 bucket_period_ticks = bucket->period_ticks; 209 + u32 bucket_duty_ticks = bucket->duty_ticks; 210 + 211 + /* If found, save an unused bucket to return it later */ 212 + if (!bucket->used) { 213 + unused = i; 214 + continue; 215 + } 216 + 217 + /* We found a matching bucket, exit early */ 218 + if (duty_ticks == bucket_duty_ticks && 219 + period_ticks == bucket_period_ticks) 220 + return i; 221 + 222 + /* 223 + * Unlike duty cycle zero, which can be handled by 224 + * disabling PWM, a generator is needed for full duty 225 + * cycle but it can be reused regardless of period 226 + */ 227 + if (duty_ticks == AIROHA_PWM_DUTY_FULL && 228 + bucket_duty_ticks == AIROHA_PWM_DUTY_FULL) 229 + return i; 230 + 231 + /* 232 + * With an unused bucket available, skip searching for 233 + * a bucket to recycle (closer to the requested period/duty) 234 + */ 235 + if (unused >= 0) 236 + continue; 237 + 238 + /* Ignore bucket with invalid period */ 239 + if (bucket_period_ticks > period_ticks) 240 + continue; 241 + 242 + /* 243 + * Search for a bucket closer to the requested period 244 + * that has the maximal possible period that isn't bigger 245 + * than the requested period. For that period pick the maximal 246 + * duty cycle that isn't bigger than the requested duty_cycle. 247 + */ 248 + if (bucket_period_ticks >= best_period_ticks) { 249 + u32 bucket_duty_ns = airoha_pwm_get_duty_ns_from_ticks(bucket_period_ticks, 250 + bucket_duty_ticks); 251 + 252 + /* Skip bucket that goes over the requested duty */ 253 + if (bucket_duty_ns > duty_ns) 254 + continue; 255 + 256 + if (bucket_duty_ns > best_duty_ns) { 257 + best_period_ticks = bucket_period_ticks; 258 + best_duty_ns = bucket_duty_ns; 259 + best = i; 260 + } 261 + } 262 + } 263 + 264 + /* Return an unused bucket or the best one found (if ever) */ 265 + return unused >= 0 ? unused : best; 266 + } 267 + 268 + static void airoha_pwm_release_bucket_config(struct airoha_pwm *pc, 269 + unsigned int hwpwm) 270 + { 271 + int bucket; 272 + 273 + /* Nothing to clear, PWM channel never used */ 274 + if (!test_bit(hwpwm, pc->initialized)) 275 + return; 276 + 277 + bucket = pc->channel_bucket[hwpwm]; 278 + pc->buckets[bucket].used--; 279 + } 280 + 281 + static int airoha_pwm_apply_bucket_config(struct airoha_pwm *pc, unsigned int bucket, 282 + u32 duty_ticks, u32 period_ticks) 283 + { 284 + u32 mask, shift, val; 285 + u32 offset; 286 + int ret; 287 + 288 + offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG; 289 + shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG; 290 + shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift); 291 + 292 + /* Configure frequency divisor */ 293 + mask = AIROHA_PWM_WAVE_GEN_CYCLE << shift; 294 + val = FIELD_PREP(AIROHA_PWM_WAVE_GEN_CYCLE, period_ticks) << shift; 295 + ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset), 296 + mask, val); 297 + if (ret) 298 + return ret; 299 + 300 + offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD; 301 + shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD; 302 + shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift); 303 + 304 + /* Configure duty cycle */ 305 + mask = AIROHA_PWM_GPIO_FLASH_PRD_HIGH << shift; 306 + val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, duty_ticks) << shift; 307 + ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), 308 + mask, val); 309 + if (ret) 310 + return ret; 311 + 312 + mask = AIROHA_PWM_GPIO_FLASH_PRD_LOW << shift; 313 + val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_LOW, 314 + AIROHA_PWM_DUTY_FULL - duty_ticks) << shift; 315 + return regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), 316 + mask, val); 317 + } 318 + 319 + static int airoha_pwm_consume_generator(struct airoha_pwm *pc, 320 + u32 duty_ticks, u32 period_ticks, 321 + unsigned int hwpwm) 322 + { 323 + bool config_bucket = false; 324 + int bucket, ret; 325 + 326 + /* 327 + * Search for a bucket that already satisfies duty and period 328 + * or an unused one. 329 + * If not found, -ENOENT is returned. 330 + */ 331 + bucket = airoha_pwm_get_generator(pc, duty_ticks, period_ticks); 332 + if (bucket < 0) 333 + return bucket; 334 + 335 + /* Release previous used bucket (if any) */ 336 + airoha_pwm_release_bucket_config(pc, hwpwm); 337 + 338 + if (!pc->buckets[bucket].used) 339 + config_bucket = true; 340 + pc->buckets[bucket].used++; 341 + 342 + if (config_bucket) { 343 + pc->buckets[bucket].period_ticks = period_ticks; 344 + pc->buckets[bucket].duty_ticks = duty_ticks; 345 + ret = airoha_pwm_apply_bucket_config(pc, bucket, 346 + duty_ticks, 347 + period_ticks); 348 + if (ret) { 349 + pc->buckets[bucket].used--; 350 + return ret; 351 + } 352 + } 353 + 354 + return bucket; 355 + } 356 + 357 + static int airoha_pwm_sipo_init(struct airoha_pwm *pc) 358 + { 359 + u32 val; 360 + int ret; 361 + 362 + ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, 363 + AIROHA_PWM_SERIAL_GPIO_MODE_74HC164); 364 + if (ret) 365 + return ret; 366 + 367 + /* Configure shift register chip clock timings, use 32x divisor */ 368 + ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DIVR, 369 + AIROHA_PWM_SGPIO_CLK_DIVR_32); 370 + if (ret) 371 + return ret; 372 + 373 + /* 374 + * Configure the shift register chip clock delay. This needs 375 + * to be configured based on the chip characteristics when the SoC 376 + * apply the shift register configuration. 377 + * This doesn't affect actual PWM operation and is only specific to 378 + * the shift register chip. 379 + * 380 + * For 74HC164 we set it to 0. 381 + * 382 + * For reference, the actual delay applied is the internal clock 383 + * feed to the SGPIO chip + 1. 384 + * 385 + * From documentation is specified that clock delay should not be 386 + * greater than (AIROHA_PWM_REG_SGPIO_CLK_DIVR / 2) - 1. 387 + */ 388 + ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DLY, 0); 389 + if (ret) 390 + return ret; 391 + 392 + /* 393 + * It is necessary to explicitly shift out all zeros after muxing 394 + * to initialize the shift register before enabling PWM 395 + * mode because in PWM mode SIPO will not start shifting until 396 + * it needs to output a non-zero value (bit 31 of led_data 397 + * indicates shifting in progress and it must return to zero 398 + * before led_data can be written or PWM mode can be set). 399 + */ 400 + ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val, 401 + !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG), 402 + 10, 200 * USEC_PER_MSEC); 403 + if (ret) 404 + return ret; 405 + 406 + ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, 407 + AIROHA_PWM_SGPIO_LED_DATA_DATA); 408 + if (ret) 409 + return ret; 410 + ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val, 411 + !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG), 412 + 10, 200 * USEC_PER_MSEC); 413 + if (ret) 414 + return ret; 415 + 416 + /* Set SIPO in PWM mode */ 417 + return regmap_set_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, 418 + AIROHA_PWM_SERIAL_GPIO_FLASH_MODE); 419 + } 420 + 421 + static int airoha_pwm_config_flash_map(struct airoha_pwm *pc, 422 + unsigned int hwpwm, int index) 423 + { 424 + unsigned int addr; 425 + u32 shift; 426 + int ret; 427 + 428 + airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift); 429 + 430 + /* negative index means disable PWM channel */ 431 + if (index < 0) { 432 + /* 433 + * If we need to disable the PWM, we just put low the 434 + * GPIO. No need to setup buckets. 435 + */ 436 + return regmap_clear_bits(pc->regmap, addr, 437 + AIROHA_PWM_GPIO_FLASH_EN << shift); 438 + } 439 + 440 + ret = regmap_update_bits(pc->regmap, addr, 441 + AIROHA_PWM_GPIO_FLASH_SET_ID << shift, 442 + FIELD_PREP(AIROHA_PWM_GPIO_FLASH_SET_ID, index) << shift); 443 + if (ret) 444 + return ret; 445 + 446 + return regmap_set_bits(pc->regmap, addr, AIROHA_PWM_GPIO_FLASH_EN << shift); 447 + } 448 + 449 + static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm, 450 + u32 period_ticks, u32 duty_ticks) 451 + { 452 + unsigned int hwpwm = pwm->hwpwm; 453 + int bucket, ret; 454 + 455 + bucket = airoha_pwm_consume_generator(pc, duty_ticks, period_ticks, 456 + hwpwm); 457 + if (bucket < 0) 458 + return bucket; 459 + 460 + ret = airoha_pwm_config_flash_map(pc, hwpwm, bucket); 461 + if (ret) { 462 + pc->buckets[bucket].used--; 463 + return ret; 464 + } 465 + 466 + __set_bit(hwpwm, pc->initialized); 467 + pc->channel_bucket[hwpwm] = bucket; 468 + 469 + /* 470 + * SIPO are special GPIO attached to a shift register chip. The handling 471 + * of this chip is internal to the SoC that takes care of applying the 472 + * values based on the flash map. To apply a new flash map, it's needed 473 + * to trigger a refresh on the shift register chip. 474 + * If a SIPO is getting configuring , always reinit the shift register 475 + * chip to make sure the correct flash map is applied. 476 + * Skip reconfiguring the shift register if the related hwpwm 477 + * is disabled (as it doesn't need to be mapped). 478 + */ 479 + if (hwpwm >= AIROHA_PWM_NUM_GPIO) { 480 + ret = airoha_pwm_sipo_init(pc); 481 + if (ret) { 482 + airoha_pwm_release_bucket_config(pc, hwpwm); 483 + return ret; 484 + } 485 + } 486 + 487 + return 0; 488 + } 489 + 490 + static void airoha_pwm_disable(struct airoha_pwm *pc, struct pwm_device *pwm) 491 + { 492 + /* Disable PWM and release the bucket */ 493 + airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1); 494 + airoha_pwm_release_bucket_config(pc, pwm->hwpwm); 495 + 496 + __clear_bit(pwm->hwpwm, pc->initialized); 497 + 498 + /* If no SIPO is used, disable the shift register chip */ 499 + if (!bitmap_read(pc->initialized, 500 + AIROHA_PWM_NUM_GPIO, AIROHA_PWM_NUM_SIPO)) 501 + regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, 502 + AIROHA_PWM_SERIAL_GPIO_FLASH_MODE); 503 + } 504 + 505 + static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 506 + const struct pwm_state *state) 507 + { 508 + struct airoha_pwm *pc = pwmchip_get_drvdata(chip); 509 + u32 period_ticks, duty_ticks; 510 + u32 period_ns, duty_ns; 511 + 512 + if (!state->enabled) { 513 + airoha_pwm_disable(pc, pwm); 514 + return 0; 515 + } 516 + 517 + /* Only normal polarity is supported */ 518 + if (state->polarity == PWM_POLARITY_INVERSED) 519 + return -EINVAL; 520 + 521 + /* Exit early if period is less than minimum supported */ 522 + if (state->period < AIROHA_PWM_PERIOD_TICK_NS) 523 + return -EINVAL; 524 + 525 + /* Clamp period to MAX supported value */ 526 + if (state->period > AIROHA_PWM_PERIOD_MAX_NS) 527 + period_ns = AIROHA_PWM_PERIOD_MAX_NS; 528 + else 529 + period_ns = state->period; 530 + 531 + /* Validate duty to configured period */ 532 + if (state->duty_cycle > period_ns) 533 + duty_ns = period_ns; 534 + else 535 + duty_ns = state->duty_cycle; 536 + 537 + /* Convert period ns to ticks */ 538 + period_ticks = airoha_pwm_get_period_ticks_from_ns(period_ns); 539 + /* Convert period ticks to ns again for cosistent duty tick calculation */ 540 + period_ns = airoha_pwm_get_period_ns_from_ticks(period_ticks); 541 + duty_ticks = airoha_pwm_get_duty_ticks_from_ns(period_ns, duty_ns); 542 + 543 + return airoha_pwm_config(pc, pwm, period_ticks, duty_ticks); 544 + } 545 + 546 + static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 547 + struct pwm_state *state) 548 + { 549 + struct airoha_pwm *pc = pwmchip_get_drvdata(chip); 550 + int ret, hwpwm = pwm->hwpwm; 551 + u32 addr, shift, val; 552 + u8 bucket; 553 + 554 + airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift); 555 + 556 + ret = regmap_read(pc->regmap, addr, &val); 557 + if (ret) 558 + return ret; 559 + 560 + state->enabled = FIELD_GET(AIROHA_PWM_GPIO_FLASH_EN, val >> shift); 561 + if (!state->enabled) 562 + return 0; 563 + 564 + state->polarity = PWM_POLARITY_NORMAL; 565 + 566 + bucket = FIELD_GET(AIROHA_PWM_GPIO_FLASH_SET_ID, val >> shift); 567 + return airoha_pwm_get_bucket(pc, bucket, &state->period, 568 + &state->duty_cycle); 569 + } 570 + 571 + static const struct pwm_ops airoha_pwm_ops = { 572 + .apply = airoha_pwm_apply, 573 + .get_state = airoha_pwm_get_state, 574 + }; 575 + 576 + static int airoha_pwm_probe(struct platform_device *pdev) 577 + { 578 + struct device *dev = &pdev->dev; 579 + struct airoha_pwm *pc; 580 + struct pwm_chip *chip; 581 + int ret; 582 + 583 + chip = devm_pwmchip_alloc(dev, AIROHA_PWM_MAX_CHANNELS, sizeof(*pc)); 584 + if (IS_ERR(chip)) 585 + return PTR_ERR(chip); 586 + 587 + chip->ops = &airoha_pwm_ops; 588 + pc = pwmchip_get_drvdata(chip); 589 + 590 + pc->regmap = device_node_to_regmap(dev_of_node(dev->parent)); 591 + if (IS_ERR(pc->regmap)) 592 + return dev_err_probe(dev, PTR_ERR(pc->regmap), "Failed to get PWM regmap\n"); 593 + 594 + ret = devm_pwmchip_add(dev, chip); 595 + if (ret) 596 + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); 597 + 598 + return 0; 599 + } 600 + 601 + static const struct of_device_id airoha_pwm_of_match[] = { 602 + { .compatible = "airoha,en7581-pwm" }, 603 + { /* sentinel */ } 604 + }; 605 + MODULE_DEVICE_TABLE(of, airoha_pwm_of_match); 606 + 607 + static struct platform_driver airoha_pwm_driver = { 608 + .driver = { 609 + .name = "pwm-airoha", 610 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 611 + .of_match_table = airoha_pwm_of_match, 612 + }, 613 + .probe = airoha_pwm_probe, 614 + }; 615 + module_platform_driver(airoha_pwm_driver); 616 + 617 + MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); 618 + MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>"); 619 + MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>"); 620 + MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); 621 + MODULE_DESCRIPTION("Airoha EN7581 PWM driver"); 622 + MODULE_LICENSE("GPL");