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 v2.6.33-rc4 384 lines 9.7 kB view raw
1/* 2 * Simple PWM driver for EP93XX 3 * 4 * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com> 5 * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * EP9307 has only one channel: 13 * - PWMOUT 14 * 15 * EP9301/02/12/15 have two channels: 16 * - PWMOUT 17 * - PWMOUT1 (alternate function for EGPIO14) 18 */ 19 20#include <linux/module.h> 21#include <linux/platform_device.h> 22#include <linux/clk.h> 23#include <linux/err.h> 24#include <linux/io.h> 25 26#include <mach/platform.h> 27 28#define EP93XX_PWMx_TERM_COUNT 0x00 29#define EP93XX_PWMx_DUTY_CYCLE 0x04 30#define EP93XX_PWMx_ENABLE 0x08 31#define EP93XX_PWMx_INVERT 0x0C 32 33#define EP93XX_PWM_MAX_COUNT 0xFFFF 34 35struct ep93xx_pwm { 36 void __iomem *mmio_base; 37 struct clk *clk; 38 u32 duty_percent; 39}; 40 41static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm, 42 unsigned int val, unsigned int off) 43{ 44 __raw_writel(val, pwm->mmio_base + off); 45} 46 47static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm, 48 unsigned int off) 49{ 50 return __raw_readl(pwm->mmio_base + off); 51} 52 53static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) 54{ 55 ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT); 56} 57 58static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) 59{ 60 return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT); 61} 62 63static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value) 64{ 65 ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE); 66} 67 68static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) 69{ 70 ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE); 71} 72 73static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) 74{ 75 ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE); 76} 77 78static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) 79{ 80 return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1; 81} 82 83static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm) 84{ 85 ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT); 86} 87 88static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) 89{ 90 ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT); 91} 92 93static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) 94{ 95 return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1; 96} 97 98/* 99 * /sys/devices/platform/ep93xx-pwm.N 100 * /min_freq read-only minimum pwm output frequency 101 * /max_req read-only maximum pwm output frequency 102 * /freq read-write pwm output frequency (0 = disable output) 103 * /duty_percent read-write pwm duty cycle percent (1..99) 104 * /invert read-write invert pwm output 105 */ 106 107static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, 108 struct device_attribute *attr, char *buf) 109{ 110 struct platform_device *pdev = to_platform_device(dev); 111 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 112 unsigned long rate = clk_get_rate(pwm->clk); 113 114 return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); 115} 116 117static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, 118 struct device_attribute *attr, char *buf) 119{ 120 struct platform_device *pdev = to_platform_device(dev); 121 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 122 unsigned long rate = clk_get_rate(pwm->clk); 123 124 return sprintf(buf, "%ld\n", rate / 2); 125} 126 127static ssize_t ep93xx_pwm_get_freq(struct device *dev, 128 struct device_attribute *attr, char *buf) 129{ 130 struct platform_device *pdev = to_platform_device(dev); 131 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 132 133 if (ep93xx_pwm_is_enabled(pwm)) { 134 unsigned long rate = clk_get_rate(pwm->clk); 135 u16 term = ep93xx_pwm_read_tc(pwm); 136 137 return sprintf(buf, "%ld\n", rate / (term + 1)); 138 } else { 139 return sprintf(buf, "disabled\n"); 140 } 141} 142 143static ssize_t ep93xx_pwm_set_freq(struct device *dev, 144 struct device_attribute *attr, const char *buf, size_t count) 145{ 146 struct platform_device *pdev = to_platform_device(dev); 147 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 148 long val; 149 int err; 150 151 err = strict_strtol(buf, 10, &val); 152 if (err) 153 return -EINVAL; 154 155 if (val == 0) { 156 ep93xx_pwm_disable(pwm); 157 } else if (val <= (clk_get_rate(pwm->clk) / 2)) { 158 u32 term, duty; 159 160 val = (clk_get_rate(pwm->clk) / val) - 1; 161 if (val > EP93XX_PWM_MAX_COUNT) 162 val = EP93XX_PWM_MAX_COUNT; 163 if (val < 1) 164 val = 1; 165 166 term = ep93xx_pwm_read_tc(pwm); 167 duty = ((val + 1) * pwm->duty_percent / 100) - 1; 168 169 /* If pwm is running, order is important */ 170 if (val > term) { 171 ep93xx_pwm_write_tc(pwm, val); 172 ep93xx_pwm_write_dc(pwm, duty); 173 } else { 174 ep93xx_pwm_write_dc(pwm, duty); 175 ep93xx_pwm_write_tc(pwm, val); 176 } 177 178 if (!ep93xx_pwm_is_enabled(pwm)) 179 ep93xx_pwm_enable(pwm); 180 } else { 181 return -EINVAL; 182 } 183 184 return count; 185} 186 187static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, 188 struct device_attribute *attr, char *buf) 189{ 190 struct platform_device *pdev = to_platform_device(dev); 191 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 192 193 return sprintf(buf, "%d\n", pwm->duty_percent); 194} 195 196static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, 197 struct device_attribute *attr, const char *buf, size_t count) 198{ 199 struct platform_device *pdev = to_platform_device(dev); 200 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 201 long val; 202 int err; 203 204 err = strict_strtol(buf, 10, &val); 205 if (err) 206 return -EINVAL; 207 208 if (val > 0 && val < 100) { 209 u32 term = ep93xx_pwm_read_tc(pwm); 210 ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1); 211 pwm->duty_percent = val; 212 return count; 213 } 214 215 return -EINVAL; 216} 217 218static ssize_t ep93xx_pwm_get_invert(struct device *dev, 219 struct device_attribute *attr, char *buf) 220{ 221 struct platform_device *pdev = to_platform_device(dev); 222 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 223 224 return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm)); 225} 226 227static ssize_t ep93xx_pwm_set_invert(struct device *dev, 228 struct device_attribute *attr, const char *buf, size_t count) 229{ 230 struct platform_device *pdev = to_platform_device(dev); 231 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 232 long val; 233 int err; 234 235 err = strict_strtol(buf, 10, &val); 236 if (err) 237 return -EINVAL; 238 239 if (val == 0) 240 ep93xx_pwm_normal(pwm); 241 else if (val == 1) 242 ep93xx_pwm_invert(pwm); 243 else 244 return -EINVAL; 245 246 return count; 247} 248 249static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); 250static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); 251static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, 252 ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); 253static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, 254 ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); 255static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, 256 ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); 257 258static struct attribute *ep93xx_pwm_attrs[] = { 259 &dev_attr_min_freq.attr, 260 &dev_attr_max_freq.attr, 261 &dev_attr_freq.attr, 262 &dev_attr_duty_percent.attr, 263 &dev_attr_invert.attr, 264 NULL 265}; 266 267static const struct attribute_group ep93xx_pwm_sysfs_files = { 268 .attrs = ep93xx_pwm_attrs, 269}; 270 271static int __init ep93xx_pwm_probe(struct platform_device *pdev) 272{ 273 struct ep93xx_pwm *pwm; 274 struct resource *res; 275 int err; 276 277 err = ep93xx_pwm_acquire_gpio(pdev); 278 if (err) 279 return err; 280 281 pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL); 282 if (!pwm) { 283 err = -ENOMEM; 284 goto fail_no_mem; 285 } 286 287 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 288 if (res == NULL) { 289 err = -ENXIO; 290 goto fail_no_mem_resource; 291 } 292 293 res = request_mem_region(res->start, resource_size(res), pdev->name); 294 if (res == NULL) { 295 err = -EBUSY; 296 goto fail_no_mem_resource; 297 } 298 299 pwm->mmio_base = ioremap(res->start, resource_size(res)); 300 if (pwm->mmio_base == NULL) { 301 err = -ENXIO; 302 goto fail_no_ioremap; 303 } 304 305 err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); 306 if (err) 307 goto fail_no_sysfs; 308 309 pwm->clk = clk_get(&pdev->dev, "pwm_clk"); 310 if (IS_ERR(pwm->clk)) { 311 err = PTR_ERR(pwm->clk); 312 goto fail_no_clk; 313 } 314 315 pwm->duty_percent = 50; 316 317 platform_set_drvdata(pdev, pwm); 318 319 /* disable pwm at startup. Avoids zero value. */ 320 ep93xx_pwm_disable(pwm); 321 ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT); 322 ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2); 323 324 clk_enable(pwm->clk); 325 326 return 0; 327 328fail_no_clk: 329 sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); 330fail_no_sysfs: 331 iounmap(pwm->mmio_base); 332fail_no_ioremap: 333 release_mem_region(res->start, resource_size(res)); 334fail_no_mem_resource: 335 kfree(pwm); 336fail_no_mem: 337 ep93xx_pwm_release_gpio(pdev); 338 return err; 339} 340 341static int __exit ep93xx_pwm_remove(struct platform_device *pdev) 342{ 343 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 344 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 345 346 ep93xx_pwm_disable(pwm); 347 clk_disable(pwm->clk); 348 clk_put(pwm->clk); 349 platform_set_drvdata(pdev, NULL); 350 sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); 351 iounmap(pwm->mmio_base); 352 release_mem_region(res->start, resource_size(res)); 353 kfree(pwm); 354 ep93xx_pwm_release_gpio(pdev); 355 356 return 0; 357} 358 359static struct platform_driver ep93xx_pwm_driver = { 360 .driver = { 361 .name = "ep93xx-pwm", 362 .owner = THIS_MODULE, 363 }, 364 .remove = __exit_p(ep93xx_pwm_remove), 365}; 366 367static int __init ep93xx_pwm_init(void) 368{ 369 return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); 370} 371 372static void __exit ep93xx_pwm_exit(void) 373{ 374 platform_driver_unregister(&ep93xx_pwm_driver); 375} 376 377module_init(ep93xx_pwm_init); 378module_exit(ep93xx_pwm_exit); 379 380MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " 381 "H Hartley Sweeten <hsweeten@visionengravers.com>"); 382MODULE_DESCRIPTION("EP93xx PWM driver"); 383MODULE_LICENSE("GPL"); 384MODULE_ALIAS("platform:ep93xx-pwm");