block_am335x_pwm.c 7.88 KB
Newer Older
Jeroen Vreeken's avatar
Jeroen Vreeken committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
	Copyright Jeroen Vreeken (jeroen@vreeken.net), 2014

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "am335x.h"

#include <controller/controller_block.h>
#include <log/log.h>
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
23
#include <math.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
24
25
26
27


struct controller_block_private {
	float *in;
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
28

Jeroen Vreeken's avatar
Jeroen Vreeken committed
29
	float tbprd;	// period
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
30
31
	double freq;
	bool sym;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
32
33
34
35
36
37
	
	void *base;
};

static void pwm_calculate(struct controller_block *pwm)
{
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
38
39
40
	struct controller_block_private *priv = pwm->private;
	float in;
	uint16_t cmp;
root's avatar
root committed
41
	float tbprd;
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
42
43
	bool sign;
	void *base = priv->base;
root's avatar
root committed
44

Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
45
46
47
	tbprd = priv->tbprd;
	in = *priv->in;
	sign = signbit(in);
root's avatar
root committed
48
	cmp = fminf(fabsf(in) * tbprd, tbprd);
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

	if (sign) {
		am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPA, 0);
		am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPB, cmp);
	} else {
		am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPA, cmp);
		am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPB, 0);
	}
}

static void set_freq(struct controller_block *pwm)
{
	struct controller_block_private *priv = pwm->private;
	void *base = priv->base;
	int clkdiv = 0;
	int div = 1;
	double period, freq;
	uint16_t tbprd;
root's avatar
root committed
67
	uint16_t aqctl;
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
	
	/* double divider for symmetric use */
	period = AM335X_SYSCLK / (priv->freq * ( priv->sym ? 2.0 : 1.0));

	while (period > AM335X_PWMSS_EPWM_TBPRD_MAX) {
		clkdiv++;
		div *= 2;
		period /= 2.0;
		if (clkdiv == AM335X_PWMSS_EPWM_TBCTL_CLKDIV_MAX)
			break;
	}
	
	tbprd = period;
	
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_TBCTL,
	    AM335X_PWMSS_EPWM_TBCTL_CLKDIV_SET(clkdiv) |
	    AM335X_PWMSS_EPWM_TBCTL_SYNC0SEL_DIS |
root's avatar
root committed
85
86
87
	    (priv->sym ? 
	        AM335X_PWMSS_EPWM_TBCTL_CTRMODE_UD : 
		AM335X_PWMSS_EPWM_TBCTL_CTRMODE_DN));
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
88
89
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPCTL, 0);

root's avatar
root committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
	if (priv->sym) {
		aqctl =
		    AM335X_PWMSS_EPWM_AQCTLA_CBD_NOP |
		    AM335X_PWMSS_EPWM_AQCTLA_CBU_NOP |
		    AM335X_PWMSS_EPWM_AQCTLA_CAD_SET |
		    AM335X_PWMSS_EPWM_AQCTLA_CAU_CLR |
		    AM335X_PWMSS_EPWM_AQCTLA_PRD_NOP;
	} else {
		aqctl =
		    AM335X_PWMSS_EPWM_AQCTLA_CBD_NOP |
		    AM335X_PWMSS_EPWM_AQCTLA_CBU_NOP |
		    AM335X_PWMSS_EPWM_AQCTLA_CAD_SET |
		    AM335X_PWMSS_EPWM_AQCTLA_CAU_CLR |
		    AM335X_PWMSS_EPWM_AQCTLA_PRD_CLR;
	}
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_AQCTLA, aqctl);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_AQCTLB, aqctl);
   
Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_AQSFRC, 0);
	
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_TBPRD, tbprd);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPA, 0);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_CMPB, 0);

	priv->tbprd = tbprd;
	
	freq = AM335X_SYSCLK / (tbprd * div * ( priv->sym ? 2.0 : 1.0));
	
	log_send(LOG_T_DEBUG, 
	    "%s: Frequency: %gHz, period: %d ticks, divider: %d",
	    pwm->name, freq, tbprd, div);
	priv->freq = freq;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
