mirror of https://github.com/torvalds/linux.git
pwm: adp5585: add support for adp5589
Add support for the adp5589 I/O expander. From a PWM point of view it is pretty similar to adp5585. Main difference is the address of registers meaningful for configuring the PWM. Acked-by: Uwe Kleine-König <ukleinek@kernel.org> Signed-off-by: Nuno Sá <nuno.sa@analog.com> Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-10-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
parent
9f425bf713
commit
75024f97e8
|
|
@ -33,21 +33,33 @@
|
||||||
#define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
#define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
||||||
#define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
#define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
||||||
|
|
||||||
|
struct adp5585_pwm_chip {
|
||||||
|
unsigned int pwm_cfg;
|
||||||
|
unsigned int pwm_offt_low;
|
||||||
|
unsigned int pwm_ont_low;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct adp5585_pwm {
|
||||||
|
const struct adp5585_pwm_chip *info;
|
||||||
|
struct regmap *regmap;
|
||||||
|
unsigned int ext_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||||
{
|
{
|
||||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||||
|
|
||||||
/* Configure the R3 pin as PWM output. */
|
/* Configure the R3 pin as PWM output. */
|
||||||
return regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
|
return regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
|
||||||
ADP5585_R3_EXTEND_CFG_MASK,
|
ADP5585_R3_EXTEND_CFG_MASK,
|
||||||
ADP5585_R3_EXTEND_CFG_PWM_OUT);
|
ADP5585_R3_EXTEND_CFG_PWM_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||||
{
|
{
|
||||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||||
|
|
||||||
regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
|
regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
|
||||||
ADP5585_R3_EXTEND_CFG_MASK,
|
ADP5585_R3_EXTEND_CFG_MASK,
|
||||||
ADP5585_R3_EXTEND_CFG_GPIO4);
|
ADP5585_R3_EXTEND_CFG_GPIO4);
|
||||||
}
|
}
|
||||||
|
|
@ -56,14 +68,16 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
|
||||||
struct pwm_device *pwm,
|
struct pwm_device *pwm,
|
||||||
const struct pwm_state *state)
|
const struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||||
|
const struct adp5585_pwm_chip *info = adp5585_pwm->info;
|
||||||
|
struct regmap *regmap = adp5585_pwm->regmap;
|
||||||
u64 period, duty_cycle;
|
u64 period, duty_cycle;
|
||||||
u32 on, off;
|
u32 on, off;
|
||||||
__le16 val;
|
__le16 val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!state->enabled) {
|
if (!state->enabled) {
|
||||||
regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
|
regmap_clear_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,41 +98,43 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
|
||||||
off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on;
|
off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on;
|
||||||
|
|
||||||
val = cpu_to_le16(off);
|
val = cpu_to_le16(off);
|
||||||
ret = regmap_bulk_write(regmap, ADP5585_PWM_OFFT_LOW, &val, 2);
|
ret = regmap_bulk_write(regmap, info->pwm_offt_low, &val, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
val = cpu_to_le16(on);
|
val = cpu_to_le16(on);
|
||||||
ret = regmap_bulk_write(regmap, ADP5585_PWM_ONT_LOW, &val, 2);
|
ret = regmap_bulk_write(regmap, info->pwm_ont_low, &val, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Enable PWM in continuous mode and no external AND'ing. */
|
/* Enable PWM in continuous mode and no external AND'ing. */
|
||||||
ret = regmap_update_bits(regmap, ADP5585_PWM_CFG,
|
ret = regmap_update_bits(regmap, info->pwm_cfg,
|
||||||
ADP5585_PWM_IN_AND | ADP5585_PWM_MODE |
|
ADP5585_PWM_IN_AND | ADP5585_PWM_MODE |
|
||||||
ADP5585_PWM_EN, ADP5585_PWM_EN);
|
ADP5585_PWM_EN, ADP5585_PWM_EN);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
|
return regmap_set_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pwm_adp5585_get_state(struct pwm_chip *chip,
|
static int pwm_adp5585_get_state(struct pwm_chip *chip,
|
||||||
struct pwm_device *pwm,
|
struct pwm_device *pwm,
|
||||||
struct pwm_state *state)
|
struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||||
|
const struct adp5585_pwm_chip *info = adp5585_pwm->info;
|
||||||
|
struct regmap *regmap = adp5585_pwm->regmap;
|
||||||
unsigned int on, off;
|
unsigned int on, off;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
__le16 on_off;
|
__le16 on_off;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = regmap_bulk_read(regmap, ADP5585_PWM_OFFT_LOW, &on_off, 2);
|
ret = regmap_bulk_read(regmap, info->pwm_offt_low, &on_off, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
off = le16_to_cpu(on_off);
|
off = le16_to_cpu(on_off);
|
||||||
|
|
||||||
ret = regmap_bulk_read(regmap, ADP5585_PWM_ONT_LOW, &on_off, 2);
|
ret = regmap_bulk_read(regmap, info->pwm_ont_low, &on_off, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
on = le16_to_cpu(on_off);
|
on = le16_to_cpu(on_off);
|
||||||
|
|
@ -128,7 +144,7 @@ static int pwm_adp5585_get_state(struct pwm_chip *chip,
|
||||||
|
|
||||||
state->polarity = PWM_POLARITY_NORMAL;
|
state->polarity = PWM_POLARITY_NORMAL;
|
||||||
|
|
||||||
regmap_read(regmap, ADP5585_PWM_CFG, &val);
|
regmap_read(regmap, info->pwm_cfg, &val);
|
||||||
state->enabled = !!(val & ADP5585_PWM_EN);
|
state->enabled = !!(val & ADP5585_PWM_EN);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -143,18 +159,28 @@ static const struct pwm_ops adp5585_pwm_ops = {
|
||||||
|
|
||||||
static int adp5585_pwm_probe(struct platform_device *pdev)
|
static int adp5585_pwm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
||||||
|
struct adp5585_pwm *adp5585_pwm;
|
||||||
struct pwm_chip *chip;
|
struct pwm_chip *chip;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM, 0);
|
chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM,
|
||||||
|
sizeof(*adp5585_pwm));
|
||||||
if (IS_ERR(chip))
|
if (IS_ERR(chip))
|
||||||
return PTR_ERR(chip);
|
return PTR_ERR(chip);
|
||||||
|
|
||||||
|
adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||||
|
adp5585_pwm->regmap = adp5585->regmap;
|
||||||
|
adp5585_pwm->ext_cfg = adp5585->regs->ext_cfg;
|
||||||
|
|
||||||
|
adp5585_pwm->info = (const struct adp5585_pwm_chip *)id->driver_data;
|
||||||
|
if (!adp5585_pwm->info)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
device_set_of_node_from_dev(dev, dev->parent);
|
device_set_of_node_from_dev(dev, dev->parent);
|
||||||
|
|
||||||
pwmchip_set_drvdata(chip, adp5585->regmap);
|
|
||||||
chip->ops = &adp5585_pwm_ops;
|
chip->ops = &adp5585_pwm_ops;
|
||||||
|
|
||||||
ret = devm_pwmchip_add(dev, chip);
|
ret = devm_pwmchip_add(dev, chip);
|
||||||
|
|
@ -164,8 +190,21 @@ static int adp5585_pwm_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct adp5585_pwm_chip adp5589_pwm_chip_info = {
|
||||||
|
.pwm_cfg = ADP5585_PWM_CFG,
|
||||||
|
.pwm_offt_low = ADP5585_PWM_OFFT_LOW,
|
||||||
|
.pwm_ont_low = ADP5585_PWM_ONT_LOW,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct adp5585_pwm_chip adp5585_pwm_chip_info = {
|
||||||
|
.pwm_cfg = ADP5589_PWM_CFG,
|
||||||
|
.pwm_offt_low = ADP5589_PWM_OFFT_LOW,
|
||||||
|
.pwm_ont_low = ADP5589_PWM_ONT_LOW,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct platform_device_id adp5585_pwm_id_table[] = {
|
static const struct platform_device_id adp5585_pwm_id_table[] = {
|
||||||
{ "adp5585-pwm" },
|
{ "adp5585-pwm", (kernel_ulong_t)&adp5585_pwm_chip_info },
|
||||||
|
{ "adp5589-pwm", (kernel_ulong_t)&adp5589_pwm_chip_info },
|
||||||
{ /* Sentinel */ }
|
{ /* Sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
|
MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,9 @@
|
||||||
#define ADP5589_GPO_DATA_OUT_A 0x2a
|
#define ADP5589_GPO_DATA_OUT_A 0x2a
|
||||||
#define ADP5589_GPO_OUT_MODE_A 0x2d
|
#define ADP5589_GPO_OUT_MODE_A 0x2d
|
||||||
#define ADP5589_GPIO_DIRECTION_A 0x30
|
#define ADP5589_GPIO_DIRECTION_A 0x30
|
||||||
|
#define ADP5589_PWM_OFFT_LOW 0x3e
|
||||||
|
#define ADP5589_PWM_ONT_LOW 0x40
|
||||||
|
#define ADP5589_PWM_CFG 0x42
|
||||||
#define ADP5589_PIN_CONFIG_D 0x4C
|
#define ADP5589_PIN_CONFIG_D 0x4C
|
||||||
#define ADP5589_INT_EN 0x4e
|
#define ADP5589_INT_EN 0x4e
|
||||||
#define ADP5589_MAX_REG ADP5589_INT_EN
|
#define ADP5589_MAX_REG ADP5589_INT_EN
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue