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 v3.11 286 lines 7.9 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/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 42/* 43 * /sys/devices/platform/ep93xx-pwm.N 44 * /min_freq read-only minimum pwm output frequency 45 * /max_req read-only maximum pwm output frequency 46 * /freq read-write pwm output frequency (0 = disable output) 47 * /duty_percent read-write pwm duty cycle percent (1..99) 48 * /invert read-write invert pwm output 49 */ 50 51static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, 52 struct device_attribute *attr, char *buf) 53{ 54 struct platform_device *pdev = to_platform_device(dev); 55 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 56 unsigned long rate = clk_get_rate(pwm->clk); 57 58 return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); 59} 60 61static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, 62 struct device_attribute *attr, char *buf) 63{ 64 struct platform_device *pdev = to_platform_device(dev); 65 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 66 unsigned long rate = clk_get_rate(pwm->clk); 67 68 return sprintf(buf, "%ld\n", rate / 2); 69} 70 71static ssize_t ep93xx_pwm_get_freq(struct device *dev, 72 struct device_attribute *attr, char *buf) 73{ 74 struct platform_device *pdev = to_platform_device(dev); 75 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 76 77 if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) { 78 unsigned long rate = clk_get_rate(pwm->clk); 79 u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 80 81 return sprintf(buf, "%ld\n", rate / (term + 1)); 82 } else { 83 return sprintf(buf, "disabled\n"); 84 } 85} 86 87static ssize_t ep93xx_pwm_set_freq(struct device *dev, 88 struct device_attribute *attr, const char *buf, size_t count) 89{ 90 struct platform_device *pdev = to_platform_device(dev); 91 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 92 long val; 93 int err; 94 95 err = kstrtol(buf, 10, &val); 96 if (err) 97 return -EINVAL; 98 99 if (val == 0) { 100 writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); 101 } else if (val <= (clk_get_rate(pwm->clk) / 2)) { 102 u32 term, duty; 103 104 val = (clk_get_rate(pwm->clk) / val) - 1; 105 if (val > EP93XX_PWM_MAX_COUNT) 106 val = EP93XX_PWM_MAX_COUNT; 107 if (val < 1) 108 val = 1; 109 110 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 111 duty = ((val + 1) * pwm->duty_percent / 100) - 1; 112 113 /* If pwm is running, order is important */ 114 if (val > term) { 115 writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 116 writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); 117 } else { 118 writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); 119 writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 120 } 121 122 if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) 123 writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); 124 } else { 125 return -EINVAL; 126 } 127 128 return count; 129} 130 131static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, 132 struct device_attribute *attr, char *buf) 133{ 134 struct platform_device *pdev = to_platform_device(dev); 135 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 136 137 return sprintf(buf, "%d\n", pwm->duty_percent); 138} 139 140static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, 141 struct device_attribute *attr, const char *buf, size_t count) 142{ 143 struct platform_device *pdev = to_platform_device(dev); 144 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 145 long val; 146 int err; 147 148 err = kstrtol(buf, 10, &val); 149 if (err) 150 return -EINVAL; 151 152 if (val > 0 && val < 100) { 153 u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 154 u32 duty = ((term + 1) * val / 100) - 1; 155 156 writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); 157 pwm->duty_percent = val; 158 return count; 159 } 160 161 return -EINVAL; 162} 163 164static ssize_t ep93xx_pwm_get_invert(struct device *dev, 165 struct device_attribute *attr, char *buf) 166{ 167 struct platform_device *pdev = to_platform_device(dev); 168 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 169 int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; 170 171 return sprintf(buf, "%d\n", inverted); 172} 173 174static ssize_t ep93xx_pwm_set_invert(struct device *dev, 175 struct device_attribute *attr, const char *buf, size_t count) 176{ 177 struct platform_device *pdev = to_platform_device(dev); 178 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 179 long val; 180 int err; 181 182 err = kstrtol(buf, 10, &val); 183 if (err) 184 return -EINVAL; 185 186 if (val == 0) 187 writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); 188 else if (val == 1) 189 writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); 190 else 191 return -EINVAL; 192 193 return count; 194} 195 196static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); 197static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); 198static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, 199 ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); 200static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, 201 ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); 202static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, 203 ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); 204 205static struct attribute *ep93xx_pwm_attrs[] = { 206 &dev_attr_min_freq.attr, 207 &dev_attr_max_freq.attr, 208 &dev_attr_freq.attr, 209 &dev_attr_duty_percent.attr, 210 &dev_attr_invert.attr, 211 NULL 212}; 213 214static const struct attribute_group ep93xx_pwm_sysfs_files = { 215 .attrs = ep93xx_pwm_attrs, 216}; 217 218static int ep93xx_pwm_probe(struct platform_device *pdev) 219{ 220 struct ep93xx_pwm *pwm; 221 struct resource *res; 222 int ret; 223 224 pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); 225 if (!pwm) 226 return -ENOMEM; 227 228 pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); 229 if (IS_ERR(pwm->clk)) 230 return PTR_ERR(pwm->clk); 231 232 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 233 pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res); 234 if (IS_ERR(pwm->mmio_base)) 235 return PTR_ERR(pwm->mmio_base); 236 237 ret = ep93xx_pwm_acquire_gpio(pdev); 238 if (ret) 239 return ret; 240 241 ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); 242 if (ret) { 243 ep93xx_pwm_release_gpio(pdev); 244 return ret; 245 } 246 247 pwm->duty_percent = 50; 248 249 /* disable pwm at startup. Avoids zero value. */ 250 writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); 251 writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); 252 writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); 253 254 clk_enable(pwm->clk); 255 256 platform_set_drvdata(pdev, pwm); 257 return 0; 258} 259 260static int ep93xx_pwm_remove(struct platform_device *pdev) 261{ 262 struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); 263 264 writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); 265 clk_disable(pwm->clk); 266 sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); 267 ep93xx_pwm_release_gpio(pdev); 268 269 return 0; 270} 271 272static struct platform_driver ep93xx_pwm_driver = { 273 .driver = { 274 .name = "ep93xx-pwm", 275 .owner = THIS_MODULE, 276 }, 277 .probe = ep93xx_pwm_probe, 278 .remove = ep93xx_pwm_remove, 279}; 280module_platform_driver(ep93xx_pwm_driver); 281 282MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " 283 "H Hartley Sweeten <hsweeten@visionengravers.com>"); 284MODULE_DESCRIPTION("EP93xx PWM driver"); 285MODULE_LICENSE("GPL"); 286MODULE_ALIAS("platform:ep93xx-pwm");