1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 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 6 * functionality. 7 * 8 * This driver implements PWM output. 9 * 10 * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net> 11 * 12 * Limitations: 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. 17 */ 18 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> 25 26 struct ntxec_pwm { 27 struct ntxec *ec; 28 }; 29 30 static struct ntxec_pwm *ntxec_pwm_from_chip(struct pwm_chip *chip) 31 { 32 return pwmchip_get_drvdata(chip); 33 } 34 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 42 43 /* 44 * The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are 45 * measured in this unit. 46 */ 47 #define TIME_BASE_NS 125 48 49 /* 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. 53 */ 54 #define MAX_PERIOD_NS (TIME_BASE_NS * 0xffff) 55 56 static int ntxec_pwm_set_raw_period_and_duty_cycle(struct pwm_chip *chip, 57 int period, int duty) 58 { 59 struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip); 60 61 /* 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. 67 * 68 * To minimize the time between the changes to period and duty cycle 69 * taking effect, the writes are interleaved. 70 */ 71 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) }, 77 }; 78 79 return regmap_multi_reg_write(priv->ec->regmap, regs, ARRAY_SIZE(regs)); 80 } 81 82 static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev, 83 const struct pwm_state *state) 84 { 85 struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip); 86 unsigned int period, duty; 87 int res; 88 89 if (state->polarity != PWM_POLARITY_NORMAL) 90 return -EINVAL; 91 92 period = min_t(u64, state->period, MAX_PERIOD_NS); 93 duty = min_t(u64, state->duty_cycle, period); 94 95 period /= TIME_BASE_NS; 96 duty /= TIME_BASE_NS; 97 98 /* 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. 102 * 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. 106 */ 107 if (state->enabled && duty != 0) { 108 res = ntxec_pwm_set_raw_period_and_duty_cycle(chip, period, duty); 109 if (res) 110 return res; 111 112 res = regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(1)); 113 if (res) 114 return res; 115 116 /* Disable the auto-off timer */ 117 res = regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_HI, ntxec_reg8(0xff)); 118 if (res) 119 return res; 120 121 return regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_LO, ntxec_reg8(0xff)); 122 } else { 123 return regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(0)); 124 } 125 } 126 127 static const struct pwm_ops ntxec_pwm_ops = { 128 .apply = ntxec_pwm_apply, 129 /* 130 * No .get_state callback, because the current state cannot be read 131 * back from the hardware. 132 */ 133 }; 134 135 static int ntxec_pwm_probe(struct platform_device *pdev) 136 { 137 struct ntxec *ec = dev_get_drvdata(pdev->dev.parent); 138 struct ntxec_pwm *priv; 139 struct pwm_chip *chip; 140 141 device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); 142 143 chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*priv)); 144 if (IS_ERR(chip)) 145 return PTR_ERR(chip); 146 priv = ntxec_pwm_from_chip(chip); 147 148 priv->ec = ec; 149 chip->ops = &ntxec_pwm_ops; 150 151 return devm_pwmchip_add(&pdev->dev, chip); 152 } 153 154 static struct platform_driver ntxec_pwm_driver = { 155 .driver = { 156 .name = "ntxec-pwm", 157 }, 158 .probe = ntxec_pwm_probe, 159 }; 160 module_platform_driver(ntxec_pwm_driver); 161 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"); 166