1 /* drivers/pwm/pwm-samsung.c 2 * 3 * Copyright (c) 2007 Ben Dooks 4 * Copyright (c) 2008 Simtec Electronics 5 * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> 6 * 7 * S3C series PWM device core 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License. 12 */ 13 14 #define pr_fmt(fmt) "pwm-samsung: " fmt 15 16 #include <linux/export.h> 17 #include <linux/kernel.h> 18 #include <linux/platform_device.h> 19 #include <linux/slab.h> 20 #include <linux/err.h> 21 #include <linux/clk.h> 22 #include <linux/io.h> 23 #include <linux/pwm.h> 24 25 #include <mach/map.h> 26 27 #include <plat/regs-timer.h> 28 29 struct s3c_chip { 30 struct platform_device *pdev; 31 32 struct clk *clk_div; 33 struct clk *clk; 34 const char *label; 35 36 unsigned int period_ns; 37 unsigned int duty_ns; 38 39 unsigned char tcon_base; 40 unsigned char pwm_id; 41 struct pwm_chip chip; 42 }; 43 44 #define to_s3c_chip(chip) container_of(chip, struct s3c_chip, chip) 45 46 #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) 47 48 static struct clk *clk_scaler[2]; 49 50 static inline int pwm_is_tdiv(struct s3c_chip *chip) 51 { 52 return clk_get_parent(chip->clk) == chip->clk_div; 53 } 54 55 #define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) 56 #define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) 57 #define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) 58 #define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) 59 60 static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 61 { 62 struct s3c_chip *s3c = to_s3c_chip(chip); 63 unsigned long flags; 64 unsigned long tcon; 65 66 local_irq_save(flags); 67 68 tcon = __raw_readl(S3C2410_TCON); 69 tcon |= pwm_tcon_start(s3c); 70 __raw_writel(tcon, S3C2410_TCON); 71 72 local_irq_restore(flags); 73 74 return 0; 75 } 76 77 static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 78 { 79 struct s3c_chip *s3c = to_s3c_chip(chip); 80 unsigned long flags; 81 unsigned long tcon; 82 83 local_irq_save(flags); 84 85 tcon = __raw_readl(S3C2410_TCON); 86 tcon &= ~pwm_tcon_start(s3c); 87 __raw_writel(tcon, S3C2410_TCON); 88 89 local_irq_restore(flags); 90 } 91 92 static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq) 93 { 94 unsigned long tin_parent_rate; 95 unsigned int div; 96 97 tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div)); 98 pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate); 99 100 for (div = 2; div <= 16; div *= 2) { 101 if ((tin_parent_rate / (div << 16)) < freq) 102 return tin_parent_rate / div; 103 } 104 105 return tin_parent_rate / 16; 106 } 107 108 #define NS_IN_HZ (1000000000UL) 109 110 static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 111 int duty_ns, int period_ns) 112 { 113 struct s3c_chip *s3c = to_s3c_chip(chip); 114 unsigned long tin_rate; 115 unsigned long tin_ns; 116 unsigned long period; 117 unsigned long flags; 118 unsigned long tcon; 119 unsigned long tcnt; 120 long tcmp; 121 122 /* We currently avoid using 64bit arithmetic by using the 123 * fact that anything faster than 1Hz is easily representable 124 * by 32bits. */ 125 126 if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) 127 return -ERANGE; 128 129 if (duty_ns > period_ns) 130 return -EINVAL; 131 132 if (period_ns == s3c->period_ns && 133 duty_ns == s3c->duty_ns) 134 return 0; 135 136 /* The TCMP and TCNT can be read without a lock, they're not 137 * shared between the timers. */ 138 139 tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id)); 140 tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id)); 141 142 period = NS_IN_HZ / period_ns; 143 144 pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n", 145 duty_ns, period_ns, period); 146 147 /* Check to see if we are changing the clock rate of the PWM */ 148 149 if (s3c->period_ns != period_ns) { 150 if (pwm_is_tdiv(s3c)) { 151 tin_rate = pwm_calc_tin(s3c, period); 152 clk_set_rate(s3c->clk_div, tin_rate); 153 } else 154 tin_rate = clk_get_rate(s3c->clk); 155 156 s3c->period_ns = period_ns; 157 158 pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate); 159 160 tin_ns = NS_IN_HZ / tin_rate; 161 tcnt = period_ns / tin_ns; 162 } else 163 tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk); 164 165 /* Note, counters count down */ 166 167 tcmp = duty_ns / tin_ns; 168 tcmp = tcnt - tcmp; 169 /* the pwm hw only checks the compare register after a decrement, 170 so the pin never toggles if tcmp = tcnt */ 171 if (tcmp == tcnt) 172 tcmp--; 173 174 pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); 175 176 if (tcmp < 0) 177 tcmp = 0; 178 179 /* Update the PWM register block. */ 180 181 local_irq_save(flags); 182 183 __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id)); 184 __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id)); 185 186 tcon = __raw_readl(S3C2410_TCON); 187 tcon |= pwm_tcon_manulupdate(s3c); 188 tcon |= pwm_tcon_autoreload(s3c); 189 __raw_writel(tcon, S3C2410_TCON); 190 191 tcon &= ~pwm_tcon_manulupdate(s3c); 192 __raw_writel(tcon, S3C2410_TCON); 193 194 local_irq_restore(flags); 195 196 return 0; 197 } 198 199 static struct pwm_ops s3c_pwm_ops = { 200 .enable = s3c_pwm_enable, 201 .disable = s3c_pwm_disable, 202 .config = s3c_pwm_config, 203 .owner = THIS_MODULE, 204 }; 205 206 static int s3c_pwm_probe(struct platform_device *pdev) 207 { 208 struct device *dev = &pdev->dev; 209 struct s3c_chip *s3c; 210 unsigned long flags; 211 unsigned long tcon; 212 unsigned int id = pdev->id; 213 int ret; 214 215 if (id == 4) { 216 dev_err(dev, "TIMER4 is currently not supported\n"); 217 return -ENXIO; 218 } 219 220 s3c = devm_kzalloc(&pdev->dev, sizeof(*s3c), GFP_KERNEL); 221 if (s3c == NULL) { 222 dev_err(dev, "failed to allocate pwm_device\n"); 223 return -ENOMEM; 224 } 225 226 /* calculate base of control bits in TCON */ 227 s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; 228 s3c->chip.dev = &pdev->dev; 229 s3c->chip.ops = &s3c_pwm_ops; 230 s3c->chip.base = -1; 231 s3c->chip.npwm = 1; 232 233 s3c->clk = devm_clk_get(dev, "pwm-tin"); 234 if (IS_ERR(s3c->clk)) { 235 dev_err(dev, "failed to get pwm tin clk\n"); 236 return PTR_ERR(s3c->clk); 237 } 238 239 s3c->clk_div = devm_clk_get(dev, "pwm-tdiv"); 240 if (IS_ERR(s3c->clk_div)) { 241 dev_err(dev, "failed to get pwm tdiv clk\n"); 242 return PTR_ERR(s3c->clk_div); 243 } 244 245 clk_enable(s3c->clk); 246 clk_enable(s3c->clk_div); 247 248 local_irq_save(flags); 249 250 tcon = __raw_readl(S3C2410_TCON); 251 tcon |= pwm_tcon_invert(s3c); 252 __raw_writel(tcon, S3C2410_TCON); 253 254 local_irq_restore(flags); 255 256 ret = pwmchip_add(&s3c->chip); 257 if (ret < 0) { 258 dev_err(dev, "failed to register pwm\n"); 259 goto err_clk_tdiv; 260 } 261 262 pwm_dbg(s3c, "config bits %02x\n", 263 (__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f); 264 265 dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", 266 clk_get_rate(s3c->clk), 267 clk_get_rate(s3c->clk_div), 268 pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base); 269 270 platform_set_drvdata(pdev, s3c); 271 return 0; 272 273 err_clk_tdiv: 274 clk_disable(s3c->clk_div); 275 clk_disable(s3c->clk); 276 return ret; 277 } 278 279 static int __devexit s3c_pwm_remove(struct platform_device *pdev) 280 { 281 struct s3c_chip *s3c = platform_get_drvdata(pdev); 282 int err; 283 284 err = pwmchip_remove(&s3c->chip); 285 if (err < 0) 286 return err; 287 288 clk_disable(s3c->clk_div); 289 clk_disable(s3c->clk); 290 291 return 0; 292 } 293 294 #ifdef CONFIG_PM 295 static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) 296 { 297 struct s3c_chip *s3c = platform_get_drvdata(pdev); 298 299 /* No one preserve these values during suspend so reset them 300 * Otherwise driver leaves PWM unconfigured if same values 301 * passed to pwm_config 302 */ 303 s3c->period_ns = 0; 304 s3c->duty_ns = 0; 305 306 return 0; 307 } 308 309 static int s3c_pwm_resume(struct platform_device *pdev) 310 { 311 struct s3c_chip *s3c = platform_get_drvdata(pdev); 312 unsigned long tcon; 313 314 /* Restore invertion */ 315 tcon = __raw_readl(S3C2410_TCON); 316 tcon |= pwm_tcon_invert(s3c); 317 __raw_writel(tcon, S3C2410_TCON); 318 319 return 0; 320 } 321 322 #else 323 #define s3c_pwm_suspend NULL 324 #define s3c_pwm_resume NULL 325 #endif 326 327 static struct platform_driver s3c_pwm_driver = { 328 .driver = { 329 .name = "s3c24xx-pwm", 330 .owner = THIS_MODULE, 331 }, 332 .probe = s3c_pwm_probe, 333 .remove = __devexit_p(s3c_pwm_remove), 334 .suspend = s3c_pwm_suspend, 335 .resume = s3c_pwm_resume, 336 }; 337 338 static int __init pwm_init(void) 339 { 340 int ret; 341 342 clk_scaler[0] = clk_get(NULL, "pwm-scaler0"); 343 clk_scaler[1] = clk_get(NULL, "pwm-scaler1"); 344 345 if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) { 346 pr_err("failed to get scaler clocks\n"); 347 return -EINVAL; 348 } 349 350 ret = platform_driver_register(&s3c_pwm_driver); 351 if (ret) 352 pr_err("failed to add pwm driver\n"); 353 354 return ret; 355 } 356 357 arch_initcall(pwm_init); 358