1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * The Netronix embedded controller is a microcontroller found in some
4 * e-book readers designed by the original design manufacturer Netronix, Inc.
5 * It contains RTC, battery monitoring, system power management, and PWM
8 * This driver implements PWM output.
10 * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
13 * - The get_state callback is not implemented, because the current state of
14 * the PWM output can't be read back from the hardware.
15 * - The hardware can only generate normal polarity output.
16 * - The period and duty cycle can't be changed together in one atomic action.
19 #include <linux/mfd/ntxec.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/pwm.h>
23 #include <linux/regmap.h>
24 #include <linux/types.h>
30 static struct ntxec_pwm
*ntxec_pwm_from_chip(struct pwm_chip
*chip
)
32 return pwmchip_get_drvdata(chip
);
35 #define NTXEC_REG_AUTO_OFF_HI 0xa1
36 #define NTXEC_REG_AUTO_OFF_LO 0xa2
37 #define NTXEC_REG_ENABLE 0xa3
38 #define NTXEC_REG_PERIOD_LOW 0xa4
39 #define NTXEC_REG_PERIOD_HIGH 0xa5
40 #define NTXEC_REG_DUTY_LOW 0xa6
41 #define NTXEC_REG_DUTY_HIGH 0xa7
44 * The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are
45 * measured in this unit.
47 #define TIME_BASE_NS 125
50 * The maximum input value (in nanoseconds) is determined by the time base and
51 * the range of the hardware registers that hold the converted value.
52 * It fits into 32 bits, so we can do our calculations in 32 bits as well.
54 #define MAX_PERIOD_NS (TIME_BASE_NS * 0xffff)
56 static int ntxec_pwm_set_raw_period_and_duty_cycle(struct pwm_chip
*chip
,
59 struct ntxec_pwm
*priv
= ntxec_pwm_from_chip(chip
);
62 * Changes to the period and duty cycle take effect as soon as the
63 * corresponding low byte is written, so the hardware may be configured
64 * to an inconsistent state after the period is written and before the
65 * duty cycle is fully written. If, in such a case, the old duty cycle
66 * is longer than the new period, the EC may output 100% for a moment.
68 * To minimize the time between the changes to period and duty cycle
69 * taking effect, the writes are interleaved.
72 struct reg_sequence regs
[] = {
73 { NTXEC_REG_PERIOD_HIGH
, ntxec_reg8(period
>> 8) },
74 { NTXEC_REG_DUTY_HIGH
, ntxec_reg8(duty
>> 8) },
75 { NTXEC_REG_PERIOD_LOW
, ntxec_reg8(period
) },
76 { NTXEC_REG_DUTY_LOW
, ntxec_reg8(duty
) },
79 return regmap_multi_reg_write(priv
->ec
->regmap
, regs
, ARRAY_SIZE(regs
));
82 static int ntxec_pwm_apply(struct pwm_chip
*chip
, struct pwm_device
*pwm_dev
,
83 const struct pwm_state
*state
)
85 struct ntxec_pwm
*priv
= ntxec_pwm_from_chip(chip
);
86 unsigned int period
, duty
;
89 if (state
->polarity
!= PWM_POLARITY_NORMAL
)
92 period
= min_t(u64
, state
->period
, MAX_PERIOD_NS
);
93 duty
= min_t(u64
, state
->duty_cycle
, period
);
95 period
/= TIME_BASE_NS
;
99 * Writing a duty cycle of zero puts the device into a state where
100 * writing a higher duty cycle doesn't result in the brightness that it
101 * usually results in. This can be fixed by cycling the ENABLE register.
103 * As a workaround, write ENABLE=0 when the duty cycle is zero.
104 * The case that something has previously set the duty cycle to zero
105 * but ENABLE=1, is not handled.
107 if (state
->enabled
&& duty
!= 0) {
108 res
= ntxec_pwm_set_raw_period_and_duty_cycle(chip
, period
, duty
);
112 res
= regmap_write(priv
->ec
->regmap
, NTXEC_REG_ENABLE
, ntxec_reg8(1));
116 /* Disable the auto-off timer */
117 res
= regmap_write(priv
->ec
->regmap
, NTXEC_REG_AUTO_OFF_HI
, ntxec_reg8(0xff));
121 return regmap_write(priv
->ec
->regmap
, NTXEC_REG_AUTO_OFF_LO
, ntxec_reg8(0xff));
123 return regmap_write(priv
->ec
->regmap
, NTXEC_REG_ENABLE
, ntxec_reg8(0));
127 static const struct pwm_ops ntxec_pwm_ops
= {
128 .apply
= ntxec_pwm_apply
,
130 * No .get_state callback, because the current state cannot be read
131 * back from the hardware.
135 static int ntxec_pwm_probe(struct platform_device
*pdev
)
137 struct ntxec
*ec
= dev_get_drvdata(pdev
->dev
.parent
);
138 struct ntxec_pwm
*priv
;
139 struct pwm_chip
*chip
;
141 device_set_of_node_from_dev(&pdev
->dev
, pdev
->dev
.parent
);
143 chip
= devm_pwmchip_alloc(&pdev
->dev
, 1, sizeof(*priv
));
145 return PTR_ERR(chip
);
146 priv
= ntxec_pwm_from_chip(chip
);
149 chip
->ops
= &ntxec_pwm_ops
;
151 return devm_pwmchip_add(&pdev
->dev
, chip
);
154 static struct platform_driver ntxec_pwm_driver
= {
158 .probe
= ntxec_pwm_probe
,
160 module_platform_driver(ntxec_pwm_driver
);
162 MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
163 MODULE_DESCRIPTION("PWM driver for Netronix EC");
164 MODULE_LICENSE("GPL");
165 MODULE_ALIAS("platform:ntxec-pwm");