Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.12-rc2 187 lines 4.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be> 4 */ 5 6#include <linux/clk.h> 7#include <linux/err.h> 8#include <linux/io.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/pwm.h> 13 14#define PWM_CONTROL 0x000 15#define PWM_CONTROL_SHIFT(x) ((x) * 8) 16#define PWM_CONTROL_MASK 0xff 17#define PWM_MODE 0x80 /* set timer in PWM mode */ 18#define PWM_ENABLE (1 << 0) 19#define PWM_POLARITY (1 << 4) 20 21#define PERIOD(x) (((x) * 0x10) + 0x10) 22#define DUTY(x) (((x) * 0x10) + 0x14) 23 24#define PERIOD_MIN 0x2 25 26struct bcm2835_pwm { 27 struct pwm_chip chip; 28 struct device *dev; 29 void __iomem *base; 30 struct clk *clk; 31}; 32 33static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip) 34{ 35 return container_of(chip, struct bcm2835_pwm, chip); 36} 37 38static int bcm2835_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 39{ 40 struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); 41 u32 value; 42 43 value = readl(pc->base + PWM_CONTROL); 44 value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); 45 value |= (PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm)); 46 writel(value, pc->base + PWM_CONTROL); 47 48 return 0; 49} 50 51static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 52{ 53 struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); 54 u32 value; 55 56 value = readl(pc->base + PWM_CONTROL); 57 value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); 58 writel(value, pc->base + PWM_CONTROL); 59} 60 61static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 62 const struct pwm_state *state) 63{ 64 65 struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); 66 unsigned long rate = clk_get_rate(pc->clk); 67 unsigned long long period; 68 unsigned long scaler; 69 u32 val; 70 71 if (!rate) { 72 dev_err(pc->dev, "failed to get clock rate\n"); 73 return -EINVAL; 74 } 75 76 scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate); 77 /* set period */ 78 period = DIV_ROUND_CLOSEST_ULL(state->period, scaler); 79 80 /* dont accept a period that is too small or has been truncated */ 81 if ((period < PERIOD_MIN) || (period > U32_MAX)) 82 return -EINVAL; 83 84 writel(period, pc->base + PERIOD(pwm->hwpwm)); 85 86 /* set duty cycle */ 87 val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler); 88 writel(val, pc->base + DUTY(pwm->hwpwm)); 89 90 /* set polarity */ 91 val = readl(pc->base + PWM_CONTROL); 92 93 if (state->polarity == PWM_POLARITY_NORMAL) 94 val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm)); 95 else 96 val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm); 97 98 /* enable/disable */ 99 if (state->enabled) 100 val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm); 101 else 102 val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm)); 103 104 writel(val, pc->base + PWM_CONTROL); 105 106 return 0; 107} 108 109static const struct pwm_ops bcm2835_pwm_ops = { 110 .request = bcm2835_pwm_request, 111 .free = bcm2835_pwm_free, 112 .apply = bcm2835_pwm_apply, 113 .owner = THIS_MODULE, 114}; 115 116static int bcm2835_pwm_probe(struct platform_device *pdev) 117{ 118 struct bcm2835_pwm *pc; 119 int ret; 120 121 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); 122 if (!pc) 123 return -ENOMEM; 124 125 pc->dev = &pdev->dev; 126 127 pc->base = devm_platform_ioremap_resource(pdev, 0); 128 if (IS_ERR(pc->base)) 129 return PTR_ERR(pc->base); 130 131 pc->clk = devm_clk_get(&pdev->dev, NULL); 132 if (IS_ERR(pc->clk)) 133 return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), 134 "clock not found\n"); 135 136 ret = clk_prepare_enable(pc->clk); 137 if (ret) 138 return ret; 139 140 pc->chip.dev = &pdev->dev; 141 pc->chip.ops = &bcm2835_pwm_ops; 142 pc->chip.base = -1; 143 pc->chip.npwm = 2; 144 pc->chip.of_xlate = of_pwm_xlate_with_flags; 145 pc->chip.of_pwm_n_cells = 3; 146 147 platform_set_drvdata(pdev, pc); 148 149 ret = pwmchip_add(&pc->chip); 150 if (ret < 0) 151 goto add_fail; 152 153 return 0; 154 155add_fail: 156 clk_disable_unprepare(pc->clk); 157 return ret; 158} 159 160static int bcm2835_pwm_remove(struct platform_device *pdev) 161{ 162 struct bcm2835_pwm *pc = platform_get_drvdata(pdev); 163 164 clk_disable_unprepare(pc->clk); 165 166 return pwmchip_remove(&pc->chip); 167} 168 169static const struct of_device_id bcm2835_pwm_of_match[] = { 170 { .compatible = "brcm,bcm2835-pwm", }, 171 { /* sentinel */ } 172}; 173MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match); 174 175static struct platform_driver bcm2835_pwm_driver = { 176 .driver = { 177 .name = "bcm2835-pwm", 178 .of_match_table = bcm2835_pwm_of_match, 179 }, 180 .probe = bcm2835_pwm_probe, 181 .remove = bcm2835_pwm_remove, 182}; 183module_platform_driver(bcm2835_pwm_driver); 184 185MODULE_AUTHOR("Bart Tanghe <bart.tanghe@thomasmore.be>"); 186MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver"); 187MODULE_LICENSE("GPL v2");