1/* 2 * linux/arch/arm/mach-pxa/pwm.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 23#include <asm/div64.h> 24 25#define HAS_SECONDARY_PWM 0x10 26#define PWM_ID_BASE(d) ((d) & 0xf) 27 28static const struct platform_device_id pwm_id_table[] = { 29 /* PWM has_secondary_pwm? */ 30 { "pxa25x-pwm", 0 }, 31 { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM }, 32 { "pxa168-pwm", 1 }, 33 { "pxa910-pwm", 1 }, 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 pwm_device { 47 struct list_head node; 48 struct pwm_device *secondary; 49 struct platform_device *pdev; 50 51 const char *label; 52 struct clk *clk; 53 int clk_enabled; 54 void __iomem *mmio_base; 55 56 unsigned int use_count; 57 unsigned int pwm_id; 58}; 59 60/* 61 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE 62 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE 63 */ 64int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) 65{ 66 unsigned long long c; 67 unsigned long period_cycles, prescale, pv, dc; 68 69 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) 70 return -EINVAL; 71 72 c = clk_get_rate(pwm->clk); 73 c = c * period_ns; 74 do_div(c, 1000000000); 75 period_cycles = c; 76 77 if (period_cycles < 1) 78 period_cycles = 1; 79 prescale = (period_cycles - 1) / 1024; 80 pv = period_cycles / (prescale + 1) - 1; 81 82 if (prescale > 63) 83 return -EINVAL; 84 85 if (duty_ns == period_ns) 86 dc = PWMDCR_FD; 87 else 88 dc = (pv + 1) * duty_ns / period_ns; 89 90 /* NOTE: the clock to PWM has to be enabled first 91 * before writing to the registers 92 */ 93 clk_enable(pwm->clk); 94 __raw_writel(prescale, pwm->mmio_base + PWMCR); 95 __raw_writel(dc, pwm->mmio_base + PWMDCR); 96 __raw_writel(pv, pwm->mmio_base + PWMPCR); 97 clk_disable(pwm->clk); 98 99 return 0; 100} 101EXPORT_SYMBOL(pwm_config); 102 103int pwm_enable(struct pwm_device *pwm) 104{ 105 int rc = 0; 106 107 if (!pwm->clk_enabled) { 108 rc = clk_enable(pwm->clk); 109 if (!rc) 110 pwm->clk_enabled = 1; 111 } 112 return rc; 113} 114EXPORT_SYMBOL(pwm_enable); 115 116void pwm_disable(struct pwm_device *pwm) 117{ 118 if (pwm->clk_enabled) { 119 clk_disable(pwm->clk); 120 pwm->clk_enabled = 0; 121 } 122} 123EXPORT_SYMBOL(pwm_disable); 124 125static DEFINE_MUTEX(pwm_lock); 126static LIST_HEAD(pwm_list); 127 128struct pwm_device *pwm_request(int pwm_id, const char *label) 129{ 130 struct pwm_device *pwm; 131 int found = 0; 132 133 mutex_lock(&pwm_lock); 134 135 list_for_each_entry(pwm, &pwm_list, node) { 136 if (pwm->pwm_id == pwm_id) { 137 found = 1; 138 break; 139 } 140 } 141 142 if (found) { 143 if (pwm->use_count == 0) { 144 pwm->use_count++; 145 pwm->label = label; 146 } else 147 pwm = ERR_PTR(-EBUSY); 148 } else 149 pwm = ERR_PTR(-ENOENT); 150 151 mutex_unlock(&pwm_lock); 152 return pwm; 153} 154EXPORT_SYMBOL(pwm_request); 155 156void pwm_free(struct pwm_device *pwm) 157{ 158 mutex_lock(&pwm_lock); 159 160 if (pwm->use_count) { 161 pwm->use_count--; 162 pwm->label = NULL; 163 } else 164 pr_warning("PWM device already freed\n"); 165 166 mutex_unlock(&pwm_lock); 167} 168EXPORT_SYMBOL(pwm_free); 169 170static inline void __add_pwm(struct pwm_device *pwm) 171{ 172 mutex_lock(&pwm_lock); 173 list_add_tail(&pwm->node, &pwm_list); 174 mutex_unlock(&pwm_lock); 175} 176 177static int __devinit pwm_probe(struct platform_device *pdev) 178{ 179 const struct platform_device_id *id = platform_get_device_id(pdev); 180 struct pwm_device *pwm, *secondary = NULL; 181 struct resource *r; 182 int ret = 0; 183 184 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); 185 if (pwm == NULL) { 186 dev_err(&pdev->dev, "failed to allocate memory\n"); 187 return -ENOMEM; 188 } 189 190 pwm->clk = clk_get(&pdev->dev, NULL); 191 if (IS_ERR(pwm->clk)) { 192 ret = PTR_ERR(pwm->clk); 193 goto err_free; 194 } 195 pwm->clk_enabled = 0; 196 197 pwm->use_count = 0; 198 pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; 199 pwm->pdev = pdev; 200 201 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 202 if (r == NULL) { 203 dev_err(&pdev->dev, "no memory resource defined\n"); 204 ret = -ENODEV; 205 goto err_free_clk; 206 } 207 208 r = request_mem_region(r->start, resource_size(r), pdev->name); 209 if (r == NULL) { 210 dev_err(&pdev->dev, "failed to request memory resource\n"); 211 ret = -EBUSY; 212 goto err_free_clk; 213 } 214 215 pwm->mmio_base = ioremap(r->start, resource_size(r)); 216 if (pwm->mmio_base == NULL) { 217 dev_err(&pdev->dev, "failed to ioremap() registers\n"); 218 ret = -ENODEV; 219 goto err_free_mem; 220 } 221 222 if (id->driver_data & HAS_SECONDARY_PWM) { 223 secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); 224 if (secondary == NULL) { 225 ret = -ENOMEM; 226 goto err_free_mem; 227 } 228 229 *secondary = *pwm; 230 pwm->secondary = secondary; 231 232 /* registers for the second PWM has offset of 0x10 */ 233 secondary->mmio_base = pwm->mmio_base + 0x10; 234 secondary->pwm_id = pdev->id + 2; 235 } 236 237 __add_pwm(pwm); 238 if (secondary) 239 __add_pwm(secondary); 240 241 platform_set_drvdata(pdev, pwm); 242 return 0; 243 244err_free_mem: 245 release_mem_region(r->start, resource_size(r)); 246err_free_clk: 247 clk_put(pwm->clk); 248err_free: 249 kfree(pwm); 250 return ret; 251} 252 253static int __devexit pwm_remove(struct platform_device *pdev) 254{ 255 struct pwm_device *pwm; 256 struct resource *r; 257 258 pwm = platform_get_drvdata(pdev); 259 if (pwm == NULL) 260 return -ENODEV; 261 262 mutex_lock(&pwm_lock); 263 264 if (pwm->secondary) { 265 list_del(&pwm->secondary->node); 266 kfree(pwm->secondary); 267 } 268 269 list_del(&pwm->node); 270 mutex_unlock(&pwm_lock); 271 272 iounmap(pwm->mmio_base); 273 274 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 275 release_mem_region(r->start, resource_size(r)); 276 277 clk_put(pwm->clk); 278 kfree(pwm); 279 return 0; 280} 281 282static struct platform_driver pwm_driver = { 283 .driver = { 284 .name = "pxa25x-pwm", 285 .owner = THIS_MODULE, 286 }, 287 .probe = pwm_probe, 288 .remove = __devexit_p(pwm_remove), 289 .id_table = pwm_id_table, 290}; 291 292static int __init pwm_init(void) 293{ 294 return platform_driver_register(&pwm_driver); 295} 296arch_initcall(pwm_init); 297 298static void __exit pwm_exit(void) 299{ 300 platform_driver_unregister(&pwm_driver); 301} 302module_exit(pwm_exit); 303 304MODULE_LICENSE("GPL v2"); 305