1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2025 Bootlin 4 * 5 * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> 6 * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 7 * 8 * PWM functionality of the MAX7360 multi-function device. 9 * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7360.pdf 10 * 11 * Limitations: 12 * - Only supports normal polarity. 13 * - The period is fixed to 2 ms. 14 * - Only the duty cycle can be changed, new values are applied at the beginning 15 * of the next cycle. 16 * - When disabled, the output is put in Hi-Z immediately. 17 */ 18 #include <linux/bits.h> 19 #include <linux/dev_printk.h> 20 #include <linux/err.h> 21 #include <linux/math64.h> 22 #include <linux/mfd/max7360.h> 23 #include <linux/minmax.h> 24 #include <linux/mod_devicetable.h> 25 #include <linux/module.h> 26 #include <linux/platform_device.h> 27 #include <linux/pwm.h> 28 #include <linux/regmap.h> 29 #include <linux/time.h> 30 #include <linux/types.h> 31 32 #define MAX7360_NUM_PWMS 8 33 #define MAX7360_PWM_MAX 255 34 #define MAX7360_PWM_STEPS 256 35 #define MAX7360_PWM_PERIOD_NS (2 * NSEC_PER_MSEC) 36 37 struct max7360_pwm_waveform { 38 u8 duty_steps; 39 bool enabled; 40 }; 41 42 static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 43 { 44 struct regmap *regmap = pwmchip_get_drvdata(chip); 45 46 /* 47 * Make sure we use the individual PWM configuration register and not 48 * the global one. 49 * We never need to use the global one, so there is no need to revert 50 * that in the .free() callback. 51 */ 52 return regmap_write_bits(regmap, MAX7360_REG_PWMCFG(pwm->hwpwm), 53 MAX7360_PORT_CFG_COMMON_PWM, 0); 54 } 55 56 static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip, 57 struct pwm_device *pwm, 58 const struct pwm_waveform *wf, 59 void *_wfhw) 60 { 61 struct max7360_pwm_waveform *wfhw = _wfhw; 62 u64 duty_steps; 63 64 /* 65 * Ignore user provided values for period_length_ns and duty_offset_ns: 66 * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0. 67 * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256 68 * to 254/256, while value 255 will provide a duty cycle of 100%. 69 */ 70 if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) { 71 duty_steps = MAX7360_PWM_MAX; 72 } else { 73 duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS; 74 if (duty_steps == MAX7360_PWM_MAX) 75 duty_steps = MAX7360_PWM_MAX - 1; 76 } 77 78 wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps); 79 wfhw->enabled = !!wf->period_length_ns; 80 81 if (wf->period_length_ns && wf->period_length_ns < MAX7360_PWM_PERIOD_NS) 82 return 1; 83 else 84 return 0; 85 } 86 87 static int max7360_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, 88 const void *_wfhw, struct pwm_waveform *wf) 89 { 90 const struct max7360_pwm_waveform *wfhw = _wfhw; 91 92 wf->period_length_ns = wfhw->enabled ? MAX7360_PWM_PERIOD_NS : 0; 93 wf->duty_offset_ns = 0; 94 95 if (wfhw->enabled) { 96 if (wfhw->duty_steps == MAX7360_PWM_MAX) 97 wf->duty_length_ns = MAX7360_PWM_PERIOD_NS; 98 else 99 wf->duty_length_ns = DIV_ROUND_UP(wfhw->duty_steps * MAX7360_PWM_PERIOD_NS, 100 MAX7360_PWM_STEPS); 101 } else { 102 wf->duty_length_ns = 0; 103 } 104 105 return 0; 106 } 107 108 static int max7360_pwm_write_waveform(struct pwm_chip *chip, 109 struct pwm_device *pwm, 110 const void *_wfhw) 111 { 112 struct regmap *regmap = pwmchip_get_drvdata(chip); 113 const struct max7360_pwm_waveform *wfhw = _wfhw; 114 unsigned int val; 115 int ret; 116 117 if (wfhw->enabled) { 118 ret = regmap_write(regmap, MAX7360_REG_PWM(pwm->hwpwm), wfhw->duty_steps); 119 if (ret) 120 return ret; 121 } 122 123 val = wfhw->enabled ? BIT(pwm->hwpwm) : 0; 124 return regmap_write_bits(regmap, MAX7360_REG_GPIOCTRL, BIT(pwm->hwpwm), val); 125 } 126 127 static int max7360_pwm_read_waveform(struct pwm_chip *chip, 128 struct pwm_device *pwm, 129 void *_wfhw) 130 { 131 struct regmap *regmap = pwmchip_get_drvdata(chip); 132 struct max7360_pwm_waveform *wfhw = _wfhw; 133 unsigned int val; 134 int ret; 135 136 ret = regmap_read(regmap, MAX7360_REG_GPIOCTRL, &val); 137 if (ret) 138 return ret; 139 140 if (val & BIT(pwm->hwpwm)) { 141 wfhw->enabled = true; 142 ret = regmap_read(regmap, MAX7360_REG_PWM(pwm->hwpwm), &val); 143 if (ret) 144 return ret; 145 146 wfhw->duty_steps = val; 147 } else { 148 wfhw->enabled = false; 149 wfhw->duty_steps = 0; 150 } 151 152 return 0; 153 } 154 155 static const struct pwm_ops max7360_pwm_ops = { 156 .request = max7360_pwm_request, 157 .round_waveform_tohw = max7360_pwm_round_waveform_tohw, 158 .round_waveform_fromhw = max7360_pwm_round_waveform_fromhw, 159 .read_waveform = max7360_pwm_read_waveform, 160 .write_waveform = max7360_pwm_write_waveform, 161 }; 162 163 static int max7360_pwm_probe(struct platform_device *pdev) 164 { 165 struct device *dev = &pdev->dev; 166 struct pwm_chip *chip; 167 struct regmap *regmap; 168 int ret; 169 170 regmap = dev_get_regmap(dev->parent, NULL); 171 if (!regmap) 172 return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 173 174 /* 175 * This MFD sub-device does not have any associated device tree node: 176 * properties are stored in the device node of the parent (MFD) device 177 * and this same node is used in phandles of client devices. 178 * Reuse this device tree node here, as otherwise the PWM subsystem 179 * would be confused by this topology. 180 */ 181 device_set_of_node_from_dev(dev, dev->parent); 182 183 chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0); 184 if (IS_ERR(chip)) 185 return PTR_ERR(chip); 186 chip->ops = &max7360_pwm_ops; 187 188 pwmchip_set_drvdata(chip, regmap); 189 190 ret = devm_pwmchip_add(dev, chip); 191 if (ret) 192 return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); 193 194 return 0; 195 } 196 197 static struct platform_driver max7360_pwm_driver = { 198 .driver = { 199 .name = "max7360-pwm", 200 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 201 }, 202 .probe = max7360_pwm_probe, 203 }; 204 module_platform_driver(max7360_pwm_driver); 205 206 MODULE_DESCRIPTION("MAX7360 PWM driver"); 207 MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); 208 MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 209 MODULE_LICENSE("GPL"); 210