Thread (1 message) 1 message, 1 author, 2020-07-03

Re: [RFC PATCH 06/10] pwm: ntxec: Add driver for PWM function in Netronix EC

From: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Date: 2020-07-03 16:17:01
Also in: linux-devicetree, linux-pwm, linux-rtc, lkml

On Mon, Jun 22, 2020 at 10:18:02AM +0200, Uwe Kleine-König wrote:
Hello,

On Sun, Jun 21, 2020 at 12:42:17AM +0200, Jonathan Neuschäfer wrote:
quoted
The Netronix EC provides a PWM output, which is used for the backlight
s/,//
quoted
on ebook readers. This patches adds a driver for the PWM output.
on *some* ebook readers
Ok, I'll fix these.
quoted
+#define NTXEC_UNK_A		0xa1
+#define NTXEC_UNK_B		0xa2
+#define NTXEC_ENABLE		0xa3
+#define NTXEC_PERIOD_LOW	0xa4
+#define NTXEC_PERIOD_HIGH	0xa5
+#define NTXEC_DUTY_LOW		0xa6
+#define NTXEC_DUTY_HIGH		0xa7
+
+/*
+ * The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are
+ * measured in this unit.
+ */
+static int ntxec_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
+				 int duty_ns, int period_ns)
+{
+	struct ntxec_pwm *pwm = pwmchip_to_pwm(chip);
+	uint64_t duty = duty_ns;
+	uint64_t period = period_ns;
As you cannot use values bigger than 8191999 anyhow, I wonder why you
use a 64 bit type here.
No particular reason; I possibly got confused by the division API. I'll
use uint32_t instead.
quoted
+	int res = 0;
+
+	do_div(period, 125);
Please use a define instead of plain 125.
Will do.
quoted
+	if (period > 0xffff) {
+		dev_warn(pwm->dev,
+			 "Period is not representable in 16 bits: %llu\n", period);
+		return -ERANGE;
+	}
+
+	do_div(duty, 125);
+	if (duty > 0xffff) {
+		dev_warn(pwm->dev, "Duty cycle is not representable in 16 bits: %llu\n",
+			duty);
+		return -ERANGE;
+	}
This check isn't necessary as the pwm core ensures that duty <= period.
Ok, I'll remove it.
quoted
+	res |= ntxec_write8(pwm->ec, NTXEC_PERIOD_HIGH, period >> 8);
+	res |= ntxec_write8(pwm->ec, NTXEC_PERIOD_LOW, period);
+	res |= ntxec_write8(pwm->ec, NTXEC_DUTY_HIGH, duty >> 8);
+	res |= ntxec_write8(pwm->ec, NTXEC_DUTY_LOW, duty);
Does this complete the currently running period? Can it happen that a
new period starts between the first and the last write and so a mixed
period can be seen at the output?
Good question. I haven't measured it, and also don't have the code
running on the EC.
quoted
+
+	return (res < 0) ? -EIO : 0;
+}
+
+static int ntxec_pwm_enable(struct pwm_chip *chip,
+				 struct pwm_device *pwm_dev)
+{
+	struct ntxec_pwm *pwm = pwmchip_to_pwm(chip);
+
+	return ntxec_write8(pwm->ec, NTXEC_ENABLE, 1);
+}
+
+static void ntxec_pwm_disable(struct pwm_chip *chip,
+				   struct pwm_device *pwm_dev)
+{
+	struct ntxec_pwm *pwm = pwmchip_to_pwm(chip);
+
+	ntxec_write8(pwm->ec, NTXEC_ENABLE, 0);
+}
+
+static struct pwm_ops ntxec_pwm_ops = {
+	.config		= ntxec_pwm_config,
+	.enable		= ntxec_pwm_enable,
+	.disable	= ntxec_pwm_disable,
+	.owner		= THIS_MODULE,
Please don't align the =, just a single space before them is fine.
Ok
More important: Please implement .apply() (and .get_state()) instead of
the old API. Also please enable PWM_DEBUG which might save us a review
iteration.
Will do!
quoted
+};
+
+static int ntxec_pwm_probe(struct platform_device *pdev)
+{
+	struct ntxec *ec = dev_get_drvdata(pdev->dev.parent);
+	struct ntxec_pwm *pwm;
+	struct pwm_chip *chip;
+	int res;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	pwm->ec = ec;
+	pwm->dev = &pdev->dev;
+
+	chip = &pwm->chip;
+	chip->dev = &pdev->dev;
+	chip->ops = &ntxec_pwm_ops;
+	chip->base = -1;
+	chip->npwm = 1;
+
+	res = pwmchip_add(chip);
+	if (res < 0)
+		return res;
+
+	platform_set_drvdata(pdev, pwm);
+
+	res |= ntxec_write8(pwm->ec, NTXEC_ENABLE, 0);
+	res |= ntxec_write8(pwm->ec, NTXEC_UNK_A, 0xff);
+	res |= ntxec_write8(pwm->ec, NTXEC_UNK_B, 0xff);
+
+	return (res < 0) ? -EIO : 0;
This is broken for several reasons:

 - You're not supposed to modify the output in .probe
 - if ntxec_write8 results in an error you keep the pwm registered.
 - From the moment on pwmchip_add returns the callbacks can be called.
   The calls to ntxec_write8 probably interfere here.
Ok, I'll rework the probe function to avoid these issues.


Thanks for the review,
Jonathan Neuschäfer
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help