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-bcm-kona.c (9775B)


      1/*
      2 * Copyright (C) 2014 Broadcom Corporation
      3 *
      4 * This program is free software; you can redistribute it and/or
      5 * modify it under the terms of the GNU General Public License as
      6 * published by the Free Software Foundation version 2.
      7 *
      8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
      9 * kind, whether express or implied; without even the implied warranty
     10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 * GNU General Public License for more details.
     12 */
     13
     14#include <linux/clk.h>
     15#include <linux/delay.h>
     16#include <linux/err.h>
     17#include <linux/io.h>
     18#include <linux/ioport.h>
     19#include <linux/math64.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/platform_device.h>
     23#include <linux/pwm.h>
     24#include <linux/slab.h>
     25#include <linux/types.h>
     26
     27/*
     28 * The Kona PWM has some unusual characteristics.  Here are the main points.
     29 *
     30 * 1) There is no disable bit and the hardware docs advise programming a zero
     31 *    duty to achieve output equivalent to that of a normal disable operation.
     32 *
     33 * 2) Changes to prescale, duty, period, and polarity do not take effect until
     34 *    a subsequent rising edge of the trigger bit.
     35 *
     36 * 3) If the smooth bit and trigger bit are both low, the output is a constant
     37 *    high signal.  Otherwise, the earlier waveform continues to be output.
     38 *
     39 * 4) If the smooth bit is set on the rising edge of the trigger bit, output
     40 *    will transition to the new settings on a period boundary (which could be
     41 *    seconds away).  If the smooth bit is clear, new settings will be applied
     42 *    as soon as possible (the hardware always has a 400ns delay).
     43 *
     44 * 5) When the external clock that feeds the PWM is disabled, output is pegged
     45 *    high or low depending on its state at that exact instant.
     46 */
     47
     48#define PWM_CONTROL_OFFSET			0x00000000
     49#define PWM_CONTROL_SMOOTH_SHIFT(chan)		(24 + (chan))
     50#define PWM_CONTROL_TYPE_SHIFT(chan)		(16 + (chan))
     51#define PWM_CONTROL_POLARITY_SHIFT(chan)	(8 + (chan))
     52#define PWM_CONTROL_TRIGGER_SHIFT(chan)		(chan)
     53
     54#define PRESCALE_OFFSET				0x00000004
     55#define PRESCALE_SHIFT(chan)			((chan) << 2)
     56#define PRESCALE_MASK(chan)			(0x7 << PRESCALE_SHIFT(chan))
     57#define PRESCALE_MIN				0x00000000
     58#define PRESCALE_MAX				0x00000007
     59
     60#define PERIOD_COUNT_OFFSET(chan)		(0x00000008 + ((chan) << 3))
     61#define PERIOD_COUNT_MIN			0x00000002
     62#define PERIOD_COUNT_MAX			0x00ffffff
     63
     64#define DUTY_CYCLE_HIGH_OFFSET(chan)		(0x0000000c + ((chan) << 3))
     65#define DUTY_CYCLE_HIGH_MIN			0x00000000
     66#define DUTY_CYCLE_HIGH_MAX			0x00ffffff
     67
     68struct kona_pwmc {
     69	struct pwm_chip chip;
     70	void __iomem *base;
     71	struct clk *clk;
     72};
     73
     74static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip)
     75{
     76	return container_of(_chip, struct kona_pwmc, chip);
     77}
     78
     79/*
     80 * Clear trigger bit but set smooth bit to maintain old output.
     81 */
     82static void kona_pwmc_prepare_for_settings(struct kona_pwmc *kp,
     83	unsigned int chan)
     84{
     85	unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
     86
     87	value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan);
     88	value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan));
     89	writel(value, kp->base + PWM_CONTROL_OFFSET);
     90
     91	/*
     92	 * There must be a min 400ns delay between clearing trigger and setting
     93	 * it. Failing to do this may result in no PWM signal.
     94	 */
     95	ndelay(400);
     96}
     97
     98static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
     99{
    100	unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
    101
    102	/* Set trigger bit and clear smooth bit to apply new settings */
    103	value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
    104	value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan);
    105	writel(value, kp->base + PWM_CONTROL_OFFSET);
    106
    107	/* Trigger bit must be held high for at least 400 ns. */
    108	ndelay(400);
    109}
    110
    111static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
    112			    u64 duty_ns, u64 period_ns)
    113{
    114	struct kona_pwmc *kp = to_kona_pwmc(chip);
    115	u64 div, rate;
    116	unsigned long prescale = PRESCALE_MIN, pc, dc;
    117	unsigned int value, chan = pwm->hwpwm;
    118
    119	/*
    120	 * Find period count, duty count and prescale to suit duty_ns and
    121	 * period_ns. This is done according to formulas described below:
    122	 *
    123	 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
    124	 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
    125	 *
    126	 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
    127	 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
    128	 */
    129
    130	rate = clk_get_rate(kp->clk);
    131
    132	while (1) {
    133		div = 1000000000;
    134		div *= 1 + prescale;
    135		pc = mul_u64_u64_div_u64(rate, period_ns, div);
    136		dc = mul_u64_u64_div_u64(rate, duty_ns, div);
    137
    138		/* If duty_ns or period_ns are not achievable then return */
    139		if (pc < PERIOD_COUNT_MIN)
    140			return -EINVAL;
    141
    142		/* If pc and dc are in bounds, the calculation is done */
    143		if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
    144			break;
    145
    146		/* Otherwise, increase prescale and recalculate pc and dc */
    147		if (++prescale > PRESCALE_MAX)
    148			return -EINVAL;
    149	}
    150
    151	kona_pwmc_prepare_for_settings(kp, chan);
    152
    153	value = readl(kp->base + PRESCALE_OFFSET);
    154	value &= ~PRESCALE_MASK(chan);
    155	value |= prescale << PRESCALE_SHIFT(chan);
    156	writel(value, kp->base + PRESCALE_OFFSET);
    157
    158	writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
    159
    160	writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
    161
    162	kona_pwmc_apply_settings(kp, chan);
    163
    164	return 0;
    165}
    166
    167static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
    168				  enum pwm_polarity polarity)
    169{
    170	struct kona_pwmc *kp = to_kona_pwmc(chip);
    171	unsigned int chan = pwm->hwpwm;
    172	unsigned int value;
    173	int ret;
    174
    175	ret = clk_prepare_enable(kp->clk);
    176	if (ret < 0) {
    177		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
    178		return ret;
    179	}
    180
    181	kona_pwmc_prepare_for_settings(kp, chan);
    182
    183	value = readl(kp->base + PWM_CONTROL_OFFSET);
    184
    185	if (polarity == PWM_POLARITY_NORMAL)
    186		value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
    187	else
    188		value &= ~(1 << PWM_CONTROL_POLARITY_SHIFT(chan));
    189
    190	writel(value, kp->base + PWM_CONTROL_OFFSET);
    191
    192	kona_pwmc_apply_settings(kp, chan);
    193
    194	clk_disable_unprepare(kp->clk);
    195
    196	return 0;
    197}
    198
    199static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
    200{
    201	struct kona_pwmc *kp = to_kona_pwmc(chip);
    202	int ret;
    203
    204	ret = clk_prepare_enable(kp->clk);
    205	if (ret < 0) {
    206		dev_err(chip->dev, "failed to enable clock: %d\n", ret);
    207		return ret;
    208	}
    209
    210	return 0;
    211}
    212
    213static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    214{
    215	struct kona_pwmc *kp = to_kona_pwmc(chip);
    216	unsigned int chan = pwm->hwpwm;
    217	unsigned int value;
    218
    219	kona_pwmc_prepare_for_settings(kp, chan);
    220
    221	/* Simulate a disable by configuring for zero duty */
    222	writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
    223	writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
    224
    225	/* Set prescale to 0 for this channel */
    226	value = readl(kp->base + PRESCALE_OFFSET);
    227	value &= ~PRESCALE_MASK(chan);
    228	writel(value, kp->base + PRESCALE_OFFSET);
    229
    230	kona_pwmc_apply_settings(kp, chan);
    231
    232	clk_disable_unprepare(kp->clk);
    233}
    234
    235static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    236			   const struct pwm_state *state)
    237{
    238	int err;
    239	struct kona_pwmc *kp = to_kona_pwmc(chip);
    240	bool enabled = pwm->state.enabled;
    241
    242	if (state->polarity != pwm->state.polarity) {
    243		if (enabled) {
    244			kona_pwmc_disable(chip, pwm);
    245			enabled = false;
    246		}
    247
    248		err = kona_pwmc_set_polarity(chip, pwm, state->polarity);
    249		if (err)
    250			return err;
    251
    252		pwm->state.polarity = state->polarity;
    253	}
    254
    255	if (!state->enabled) {
    256		if (enabled)
    257			kona_pwmc_disable(chip, pwm);
    258		return 0;
    259	} else if (!enabled) {
    260		/*
    261		 * This is a bit special here, usually the PWM should only be
    262		 * enabled when duty and period are setup. But before this
    263		 * driver was converted to .apply it was done the other way
    264		 * around and so this behaviour was kept even though this might
    265		 * result in a glitch. This might be improvable by someone with
    266		 * hardware and/or documentation.
    267		 */
    268		err = kona_pwmc_enable(chip, pwm);
    269		if (err)
    270			return err;
    271	}
    272
    273	err = kona_pwmc_config(pwm->chip, pwm, state->duty_cycle, state->period);
    274	if (err && !pwm->state.enabled)
    275		clk_disable_unprepare(kp->clk);
    276
    277	return err;
    278}
    279
    280static const struct pwm_ops kona_pwm_ops = {
    281	.apply = kona_pwmc_apply,
    282	.owner = THIS_MODULE,
    283};
    284
    285static int kona_pwmc_probe(struct platform_device *pdev)
    286{
    287	struct kona_pwmc *kp;
    288	unsigned int chan;
    289	unsigned int value = 0;
    290	int ret = 0;
    291
    292	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
    293	if (kp == NULL)
    294		return -ENOMEM;
    295
    296	kp->chip.dev = &pdev->dev;
    297	kp->chip.ops = &kona_pwm_ops;
    298	kp->chip.npwm = 6;
    299
    300	kp->base = devm_platform_ioremap_resource(pdev, 0);
    301	if (IS_ERR(kp->base))
    302		return PTR_ERR(kp->base);
    303
    304	kp->clk = devm_clk_get(&pdev->dev, NULL);
    305	if (IS_ERR(kp->clk)) {
    306		dev_err(&pdev->dev, "failed to get clock: %ld\n",
    307			PTR_ERR(kp->clk));
    308		return PTR_ERR(kp->clk);
    309	}
    310
    311	ret = clk_prepare_enable(kp->clk);
    312	if (ret < 0) {
    313		dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
    314		return ret;
    315	}
    316
    317	/* Set push/pull for all channels */
    318	for (chan = 0; chan < kp->chip.npwm; chan++)
    319		value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan));
    320
    321	writel(value, kp->base + PWM_CONTROL_OFFSET);
    322
    323	clk_disable_unprepare(kp->clk);
    324
    325	ret = devm_pwmchip_add(&pdev->dev, &kp->chip);
    326	if (ret < 0)
    327		dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
    328
    329	return ret;
    330}
    331
    332static const struct of_device_id bcm_kona_pwmc_dt[] = {
    333	{ .compatible = "brcm,kona-pwm" },
    334	{ },
    335};
    336MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt);
    337
    338static struct platform_driver kona_pwmc_driver = {
    339	.driver = {
    340		.name = "bcm-kona-pwm",
    341		.of_match_table = bcm_kona_pwmc_dt,
    342	},
    343	.probe = kona_pwmc_probe,
    344};
    345module_platform_driver(kona_pwmc_driver);
    346
    347MODULE_AUTHOR("Broadcom Corporation <bcm-kernel-feedback-list@broadcom.com>");
    348MODULE_AUTHOR("Tim Kryger <tkryger@broadcom.com>");
    349MODULE_DESCRIPTION("Broadcom Kona PWM driver");
    350MODULE_LICENSE("GPL v2");