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


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * ECAP PWM driver
      4 *
      5 * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/platform_device.h>
     10#include <linux/io.h>
     11#include <linux/err.h>
     12#include <linux/clk.h>
     13#include <linux/pm_runtime.h>
     14#include <linux/pwm.h>
     15#include <linux/of_device.h>
     16
     17/* ECAP registers and bits definitions */
     18#define CAP1			0x08
     19#define CAP2			0x0C
     20#define CAP3			0x10
     21#define CAP4			0x14
     22#define ECCTL2			0x2A
     23#define ECCTL2_APWM_POL_LOW	BIT(10)
     24#define ECCTL2_APWM_MODE	BIT(9)
     25#define ECCTL2_SYNC_SEL_DISA	(BIT(7) | BIT(6))
     26#define ECCTL2_TSCTR_FREERUN	BIT(4)
     27
     28struct ecap_context {
     29	u32 cap3;
     30	u32 cap4;
     31	u16 ecctl2;
     32};
     33
     34struct ecap_pwm_chip {
     35	struct pwm_chip chip;
     36	unsigned int clk_rate;
     37	void __iomem *mmio_base;
     38	struct ecap_context ctx;
     39};
     40
     41static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
     42{
     43	return container_of(chip, struct ecap_pwm_chip, chip);
     44}
     45
     46/*
     47 * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
     48 * duty_ns   = 10^9 * duty_cycles / PWM_CLK_RATE
     49 */
     50static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
     51			   int duty_ns, int period_ns, int enabled)
     52{
     53	struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
     54	u32 period_cycles, duty_cycles;
     55	unsigned long long c;
     56	u16 value;
     57
     58	c = pc->clk_rate;
     59	c = c * period_ns;
     60	do_div(c, NSEC_PER_SEC);
     61	period_cycles = (u32)c;
     62
     63	if (period_cycles < 1) {
     64		period_cycles = 1;
     65		duty_cycles = 1;
     66	} else {
     67		c = pc->clk_rate;
     68		c = c * duty_ns;
     69		do_div(c, NSEC_PER_SEC);
     70		duty_cycles = (u32)c;
     71	}
     72
     73	pm_runtime_get_sync(pc->chip.dev);
     74
     75	value = readw(pc->mmio_base + ECCTL2);
     76
     77	/* Configure APWM mode & disable sync option */
     78	value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
     79
     80	writew(value, pc->mmio_base + ECCTL2);
     81
     82	if (!enabled) {
     83		/* Update active registers if not running */
     84		writel(duty_cycles, pc->mmio_base + CAP2);
     85		writel(period_cycles, pc->mmio_base + CAP1);
     86	} else {
     87		/*
     88		 * Update shadow registers to configure period and
     89		 * compare values. This helps current PWM period to
     90		 * complete on reconfiguring
     91		 */
     92		writel(duty_cycles, pc->mmio_base + CAP4);
     93		writel(period_cycles, pc->mmio_base + CAP3);
     94	}
     95
     96	if (!enabled) {
     97		value = readw(pc->mmio_base + ECCTL2);
     98		/* Disable APWM mode to put APWM output Low */
     99		value &= ~ECCTL2_APWM_MODE;
    100		writew(value, pc->mmio_base + ECCTL2);
    101	}
    102
    103	pm_runtime_put_sync(pc->chip.dev);
    104
    105	return 0;
    106}
    107
    108static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
    109				 enum pwm_polarity polarity)
    110{
    111	struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
    112	u16 value;
    113
    114	pm_runtime_get_sync(pc->chip.dev);
    115
    116	value = readw(pc->mmio_base + ECCTL2);
    117
    118	if (polarity == PWM_POLARITY_INVERSED)
    119		/* Duty cycle defines LOW period of PWM */
    120		value |= ECCTL2_APWM_POL_LOW;
    121	else
    122		/* Duty cycle defines HIGH period of PWM */
    123		value &= ~ECCTL2_APWM_POL_LOW;
    124
    125	writew(value, pc->mmio_base + ECCTL2);
    126
    127	pm_runtime_put_sync(pc->chip.dev);
    128
    129	return 0;
    130}
    131
    132static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
    133{
    134	struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
    135	u16 value;
    136
    137	/* Leave clock enabled on enabling PWM */
    138	pm_runtime_get_sync(pc->chip.dev);
    139
    140	/*
    141	 * Enable 'Free run Time stamp counter mode' to start counter
    142	 * and  'APWM mode' to enable APWM output
    143	 */
    144	value = readw(pc->mmio_base + ECCTL2);
    145	value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
    146	writew(value, pc->mmio_base + ECCTL2);
    147
    148	return 0;
    149}
    150
    151static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    152{
    153	struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
    154	u16 value;
    155
    156	/*
    157	 * Disable 'Free run Time stamp counter mode' to stop counter
    158	 * and 'APWM mode' to put APWM output to low
    159	 */
    160	value = readw(pc->mmio_base + ECCTL2);
    161	value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
    162	writew(value, pc->mmio_base + ECCTL2);
    163
    164	/* Disable clock on PWM disable */
    165	pm_runtime_put_sync(pc->chip.dev);
    166}
    167
    168static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    169			  const struct pwm_state *state)
    170{
    171	int err;
    172	int enabled = pwm->state.enabled;
    173
    174	if (state->polarity != pwm->state.polarity) {
    175
    176		if (enabled) {
    177			ecap_pwm_disable(chip, pwm);
    178			enabled = false;
    179		}
    180
    181		err = ecap_pwm_set_polarity(chip, pwm, state->polarity);
    182		if (err)
    183			return err;
    184	}
    185
    186	if (!state->enabled) {
    187		if (enabled)
    188			ecap_pwm_disable(chip, pwm);
    189		return 0;
    190	}
    191
    192	if (state->period > NSEC_PER_SEC)
    193		return -ERANGE;
    194
    195	err = ecap_pwm_config(chip, pwm, state->duty_cycle,
    196			      state->period, enabled);
    197	if (err)
    198		return err;
    199
    200	if (!enabled)
    201		return ecap_pwm_enable(chip, pwm);
    202
    203	return 0;
    204}
    205
    206static const struct pwm_ops ecap_pwm_ops = {
    207	.apply = ecap_pwm_apply,
    208	.owner = THIS_MODULE,
    209};
    210
    211static const struct of_device_id ecap_of_match[] = {
    212	{ .compatible	= "ti,am3352-ecap" },
    213	{ .compatible	= "ti,am33xx-ecap" },
    214	{},
    215};
    216MODULE_DEVICE_TABLE(of, ecap_of_match);
    217
    218static int ecap_pwm_probe(struct platform_device *pdev)
    219{
    220	struct device_node *np = pdev->dev.of_node;
    221	struct ecap_pwm_chip *pc;
    222	struct clk *clk;
    223	int ret;
    224
    225	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
    226	if (!pc)
    227		return -ENOMEM;
    228
    229	clk = devm_clk_get(&pdev->dev, "fck");
    230	if (IS_ERR(clk)) {
    231		if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
    232			dev_warn(&pdev->dev, "Binding is obsolete.\n");
    233			clk = devm_clk_get(pdev->dev.parent, "fck");
    234		}
    235	}
    236
    237	if (IS_ERR(clk)) {
    238		dev_err(&pdev->dev, "failed to get clock\n");
    239		return PTR_ERR(clk);
    240	}
    241
    242	pc->clk_rate = clk_get_rate(clk);
    243	if (!pc->clk_rate) {
    244		dev_err(&pdev->dev, "failed to get clock rate\n");
    245		return -EINVAL;
    246	}
    247
    248	pc->chip.dev = &pdev->dev;
    249	pc->chip.ops = &ecap_pwm_ops;
    250	pc->chip.npwm = 1;
    251
    252	pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
    253	if (IS_ERR(pc->mmio_base))
    254		return PTR_ERR(pc->mmio_base);
    255
    256	ret = devm_pwmchip_add(&pdev->dev, &pc->chip);
    257	if (ret < 0) {
    258		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
    259		return ret;
    260	}
    261
    262	platform_set_drvdata(pdev, pc);
    263	pm_runtime_enable(&pdev->dev);
    264
    265	return 0;
    266}
    267
    268static int ecap_pwm_remove(struct platform_device *pdev)
    269{
    270	pm_runtime_disable(&pdev->dev);
    271
    272	return 0;
    273}
    274
    275#ifdef CONFIG_PM_SLEEP
    276static void ecap_pwm_save_context(struct ecap_pwm_chip *pc)
    277{
    278	pm_runtime_get_sync(pc->chip.dev);
    279	pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2);
    280	pc->ctx.cap4 = readl(pc->mmio_base + CAP4);
    281	pc->ctx.cap3 = readl(pc->mmio_base + CAP3);
    282	pm_runtime_put_sync(pc->chip.dev);
    283}
    284
    285static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc)
    286{
    287	writel(pc->ctx.cap3, pc->mmio_base + CAP3);
    288	writel(pc->ctx.cap4, pc->mmio_base + CAP4);
    289	writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2);
    290}
    291
    292static int ecap_pwm_suspend(struct device *dev)
    293{
    294	struct ecap_pwm_chip *pc = dev_get_drvdata(dev);
    295	struct pwm_device *pwm = pc->chip.pwms;
    296
    297	ecap_pwm_save_context(pc);
    298
    299	/* Disable explicitly if PWM is running */
    300	if (pwm_is_enabled(pwm))
    301		pm_runtime_put_sync(dev);
    302
    303	return 0;
    304}
    305
    306static int ecap_pwm_resume(struct device *dev)
    307{
    308	struct ecap_pwm_chip *pc = dev_get_drvdata(dev);
    309	struct pwm_device *pwm = pc->chip.pwms;
    310
    311	/* Enable explicitly if PWM was running */
    312	if (pwm_is_enabled(pwm))
    313		pm_runtime_get_sync(dev);
    314
    315	ecap_pwm_restore_context(pc);
    316	return 0;
    317}
    318#endif
    319
    320static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume);
    321
    322static struct platform_driver ecap_pwm_driver = {
    323	.driver = {
    324		.name = "ecap",
    325		.of_match_table = ecap_of_match,
    326		.pm = &ecap_pwm_pm_ops,
    327	},
    328	.probe = ecap_pwm_probe,
    329	.remove = ecap_pwm_remove,
    330};
    331module_platform_driver(ecap_pwm_driver);
    332
    333MODULE_DESCRIPTION("ECAP PWM driver");
    334MODULE_AUTHOR("Texas Instruments");
    335MODULE_LICENSE("GPL");