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.c (9562B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for TWL4030/6030 Generic Pulse Width Modulator
      4 *
      5 * Copyright (C) 2012 Texas Instruments
      6 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/of.h>
     11#include <linux/platform_device.h>
     12#include <linux/pwm.h>
     13#include <linux/mfd/twl.h>
     14#include <linux/slab.h>
     15
     16/*
     17 * This driver handles the PWMs of TWL4030 and TWL6030.
     18 * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1
     19 * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2
     20 */
     21
     22#define TWL_PWM_MAX		0x7f
     23
     24/* Registers, bits and macro for TWL4030 */
     25#define TWL4030_GPBR1_REG	0x0c
     26#define TWL4030_PMBR1_REG	0x0d
     27
     28/* GPBR1 register bits */
     29#define TWL4030_PWMXCLK_ENABLE	(1 << 0)
     30#define TWL4030_PWMX_ENABLE	(1 << 2)
     31#define TWL4030_PWMX_BITS	(TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE)
     32#define TWL4030_PWM_TOGGLE(pwm, x)	((x) << (pwm))
     33
     34/* PMBR1 register bits */
     35#define TWL4030_GPIO6_PWM0_MUTE_MASK		(0x03 << 2)
     36#define TWL4030_GPIO6_PWM0_MUTE_PWM0		(0x01 << 2)
     37#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK	(0x03 << 4)
     38#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1	(0x03 << 4)
     39
     40/* Register, bits and macro for TWL6030 */
     41#define TWL6030_TOGGLE3_REG	0x92
     42
     43#define TWL6030_PWMXR		(1 << 0)
     44#define TWL6030_PWMXS		(1 << 1)
     45#define TWL6030_PWMXEN		(1 << 2)
     46#define TWL6030_PWM_TOGGLE(pwm, x)	((x) << (pwm * 3))
     47
     48struct twl_pwm_chip {
     49	struct pwm_chip chip;
     50	struct mutex mutex;
     51	u8 twl6030_toggle3;
     52	u8 twl4030_pwm_mux;
     53};
     54
     55static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip)
     56{
     57	return container_of(chip, struct twl_pwm_chip, chip);
     58}
     59
     60static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
     61			  u64 duty_ns, u64 period_ns)
     62{
     63	int duty_cycle = DIV64_U64_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1;
     64	u8 pwm_config[2] = { 1, 0 };
     65	int base, ret;
     66
     67	/*
     68	 * To configure the duty period:
     69	 * On-cycle is set to 1 (the minimum allowed value)
     70	 * The off time of 0 is not configurable, so the mapping is:
     71	 * 0 -> off cycle = 2,
     72	 * 1 -> off cycle = 2,
     73	 * 2 -> off cycle = 3,
     74	 * 126 - > off cycle 127,
     75	 * 127 - > off cycle 1
     76	 * When on cycle == off cycle the PWM will be always on
     77	 */
     78	if (duty_cycle == 1)
     79		duty_cycle = 2;
     80	else if (duty_cycle > TWL_PWM_MAX)
     81		duty_cycle = 1;
     82
     83	base = pwm->hwpwm * 3;
     84
     85	pwm_config[1] = duty_cycle;
     86
     87	ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2);
     88	if (ret < 0)
     89		dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label);
     90
     91	return ret;
     92}
     93
     94static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
     95{
     96	struct twl_pwm_chip *twl = to_twl(chip);
     97	int ret;
     98	u8 val;
     99
    100	mutex_lock(&twl->mutex);
    101	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
    102	if (ret < 0) {
    103		dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label);
    104		goto out;
    105	}
    106
    107	val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
    108
    109	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
    110	if (ret < 0)
    111		dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
    112
    113	val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
    114
    115	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
    116	if (ret < 0)
    117		dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
    118
    119out:
    120	mutex_unlock(&twl->mutex);
    121	return ret;
    122}
    123
    124static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    125{
    126	struct twl_pwm_chip *twl = to_twl(chip);
    127	int ret;
    128	u8 val;
    129
    130	mutex_lock(&twl->mutex);
    131	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
    132	if (ret < 0) {
    133		dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label);
    134		goto out;
    135	}
    136
    137	val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
    138
    139	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
    140	if (ret < 0)
    141		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    142
    143	val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
    144
    145	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
    146	if (ret < 0)
    147		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    148
    149out:
    150	mutex_unlock(&twl->mutex);
    151}
    152
    153static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
    154{
    155	struct twl_pwm_chip *twl = to_twl(chip);
    156	int ret;
    157	u8 val, mask, bits;
    158
    159	if (pwm->hwpwm == 1) {
    160		mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
    161		bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1;
    162	} else {
    163		mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
    164		bits = TWL4030_GPIO6_PWM0_MUTE_PWM0;
    165	}
    166
    167	mutex_lock(&twl->mutex);
    168	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
    169	if (ret < 0) {
    170		dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label);
    171		goto out;
    172	}
    173
    174	/* Save the current MUX configuration for the PWM */
    175	twl->twl4030_pwm_mux &= ~mask;
    176	twl->twl4030_pwm_mux |= (val & mask);
    177
    178	/* Select PWM functionality */
    179	val &= ~mask;
    180	val |= bits;
    181
    182	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
    183	if (ret < 0)
    184		dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label);
    185
    186out:
    187	mutex_unlock(&twl->mutex);
    188	return ret;
    189}
    190
    191static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
    192{
    193	struct twl_pwm_chip *twl = to_twl(chip);
    194	int ret;
    195	u8 val, mask;
    196
    197	if (pwm->hwpwm == 1)
    198		mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
    199	else
    200		mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
    201
    202	mutex_lock(&twl->mutex);
    203	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
    204	if (ret < 0) {
    205		dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label);
    206		goto out;
    207	}
    208
    209	/* Restore the MUX configuration for the PWM */
    210	val &= ~mask;
    211	val |= (twl->twl4030_pwm_mux & mask);
    212
    213	ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
    214	if (ret < 0)
    215		dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label);
    216
    217out:
    218	mutex_unlock(&twl->mutex);
    219}
    220
    221static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
    222{
    223	struct twl_pwm_chip *twl = to_twl(chip);
    224	int ret;
    225	u8 val;
    226
    227	mutex_lock(&twl->mutex);
    228	val = twl->twl6030_toggle3;
    229	val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
    230	val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
    231
    232	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
    233	if (ret < 0) {
    234		dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
    235		goto out;
    236	}
    237
    238	twl->twl6030_toggle3 = val;
    239out:
    240	mutex_unlock(&twl->mutex);
    241	return ret;
    242}
    243
    244static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    245{
    246	struct twl_pwm_chip *twl = to_twl(chip);
    247	int ret;
    248	u8 val;
    249
    250	mutex_lock(&twl->mutex);
    251	val = twl->twl6030_toggle3;
    252	val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
    253	val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
    254
    255	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
    256	if (ret < 0) {
    257		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    258		goto out;
    259	}
    260
    261	val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
    262
    263	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
    264	if (ret < 0) {
    265		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    266		goto out;
    267	}
    268
    269	val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
    270
    271	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
    272	if (ret < 0) {
    273		dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
    274		goto out;
    275	}
    276
    277	twl->twl6030_toggle3 = val;
    278out:
    279	mutex_unlock(&twl->mutex);
    280}
    281
    282static int twl4030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    283			     const struct pwm_state *state)
    284{
    285	int err;
    286
    287	if (state->polarity != PWM_POLARITY_NORMAL)
    288		return -EINVAL;
    289
    290	if (!state->enabled) {
    291		if (pwm->state.enabled)
    292			twl4030_pwm_disable(chip, pwm);
    293
    294		return 0;
    295	}
    296
    297	err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
    298	if (err)
    299		return err;
    300
    301	if (!pwm->state.enabled)
    302		err = twl4030_pwm_enable(chip, pwm);
    303
    304	return err;
    305}
    306
    307static int twl6030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    308			     const struct pwm_state *state)
    309{
    310	int err;
    311
    312	if (state->polarity != PWM_POLARITY_NORMAL)
    313		return -EINVAL;
    314
    315	if (!state->enabled) {
    316		if (pwm->state.enabled)
    317			twl6030_pwm_disable(chip, pwm);
    318
    319		return 0;
    320	}
    321
    322	err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
    323	if (err)
    324		return err;
    325
    326	if (!pwm->state.enabled)
    327		err = twl6030_pwm_enable(chip, pwm);
    328
    329	return err;
    330}
    331
    332static const struct pwm_ops twl4030_pwm_ops = {
    333	.apply = twl4030_pwm_apply,
    334	.request = twl4030_pwm_request,
    335	.free = twl4030_pwm_free,
    336	.owner = THIS_MODULE,
    337};
    338
    339static const struct pwm_ops twl6030_pwm_ops = {
    340	.apply = twl6030_pwm_apply,
    341	.owner = THIS_MODULE,
    342};
    343
    344static int twl_pwm_probe(struct platform_device *pdev)
    345{
    346	struct twl_pwm_chip *twl;
    347
    348	twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
    349	if (!twl)
    350		return -ENOMEM;
    351
    352	if (twl_class_is_4030())
    353		twl->chip.ops = &twl4030_pwm_ops;
    354	else
    355		twl->chip.ops = &twl6030_pwm_ops;
    356
    357	twl->chip.dev = &pdev->dev;
    358	twl->chip.npwm = 2;
    359
    360	mutex_init(&twl->mutex);
    361
    362	return devm_pwmchip_add(&pdev->dev, &twl->chip);
    363}
    364
    365#ifdef CONFIG_OF
    366static const struct of_device_id twl_pwm_of_match[] = {
    367	{ .compatible = "ti,twl4030-pwm" },
    368	{ .compatible = "ti,twl6030-pwm" },
    369	{ },
    370};
    371MODULE_DEVICE_TABLE(of, twl_pwm_of_match);
    372#endif
    373
    374static struct platform_driver twl_pwm_driver = {
    375	.driver = {
    376		.name = "twl-pwm",
    377		.of_match_table = of_match_ptr(twl_pwm_of_match),
    378	},
    379	.probe = twl_pwm_probe,
    380};
    381module_platform_driver(twl_pwm_driver);
    382
    383MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
    384MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030");
    385MODULE_ALIAS("platform:twl-pwm");
    386MODULE_LICENSE("GPL");