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


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * drivers/pwm/pwm-pxa.c
      4 *
      5 * simple driver for PWM (Pulse Width Modulator) controller
      6 *
      7 * 2008-02-13	initial version
      8 *		eric miao <eric.miao@marvell.com>
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/kernel.h>
     13#include <linux/platform_device.h>
     14#include <linux/slab.h>
     15#include <linux/err.h>
     16#include <linux/clk.h>
     17#include <linux/io.h>
     18#include <linux/pwm.h>
     19#include <linux/of_device.h>
     20
     21#include <asm/div64.h>
     22
     23#define HAS_SECONDARY_PWM	0x10
     24
     25static const struct platform_device_id pwm_id_table[] = {
     26	/*   PWM    has_secondary_pwm? */
     27	{ "pxa25x-pwm", 0 },
     28	{ "pxa27x-pwm", HAS_SECONDARY_PWM },
     29	{ "pxa168-pwm", 0 },
     30	{ "pxa910-pwm", 0 },
     31	{ },
     32};
     33MODULE_DEVICE_TABLE(platform, pwm_id_table);
     34
     35/* PWM registers and bits definitions */
     36#define PWMCR		(0x00)
     37#define PWMDCR		(0x04)
     38#define PWMPCR		(0x08)
     39
     40#define PWMCR_SD	(1 << 6)
     41#define PWMDCR_FD	(1 << 10)
     42
     43struct pxa_pwm_chip {
     44	struct pwm_chip	chip;
     45	struct device	*dev;
     46
     47	struct clk	*clk;
     48	void __iomem	*mmio_base;
     49};
     50
     51static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
     52{
     53	return container_of(chip, struct pxa_pwm_chip, chip);
     54}
     55
     56/*
     57 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
     58 * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
     59 */
     60static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
     61			  u64 duty_ns, u64 period_ns)
     62{
     63	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
     64	unsigned long long c;
     65	unsigned long period_cycles, prescale, pv, dc;
     66	unsigned long offset;
     67	int rc;
     68
     69	offset = pwm->hwpwm ? 0x10 : 0;
     70
     71	c = clk_get_rate(pc->clk);
     72	c = c * period_ns;
     73	do_div(c, 1000000000);
     74	period_cycles = c;
     75
     76	if (period_cycles < 1)
     77		period_cycles = 1;
     78	prescale = (period_cycles - 1) / 1024;
     79	pv = period_cycles / (prescale + 1) - 1;
     80
     81	if (prescale > 63)
     82		return -EINVAL;
     83
     84	if (duty_ns == period_ns)
     85		dc = PWMDCR_FD;
     86	else
     87		dc = mul_u64_u64_div_u64(pv + 1, duty_ns, period_ns);
     88
     89	/* NOTE: the clock to PWM has to be enabled first
     90	 * before writing to the registers
     91	 */
     92	rc = clk_prepare_enable(pc->clk);
     93	if (rc < 0)
     94		return rc;
     95
     96	writel(prescale, pc->mmio_base + offset + PWMCR);
     97	writel(dc, pc->mmio_base + offset + PWMDCR);
     98	writel(pv, pc->mmio_base + offset + PWMPCR);
     99
    100	clk_disable_unprepare(pc->clk);
    101	return 0;
    102}
    103
    104static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
    105{
    106	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
    107
    108	return clk_prepare_enable(pc->clk);
    109}
    110
    111static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
    112{
    113	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
    114
    115	clk_disable_unprepare(pc->clk);
    116}
    117
    118static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    119			 const struct pwm_state *state)
    120{
    121	int err;
    122
    123	if (state->polarity != PWM_POLARITY_NORMAL)
    124		return -EINVAL;
    125
    126	if (!state->enabled) {
    127		if (pwm->state.enabled)
    128			pxa_pwm_disable(chip, pwm);
    129
    130		return 0;
    131	}
    132
    133	err = pxa_pwm_config(chip, pwm, state->duty_cycle, state->period);
    134	if (err)
    135		return err;
    136
    137	if (!pwm->state.enabled)
    138		return pxa_pwm_enable(chip, pwm);
    139
    140	return 0;
    141}
    142
    143static const struct pwm_ops pxa_pwm_ops = {
    144	.apply = pxa_pwm_apply,
    145	.owner = THIS_MODULE,
    146};
    147
    148#ifdef CONFIG_OF
    149/*
    150 * Device tree users must create one device instance for each PWM channel.
    151 * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
    152 * code that this is a single channel pxa25x-pwm.  Currently all devices are
    153 * supported identically.
    154 */
    155static const struct of_device_id pwm_of_match[] = {
    156	{ .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
    157	{ .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
    158	{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
    159	{ .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0]},
    160	{ }
    161};
    162MODULE_DEVICE_TABLE(of, pwm_of_match);
    163#else
    164#define pwm_of_match NULL
    165#endif
    166
    167static const struct platform_device_id *pxa_pwm_get_id_dt(struct device *dev)
    168{
    169	const struct of_device_id *id = of_match_device(pwm_of_match, dev);
    170
    171	return id ? id->data : NULL;
    172}
    173
    174static int pwm_probe(struct platform_device *pdev)
    175{
    176	const struct platform_device_id *id = platform_get_device_id(pdev);
    177	struct pxa_pwm_chip *pc;
    178	int ret = 0;
    179
    180	if (IS_ENABLED(CONFIG_OF) && id == NULL)
    181		id = pxa_pwm_get_id_dt(&pdev->dev);
    182
    183	if (id == NULL)
    184		return -EINVAL;
    185
    186	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
    187	if (pc == NULL)
    188		return -ENOMEM;
    189
    190	pc->clk = devm_clk_get(&pdev->dev, NULL);
    191	if (IS_ERR(pc->clk))
    192		return PTR_ERR(pc->clk);
    193
    194	pc->chip.dev = &pdev->dev;
    195	pc->chip.ops = &pxa_pwm_ops;
    196	pc->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
    197
    198	if (IS_ENABLED(CONFIG_OF)) {
    199		pc->chip.of_xlate = of_pwm_single_xlate;
    200		pc->chip.of_pwm_n_cells = 1;
    201	}
    202
    203	pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
    204	if (IS_ERR(pc->mmio_base))
    205		return PTR_ERR(pc->mmio_base);
    206
    207	ret = devm_pwmchip_add(&pdev->dev, &pc->chip);
    208	if (ret < 0) {
    209		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
    210		return ret;
    211	}
    212
    213	return 0;
    214}
    215
    216static struct platform_driver pwm_driver = {
    217	.driver		= {
    218		.name	= "pxa25x-pwm",
    219		.of_match_table = pwm_of_match,
    220	},
    221	.probe		= pwm_probe,
    222	.id_table	= pwm_id_table,
    223};
    224
    225module_platform_driver(pwm_driver);
    226
    227MODULE_LICENSE("GPL v2");