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


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@gmail.com>
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/err.h>
      8#include <linux/io.h>
      9#include <linux/kernel.h>
     10#include <linux/module.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13#include <linux/platform_device.h>
     14#include <linux/pwm.h>
     15#include <linux/slab.h>
     16
     17struct lpc32xx_pwm_chip {
     18	struct pwm_chip chip;
     19	struct clk *clk;
     20	void __iomem *base;
     21};
     22
     23#define PWM_ENABLE	BIT(31)
     24#define PWM_PIN_LEVEL	BIT(30)
     25
     26#define to_lpc32xx_pwm_chip(_chip) \
     27	container_of(_chip, struct lpc32xx_pwm_chip, chip)
     28
     29static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
     30			      int duty_ns, int period_ns)
     31{
     32	struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
     33	unsigned long long c;
     34	int period_cycles, duty_cycles;
     35	u32 val;
     36	c = clk_get_rate(lpc32xx->clk);
     37
     38	/* The highest acceptable divisor is 256, which is represented by 0 */
     39	period_cycles = div64_u64(c * period_ns,
     40			       (unsigned long long)NSEC_PER_SEC * 256);
     41	if (!period_cycles || period_cycles > 256)
     42		return -ERANGE;
     43	if (period_cycles == 256)
     44		period_cycles = 0;
     45
     46	/* Compute 256 x #duty/period value and care for corner cases */
     47	duty_cycles = div64_u64((unsigned long long)(period_ns - duty_ns) * 256,
     48				period_ns);
     49	if (!duty_cycles)
     50		duty_cycles = 1;
     51	if (duty_cycles > 255)
     52		duty_cycles = 255;
     53
     54	val = readl(lpc32xx->base + (pwm->hwpwm << 2));
     55	val &= ~0xFFFF;
     56	val |= (period_cycles << 8) | duty_cycles;
     57	writel(val, lpc32xx->base + (pwm->hwpwm << 2));
     58
     59	return 0;
     60}
     61
     62static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
     63{
     64	struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
     65	u32 val;
     66	int ret;
     67
     68	ret = clk_prepare_enable(lpc32xx->clk);
     69	if (ret)
     70		return ret;
     71
     72	val = readl(lpc32xx->base + (pwm->hwpwm << 2));
     73	val |= PWM_ENABLE;
     74	writel(val, lpc32xx->base + (pwm->hwpwm << 2));
     75
     76	return 0;
     77}
     78
     79static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
     80{
     81	struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
     82	u32 val;
     83
     84	val = readl(lpc32xx->base + (pwm->hwpwm << 2));
     85	val &= ~PWM_ENABLE;
     86	writel(val, lpc32xx->base + (pwm->hwpwm << 2));
     87
     88	clk_disable_unprepare(lpc32xx->clk);
     89}
     90
     91static int lpc32xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
     92			     const struct pwm_state *state)
     93{
     94	int err;
     95
     96	if (state->polarity != PWM_POLARITY_NORMAL)
     97		return -EINVAL;
     98
     99	if (!state->enabled) {
    100		if (pwm->state.enabled)
    101			lpc32xx_pwm_disable(chip, pwm);
    102
    103		return 0;
    104	}
    105
    106	err = lpc32xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
    107	if (err)
    108		return err;
    109
    110	if (!pwm->state.enabled)
    111		err = lpc32xx_pwm_enable(chip, pwm);
    112
    113	return err;
    114}
    115
    116static const struct pwm_ops lpc32xx_pwm_ops = {
    117	.apply = lpc32xx_pwm_apply,
    118	.owner = THIS_MODULE,
    119};
    120
    121static int lpc32xx_pwm_probe(struct platform_device *pdev)
    122{
    123	struct lpc32xx_pwm_chip *lpc32xx;
    124	int ret;
    125	u32 val;
    126
    127	lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL);
    128	if (!lpc32xx)
    129		return -ENOMEM;
    130
    131	lpc32xx->base = devm_platform_ioremap_resource(pdev, 0);
    132	if (IS_ERR(lpc32xx->base))
    133		return PTR_ERR(lpc32xx->base);
    134
    135	lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
    136	if (IS_ERR(lpc32xx->clk))
    137		return PTR_ERR(lpc32xx->clk);
    138
    139	lpc32xx->chip.dev = &pdev->dev;
    140	lpc32xx->chip.ops = &lpc32xx_pwm_ops;
    141	lpc32xx->chip.npwm = 1;
    142
    143	/* If PWM is disabled, configure the output to the default value */
    144	val = readl(lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2));
    145	val &= ~PWM_PIN_LEVEL;
    146	writel(val, lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2));
    147
    148	ret = devm_pwmchip_add(&pdev->dev, &lpc32xx->chip);
    149	if (ret < 0) {
    150		dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret);
    151		return ret;
    152	}
    153
    154	return 0;
    155}
    156
    157static const struct of_device_id lpc32xx_pwm_dt_ids[] = {
    158	{ .compatible = "nxp,lpc3220-pwm", },
    159	{ /* sentinel */ }
    160};
    161MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids);
    162
    163static struct platform_driver lpc32xx_pwm_driver = {
    164	.driver = {
    165		.name = "lpc32xx-pwm",
    166		.of_match_table = lpc32xx_pwm_dt_ids,
    167	},
    168	.probe = lpc32xx_pwm_probe,
    169};
    170module_platform_driver(lpc32xx_pwm_driver);
    171
    172MODULE_ALIAS("platform:lpc32xx-pwm");
    173MODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>");
    174MODULE_DESCRIPTION("LPC32XX PWM Driver");
    175MODULE_LICENSE("GPL v2");