cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pwm-twl-led.c (9121B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver
      4 *
      5 * Copyright (C) 2012 Texas Instruments
      6 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
      7 *
      8 * This driver is a complete rewrite of the former pwm-twl6030.c authorded by:
      9 * Hemanth V <hemanthv@ti.com>
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/platform_device.h>
     15#include <linux/pwm.h>
     16#include <linux/mfd/twl.h>
     17#include <linux/slab.h>
     18
     19/*
     20 * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030.
     21 * To generate the signal on TWL4030:
     22 *  - LEDA uses PWMA
     23 *  - LEDB uses PWMB
     24 * TWL6030 has one LED pin with dedicated LEDPWM
     25 */
     26
     27#define TWL4030_LED_MAX		0x7f
     28#define TWL6030_LED_MAX		0xff
     29
     30/* Registers, bits and macro for TWL4030 */
     31#define TWL4030_LEDEN_REG	0x00
     32#define TWL4030_PWMA_REG	0x01
     33
     34#define TWL4030_LEDXON		(1 << 0)
     35#define TWL4030_LEDXPWM		(1 << 4)
     36#define TWL4030_LED_PINS	(TWL4030_LEDXON | TWL4030_LEDXPWM)
     37#define TWL4030_LED_TOGGLE(led, x)	((x) << (led))
     38
     39/* Register, bits and macro for TWL6030 */
     40#define TWL6030_LED_PWM_CTRL1	0xf4
     41#define TWL6030_LED_PWM_CTRL2	0xf5
     42
     43#define TWL6040_LED_MODE_HW	0x00
     44#define TWL6040_LED_MODE_ON	0x01
     45#define TWL6040_LED_MODE_OFF	0x02
     46#define TWL6040_LED_MODE_MASK	0x03
     47
     48struct twl_pwmled_chip {
     49	struct pwm_chip chip;
     50	struct mutex mutex;
     51};
     52
     53static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip)
     54{
     55	return container_of(chip, struct twl_pwmled_chip, chip);
     56}
     57
     58static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
     59			      int duty_ns, int period_ns)
     60{
     61	int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1;
     62	u8 pwm_config[2] = { 1, 0 };
     63	int base, ret;
     64
     65	/*
     66	 * To configure the duty period:
     67	 * On-cycle is set to 1 (the minimum allowed value)
     68	 * The off time of 0 is not configurable, so the mapping is:
     69	 * 0 -> off cycle = 2,
     70	 * 1 -> off cycle = 2,
     71	 * 2 -> off cycle = 3,
     72	 * 126 - > off cycle 127,
     73	 * 127 - > off cycle 1
     74	 * When on cycle == off cycle the PWM will be always on
     75	 */
     76	if (duty_cycle == 1)
     77		duty_cycle = 2;
     78	else if (duty_cycle > TWL4030_LED_MAX)
     79		duty_cycle = 1;
     80
     81	base = pwm->hwpwm * 2 + TWL4030_PWMA_REG;
     82
     83	pwm_config[1] = duty_cycle;
     84
     85	ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2);
     86	if (ret < 0)
     87		dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label);
     88
     89	return ret;
     90}
     91
     92static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
     93{
     94	struct twl_pwmled_chip *twl = to_twl(chip);
     95	int ret;
     96	u8 val;
     97
     98	mutex_lock(&twl->mutex);
     99	ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
    100	if (ret < 0) {
    101		dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label);
    102		goto out;
    103	}
    104
    105	val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
    106
    107	ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
    108	if (ret < 0)
    109		dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
    110
    111out:
    112	mutex_unlock(&twl->mutex);
    113	return ret;
    114}
    115
    116static void twl4030_pwmled_disable(struct pwm_chip *chip,
    117				   struct pwm_device *pwm)
    118{
    119	struct twl_pwmled_chip *twl = to_twl(chip);
    120	int ret;
    121	u8 val;
    122
    123	mutex_lock(&twl->mutex);
    124	ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
    125	if (ret < 0) {
    126		dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label);
    127		goto out;
    128	}
    129
    130	val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
    131
    132	ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
    133	if (ret < 0)
    134		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    135
    136out:
    137	mutex_unlock(&twl->mutex);
    138}
    139
    140static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    141				const struct pwm_state *state)
    142{
    143	int ret;
    144
    145	if (state->polarity != PWM_POLARITY_NORMAL)
    146		return -EINVAL;
    147
    148	if (!state->enabled) {
    149		if (pwm->state.enabled)
    150			twl4030_pwmled_disable(chip, pwm);
    151
    152		return 0;
    153	}
    154
    155	/*
    156	 * We cannot skip calling ->config even if state->period ==
    157	 * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
    158	 * because we might have exited early in the last call to
    159	 * pwm_apply_state because of !state->enabled and so the two values in
    160	 * pwm->state might not be configured in hardware.
    161	 */
    162	ret = twl4030_pwmled_config(pwm->chip, pwm,
    163				    state->duty_cycle, state->period);
    164	if (ret)
    165		return ret;
    166
    167	if (!pwm->state.enabled)
    168		ret = twl4030_pwmled_enable(chip, pwm);
    169
    170	return ret;
    171}
    172
    173
    174static const struct pwm_ops twl4030_pwmled_ops = {
    175	.apply = twl4030_pwmled_apply,
    176	.owner = THIS_MODULE,
    177};
    178
    179static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
    180			      int duty_ns, int period_ns)
    181{
    182	int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns;
    183	u8 on_time;
    184	int ret;
    185
    186	on_time = duty_cycle & 0xff;
    187
    188	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time,
    189			       TWL6030_LED_PWM_CTRL1);
    190	if (ret < 0)
    191		dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label);
    192
    193	return ret;
    194}
    195
    196static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
    197{
    198	struct twl_pwmled_chip *twl = to_twl(chip);
    199	int ret;
    200	u8 val;
    201
    202	mutex_lock(&twl->mutex);
    203	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
    204	if (ret < 0) {
    205		dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
    206			pwm->label);
    207		goto out;
    208	}
    209
    210	val &= ~TWL6040_LED_MODE_MASK;
    211	val |= TWL6040_LED_MODE_ON;
    212
    213	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
    214	if (ret < 0)
    215		dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
    216
    217out:
    218	mutex_unlock(&twl->mutex);
    219	return ret;
    220}
    221
    222static void twl6030_pwmled_disable(struct pwm_chip *chip,
    223				   struct pwm_device *pwm)
    224{
    225	struct twl_pwmled_chip *twl = to_twl(chip);
    226	int ret;
    227	u8 val;
    228
    229	mutex_lock(&twl->mutex);
    230	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
    231	if (ret < 0) {
    232		dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
    233			pwm->label);
    234		goto out;
    235	}
    236
    237	val &= ~TWL6040_LED_MODE_MASK;
    238	val |= TWL6040_LED_MODE_OFF;
    239
    240	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
    241	if (ret < 0)
    242		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    243
    244out:
    245	mutex_unlock(&twl->mutex);
    246}
    247
    248static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    249				const struct pwm_state *state)
    250{
    251	int err;
    252
    253	if (state->polarity != pwm->state.polarity)
    254		return -EINVAL;
    255
    256	if (!state->enabled) {
    257		if (pwm->state.enabled)
    258			twl6030_pwmled_disable(chip, pwm);
    259
    260		return 0;
    261	}
    262
    263	err = twl6030_pwmled_config(pwm->chip, pwm,
    264				    state->duty_cycle, state->period);
    265	if (err)
    266		return err;
    267
    268	if (!pwm->state.enabled)
    269		err = twl6030_pwmled_enable(chip, pwm);
    270
    271	return err;
    272}
    273
    274static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm)
    275{
    276	struct twl_pwmled_chip *twl = to_twl(chip);
    277	int ret;
    278	u8 val;
    279
    280	mutex_lock(&twl->mutex);
    281	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
    282	if (ret < 0) {
    283		dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
    284			pwm->label);
    285		goto out;
    286	}
    287
    288	val &= ~TWL6040_LED_MODE_MASK;
    289	val |= TWL6040_LED_MODE_OFF;
    290
    291	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
    292	if (ret < 0)
    293		dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label);
    294
    295out:
    296	mutex_unlock(&twl->mutex);
    297	return ret;
    298}
    299
    300static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm)
    301{
    302	struct twl_pwmled_chip *twl = to_twl(chip);
    303	int ret;
    304	u8 val;
    305
    306	mutex_lock(&twl->mutex);
    307	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
    308	if (ret < 0) {
    309		dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
    310			pwm->label);
    311		goto out;
    312	}
    313
    314	val &= ~TWL6040_LED_MODE_MASK;
    315	val |= TWL6040_LED_MODE_HW;
    316
    317	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
    318	if (ret < 0)
    319		dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label);
    320
    321out:
    322	mutex_unlock(&twl->mutex);
    323}
    324
    325static const struct pwm_ops twl6030_pwmled_ops = {
    326	.apply = twl6030_pwmled_apply,
    327	.request = twl6030_pwmled_request,
    328	.free = twl6030_pwmled_free,
    329	.owner = THIS_MODULE,
    330};
    331
    332static int twl_pwmled_probe(struct platform_device *pdev)
    333{
    334	struct twl_pwmled_chip *twl;
    335
    336	twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
    337	if (!twl)
    338		return -ENOMEM;
    339
    340	if (twl_class_is_4030()) {
    341		twl->chip.ops = &twl4030_pwmled_ops;
    342		twl->chip.npwm = 2;
    343	} else {
    344		twl->chip.ops = &twl6030_pwmled_ops;
    345		twl->chip.npwm = 1;
    346	}
    347
    348	twl->chip.dev = &pdev->dev;
    349
    350	mutex_init(&twl->mutex);
    351
    352	return devm_pwmchip_add(&pdev->dev, &twl->chip);
    353}
    354
    355#ifdef CONFIG_OF
    356static const struct of_device_id twl_pwmled_of_match[] = {
    357	{ .compatible = "ti,twl4030-pwmled" },
    358	{ .compatible = "ti,twl6030-pwmled" },
    359	{ },
    360};
    361MODULE_DEVICE_TABLE(of, twl_pwmled_of_match);
    362#endif
    363
    364static struct platform_driver twl_pwmled_driver = {
    365	.driver = {
    366		.name = "twl-pwmled",
    367		.of_match_table = of_match_ptr(twl_pwmled_of_match),
    368	},
    369	.probe = twl_pwmled_probe,
    370};
    371module_platform_driver(twl_pwmled_driver);
    372
    373MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
    374MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs");
    375MODULE_ALIAS("platform:twl-pwmled");
    376MODULE_LICENSE("GPL");