diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-02-27 22:08:58 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-03-28 00:01:37 +0000 |
commit | 3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch) | |
tree | b647717f83ad56b15dc42cfdef5d04d68cd9bd6b /firmware/target/mips/ingenic_x1000/pwm-x1000.c | |
parent | 83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff) | |
download | rockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.tar.gz rockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.zip |
New port: FiiO M3K on bare metal
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/pwm-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/pwm-x1000.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/pwm-x1000.c b/firmware/target/mips/ingenic_x1000/pwm-x1000.c new file mode 100644 index 0000000000..37d2856c1a --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/pwm-x1000.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "pwm-x1000.h" | ||
23 | #include "clk-x1000.h" | ||
24 | #include "gpio-x1000.h" | ||
25 | #include "system.h" | ||
26 | #include "kernel.h" | ||
27 | #include "x1000/tcu.h" | ||
28 | |||
29 | struct pwm_gpio_data { | ||
30 | int port; | ||
31 | unsigned pin; | ||
32 | int func; | ||
33 | }; | ||
34 | |||
35 | struct pwm_state { | ||
36 | struct pwm_gpio_data gpio; | ||
37 | int period_ns; | ||
38 | int duty_ns; | ||
39 | int full_ticks; | ||
40 | int half_ticks; | ||
41 | int prescaler; | ||
42 | }; | ||
43 | |||
44 | static struct pwm_state pwm_state[] = { | ||
45 | {{GPIO_C, 1 << 25, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1}, | ||
46 | {{GPIO_C, 1 << 26, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1}, | ||
47 | {{GPIO_C, 1 << 27, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1}, | ||
48 | {{GPIO_B, 1 << 6, GPIO_DEVICE(2)}, -1, -1, -1, -1, -1}, | ||
49 | {{GPIO_C, 1 << 24, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1}, | ||
50 | }; | ||
51 | |||
52 | void pwm_init(int chn) | ||
53 | { | ||
54 | /* clear cached state */ | ||
55 | struct pwm_state* st = &pwm_state[chn]; | ||
56 | st->period_ns = -1; | ||
57 | st->duty_ns = -1; | ||
58 | st->full_ticks = -1; | ||
59 | st->prescaler = -1; | ||
60 | st->prescaler = -1; | ||
61 | |||
62 | /* clear GPIO and disable timer */ | ||
63 | gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0)); | ||
64 | jz_clr(TCU_STOP, 1 << chn); | ||
65 | jz_clr(TCU_ENABLE, 1 << chn); | ||
66 | jz_set(TCU_STOP, 1 << chn); | ||
67 | } | ||
68 | |||
69 | void pwm_set_period(int chn, int period_ns, int duty_ns) | ||
70 | { | ||
71 | struct pwm_state* st = &pwm_state[chn]; | ||
72 | unsigned long long tmp; | ||
73 | int full_ticks = st->full_ticks; | ||
74 | int half_ticks = st->half_ticks; | ||
75 | int prescaler = st->prescaler; | ||
76 | |||
77 | if(period_ns != st->period_ns) { | ||
78 | /* calculate full tick period and prescaler */ | ||
79 | tmp = clk_get(X1000_CLK_PCLK) / 1000000; | ||
80 | tmp *= period_ns; | ||
81 | tmp /= 1000; | ||
82 | |||
83 | prescaler = 0; | ||
84 | while(tmp > 0xffff && prescaler < 5) { | ||
85 | tmp /= 4; | ||
86 | prescaler += 1; | ||
87 | } | ||
88 | |||
89 | full_ticks = (tmp > 0xffff) ? 0xffff : tmp; | ||
90 | st->period_ns = period_ns; | ||
91 | } | ||
92 | |||
93 | if(duty_ns != st->duty_ns) { | ||
94 | /* calculate half tick value */ | ||
95 | tmp = full_ticks; | ||
96 | tmp *= duty_ns; | ||
97 | tmp /= period_ns; | ||
98 | |||
99 | half_ticks = (tmp > 0xffff) ? 0xffff : tmp; | ||
100 | if(half_ticks >= full_ticks) | ||
101 | half_ticks = full_ticks - 1; | ||
102 | st->duty_ns = duty_ns; | ||
103 | } | ||
104 | |||
105 | /* need to clear STOP bit to access timer unit registers */ | ||
106 | int was_stopped = !!(jz_read(TCU_STOP) & (1 << chn)); | ||
107 | if(was_stopped) | ||
108 | jz_clr(TCU_STOP, 1 << chn); | ||
109 | |||
110 | /* check if timer is currently running */ | ||
111 | int was_enabled = !!(jz_read(TCU_ENABLE) & (1 << chn)); | ||
112 | int enabled = was_enabled; | ||
113 | |||
114 | if(prescaler != st->prescaler) { | ||
115 | /* must disable timer to change these settings */ | ||
116 | if(was_enabled) { | ||
117 | jz_clr(TCU_ENABLE, 1 << chn); | ||
118 | enabled = 0; | ||
119 | } | ||
120 | |||
121 | jz_overwritef(TCU_CTRL(chn), SHUTDOWN_V(GRACEFUL), INIT_LVL(0), | ||
122 | PWM_EN(1), PRESCALE(prescaler), SOURCE_V(PCLK)); | ||
123 | REG_TCU_COUNT(chn) = 0; | ||
124 | st->prescaler = prescaler; | ||
125 | } | ||
126 | |||
127 | if(full_ticks != st->full_ticks || half_ticks != st->half_ticks) { | ||
128 | if(enabled) { | ||
129 | /* avoid changing PWM settings in the middle of a cycle */ | ||
130 | unsigned cmp = REG_TCU_CMP_FULL(chn) - 1; | ||
131 | long deadline = current_tick + 3; | ||
132 | while(REG_TCU_COUNT(chn) < cmp | ||
133 | && TIME_BEFORE(current_tick, deadline)); | ||
134 | } | ||
135 | |||
136 | /* these can be changed while the timer is running */ | ||
137 | REG_TCU_CMP_FULL(chn) = full_ticks; | ||
138 | REG_TCU_CMP_HALF(chn) = full_ticks - half_ticks; | ||
139 | st->full_ticks = full_ticks; | ||
140 | st->half_ticks = half_ticks; | ||
141 | } | ||
142 | |||
143 | /* restore the enable/stop state */ | ||
144 | if(was_enabled && !enabled) | ||
145 | jz_set(TCU_ENABLE, 1 << chn); | ||
146 | if(was_stopped) | ||
147 | jz_set(TCU_STOP, 1 << chn); | ||
148 | } | ||
149 | |||
150 | void pwm_enable(int chn) | ||
151 | { | ||
152 | /* Start timer */ | ||
153 | jz_clr(TCU_STOP, 1 << chn); | ||
154 | jz_set(TCU_ENABLE, 1 << chn); | ||
155 | |||
156 | /* Configure GPIO function */ | ||
157 | struct pwm_state* st = &pwm_state[chn]; | ||
158 | gpio_config(st->gpio.port, st->gpio.pin, st->gpio.func); | ||
159 | } | ||
160 | |||
161 | void pwm_disable(int chn) | ||
162 | { | ||
163 | /* Set GPIO to output 0 */ | ||
164 | struct pwm_state* st = &pwm_state[chn]; | ||
165 | gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0)); | ||
166 | |||
167 | /* Stop timer */ | ||
168 | jz_clr(TCU_ENABLE, 1 << chn); | ||
169 | jz_set(TCU_STOP, 1 << chn); | ||
170 | } | ||