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


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
      4 *  JZ4740 platform PWM support
      5 *
      6 * Limitations:
      7 * - The .apply callback doesn't complete the currently running period before
      8 *   reconfiguring the hardware.
      9 */
     10
     11#include <linux/clk.h>
     12#include <linux/err.h>
     13#include <linux/gpio.h>
     14#include <linux/kernel.h>
     15#include <linux/mfd/ingenic-tcu.h>
     16#include <linux/mfd/syscon.h>
     17#include <linux/module.h>
     18#include <linux/of_device.h>
     19#include <linux/platform_device.h>
     20#include <linux/pwm.h>
     21#include <linux/regmap.h>
     22
     23struct soc_info {
     24	unsigned int num_pwms;
     25};
     26
     27struct jz4740_pwm_chip {
     28	struct pwm_chip chip;
     29	struct regmap *map;
     30};
     31
     32static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
     33{
     34	return container_of(chip, struct jz4740_pwm_chip, chip);
     35}
     36
     37static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
     38				   unsigned int channel)
     39{
     40	/* Enable all TCU channels for PWM use by default except channels 0/1 */
     41	u32 pwm_channels_mask = GENMASK(jz->chip.npwm - 1, 2);
     42
     43	device_property_read_u32(jz->chip.dev->parent,
     44				 "ingenic,pwm-channels-mask",
     45				 &pwm_channels_mask);
     46
     47	return !!(pwm_channels_mask & BIT(channel));
     48}
     49
     50static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
     51{
     52	struct jz4740_pwm_chip *jz = to_jz4740(chip);
     53	struct clk *clk;
     54	char name[16];
     55	int err;
     56
     57	if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm))
     58		return -EBUSY;
     59
     60	snprintf(name, sizeof(name), "timer%u", pwm->hwpwm);
     61
     62	clk = clk_get(chip->dev, name);
     63	if (IS_ERR(clk))
     64		return dev_err_probe(chip->dev, PTR_ERR(clk),
     65				     "Failed to get clock\n");
     66
     67	err = clk_prepare_enable(clk);
     68	if (err < 0) {
     69		clk_put(clk);
     70		return err;
     71	}
     72
     73	pwm_set_chip_data(pwm, clk);
     74
     75	return 0;
     76}
     77
     78static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
     79{
     80	struct clk *clk = pwm_get_chip_data(pwm);
     81
     82	clk_disable_unprepare(clk);
     83	clk_put(clk);
     84}
     85
     86static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
     87{
     88	struct jz4740_pwm_chip *jz = to_jz4740(chip);
     89
     90	/* Enable PWM output */
     91	regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
     92			   TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
     93
     94	/* Start counter */
     95	regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
     96
     97	return 0;
     98}
     99
    100static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    101{
    102	struct jz4740_pwm_chip *jz = to_jz4740(chip);
    103
    104	/*
    105	 * Set duty > period. This trick allows the TCU channels in TCU2 mode to
    106	 * properly return to their init level.
    107	 */
    108	regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
    109	regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
    110
    111	/*
    112	 * Disable PWM output.
    113	 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
    114	 * counter is stopped, while in TCU1 mode the order does not matter.
    115	 */
    116	regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
    117			   TCU_TCSR_PWM_EN, 0);
    118
    119	/* Stop counter */
    120	regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
    121}
    122
    123static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    124			    const struct pwm_state *state)
    125{
    126	struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
    127	unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
    128	struct clk *clk = pwm_get_chip_data(pwm);
    129	unsigned long period, duty;
    130	long rate;
    131	int err;
    132
    133	/*
    134	 * Limit the clock to a maximum rate that still gives us a period value
    135	 * which fits in 16 bits.
    136	 */
    137	do_div(tmp, state->period);
    138
    139	/*
    140	 * /!\ IMPORTANT NOTE:
    141	 * -------------------
    142	 * This code relies on the fact that clk_round_rate() will always round
    143	 * down, which is not a valid assumption given by the clk API, but only
    144	 * happens to be true with the clk drivers used for Ingenic SoCs.
    145	 *
    146	 * Right now, there is no alternative as the clk API does not have a
    147	 * round-down function (and won't have one for a while), but if it ever
    148	 * comes to light, a round-down function should be used instead.
    149	 */
    150	rate = clk_round_rate(clk, tmp);
    151	if (rate < 0) {
    152		dev_err(chip->dev, "Unable to round rate: %ld", rate);
    153		return rate;
    154	}
    155
    156	/* Calculate period value */
    157	tmp = (unsigned long long)rate * state->period;
    158	do_div(tmp, NSEC_PER_SEC);
    159	period = tmp;
    160
    161	/* Calculate duty value */
    162	tmp = (unsigned long long)rate * state->duty_cycle;
    163	do_div(tmp, NSEC_PER_SEC);
    164	duty = tmp;
    165
    166	if (duty >= period)
    167		duty = period - 1;
    168
    169	jz4740_pwm_disable(chip, pwm);
    170
    171	err = clk_set_rate(clk, rate);
    172	if (err) {
    173		dev_err(chip->dev, "Unable to set rate: %d", err);
    174		return err;
    175	}
    176
    177	/* Reset counter to 0 */
    178	regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
    179
    180	/* Set duty */
    181	regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
    182
    183	/* Set period */
    184	regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
    185
    186	/* Set abrupt shutdown */
    187	regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
    188			   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
    189
    190	/*
    191	 * Set polarity.
    192	 *
    193	 * The PWM starts in inactive state until the internal timer reaches the
    194	 * duty value, then becomes active until the timer reaches the period
    195	 * value. In theory, we should then use (period - duty) as the real duty
    196	 * value, as a high duty value would otherwise result in the PWM pin
    197	 * being inactive most of the time.
    198	 *
    199	 * Here, we don't do that, and instead invert the polarity of the PWM
    200	 * when it is active. This trick makes the PWM start with its active
    201	 * state instead of its inactive state.
    202	 */
    203	if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
    204		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
    205				   TCU_TCSR_PWM_INITL_HIGH, 0);
    206	else
    207		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
    208				   TCU_TCSR_PWM_INITL_HIGH,
    209				   TCU_TCSR_PWM_INITL_HIGH);
    210
    211	if (state->enabled)
    212		jz4740_pwm_enable(chip, pwm);
    213
    214	return 0;
    215}
    216
    217static const struct pwm_ops jz4740_pwm_ops = {
    218	.request = jz4740_pwm_request,
    219	.free = jz4740_pwm_free,
    220	.apply = jz4740_pwm_apply,
    221	.owner = THIS_MODULE,
    222};
    223
    224static int jz4740_pwm_probe(struct platform_device *pdev)
    225{
    226	struct device *dev = &pdev->dev;
    227	struct jz4740_pwm_chip *jz4740;
    228	const struct soc_info *info;
    229
    230	info = device_get_match_data(dev);
    231	if (!info)
    232		return -EINVAL;
    233
    234	jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
    235	if (!jz4740)
    236		return -ENOMEM;
    237
    238	jz4740->map = device_node_to_regmap(dev->parent->of_node);
    239	if (IS_ERR(jz4740->map)) {
    240		dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map));
    241		return PTR_ERR(jz4740->map);
    242	}
    243
    244	jz4740->chip.dev = dev;
    245	jz4740->chip.ops = &jz4740_pwm_ops;
    246	jz4740->chip.npwm = info->num_pwms;
    247
    248	return devm_pwmchip_add(dev, &jz4740->chip);
    249}
    250
    251static const struct soc_info __maybe_unused jz4740_soc_info = {
    252	.num_pwms = 8,
    253};
    254
    255static const struct soc_info __maybe_unused jz4725b_soc_info = {
    256	.num_pwms = 6,
    257};
    258
    259static const struct soc_info __maybe_unused x1000_soc_info = {
    260	.num_pwms = 5,
    261};
    262
    263#ifdef CONFIG_OF
    264static const struct of_device_id jz4740_pwm_dt_ids[] = {
    265	{ .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info },
    266	{ .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info },
    267	{ .compatible = "ingenic,x1000-pwm", .data = &x1000_soc_info },
    268	{},
    269};
    270MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
    271#endif
    272
    273static struct platform_driver jz4740_pwm_driver = {
    274	.driver = {
    275		.name = "jz4740-pwm",
    276		.of_match_table = of_match_ptr(jz4740_pwm_dt_ids),
    277	},
    278	.probe = jz4740_pwm_probe,
    279};
    280module_platform_driver(jz4740_pwm_driver);
    281
    282MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
    283MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
    284MODULE_ALIAS("platform:jz4740-pwm");
    285MODULE_LICENSE("GPL");