File Index
Files and directories
Sign in
FILE

pwm-max7360.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pwm/pwm-max7360.c C
  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
 37struct max7360_pwm_waveform {
 38	u8 duty_steps;
 39	bool enabled;
 40};
 41
 42static 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
 56static 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 = 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
 87static 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
108static 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
127static 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
155static const struct pwm_ops max7360_pwm_ops = {
156	.sizeof_wfhw = sizeof(struct max7360_pwm_waveform),
157	.request = max7360_pwm_request,
158	.round_waveform_tohw = max7360_pwm_round_waveform_tohw,
159	.round_waveform_fromhw = max7360_pwm_round_waveform_fromhw,
160	.read_waveform = max7360_pwm_read_waveform,
161	.write_waveform = max7360_pwm_write_waveform,
162};
163
164static int max7360_pwm_probe(struct platform_device *pdev)
165{
166	struct device *dev = &pdev->dev;
167	struct pwm_chip *chip;
168	struct regmap *regmap;
169	int ret;
170
171	regmap = dev_get_regmap(dev->parent, NULL);
172	if (!regmap)
173		return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");
174
175	/*
176	 * This MFD sub-device does not have any associated device tree node:
177	 * properties are stored in the device node of the parent (MFD) device
178	 * and this same node is used in phandles of client devices.
179	 * Reuse this device tree node here, as otherwise the PWM subsystem
180	 * would be confused by this topology.
181	 */
182	device_set_of_node_from_dev(dev, dev->parent);
183
184	chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0);
185	if (IS_ERR(chip))
186		return PTR_ERR(chip);
187	chip->ops = &max7360_pwm_ops;
188
189	pwmchip_set_drvdata(chip, regmap);
190
191	ret = devm_pwmchip_add(dev, chip);
192	if (ret)
193		return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
194
195	return 0;
196}
197
198static struct platform_driver max7360_pwm_driver = {
199	.driver = {
200		.name = "max7360-pwm",
201		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
202	},
203	.probe = max7360_pwm_probe,
204};
205module_platform_driver(max7360_pwm_driver);
206
207MODULE_DESCRIPTION("MAX7360 PWM driver");
208MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");
209MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
210MODULE_LICENSE("GPL");
~/ Read-only file preview.