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

Configure Feed

Select the types of activity you want to include in your feed.

pwm: pca9685: Use bulk write to atomicially update registers

The output of a PWM channel is configured by four register values. Write
them in a single i2c transaction to ensure glitch free updates.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://lore.kernel.org/r/bfa8c0267c9ec059d0d77f146998d564654c75ca.1753784092.git.u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Uwe Kleine-König and committed by
Uwe Kleine-König
de585561 ca478d8a

+29 -17
+29 -17
drivers/pwm/pwm-pca9685.c
··· 61 61 #define MODE1_SUB2 BIT(2) 62 62 #define MODE1_SUB1 BIT(3) 63 63 #define MODE1_SLEEP BIT(4) 64 + #define MODE1_AI BIT(5) 65 + 64 66 #define MODE2_INVRT BIT(4) 65 67 #define MODE2_OUTDRV BIT(2) 66 68 ··· 133 131 return err; 134 132 } 135 133 134 + static int pca9685_write_4reg(struct pwm_chip *chip, unsigned int reg, u8 val[4]) 135 + { 136 + struct pca9685 *pca = to_pca(chip); 137 + struct device *dev = pwmchip_parent(chip); 138 + int err; 139 + 140 + err = regmap_bulk_write(pca->regmap, reg, val, 4); 141 + if (err) 142 + dev_err(dev, "regmap_write to register 0x%x failed: %pe\n", reg, ERR_PTR(err)); 143 + 144 + return err; 145 + } 146 + 136 147 /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ 137 148 static void pca9685_pwm_set_duty(struct pwm_chip *chip, int channel, unsigned int duty) 138 149 { ··· 158 143 return; 159 144 } else if (duty >= PCA9685_COUNTER_RANGE) { 160 145 /* Set the full ON bit and clear the full OFF bit */ 161 - pca9685_write_reg(chip, REG_ON_H(channel), LED_FULL); 162 - pca9685_write_reg(chip, REG_OFF_H(channel), 0); 146 + pca9685_write_4reg(chip, REG_ON_L(channel), (u8[4]){ 0, LED_FULL, 0, 0 }); 163 147 return; 164 148 } 165 - 166 149 167 150 if (pwm->state.usage_power && channel < PCA9685_MAXCHAN) { 168 151 /* ··· 176 163 177 164 off = (on + duty) % PCA9685_COUNTER_RANGE; 178 165 179 - /* Set ON time (clears full ON bit) */ 180 - pca9685_write_reg(chip, REG_ON_L(channel), on & 0xff); 181 - pca9685_write_reg(chip, REG_ON_H(channel), (on >> 8) & 0xf); 182 - /* Set OFF time (clears full OFF bit) */ 183 - pca9685_write_reg(chip, REG_OFF_L(channel), off & 0xff); 184 - pca9685_write_reg(chip, REG_OFF_H(channel), (off >> 8) & 0xf); 166 + /* implicitly clear full ON and full OFF bit */ 167 + pca9685_write_4reg(chip, REG_ON_L(channel), 168 + (u8[4]){ on & 0xff, (on >> 8) & 0xf, off & 0xff, (off >> 8) & 0xf }); 185 169 } 186 170 187 171 static unsigned int pca9685_pwm_get_duty(struct pwm_chip *chip, int channel) ··· 553 543 554 544 mutex_init(&pca->lock); 555 545 556 - ret = pca9685_read_reg(chip, PCA9685_MODE2, &reg); 557 - if (ret) 558 - return ret; 546 + /* clear MODE2_OCH */ 547 + reg = 0; 559 548 560 549 if (device_property_read_bool(&client->dev, "invert")) 561 550 reg |= MODE2_INVRT; ··· 570 561 if (ret) 571 562 return ret; 572 563 573 - /* Disable all LED ALLCALL and SUBx addresses to avoid bus collisions */ 564 + /* 565 + * Disable all LED ALLCALL and SUBx addresses to avoid bus collisions, 566 + * enable Auto-Increment. 567 + */ 574 568 pca9685_read_reg(chip, PCA9685_MODE1, &reg); 575 569 reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3); 570 + reg |= MODE1_AI; 576 571 pca9685_write_reg(chip, PCA9685_MODE1, reg); 577 572 578 573 /* Reset OFF/ON registers to POR default */ 579 - pca9685_write_reg(chip, PCA9685_ALL_LED_OFF_L, 0); 580 - pca9685_write_reg(chip, PCA9685_ALL_LED_OFF_H, LED_FULL); 581 - pca9685_write_reg(chip, PCA9685_ALL_LED_ON_L, 0); 582 - pca9685_write_reg(chip, PCA9685_ALL_LED_ON_H, LED_FULL); 574 + ret = pca9685_write_4reg(chip, PCA9685_ALL_LED_ON_L, (u8[]){ 0, LED_FULL, 0, LED_FULL }); 575 + if (ret < 0) 576 + return dev_err_probe(&client->dev, ret, "Failed to reset ON/OFF registers\n"); 583 577 584 578 chip->ops = &pca9685_pwm_ops; 585 579