1 /* $NetBSD: imxpwm.c,v 1.2 2020/05/20 05:10:42 hkenken Exp $ */
2
3 /*
4 * Copyright (c) 2014 Genetec Corporation. All rights reserved.
5 * Written by Hashimoto Kenichi for Genetec Corporation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: imxpwm.c,v 1.2 2020/05/20 05:10:42 hkenken Exp $");
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/device.h>
36
37 #include <dev/clk/clk_backend.h>
38
39 #include <arm/imx/imxpwmreg.h>
40 #include <arm/imx/imxpwmvar.h>
41
42 #include <dev/pwm/pwmvar.h>
43
44 #define PWM_READ(sc, reg) \
45 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
46 #define PWM_WRITE(sc, reg, val) \
47 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
48
49 int
imxpwm_intr(void * arg)50 imxpwm_intr(void *arg)
51 {
52 struct imxpwm_softc *sc = arg;
53
54 uint32_t sts = PWM_READ(sc, PWM_SR);
55
56 if (sts & PWM_SR_ROV) {
57 if (sc->sc_handler != NULL)
58 sc->sc_handler(sc->sc_cookie);
59 }
60
61 PWM_WRITE(sc, PWM_SR, sts);
62
63 return 1;
64 }
65
66 static int
imxpwm_enable(pwm_tag_t pwm,bool enable)67 imxpwm_enable(pwm_tag_t pwm, bool enable)
68 {
69 struct imxpwm_softc * const sc = device_private(pwm->pwm_dev);
70 uint32_t cr, ocr;
71
72 ocr = cr = PWM_READ(sc, PWM_CR);
73 if (enable)
74 cr |= PWM_CR_EN;
75 else
76 cr &= ~PWM_CR_EN;
77
78 if (cr != ocr)
79 PWM_WRITE(sc, PWM_CR, cr);
80
81 return 0;
82 }
83
84 static int
imxpwm_get_config(pwm_tag_t pwm,struct pwm_config * conf)85 imxpwm_get_config(pwm_tag_t pwm, struct pwm_config *conf)
86 {
87 struct imxpwm_softc * const sc = device_private(pwm->pwm_dev);
88 uint32_t cr, sar, pr;
89
90 cr = PWM_READ(sc, PWM_CR);
91 sar = PWM_READ(sc, PWM_SAR);
92 pr = PWM_READ(sc, PWM_PR);
93
94 const int div = __SHIFTOUT(cr, PWM_CR_PRESCALER) + 1;
95 const int polarity = __SHIFTOUT(cr, PWM_CR_POUTC);
96 const uint64_t rate = sc->sc_freq / div;
97 const u_int cycles = __SHIFTOUT(pr, PWM_PR_PERIOD) + 2;
98 const u_int act_cycles = __SHIFTOUT(sar, PWM_SAR_SAMPLE);
99
100 conf->polarity = polarity ? PWM_ACTIVE_HIGH : PWM_ACTIVE_LOW;
101 conf->period = (u_int)(((uint64_t)cycles * 1000000000) / rate);
102 conf->duty_cycle = (u_int)(((uint64_t)act_cycles * 1000000000) / rate);
103
104 return 0;
105 }
106
107 static int
imxpwm_set_config(pwm_tag_t pwm,const struct pwm_config * conf)108 imxpwm_set_config(pwm_tag_t pwm, const struct pwm_config *conf)
109 {
110 struct imxpwm_softc * const sc = device_private(pwm->pwm_dev);
111 uint32_t cr, sar, pr;
112
113 if (conf->period == 0)
114 return EINVAL;
115 uint64_t rate;
116 u_int cycles;
117 int div = 0;
118 do {
119 div++;
120 rate = sc->sc_freq / div;
121 cycles = (u_int)((conf->period * rate) / 1000000000);
122 } while (cycles > 0xffff);
123 pr = __SHIFTIN(cycles - 2, PWM_PR_PERIOD);
124
125 cr = PWM_READ(sc, PWM_CR);
126 cr &= ~PWM_CR_PRESCALER;
127 cr |= __SHIFTIN(div - 1, PWM_CR_PRESCALER);
128 cr &= ~PWM_CR_POUTC;
129 if (conf->polarity == PWM_ACTIVE_LOW)
130 cr |= __SHIFTIN(1, PWM_CR_POUTC);
131
132 u_int act_cycles = (u_int)((conf->duty_cycle * rate) / 1000000000);
133 sar = __SHIFTIN(act_cycles, PWM_PR_PERIOD);
134
135 PWM_WRITE(sc, PWM_SAR, sar);
136 PWM_WRITE(sc, PWM_PR, pr);
137 PWM_WRITE(sc, PWM_CR, cr);
138
139 sc->sc_conf = *conf;
140
141 return 0;
142 }
143
144 void
imxpwm_attach_common(struct imxpwm_softc * sc)145 imxpwm_attach_common(struct imxpwm_softc *sc)
146 {
147 uint32_t reg;
148 int error;
149
150 if (sc->sc_handler != NULL) {
151 reg = PWM_IR_RIE;
152 PWM_WRITE(sc, PWM_IR, reg);
153 }
154
155 if (sc->sc_clk) {
156 error = clk_enable(sc->sc_clk);
157 if (error != 0) {
158 aprint_error(": couldn't enable clk\n");
159 return;
160 }
161 }
162
163 reg = PWM_READ(sc, PWM_CR);
164 reg &= PWM_CR_CLKSRC;
165 reg |= __SHIFTIN(CLKSRC_IPG_CLK, PWM_CR_CLKSRC);
166 PWM_WRITE(sc, PWM_CR, reg);
167
168 sc->sc_pwm.pwm_enable = imxpwm_enable;
169 sc->sc_pwm.pwm_get_config = imxpwm_get_config;
170 sc->sc_pwm.pwm_set_config = imxpwm_set_config;
171 sc->sc_pwm.pwm_dev = sc->sc_dev;
172
173 /* Set default settings */
174 struct pwm_config conf = {
175 .period = 1000000,
176 .duty_cycle = 0,
177 .polarity = PWM_ACTIVE_HIGH,
178 };
179 pwm_set_config(&sc->sc_pwm, &conf);
180 }
181
182