diff options
author | Caesar Wang <caesar.wang@rock-chips.com> | 2014-08-08 03:28:49 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2014-08-08 07:12:47 -0400 |
commit | f6306299080bbb1a77ad39494203f5397a5c2630 (patch) | |
tree | 11248806da1f934b3fdfea64ebae35fd7963393c /drivers/pwm | |
parent | 695d49d941387baa3b2f3f412845a25918e030d5 (diff) |
pwm: rockchip: Added to support for RK3288 SoC
This patch added to support the PWM controller found on
RK3288 SoC.
Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/pwm-rockchip.c | 135 |
1 files changed, 111 insertions, 24 deletions
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index eec214568193..bdd8644c01cf 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * PWM driver for Rockchip SoCs | 2 | * PWM driver for Rockchip SoCs |
3 | * | 3 | * |
4 | * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> | 4 | * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> |
5 | * Copyright (C) 2014 ROCKCHIP, Inc. | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
@@ -12,30 +13,81 @@ | |||
12 | #include <linux/io.h> | 13 | #include <linux/io.h> |
13 | #include <linux/module.h> | 14 | #include <linux/module.h> |
14 | #include <linux/of.h> | 15 | #include <linux/of.h> |
16 | #include <linux/of_device.h> | ||
15 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
16 | #include <linux/pwm.h> | 18 | #include <linux/pwm.h> |
17 | #include <linux/time.h> | 19 | #include <linux/time.h> |
18 | 20 | ||
19 | #define PWM_CNTR 0x00 /* Counter register */ | ||
20 | #define PWM_HRC 0x04 /* High reference register */ | ||
21 | #define PWM_LRC 0x08 /* Low reference register */ | ||
22 | #define PWM_CTRL 0x0c /* Control register */ | ||
23 | #define PWM_CTRL_TIMER_EN (1 << 0) | 21 | #define PWM_CTRL_TIMER_EN (1 << 0) |
24 | #define PWM_CTRL_OUTPUT_EN (1 << 3) | 22 | #define PWM_CTRL_OUTPUT_EN (1 << 3) |
25 | 23 | ||
26 | #define PRESCALER 2 | 24 | #define PWM_ENABLE (1 << 0) |
25 | #define PWM_CONTINUOUS (1 << 1) | ||
26 | #define PWM_DUTY_POSITIVE (1 << 3) | ||
27 | #define PWM_INACTIVE_NEGATIVE (0 << 4) | ||
28 | #define PWM_OUTPUT_LEFT (0 << 5) | ||
29 | #define PWM_LP_DISABLE (0 << 8) | ||
27 | 30 | ||
28 | struct rockchip_pwm_chip { | 31 | struct rockchip_pwm_chip { |
29 | struct pwm_chip chip; | 32 | struct pwm_chip chip; |
30 | struct clk *clk; | 33 | struct clk *clk; |
34 | const struct rockchip_pwm_data *data; | ||
31 | void __iomem *base; | 35 | void __iomem *base; |
32 | }; | 36 | }; |
33 | 37 | ||
38 | struct rockchip_pwm_regs { | ||
39 | unsigned long duty; | ||
40 | unsigned long period; | ||
41 | unsigned long cntr; | ||
42 | unsigned long ctrl; | ||
43 | }; | ||
44 | |||
45 | struct rockchip_pwm_data { | ||
46 | struct rockchip_pwm_regs regs; | ||
47 | unsigned int prescaler; | ||
48 | |||
49 | void (*set_enable)(struct pwm_chip *chip, bool enable); | ||
50 | }; | ||
51 | |||
34 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | 52 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) |
35 | { | 53 | { |
36 | return container_of(c, struct rockchip_pwm_chip, chip); | 54 | return container_of(c, struct rockchip_pwm_chip, chip); |
37 | } | 55 | } |
38 | 56 | ||
57 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | ||
58 | { | ||
59 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
60 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; | ||
61 | u32 val; | ||
62 | |||
63 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | ||
64 | |||
65 | if (enable) | ||
66 | val |= enable_conf; | ||
67 | else | ||
68 | val &= ~enable_conf; | ||
69 | |||
70 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | ||
71 | } | ||
72 | |||
73 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) | ||
74 | { | ||
75 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
76 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | | ||
77 | PWM_CONTINUOUS | PWM_DUTY_POSITIVE | | ||
78 | PWM_INACTIVE_NEGATIVE; | ||
79 | u32 val; | ||
80 | |||
81 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | ||
82 | |||
83 | if (enable) | ||
84 | val |= enable_conf; | ||
85 | else | ||
86 | val &= ~enable_conf; | ||
87 | |||
88 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | ||
89 | } | ||
90 | |||
39 | static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | 91 | static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
40 | int duty_ns, int period_ns) | 92 | int duty_ns, int period_ns) |
41 | { | 93 | { |
@@ -52,20 +104,20 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
52 | * default prescaler value for all practical clock rate values. | 104 | * default prescaler value for all practical clock rate values. |
53 | */ | 105 | */ |
54 | div = clk_rate * period_ns; | 106 | div = clk_rate * period_ns; |
55 | do_div(div, PRESCALER * NSEC_PER_SEC); | 107 | do_div(div, pc->data->prescaler * NSEC_PER_SEC); |
56 | period = div; | 108 | period = div; |
57 | 109 | ||
58 | div = clk_rate * duty_ns; | 110 | div = clk_rate * duty_ns; |
59 | do_div(div, PRESCALER * NSEC_PER_SEC); | 111 | do_div(div, pc->data->prescaler * NSEC_PER_SEC); |
60 | duty = div; | 112 | duty = div; |
61 | 113 | ||
62 | ret = clk_enable(pc->clk); | 114 | ret = clk_enable(pc->clk); |
63 | if (ret) | 115 | if (ret) |
64 | return ret; | 116 | return ret; |
65 | 117 | ||
66 | writel(period, pc->base + PWM_LRC); | 118 | writel(period, pc->base + pc->data->regs.period); |
67 | writel(duty, pc->base + PWM_HRC); | 119 | writel(duty, pc->base + pc->data->regs.duty); |
68 | writel(0, pc->base + PWM_CNTR); | 120 | writel(0, pc->base + pc->data->regs.cntr); |
69 | 121 | ||
70 | clk_disable(pc->clk); | 122 | clk_disable(pc->clk); |
71 | 123 | ||
@@ -76,15 +128,12 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
76 | { | 128 | { |
77 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 129 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
78 | int ret; | 130 | int ret; |
79 | u32 val; | ||
80 | 131 | ||
81 | ret = clk_enable(pc->clk); | 132 | ret = clk_enable(pc->clk); |
82 | if (ret) | 133 | if (ret) |
83 | return ret; | 134 | return ret; |
84 | 135 | ||
85 | val = readl_relaxed(pc->base + PWM_CTRL); | 136 | pc->data->set_enable(chip, true); |
86 | val |= PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; | ||
87 | writel_relaxed(val, pc->base + PWM_CTRL); | ||
88 | 137 | ||
89 | return 0; | 138 | return 0; |
90 | } | 139 | } |
@@ -92,11 +141,8 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
92 | static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | 141 | static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
93 | { | 142 | { |
94 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 143 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
95 | u32 val; | ||
96 | 144 | ||
97 | val = readl_relaxed(pc->base + PWM_CTRL); | 145 | pc->data->set_enable(chip, false); |
98 | val &= ~(PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN); | ||
99 | writel_relaxed(val, pc->base + PWM_CTRL); | ||
100 | 146 | ||
101 | clk_disable(pc->clk); | 147 | clk_disable(pc->clk); |
102 | } | 148 | } |
@@ -108,12 +154,58 @@ static const struct pwm_ops rockchip_pwm_ops = { | |||
108 | .owner = THIS_MODULE, | 154 | .owner = THIS_MODULE, |
109 | }; | 155 | }; |
110 | 156 | ||
157 | static const struct rockchip_pwm_data pwm_data_v1 = { | ||
158 | .regs = { | ||
159 | .duty = 0x04, | ||
160 | .period = 0x08, | ||
161 | .cntr = 0x00, | ||
162 | .ctrl = 0x0c, | ||
163 | }, | ||
164 | .prescaler = 2, | ||
165 | .set_enable = rockchip_pwm_set_enable_v1, | ||
166 | }; | ||
167 | |||
168 | static const struct rockchip_pwm_data pwm_data_v2 = { | ||
169 | .regs = { | ||
170 | .duty = 0x08, | ||
171 | .period = 0x04, | ||
172 | .cntr = 0x00, | ||
173 | .ctrl = 0x0c, | ||
174 | }, | ||
175 | .prescaler = 1, | ||
176 | .set_enable = rockchip_pwm_set_enable_v2, | ||
177 | }; | ||
178 | |||
179 | static const struct rockchip_pwm_data pwm_data_vop = { | ||
180 | .regs = { | ||
181 | .duty = 0x08, | ||
182 | .period = 0x04, | ||
183 | .cntr = 0x0c, | ||
184 | .ctrl = 0x00, | ||
185 | }, | ||
186 | .prescaler = 1, | ||
187 | .set_enable = rockchip_pwm_set_enable_v2, | ||
188 | }; | ||
189 | |||
190 | static const struct of_device_id rockchip_pwm_dt_ids[] = { | ||
191 | { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, | ||
192 | { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, | ||
193 | { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, | ||
194 | { /* sentinel */ } | ||
195 | }; | ||
196 | MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); | ||
197 | |||
111 | static int rockchip_pwm_probe(struct platform_device *pdev) | 198 | static int rockchip_pwm_probe(struct platform_device *pdev) |
112 | { | 199 | { |
200 | const struct of_device_id *id; | ||
113 | struct rockchip_pwm_chip *pc; | 201 | struct rockchip_pwm_chip *pc; |
114 | struct resource *r; | 202 | struct resource *r; |
115 | int ret; | 203 | int ret; |
116 | 204 | ||
205 | id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); | ||
206 | if (!id) | ||
207 | return -EINVAL; | ||
208 | |||
117 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | 209 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); |
118 | if (!pc) | 210 | if (!pc) |
119 | return -ENOMEM; | 211 | return -ENOMEM; |
@@ -133,6 +225,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) | |||
133 | 225 | ||
134 | platform_set_drvdata(pdev, pc); | 226 | platform_set_drvdata(pdev, pc); |
135 | 227 | ||
228 | pc->data = id->data; | ||
136 | pc->chip.dev = &pdev->dev; | 229 | pc->chip.dev = &pdev->dev; |
137 | pc->chip.ops = &rockchip_pwm_ops; | 230 | pc->chip.ops = &rockchip_pwm_ops; |
138 | pc->chip.base = -1; | 231 | pc->chip.base = -1; |
@@ -156,12 +249,6 @@ static int rockchip_pwm_remove(struct platform_device *pdev) | |||
156 | return pwmchip_remove(&pc->chip); | 249 | return pwmchip_remove(&pc->chip); |
157 | } | 250 | } |
158 | 251 | ||
159 | static const struct of_device_id rockchip_pwm_dt_ids[] = { | ||
160 | { .compatible = "rockchip,rk2928-pwm" }, | ||
161 | { /* sentinel */ } | ||
162 | }; | ||
163 | MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); | ||
164 | |||
165 | static struct platform_driver rockchip_pwm_driver = { | 252 | static struct platform_driver rockchip_pwm_driver = { |
166 | .driver = { | 253 | .driver = { |
167 | .name = "rockchip-pwm", | 254 | .name = "rockchip-pwm", |