122
123
}

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
static int param_set_freq(struct controller_block *pwm, char *param, int argc,
   va_list val)
{
	pwm->private->freq = va_arg(val, double);
	set_freq(pwm);
	return 0;
}

static int param_set_sym(struct controller_block *pwm, char *param, int argc,
   va_list val)
{
	pwm->private->sym = va_arg(val, int);
	set_freq(pwm);
	return 0;
}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
140
static struct controller_block_param_list params[] = {
141
142
	{ "frequency",   false, param_set_freq, .args = { "double", NULL } },
	{ "symmetrical", false, param_set_sym, .args = { "int", NULL } },
Jeroen Vreeken's avatar
Jeroen Vreeken committed
143
144
145
146
147
148
149
150
151
152
	{ NULL },
};


static struct controller_block_interm_list interms[] = {
	{ "in",  CONTROLLER_BLOCK_TERM_FLOAT, offsetof(struct controller_block_private, in) },
	{ NULL },
};


Jeroen Vreeken's avatar
Jeroen Vreeken committed
153
static struct controller_block * block_am335x_pwm_create(char *name, int argc, va_list ap)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
154
155
156
157
158
159
{
	struct controller_block *pwm;
	void *base;
	uint32_t reg;
	int pwm_nr;
	size_t offset;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
160
161
162
163
164
	size_t cm_offset;
	size_t pin_a;
	size_t pin_b;
	unsigned mode_a;
	unsigned mode_b;
165
	uint32_t ctrl_bits;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
166
167
168
169
170
171
172
173
174
175
	
	pwm_nr = va_arg(ap, int);
	if (pwm_nr < 0 || pwm_nr > 2) {
		log_send(LOG_T_ERROR, "%s: pwm%d is not valid. (valid: 0-2)",
		    name, pwm_nr);
		return NULL;
	}
	switch (pwm_nr) {
		case 0:
			offset = AM335X_PWMSS0_BASE;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
176
177
178
179
180
			cm_offset = AM335X_CM_PER_EPWMSS0_CLKCTRL;
			pin_a = AM335X_CONTROL_MODULE_CONF_MCASP0_ACLKX;
			pin_b = AM335X_CONTROL_MODULE_CONF_MCASP0_FSX;
			mode_a = 1;
			mode_b = 1;
181
			ctrl_bits = AM335X_CONTROL_MODULE_PWMSS0_TBCLKEN;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
182
183
184
			break;
		case 1:
			offset = AM335X_PWMSS1_BASE;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
185
186
187
188
189
			cm_offset = AM335X_CM_PER_EPWMSS1_CLKCTRL;
			pin_a = AM335X_CONTROL_MODULE_CONF_LCD_DATA10;
			pin_b = AM335X_CONTROL_MODULE_CONF_LCD_DATA11;
			mode_a = 2;
			mode_b = 2;
190
			ctrl_bits = AM335X_CONTROL_MODULE_PWMSS1_TBCLKEN;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
191
192
193
			break;
		case 2:
			offset = AM335X_PWMSS2_BASE;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
194
195
196
197
198
			cm_offset = AM335X_CM_PER_EPWMSS2_CLKCTRL;
			pin_a = AM335X_CONTROL_MODULE_CONF_LCD_DATA0;
			pin_b = AM335X_CONTROL_MODULE_CONF_LCD_DATA1;
			mode_a = 3;
			mode_b = 3;
199
			ctrl_bits = AM335X_CONTROL_MODULE_PWMSS2_TBCLKEN;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
200
201
202
			break;
	}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
203
204
205
206
	if (am335x_cm_enable(AM335X_CM_PER_L4LS_CLKCTRL)) {
		log_send(LOG_T_ERROR, "%s: Enabling L4LS clock failed", name);
		return NULL;
	}
207
208
209
210
211
212
213
	if (am335x_control_module_set(AM335X_CONTROL_MODULE_PWMSS_CTRL,
	    ctrl_bits)) {
		log_send(LOG_T_ERROR,
		    "%s: Enabling TBCLK in control module failed", name);
		return NULL;
	}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
	if (am335x_cm_enable(cm_offset)) {
		log_send(LOG_T_ERROR, "%s: Enabling module failed", name);
		return NULL;
	}

	if (am335x_pinmux_set(pin_a, mode_a, 
	    AM335X_CONTROL_MODULE_PINMUX_SLEWCTRL_FAST |
	    AM335X_CONTROL_MODULE_PINMUX_RX)) {
		log_send(LOG_T_ERROR, "%s: Could not set mux for pin A", name);
		return NULL;
	}
	if (am335x_pinmux_set(pin_b, mode_b,
	    AM335X_CONTROL_MODULE_PINMUX_SLEWCTRL_FAST |
	    AM335X_CONTROL_MODULE_PINMUX_RX)) {
		log_send(LOG_T_ERROR, "%s: Could not set mux for pin B", name);
		return NULL;
	}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
232
	base = am335x_mem(offset, AM335X_PWMSS_SIZE);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
233
	if (!base) {
Jeroen Vreeken's avatar
Jeroen Vreeken committed
234
		log_send(LOG_T_ERROR, "%s: Mapping PWM failed", name);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
		return NULL;
	}
	log_send(LOG_T_DEBUG, "%s: PWM mapped @ %p", name, base);
	
	reg = am335x_read32(base, AM335X_PWMSS_REG_IDVER);
	log_send(LOG_T_DEBUG, 
	    "%s: IDVER: 0x%" PRIx32 ": func: 0x%03x rev: %d.%d",
	    name, reg, AM335X_PWMSS_IDVER_FUNC_GET(reg),
	    AM335X_PWMSS_IDVER_X_MAJOR_GET(reg),
	    AM335X_PWMSS_IDVER_Y_MINOR_GET(reg));
	if (AM335X_PWMSS_IDVER_FUNC_GET(reg) != AM335X_PWMSS_IDVER_FUNC) {
		log_send(LOG_T_ERROR, "Unexpected functional number");
		goto err_rev;
	}

Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
250
251
252
253
254
255
256
257
258
259
260
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_PCCTL, 0);

	am335x_write16(base, AM335X_PWMSS_REG_EPWM_DBCTL, 0);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_TZCTL,
	    AM335X_PWMSS_EPWM_TZCTL_TZA_NOP |
	    AM335X_PWMSS_EPWM_TZCTL_TZB_NOP );

	am335x_write16(base, AM335X_PWMSS_REG_EPWM_TZEINT, 0);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_ETSEL, 0);
	am335x_write16(base, AM335X_PWMSS_REG_EPWM_HRCNFG, 0);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
