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