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