261
262
263
264
265
266
267
268
	if (!(pwm = controller_block_alloc("am355x_pwm", name, sizeof(struct controller_block_private))))
		goto err_alloc;

	if (controller_block_interm_list_init(pwm, interms))
		goto err_interm;

	pwm->private->base = base;

Jeroen Vreeken's avatar
Add qed    
Jeroen Vreeken committed
269
270
271
272
273
274
275
276
277
	pwm->private->sym = true;
	pwm->private->freq = 10000;
	set_freq(pwm);

	am335x_write32(base, AM335X_PWMSS_REG_CLKCONFIG,
	    (am335x_read32(base, AM335X_PWMSS_REG_CLKCONFIG) &
	    ~AM335X_PWMSS_CLKCONFIG_EPWMCLKSTOP_REQ) |
	    AM335X_PWMSS_CLKCONFIG_EPWMCLK_EN);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
278
279
	pwm->calculate = pwm_calculate;

Jeroen Vreeken's avatar
Jeroen Vreeken committed
280
	if (controller_block_param_list_add(pwm, params))
Jeroen Vreeken's avatar
Jeroen Vreeken committed
281
282
		goto err_param;

283
284
285
	if (controller_block_add(pwm))
		goto err_add;
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
286
287
	return pwm;

288
err_add:
Jeroen Vreeken's avatar
Jeroen Vreeken committed
289
err_param:
Jeroen Vreeken's avatar
Jeroen Vreeken committed
290
291
292
293
294
295
err_interm:
	controller_block_free(pwm);
err_alloc:
err_rev:
	return NULL;
}
296
297
298
299
300

BLOCK_CREATE(am335x_pwm) = {
	.create = block_am335x_pwm_create,
	.args = { "int", NULL },
};