Increase ethernet irq priority.
[bertos.git] / bertos / cpu / arm / drv / stepper_at91.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos 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, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30  * All Rights Reserved.
31  * -->
32  *
33  * \brief Stepper driver interface implementation.
34  *
35  * This module use the three timer on the at91 family, to generate a
36  * six periodic variable pwm waveform. The pulse width is fix, and could
37  * change by setting the STEPPER_DELAY_ON_COMPARE_C define, but you make
38  * an attention to do this, becouse the pulse width is not exactly 
39  * STEPPER_DELAY_ON_COMPARE_C. The pulse width depend also to latency 
40  * time of cpu to serve an interrupt, this generate an pwm waveform affect
41  * to noise. This noise not effect the period but only the pulse width,
42  * becouse the raising edge is generate by hardware comply with the our
43  * period settings.
44  *
45  * Note: is most important to set STEPPER_DELAY_ON_COMPARE_C value minor
46  * than a interrupt time service, becouse the falling edge must be happen
47  * inside to inerrupt service to guarantee a correct functionaly of pwm
48  * generator.
49  * 
50  *
51  *
52  * \author Daniele Basile <asterix@develer.com>
53  */
54
55 #include "stepper_at91.h"
56
57 #include "cfg/cfg_stepper.h"
58 #include <cfg/macros.h>
59 #include <cfg/debug.h>
60
61 #include <cpu/types.h>
62 #include <cpu/irq.h>
63
64 #include <io/arm.h>
65
66
67 /*
68  * Delay to set C compare to clear output
69  * on select TIO output
70  */
71 #define STEPPER_DELAY_ON_COMPARE_C 20
72
73 /*
74  * Forward declaration for interrupt handler
75  */
76 static ISR_PROTO(stepper_tc0_irq);
77 static ISR_PROTO(stepper_tc1_irq);
78 static ISR_PROTO(stepper_tc2_irq);
79
80 ///< Static array of timer counter struct for stepper.
81 static struct TimerCounter stepper_timers[CONFIG_TC_STEPPER_MAX_NUM] =
82 {
83         { //Timer Counter settings for TIOA0 output pin
84                 .timer_id = TC0_ID,
85                 .blk_ctrl_set = TC_NONEXC0,
86                 .chl_mode_reg = &TC0_CMR,
87                 .chl_ctrl_reg = &TC0_CCR,
88                 .comp_effect_mask = TC_ACPA_MASK,
89                 .comp_effect_set = TC_ACPA_SET_OUTPUT,
90                 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
91                 .comp_effect_c_mask = TC_ACPC_MASK,
92                 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
93                 .ext_event_set = TC_EEVT_XC0,
94                 .comp_reg = &TC0_RA,
95                 .comp_c_reg = &TC0_RC,
96                 .count_val_reg = &TC0_CV,
97                 .irq_enable_reg = &TC0_IER,
98                 .irq_disable_reg = &TC0_IDR,
99                 .irq_set_mask = BV(TC_CPAS),
100                 .irq_mask_reg = &TC0_IMR,
101                 .isr = stepper_tc0_irq,
102                 .status_reg = &TC0_SR,
103                 .tio_pin = TIOA0,
104                 .callback = NULL,
105                 .motor = NULL,
106         },
107         { //Timer Counter settings for TIOB0 output pin
108                 .timer_id = TC0_ID,
109                 .blk_ctrl_set = TC_NONEXC0,
110                 .chl_mode_reg = &TC0_CMR,
111                 .chl_ctrl_reg = &TC0_CCR,
112                 .comp_reg = &TC0_RB,
113                 .comp_c_reg = &TC0_RC,
114                 .count_val_reg = &TC0_CV,
115                 .comp_effect_mask = TC_BCPB_MASK,
116                 .comp_effect_set = TC_BCPB_SET_OUTPUT,
117                 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
118                 .comp_effect_c_mask = TC_BCPC_MASK,
119                 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
120                 .ext_event_set = TC_EEVT_XC0,
121                 .irq_enable_reg = &TC0_IER,
122                 .irq_disable_reg = &TC0_IDR,
123                 .irq_set_mask = BV(TC_CPBS),
124                 .irq_mask_reg = &TC0_IMR,
125                 .isr = stepper_tc0_irq,
126                 .status_reg = &TC0_SR,
127                 .tio_pin = TIOB0,
128                 .callback = NULL,
129                 .motor = NULL,
130         },
131         { //Timer Counter settings for TIOA1 output pin
132                 .timer_id = TC1_ID,
133                 .blk_ctrl_set = TC_NONEXC1,
134                 .chl_mode_reg = &TC1_CMR,
135                 .chl_ctrl_reg = &TC1_CCR,
136                 .comp_reg = &TC1_RA,
137                 .comp_c_reg = &TC1_RC,
138                 .count_val_reg = &TC1_CV,
139                 .comp_effect_mask = TC_ACPA_MASK,
140                 .comp_effect_set = TC_ACPA_SET_OUTPUT,
141                 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
142                 .comp_effect_c_mask = TC_ACPC_MASK,
143                 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
144                 .ext_event_set = TC_EEVT_XC1,
145                 .irq_enable_reg = &TC1_IER,
146                 .irq_disable_reg = &TC1_IDR,
147                 .irq_set_mask = BV(TC_CPAS),
148                 .irq_mask_reg = &TC1_IMR,
149                 .isr = stepper_tc1_irq,
150                 .status_reg = &TC1_SR,
151                 .tio_pin = TIOA1,
152                 .callback = NULL,
153                 .motor = NULL,
154         },
155         { //Timer Counter settings for TIOB1 output pin
156                 .timer_id = TC1_ID,
157                 .blk_ctrl_set = TC_NONEXC1,
158                 .chl_mode_reg = &TC1_CMR,
159                 .chl_ctrl_reg = &TC1_CCR,
160                 .comp_reg = &TC1_RB,
161                 .comp_c_reg = &TC1_RC,
162                 .count_val_reg = &TC1_CV,
163                 .comp_effect_mask = TC_BCPB_MASK,
164                 .comp_effect_set = TC_BCPB_SET_OUTPUT,
165                 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
166                 .comp_effect_c_mask = TC_BCPC_MASK,
167                 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
168                 .ext_event_set = TC_EEVT_XC1,
169                 .irq_enable_reg = &TC1_IER,
170                 .irq_disable_reg = &TC1_IDR,
171                 .irq_set_mask = BV(TC_CPBS),
172                 .irq_mask_reg = &TC1_IMR,
173                 .isr = stepper_tc1_irq,
174                 .status_reg = &TC1_SR,
175                 .tio_pin = TIOB1,
176                 .callback = NULL,
177                 .motor = NULL,
178         },
179         { //Timer Counter settings for TIOA2 output pin
180                 .timer_id = TC2_ID,
181                 .blk_ctrl_set = TC_NONEXC2,
182                 .chl_mode_reg = &TC2_CMR,
183                 .chl_ctrl_reg = &TC2_CCR,
184                 .comp_reg = &TC2_RA,
185                 .comp_c_reg = &TC2_RC,
186                 .count_val_reg = &TC2_CV,
187                 .comp_effect_mask = TC_ACPA_MASK,
188                 .comp_effect_set = TC_ACPA_SET_OUTPUT,
189                 .comp_effect_clear = TC_ACPA_CLEAR_OUTPUT,
190                 .comp_effect_c_mask = TC_ACPC_MASK,
191                 .comp_effect_c_clear = TC_ACPC_CLEAR_OUTPUT,
192                 .ext_event_set = TC_EEVT_XC2,
193                 .irq_enable_reg = &TC2_IER,
194                 .irq_disable_reg = &TC2_IDR,
195                 .irq_set_mask = BV(TC_CPAS),
196                 .irq_mask_reg = &TC2_IMR,
197                 .isr = stepper_tc2_irq,
198                 .status_reg = &TC2_SR,
199                 .tio_pin = TIOA2,
200                 .callback = NULL,
201                 .motor = NULL,
202         },
203         { //Timer Counter settings for TIOB2 output pin
204                 .timer_id = TC2_ID,
205                 .blk_ctrl_set = TC_NONEXC2,
206                 .chl_mode_reg = &TC2_CMR,
207                 .chl_ctrl_reg = &TC2_CCR,
208                 .comp_reg = &TC2_RB,
209                 .comp_c_reg = &TC2_RC,
210                 .count_val_reg = &TC2_CV,
211                 .comp_effect_mask = TC_BCPB_MASK,
212                 .comp_effect_set = TC_BCPB_SET_OUTPUT,
213                 .comp_effect_clear = TC_BCPB_CLEAR_OUTPUT,
214                 .comp_effect_c_mask = TC_BCPC_MASK,
215                 .comp_effect_c_clear = TC_BCPC_CLEAR_OUTPUT,
216                 .ext_event_set = TC_EEVT_XC2,
217                 .irq_enable_reg = &TC2_IER,
218                 .irq_disable_reg = &TC2_IDR,
219                 .irq_set_mask = BV(TC_CPBS),
220                 .irq_mask_reg = &TC2_IMR,
221                 .isr = stepper_tc2_irq,
222                 .status_reg = &TC2_SR,
223                 .tio_pin = TIOB2,
224                 .callback = NULL,
225                 .motor = NULL,
226         }
227 };
228
229 /**
230  * Generic TIO interrupt handler.
231  */
232 INLINE void stepper_tc_tio_irq(struct TimerCounter * t)
233 {
234         //
235         *t->chl_mode_reg &= ~t->comp_effect_c_mask;
236         *t->chl_mode_reg |= t->comp_effect_c_clear;
237
238         /*
239          * Cleat TIO output on c register compare.
240          * This generate an pulse with variable lenght, this
241          * depend to delay that interrupt is realy service.
242          */
243         *t->comp_c_reg = *t->count_val_reg + STEPPER_DELAY_ON_COMPARE_C;
244
245         //Call the associate callback
246         t->callback(t->motor);
247
248         *t->chl_mode_reg &= ~t->comp_effect_c_mask;
249 }
250
251
252 /*
253  * Interrupt handler for timer counter TCKL0
254  */
255 DECLARE_ISR(stepper_tc0_irq)
256 {
257         /*
258          * Warning: when we read the status_reg register, we reset it.
259          * That mean if is occur an interrupt event we can read only
260          * the last that has been occur. To not miss an interrupt event
261          * we save the status_reg register and then we read it.
262          */
263         uint32_t  status_reg = TC0_SR & TC0_IMR;
264
265         if (status_reg & BV(TC_CPAS))
266                 stepper_tc_tio_irq(&stepper_timers[TC_TIOA0]);
267
268         if (status_reg & BV(TC_CPBS))
269                 stepper_tc_tio_irq(&stepper_timers[TC_TIOB0]);
270
271         /* Inform hw that we have served the IRQ */
272         AIC_EOICR = 0;
273
274 }
275
276 /*
277  * Interrupt handler for timer counter TCKL1
278  */
279 DECLARE_ISR(stepper_tc1_irq)
280 {
281         /*
282          * Warning: when we read the status_reg register, we reset it.
283          * That mean if is occur an interrupt event we can read only
284          * the last that has been occur. To not miss an interrupt event
285          * we save the status_reg register and then we read it.
286          */
287         uint32_t  status_reg = TC1_SR & TC1_IMR;
288
289         if (status_reg & BV(TC_CPAS))
290                 stepper_tc_tio_irq(&stepper_timers[TC_TIOA1]);
291
292         if (status_reg & BV(TC_CPBS))
293                 stepper_tc_tio_irq(&stepper_timers[TC_TIOB1]);
294
295
296         /* Inform hw that we have served the IRQ */
297         AIC_EOICR = 0;
298 }
299
300
301 /*
302  * Interrupt handler for timer counter TCKL2
303  */
304 DECLARE_ISR(stepper_tc2_irq)
305 {
306
307         /*
308          * Warning: when we read the status_reg register, we reset it.
309          * That mean if is occur an interrupt event we can read only
310          * the last that has been occur. To not miss an interrupt event
311          * we save the status_reg register and then we read it.
312          */
313         uint32_t  status_reg = TC2_SR & TC2_IMR;
314
315         if (status_reg & BV(TC_CPAS))
316                 stepper_tc_tio_irq(&stepper_timers[TC_TIOA2]);
317
318         if (status_reg & BV(TC_CPBS))
319                 stepper_tc_tio_irq(&stepper_timers[TC_TIOB2]);
320
321         /* Inform hw that we have served the IRQ */
322         AIC_EOICR = 0;
323
324 }
325
326 /**
327  * Timer couter setup.
328  *
329  * This function apply to select timer couter all needed settings.
330  * Every settings are stored in stepper_timers[].
331  */
332 void stepper_tc_setup(int index, stepper_isr_t callback, struct Stepper *motor)
333 {
334         ASSERT(index < CONFIG_TC_STEPPER_MAX_NUM);
335
336         motor->timer = &stepper_timers[index];
337
338         //Disable PIO controller and enable TIO function
339         TIO_PIO_PDR = BV(motor->timer->tio_pin);
340         TIO_PIO_ABSR = BV(motor->timer->tio_pin);
341
342         /*
343          * Sets timer counter in waveform mode.
344          * We set as default:
345          * - Waveform mode 00 (see datasheet for more detail.)
346          * - Master clock prescaler to STEPPER_MCK_PRESCALER
347          * - Set none external event
348          * - Clear pin output on comp_reg
349          * - None effect on reg C compare
350          */
351         *motor->timer->chl_mode_reg = BV(TC_WAVE);
352         *motor->timer->chl_mode_reg |= motor->timer->ext_event_set;
353         *motor->timer->chl_mode_reg &= ~TC_WAVSEL_MASK;
354         *motor->timer->chl_mode_reg |= TC_WAVSEL_UP;
355         *motor->timer->chl_mode_reg |= STEPPER_MCK_PRESCALER;
356         *motor->timer->chl_mode_reg |= motor->timer->comp_effect_clear;
357         *motor->timer->chl_mode_reg &= ~motor->timer->comp_effect_c_mask;
358
359         //Reset comp_reg and C compare register
360         *motor->timer->comp_reg = 0;
361         *motor->timer->comp_c_reg = 0;
362
363         //Register interrupt vector
364         cpu_flags_t flags;
365         IRQ_SAVE_DISABLE(flags);
366
367         /*
368          * Warning: To guarantee a correct management of interrupt event, we must
369          * trig the interrupt on level sensitive. This becouse, we have only a common
370          * line for interrupt request, and if we have at the same time two interrupt
371          * request could be that the is service normaly but the second will never
372          *  been detected and interrupt will stay active but never serviced.
373          */
374         AIC_SVR(motor->timer->timer_id) = motor->timer->isr;
375         AIC_SMR(motor->timer->timer_id) = AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
376         AIC_IECR = BV(motor->timer->timer_id);
377
378         // Disable interrupt on select timer counter
379         stepper_tc_irq_disable(motor->timer);
380
381         IRQ_RESTORE(flags);
382
383         //Register callback
384         motor->timer->callback = callback;
385         motor->timer->motor = motor;
386 }
387
388 /**
389  * Timer counter init.
390  */
391 void stepper_tc_init(void)
392 {
393         STEPPER_STROBE_INIT;
394
395         ASSERT(CONFIG_NUM_STEPPER_MOTORS <= CONFIG_TC_STEPPER_MAX_NUM);
396
397         /*
398          * Enable timer counter:
399          * - power on all timer counter
400          * - disable all interrupt
401          * - disable all external event/timer source
402          */
403         for (int i = 0; i < CONFIG_TC_STEPPER_MAX_NUM; i++)
404         {
405                 PMC_PCER = BV(stepper_timers[i].timer_id);
406                 *stepper_timers[i].irq_disable_reg = 0xFFFFFFFF;
407                 TC_BMR = stepper_timers[i].blk_ctrl_set;
408         }
409
410         /*
411          * Enable timer counter and start it.
412          */
413         for (int i = 0; i < CONFIG_TC_STEPPER_MAX_NUM; i++)
414                 *stepper_timers[i].chl_ctrl_reg = (BV(TC_CLKEN) | BV(TC_SWTRG));
415
416 }
417