diff options
Diffstat (limited to 'drivers/pwm/pwm-pca9685.c')
| -rw-r--r-- | drivers/pwm/pwm-pca9685.c | 303 | 
1 files changed, 136 insertions, 167 deletions
| diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 4a55dc18656c..7c9f174de64e 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -51,7 +51,6 @@  #define PCA9685_PRESCALE_MAX	0xFF	/* => min. frequency of 24 Hz */  #define PCA9685_COUNTER_RANGE	4096 -#define PCA9685_DEFAULT_PERIOD	5000000	/* Default period_ns = 1/200 Hz */  #define PCA9685_OSC_CLOCK_MHZ	25	/* Internal oscillator with 25 MHz */  #define PCA9685_NUMREGS		0xFF @@ -71,10 +70,14 @@  #define LED_N_OFF_H(N)	(PCA9685_LEDX_OFF_H + (4 * (N)))  #define LED_N_OFF_L(N)	(PCA9685_LEDX_OFF_L + (4 * (N))) +#define REG_ON_H(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_H : LED_N_ON_H((C))) +#define REG_ON_L(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_L : LED_N_ON_L((C))) +#define REG_OFF_H(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_H : LED_N_OFF_H((C))) +#define REG_OFF_L(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_L : LED_N_OFF_L((C))) +  struct pca9685 {  	struct pwm_chip chip;  	struct regmap *regmap; -	int period_ns;  #if IS_ENABLED(CONFIG_GPIOLIB)  	struct mutex lock;  	struct gpio_chip gpio; @@ -87,6 +90,53 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)  	return container_of(chip, struct pca9685, chip);  } +/* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ +static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty) +{ +	if (duty == 0) { +		/* Set the full OFF bit, which has the highest precedence */ +		regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL); +	} else if (duty >= PCA9685_COUNTER_RANGE) { +		/* Set the full ON bit and clear the full OFF bit */ +		regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL); +		regmap_write(pca->regmap, REG_OFF_H(channel), 0); +	} else { +		/* Set OFF time (clears the full OFF bit) */ +		regmap_write(pca->regmap, REG_OFF_L(channel), duty & 0xff); +		regmap_write(pca->regmap, REG_OFF_H(channel), (duty >> 8) & 0xf); +		/* Clear the full ON bit */ +		regmap_write(pca->regmap, REG_ON_H(channel), 0); +	} +} + +static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel) +{ +	unsigned int off_h = 0, val = 0; + +	if (WARN_ON(channel >= PCA9685_MAXCHAN)) { +		/* HW does not support reading state of "all LEDs" channel */ +		return 0; +	} + +	regmap_read(pca->regmap, LED_N_OFF_H(channel), &off_h); +	if (off_h & LED_FULL) { +		/* Full OFF bit is set */ +		return 0; +	} + +	regmap_read(pca->regmap, LED_N_ON_H(channel), &val); +	if (val & LED_FULL) { +		/* Full ON bit is set */ +		return PCA9685_COUNTER_RANGE; +	} + +	if (regmap_read(pca->regmap, LED_N_OFF_L(channel), &val)) { +		/* Reset val to 0 in case reading LED_N_OFF_L failed */ +		val = 0; +	} +	return ((off_h & 0xf) << 8) | (val & 0xff); +} +  #if IS_ENABLED(CONFIG_GPIOLIB)  static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)  { @@ -138,34 +188,23 @@ static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)  static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)  {  	struct pca9685 *pca = gpiochip_get_data(gpio); -	struct pwm_device *pwm = &pca->chip.pwms[offset]; -	unsigned int value; - -	regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value); -	return value & LED_FULL; +	return pca9685_pwm_get_duty(pca, offset) != 0;  }  static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,  				 int value)  {  	struct pca9685 *pca = gpiochip_get_data(gpio); -	struct pwm_device *pwm = &pca->chip.pwms[offset]; -	unsigned int on = value ? LED_FULL : 0; -	/* Clear both OFF registers */ -	regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0); -	regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0); - -	/* Set the full ON bit */ -	regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on); +	pca9685_pwm_set_duty(pca, offset, value ? PCA9685_COUNTER_RANGE : 0);  }  static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)  {  	struct pca9685 *pca = gpiochip_get_data(gpio); -	pca9685_pwm_gpio_set(gpio, offset, 0); +	pca9685_pwm_set_duty(pca, offset, 0);  	pm_runtime_put(pca->chip.dev);  	pca9685_pwm_clear_inuse(pca, offset);  } @@ -246,165 +285,85 @@ static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable)  	}  } -static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, -			      int duty_ns, int period_ns) +static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +			     const struct pwm_state *state)  {  	struct pca9685 *pca = to_pca(chip); -	unsigned long long duty; -	unsigned int reg; -	int prescale; - -	if (period_ns != pca->period_ns) { -		prescale = DIV_ROUND_CLOSEST(PCA9685_OSC_CLOCK_MHZ * period_ns, -					     PCA9685_COUNTER_RANGE * 1000) - 1; - -		if (prescale >= PCA9685_PRESCALE_MIN && -			prescale <= PCA9685_PRESCALE_MAX) { -			/* -			 * Putting the chip briefly into SLEEP mode -			 * at this point won't interfere with the -			 * pm_runtime framework, because the pm_runtime -			 * state is guaranteed active here. -			 */ -			/* Put chip into sleep mode */ -			pca9685_set_sleep_mode(pca, true); - -			/* Change the chip-wide output frequency */ -			regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); - -			/* Wake the chip up */ -			pca9685_set_sleep_mode(pca, false); - -			pca->period_ns = period_ns; -		} else { -			dev_err(chip->dev, -				"prescaler not set: period out of bounds!\n"); -			return -EINVAL; -		} -	} +	unsigned long long duty, prescale; +	unsigned int val = 0; -	if (duty_ns < 1) { -		if (pwm->hwpwm >= PCA9685_MAXCHAN) -			reg = PCA9685_ALL_LED_OFF_H; -		else -			reg = LED_N_OFF_H(pwm->hwpwm); +	if (state->polarity != PWM_POLARITY_NORMAL) +		return -EINVAL; -		regmap_write(pca->regmap, reg, LED_FULL); - -		return 0; +	prescale = DIV_ROUND_CLOSEST_ULL(PCA9685_OSC_CLOCK_MHZ * state->period, +					 PCA9685_COUNTER_RANGE * 1000) - 1; +	if (prescale < PCA9685_PRESCALE_MIN || prescale > PCA9685_PRESCALE_MAX) { +		dev_err(chip->dev, "pwm not changed: period out of bounds!\n"); +		return -EINVAL;  	} -	if (duty_ns == period_ns) { -		/* Clear both OFF registers */ -		if (pwm->hwpwm >= PCA9685_MAXCHAN) -			reg = PCA9685_ALL_LED_OFF_L; -		else -			reg = LED_N_OFF_L(pwm->hwpwm); - -		regmap_write(pca->regmap, reg, 0x0); - -		if (pwm->hwpwm >= PCA9685_MAXCHAN) -			reg = PCA9685_ALL_LED_OFF_H; -		else -			reg = LED_N_OFF_H(pwm->hwpwm); - -		regmap_write(pca->regmap, reg, 0x0); - -		/* Set the full ON bit */ -		if (pwm->hwpwm >= PCA9685_MAXCHAN) -			reg = PCA9685_ALL_LED_ON_H; -		else -			reg = LED_N_ON_H(pwm->hwpwm); - -		regmap_write(pca->regmap, reg, LED_FULL); - +	if (!state->enabled) { +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);  		return 0;  	} -	duty = PCA9685_COUNTER_RANGE * (unsigned long long)duty_ns; -	duty = DIV_ROUND_UP_ULL(duty, period_ns); - -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_OFF_L; -	else -		reg = LED_N_OFF_L(pwm->hwpwm); - -	regmap_write(pca->regmap, reg, (int)duty & 0xff); - -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_OFF_H; -	else -		reg = LED_N_OFF_H(pwm->hwpwm); - -	regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf); +	regmap_read(pca->regmap, PCA9685_PRESCALE, &val); +	if (prescale != val) { +		/* +		 * Putting the chip briefly into SLEEP mode +		 * at this point won't interfere with the +		 * pm_runtime framework, because the pm_runtime +		 * state is guaranteed active here. +		 */ +		/* Put chip into sleep mode */ +		pca9685_set_sleep_mode(pca, true); -	/* Clear the full ON bit, otherwise the set OFF time has no effect */ -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_ON_H; -	else -		reg = LED_N_ON_H(pwm->hwpwm); +		/* Change the chip-wide output frequency */ +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); -	regmap_write(pca->regmap, reg, 0); +		/* Wake the chip up */ +		pca9685_set_sleep_mode(pca, false); +	} +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle; +	duty = DIV_ROUND_UP_ULL(duty, state->period); +	pca9685_pwm_set_duty(pca, pwm->hwpwm, duty);  	return 0;  } -static int pca9685_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, +				  struct pwm_state *state)  {  	struct pca9685 *pca = to_pca(chip); -	unsigned int reg; - -	/* -	 * The PWM subsystem does not support a pre-delay. -	 * So, set the ON-timeout to 0 -	 */ -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_ON_L; -	else -		reg = LED_N_ON_L(pwm->hwpwm); - -	regmap_write(pca->regmap, reg, 0); - -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_ON_H; -	else -		reg = LED_N_ON_H(pwm->hwpwm); - -	regmap_write(pca->regmap, reg, 0); +	unsigned long long duty; +	unsigned int val = 0; +	/* Calculate (chip-wide) period from prescale value */ +	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);  	/* -	 * Clear the full-off bit. -	 * It has precedence over the others and must be off. +	 * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000. +	 * The following calculation is therefore only a multiplication +	 * and we are not losing precision.  	 */ -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_OFF_H; -	else -		reg = LED_N_OFF_H(pwm->hwpwm); - -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0); +	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) * +			(val + 1); -	return 0; -} +	/* The (per-channel) polarity is fixed */ +	state->polarity = PWM_POLARITY_NORMAL; -static void pca9685_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ -	struct pca9685 *pca = to_pca(chip); -	unsigned int reg; - -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_OFF_H; -	else -		reg = LED_N_OFF_H(pwm->hwpwm); - -	regmap_write(pca->regmap, reg, LED_FULL); - -	/* Clear the LED_OFF counter. */ -	if (pwm->hwpwm >= PCA9685_MAXCHAN) -		reg = PCA9685_ALL_LED_OFF_L; -	else -		reg = LED_N_OFF_L(pwm->hwpwm); +	if (pwm->hwpwm >= PCA9685_MAXCHAN) { +		/* +		 * The "all LEDs" channel does not support HW readout +		 * Return 0 and disabled for backwards compatibility +		 */ +		state->duty_cycle = 0; +		state->enabled = false; +		return; +	} -	regmap_write(pca->regmap, reg, 0x0); +	state->enabled = true; +	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm); +	state->duty_cycle = DIV_ROUND_DOWN_ULL(duty * state->period, PCA9685_COUNTER_RANGE);  }  static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -422,15 +381,14 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)  {  	struct pca9685 *pca = to_pca(chip); -	pca9685_pwm_disable(chip, pwm); +	pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);  	pm_runtime_put(chip->dev);  	pca9685_pwm_clear_inuse(pca, pwm->hwpwm);  }  static const struct pwm_ops pca9685_pwm_ops = { -	.enable = pca9685_pwm_enable, -	.disable = pca9685_pwm_disable, -	.config = pca9685_pwm_config, +	.apply = pca9685_pwm_apply, +	.get_state = pca9685_pwm_get_state,  	.request = pca9685_pwm_request,  	.free = pca9685_pwm_free,  	.owner = THIS_MODULE, @@ -461,7 +419,6 @@ static int pca9685_pwm_probe(struct i2c_client *client,  			ret);  		return ret;  	} -	pca->period_ns = PCA9685_DEFAULT_PERIOD;  	i2c_set_clientdata(client, pca); @@ -484,16 +441,15 @@ static int pca9685_pwm_probe(struct i2c_client *client,  	reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3);  	regmap_write(pca->regmap, PCA9685_MODE1, reg); -	/* Clear all "full off" bits */ -	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, 0); -	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, 0); +	/* Reset OFF registers to POR default */ +	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL); +	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL);  	pca->chip.ops = &pca9685_pwm_ops;  	/* Add an extra channel for ALL_LED */  	pca->chip.npwm = PCA9685_MAXCHAN + 1;  	pca->chip.dev = &client->dev; -	pca->chip.base = -1;  	ret = pwmchip_add(&pca->chip);  	if (ret < 0) @@ -505,14 +461,20 @@ static int pca9685_pwm_probe(struct i2c_client *client,  		return ret;  	} -	/* The chip comes out of power-up in the active state */ -	pm_runtime_set_active(&client->dev); -	/* -	 * Enable will put the chip into suspend, which is what we -	 * want as all outputs are disabled at this point -	 */  	pm_runtime_enable(&client->dev); +	if (pm_runtime_enabled(&client->dev)) { +		/* +		 * Although the chip comes out of power-up in the sleep state, +		 * we force it to sleep in case it was woken up before +		 */ +		pca9685_set_sleep_mode(pca, true); +		pm_runtime_set_suspended(&client->dev); +	} else { +		/* Wake the chip up if runtime PM is disabled */ +		pca9685_set_sleep_mode(pca, false); +	} +  	return 0;  } @@ -524,7 +486,14 @@ static int pca9685_pwm_remove(struct i2c_client *client)  	ret = pwmchip_remove(&pca->chip);  	if (ret)  		return ret; + +	if (!pm_runtime_enabled(&client->dev)) { +		/* Put chip in sleep state if runtime PM is disabled */ +		pca9685_set_sleep_mode(pca, true); +	} +  	pm_runtime_disable(&client->dev); +  	return 0;  } | 
