Root/
1 | /* |
2 | * linux/arch/unicore32/kernel/pwm.c |
3 | * |
4 | * Code specific to PKUnity SoC and UniCore ISA |
5 | * |
6 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> |
7 | * Copyright (C) 2001-2010 Guan Xuetao |
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 version 2 as |
11 | * published by the Free Software Foundation. |
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 | #include <mach/hardware.h> |
25 | |
26 | struct puv3_pwm_chip { |
27 | struct pwm_chip chip; |
28 | void __iomem *base; |
29 | struct clk *clk; |
30 | bool enabled; |
31 | }; |
32 | |
33 | static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip) |
34 | { |
35 | return container_of(chip, struct puv3_pwm_chip, chip); |
36 | } |
37 | |
38 | /* |
39 | * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE |
40 | * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE |
41 | */ |
42 | static int puv3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
43 | int duty_ns, int period_ns) |
44 | { |
45 | unsigned long period_cycles, prescale, pv, dc; |
46 | struct puv3_pwm_chip *puv3 = to_puv3(chip); |
47 | unsigned long long c; |
48 | |
49 | c = clk_get_rate(puv3->clk); |
50 | c = c * period_ns; |
51 | do_div(c, 1000000000); |
52 | period_cycles = c; |
53 | |
54 | if (period_cycles < 1) |
55 | period_cycles = 1; |
56 | |
57 | prescale = (period_cycles - 1) / 1024; |
58 | pv = period_cycles / (prescale + 1) - 1; |
59 | |
60 | if (prescale > 63) |
61 | return -EINVAL; |
62 | |
63 | if (duty_ns == period_ns) |
64 | dc = OST_PWMDCCR_FDCYCLE; |
65 | else |
66 | dc = (pv + 1) * duty_ns / period_ns; |
67 | |
68 | /* |
69 | * NOTE: the clock to PWM has to be enabled first |
70 | * before writing to the registers |
71 | */ |
72 | clk_prepare_enable(puv3->clk); |
73 | |
74 | writel(prescale, puv3->base + OST_PWM_PWCR); |
75 | writel(pv - dc, puv3->base + OST_PWM_DCCR); |
76 | writel(pv, puv3->base + OST_PWM_PCR); |
77 | |
78 | clk_disable_unprepare(puv3->clk); |
79 | |
80 | return 0; |
81 | } |
82 | |
83 | static int puv3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
84 | { |
85 | struct puv3_pwm_chip *puv3 = to_puv3(chip); |
86 | |
87 | return clk_prepare_enable(puv3->clk); |
88 | } |
89 | |
90 | static void puv3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
91 | { |
92 | struct puv3_pwm_chip *puv3 = to_puv3(chip); |
93 | |
94 | clk_disable_unprepare(puv3->clk); |
95 | } |
96 | |
97 | static const struct pwm_ops puv3_pwm_ops = { |
98 | .config = puv3_pwm_config, |
99 | .enable = puv3_pwm_enable, |
100 | .disable = puv3_pwm_disable, |
101 | .owner = THIS_MODULE, |
102 | }; |
103 | |
104 | static int pwm_probe(struct platform_device *pdev) |
105 | { |
106 | struct puv3_pwm_chip *puv3; |
107 | struct resource *r; |
108 | int ret; |
109 | |
110 | puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); |
111 | if (puv3 == NULL) { |
112 | dev_err(&pdev->dev, "failed to allocate memory\n"); |
113 | return -ENOMEM; |
114 | } |
115 | |
116 | puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); |
117 | if (IS_ERR(puv3->clk)) |
118 | return PTR_ERR(puv3->clk); |
119 | |
120 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
121 | if (r == NULL) { |
122 | dev_err(&pdev->dev, "no memory resource defined\n"); |
123 | return -ENODEV; |
124 | } |
125 | |
126 | puv3->base = devm_ioremap_resource(&pdev->dev, r); |
127 | if (IS_ERR(puv3->base)) |
128 | return PTR_ERR(puv3->base); |
129 | |
130 | puv3->chip.dev = &pdev->dev; |
131 | puv3->chip.ops = &puv3_pwm_ops; |
132 | puv3->chip.base = -1; |
133 | puv3->chip.npwm = 1; |
134 | |
135 | ret = pwmchip_add(&puv3->chip); |
136 | if (ret < 0) { |
137 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); |
138 | return ret; |
139 | } |
140 | |
141 | platform_set_drvdata(pdev, puv3); |
142 | return 0; |
143 | } |
144 | |
145 | static int pwm_remove(struct platform_device *pdev) |
146 | { |
147 | struct puv3_pwm_chip *puv3 = platform_get_drvdata(pdev); |
148 | |
149 | return pwmchip_remove(&puv3->chip); |
150 | } |
151 | |
152 | static struct platform_driver puv3_pwm_driver = { |
153 | .driver = { |
154 | .name = "PKUnity-v3-PWM", |
155 | }, |
156 | .probe = pwm_probe, |
157 | .remove = pwm_remove, |
158 | }; |
159 | module_platform_driver(puv3_pwm_driver); |
160 | |
161 | MODULE_LICENSE("GPL v2"); |
162 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9