aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pwm/Kconfig12
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-lpc18xx-sct.c465
3 files changed, 478 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index b1541f40fd8d..ac8b0e968b6b 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -173,6 +173,18 @@ config PWM_LP3943
173 To compile this driver as a module, choose M here: the module 173 To compile this driver as a module, choose M here: the module
174 will be called pwm-lp3943. 174 will be called pwm-lp3943.
175 175
176config PWM_LPC18XX_SCT
177 tristate "LPC18xx/43xx PWM/SCT support"
178 depends on ARCH_LPC18XX
179 help
180 Generic PWM framework driver for NXP LPC18xx PWM/SCT which
181 supports 16 channels.
182 A maximum of 15 channels can be requested simultaneously and
183 must have the same period.
184
185 To compile this driver as a module, choose M here: the module
186 will be called pwm-lpc18xx-sct.
187
176config PWM_LPC32XX 188config PWM_LPC32XX
177 tristate "LPC32XX PWM support" 189 tristate "LPC32XX PWM support"
178 depends on ARCH_LPC32XX 190 depends on ARCH_LPC32XX
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index ec50eb5b5a8f..cd9abef2d5b1 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PWM_IMG) += pwm-img.o
14obj-$(CONFIG_PWM_IMX) += pwm-imx.o 14obj-$(CONFIG_PWM_IMX) += pwm-imx.o
15obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o 15obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
16obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o 16obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
17obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
17obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o 18obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
18obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o 19obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
19obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o 20obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
new file mode 100644
index 000000000000..9163085101bc
--- /dev/null
+++ b/drivers/pwm/pwm-lpc18xx-sct.c
@@ -0,0 +1,465 @@
1/*
2 * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver
3 *
4 * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License.
9 *
10 * Notes
11 * =====
12 * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured
13 * as a Pulse Width Modulator.
14 *
15 * SCT supports 16 outputs, 16 events and 16 registers. Each event will be
16 * triggered when its related register matches the SCT counter value, and it
17 * will set or clear a selected output.
18 *
19 * One of the events is preselected to generate the period, thus the maximum
20 * number of simultaneous channels is limited to 15. Notice that period is
21 * global to all the channels, thus PWM driver will refuse setting different
22 * values to it, unless there's only one channel requested.
23 */
24
25#include <linux/clk.h>
26#include <linux/err.h>
27#include <linux/io.h>
28#include <linux/module.h>
29#include <linux/platform_device.h>
30#include <linux/pwm.h>
31
32/* LPC18xx SCT registers */
33#define LPC18XX_PWM_CONFIG 0x000
34#define LPC18XX_PWM_CONFIG_UNIFY BIT(0)
35#define LPC18XX_PWM_CONFIG_NORELOAD BIT(7)
36
37#define LPC18XX_PWM_CTRL 0x004
38#define LPC18XX_PWM_CTRL_HALT BIT(2)
39#define LPC18XX_PWM_BIDIR BIT(4)
40#define LPC18XX_PWM_PRE_SHIFT 5
41#define LPC18XX_PWM_PRE_MASK (0xff << LPC18XX_PWM_PRE_SHIFT)
42#define LPC18XX_PWM_PRE(x) (x << LPC18XX_PWM_PRE_SHIFT)
43
44#define LPC18XX_PWM_LIMIT 0x008
45
46#define LPC18XX_PWM_RES_BASE 0x058
47#define LPC18XX_PWM_RES_SHIFT(_ch) (_ch * 2)
48#define LPC18XX_PWM_RES(_ch, _action) (_action << LPC18XX_PWM_RES_SHIFT(_ch))
49#define LPC18XX_PWM_RES_MASK(_ch) (0x3 << LPC18XX_PWM_RES_SHIFT(_ch))
50
51#define LPC18XX_PWM_MATCH_BASE 0x100
52#define LPC18XX_PWM_MATCH(_ch) (LPC18XX_PWM_MATCH_BASE + _ch * 4)
53
54#define LPC18XX_PWM_MATCHREL_BASE 0x200
55#define LPC18XX_PWM_MATCHREL(_ch) (LPC18XX_PWM_MATCHREL_BASE + _ch * 4)
56
57#define LPC18XX_PWM_EVSTATEMSK_BASE 0x300
58#define LPC18XX_PWM_EVSTATEMSK(_ch) (LPC18XX_PWM_EVSTATEMSK_BASE + _ch * 8)
59#define LPC18XX_PWM_EVSTATEMSK_ALL 0xffffffff
60
61#define LPC18XX_PWM_EVCTRL_BASE 0x304
62#define LPC18XX_PWM_EVCTRL(_ev) (LPC18XX_PWM_EVCTRL_BASE + _ev * 8)
63
64#define LPC18XX_PWM_EVCTRL_MATCH(_ch) _ch
65
66#define LPC18XX_PWM_EVCTRL_COMB_SHIFT 12
67#define LPC18XX_PWM_EVCTRL_COMB_MATCH (0x1 << LPC18XX_PWM_EVCTRL_COMB_SHIFT)
68
69#define LPC18XX_PWM_OUTPUTSET_BASE 0x500
70#define LPC18XX_PWM_OUTPUTSET(_ch) (LPC18XX_PWM_OUTPUTSET_BASE + _ch * 8)
71
72#define LPC18XX_PWM_OUTPUTCL_BASE 0x504
73#define LPC18XX_PWM_OUTPUTCL(_ch) (LPC18XX_PWM_OUTPUTCL_BASE + _ch * 8)
74
75/* LPC18xx SCT unified counter */
76#define LPC18XX_PWM_TIMER_MAX 0xffffffff
77
78/* LPC18xx SCT events */
79#define LPC18XX_PWM_EVENT_PERIOD 0
80#define LPC18XX_PWM_EVENT_MAX 16
81
82/* SCT conflict resolution */
83enum lpc18xx_pwm_res_action {
84 LPC18XX_PWM_RES_NONE,
85 LPC18XX_PWM_RES_SET,
86 LPC18XX_PWM_RES_CLEAR,
87 LPC18XX_PWM_RES_TOGGLE,
88};
89
90struct lpc18xx_pwm_data {
91 unsigned int duty_event;
92};
93
94struct lpc18xx_pwm_chip {
95 struct device *dev;
96 struct pwm_chip chip;
97 void __iomem *base;
98 struct clk *pwm_clk;
99 unsigned long clk_rate;
100 unsigned int period_ns;
101 unsigned int min_period_ns;
102 unsigned int max_period_ns;
103 unsigned int period_event;
104 unsigned long event_map;
105 struct mutex res_lock;
106 struct mutex period_lock;
107};
108
109static inline struct lpc18xx_pwm_chip *
110to_lpc18xx_pwm_chip(struct pwm_chip *chip)
111{
112 return container_of(chip, struct lpc18xx_pwm_chip, chip);
113}
114
115static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm,
116 u32 reg, u32 val)
117{
118 writel(val, lpc18xx_pwm->base + reg);
119}
120
121static inline u32 lpc18xx_pwm_readl(struct lpc18xx_pwm_chip *lpc18xx_pwm,
122 u32 reg)
123{
124 return readl(lpc18xx_pwm->base + reg);
125}
126
127static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm,
128 struct pwm_device *pwm,
129 enum lpc18xx_pwm_res_action action)
130{
131 u32 val;
132
133 mutex_lock(&lpc18xx_pwm->res_lock);
134
135 /*
136 * Simultaneous set and clear may happen on an output, that is the case
137 * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict
138 * resolution action to be taken in such a case.
139 */
140 val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE);
141 val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm);
142 val |= LPC18XX_PWM_RES(pwm->hwpwm, action);
143 lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val);
144
145 mutex_unlock(&lpc18xx_pwm->res_lock);
146}
147
148static void lpc18xx_pwm_config_period(struct pwm_chip *chip, int period_ns)
149{
150 struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
151 u64 val;
152
153 val = (u64)period_ns * lpc18xx_pwm->clk_rate;
154 do_div(val, NSEC_PER_SEC);
155
156 lpc18xx_pwm_writel(lpc18xx_pwm,
157 LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event),
158 (u32)val - 1);
159
160 lpc18xx_pwm_writel(lpc18xx_pwm,
161 LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event),
162 (u32)val - 1);
163}
164
165static void lpc18xx_pwm_config_duty(struct pwm_chip *chip,
166 struct pwm_device *pwm, int duty_ns)
167{
168 struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
169 struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm);
170 u64 val;
171
172 val = (u64)duty_ns * lpc18xx_pwm->clk_rate;
173 do_div(val, NSEC_PER_SEC);
174
175 lpc18xx_pwm_writel(lpc18xx_pwm,
176 LPC18XX_PWM_MATCH(lpc18xx_data->duty_event),
177 (u32)val);