source: rtems/c/src/lib/libbsp/sparc/shared/timer/gptimer.c @ 76d1198

Last change on this file since 76d1198 was 76d1198, checked in by Sebastian Huber <sebastian.huber@…>, on Jul 7, 2017 at 5:45:57 AM

score: Introduce _SMP_Get_online_processors()

Update #3059.

  • Property mode set to 100644
File size: 15.1 KB
Line 
1/*  This file contains the driver for the GRLIB GPTIMER timers port. The driver
2 *  is implemented by using the tlib.c simple timer layer and the Driver
3 *  Manager.
4 *
5 *  The Driver can be configured using driver resources:
6 *
7 *  - timerStart  Timer Index if first Timer, this parameters is typically used
8 *                in AMP systems for resource allocation. The Timers before
9 *                timerStart will not be accessed.
10 *  - timerCnt    Number of timers that the driver will use, this parameters is
11 *                typically used in AMP systems for resource allocation between
12 *                OS instances.
13 *  - prescaler   Base prescaler, normally set by bootloader but can be
14 *                overridden. The default scaler reload value set by bootloader
15 *                is so that Timers operate in 1MHz. Setting the prescaler to a
16 *                lower value increase the accuracy of the timers but shortens
17 *                the time until underflow happens.
18 *  - clockTimer  Used to select a particular timer to be the system clock
19 *                timer. This is useful when multiple GPTIMERs cores are
20 *                available, or in AMP systems. By default the TLIB selects the
21 *                first timer registered as system clock timer.
22 *
23 *  The BSP define APBUART_INFO_AVAIL in order to add the info routine
24 *  used for debugging.
25 *
26 *  COPYRIGHT (c) 2010.
27 *  Cobham Gaisler AB.
28 *
29 *  The license and distribution terms for this file may be
30 *  found in the file LICENSE in this distribution or at
31 *  http://www.rtems.org/license/LICENSE.
32 */
33
34#include <rtems.h>
35#include <bsp.h>
36#include <stdlib.h>
37#include <drvmgr/drvmgr.h>
38#include <drvmgr/ambapp_bus.h>
39#include <grlib.h>
40#include <bsp/gptimer.h>
41#include <bsp/tlib.h>
42
43#if defined(LEON3)
44#include <leon.h>
45#endif
46
47#ifdef GPTIMER_INFO_AVAIL
48#include <stdio.h>
49#endif
50
51#ifdef RTEMS_SMP
52#include <rtems/score/processormask.h>
53#include <rtems/score/smpimpl.h>
54#endif
55
56/* GPTIMER Core Configuration Register (READ-ONLY) */
57#define GPTIMER_CFG_TIMERS_BIT  0
58#define GPTIMER_CFG_IRQ_BIT     3
59#define GPTIMER_CFG_SI_BIT      8
60#define GPTIMER_CFG_DF_BIT      9
61
62#define GPTIMER_CFG_TIMERS      (0x7<<GPTIMER_CFG_TIMERS_BIT)
63#define GPTIMER_CFG_IRQ         (0x1f<<GPTIMER_CFG_IRQ_BIT)
64#define GPTIMER_CFG_SI          (1<<GPTIMER_CFG_SI_BIT)
65#define GPTIMER_CFG_DF          (1<<GPTIMER_CFG_DF_BIT)
66
67/* GPTIMER Timer Control Register */
68#define GPTIMER_CTRL_EN_BIT     0
69#define GPTIMER_CTRL_RS_BIT     1
70#define GPTIMER_CTRL_LD_BIT     2
71#define GPTIMER_CTRL_IE_BIT     3
72#define GPTIMER_CTRL_IP_BIT     4
73#define GPTIMER_CTRL_CH_BIT     5
74#define GPTIMER_CTRL_DH_BIT     6
75
76#define GPTIMER_CTRL_EN (1<<GPTIMER_CTRL_EN_BIT)
77#define GPTIMER_CTRL_RS (1<<GPTIMER_CTRL_RS_BIT)
78#define GPTIMER_CTRL_LD (1<<GPTIMER_CTRL_LD_BIT)
79#define GPTIMER_CTRL_IE (1<<GPTIMER_CTRL_IE_BIT)
80#define GPTIMER_CTRL_IP (1<<GPTIMER_CTRL_IP_BIT)
81#define GPTIMER_CTRL_CH (1<<GPTIMER_CTRL_CH_BIT)
82#define GPTIMER_CTRL_DH (1<<GPTIMER_CTRL_DH_BIT)
83
84#define DBG(x...)
85
86/* GPTIMER timer private */
87struct gptimer_timer {
88        struct tlib_dev tdev;   /* Must be first in struct */
89        struct gptimer_timer_regs *tregs;
90        char index; /* Timer Index in this driver */
91        char tindex; /* Timer Index In Hardware */
92        unsigned char irq_ack_mask;
93};
94
95/* GPTIMER Core private */
96struct gptimer_priv {
97        struct drvmgr_dev *dev;
98        struct gptimer_regs *regs;
99        unsigned int base_clk;
100        unsigned int base_freq;
101        unsigned int widthmask;
102        char separate_interrupt;
103        char isr_installed;
104
105        /* Structure per Timer unit, the core supports up to 8 timers */
106        int timer_cnt;
107        struct gptimer_timer timers[0];
108};
109
110void gptimer_isr(void *data);
111
112#if 0
113void gptimer_tlib_irq_register(struct tlib_drv *tdrv, tlib_isr_t func, void *data)
114{
115        struct gptimer_priv *priv = (struct gptimer_priv *)tdrv;
116
117        if ( SHARED ...)
118       
119       
120        drvmgr_interrupt_register();
121}
122#endif
123
124/******************* Driver manager interface ***********************/
125
126/* Driver prototypes */
127static struct tlib_drv gptimer_tlib_drv;
128int gptimer_device_init(struct gptimer_priv *priv);
129
130int gptimer_init1(struct drvmgr_dev *dev);
131#ifdef GPTIMER_INFO_AVAIL
132static int gptimer_info(
133        struct drvmgr_dev *dev,
134        void (*print_line)(void *p, char *str),
135        void *p, int, char *argv[]);
136#define GTIMER_INFO_FUNC gptimer_info
137#else
138#define GTIMER_INFO_FUNC NULL
139#endif
140
141struct drvmgr_drv_ops gptimer_ops =
142{
143        .init = {gptimer_init1, NULL, NULL, NULL},
144        .remove = NULL,
145        .info = GTIMER_INFO_FUNC,
146};
147
148struct amba_dev_id gptimer_ids[] =
149{
150        {VENDOR_GAISLER, GAISLER_GPTIMER},
151        {VENDOR_GAISLER, GAISLER_GRTIMER},
152        {0, 0}          /* Mark end of table */
153};
154
155struct amba_drv_info gptimer_drv_info =
156{
157        {
158                DRVMGR_OBJ_DRV,                 /* Driver */
159                NULL,                           /* Next driver */
160                NULL,                           /* Device list */
161                DRIVER_AMBAPP_GAISLER_GPTIMER_ID,/* Driver ID */
162                "GPTIMER_DRV",                  /* Driver Name */
163                DRVMGR_BUS_TYPE_AMBAPP,         /* Bus Type */
164                &gptimer_ops,
165                NULL,                           /* Funcs */
166                0,                              /* No devices yet */
167                0,
168        },
169        &gptimer_ids[0]
170};
171
172void gptimer_register_drv (void)
173{
174        DBG("Registering GPTIMER driver\n");
175        drvmgr_drv_register(&gptimer_drv_info.general);
176}
177
178int gptimer_init1(struct drvmgr_dev *dev)
179{
180        struct gptimer_priv *priv;
181        struct gptimer_regs *regs;
182        struct amba_dev_info *ambadev;
183        struct ambapp_core *pnpinfo;
184        int timer_hw_cnt, timer_cnt, timer_start;
185        int i, size;
186        struct gptimer_timer *timer;
187        union drvmgr_key_value *value;
188        unsigned char irq_ack_mask;
189
190        /* Get device information from AMBA PnP information */
191        ambadev = (struct amba_dev_info *)dev->businfo;
192        if ( ambadev == NULL ) {
193                return -1;
194        }
195        pnpinfo = &ambadev->info;
196        regs = (struct gptimer_regs *)pnpinfo->apb_slv->start;
197
198        DBG("GPTIMER[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
199
200        /* Get number of Timers */
201        timer_hw_cnt = regs->cfg & GPTIMER_CFG_TIMERS;
202
203        /* Let user spelect a range of timers to be used. In AMP systems
204         * it is sometimes neccessary to leave timers for other CPU instances.
205         *
206         * The default operation in AMP is to shared the timers within the
207         * first GPTIMER core as below. This can of course be overrided by
208         * driver resources.
209         */
210        timer_cnt = timer_hw_cnt;
211        timer_start = 0;
212#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3)
213        if ((dev->minor_drv == 0) && drvmgr_on_rootbus(dev)) {
214                timer_cnt = 1;
215                timer_start = LEON3_Cpu_Index;
216        }
217#endif
218        value = drvmgr_dev_key_get(dev, "timerStart", DRVMGR_KT_INT);
219        if ( value) {
220                timer_start = value->i;
221                timer_cnt = timer_hw_cnt - timer_start;
222        }
223        value = drvmgr_dev_key_get(dev, "timerCnt", DRVMGR_KT_INT);
224        if ( value && (value->i < timer_cnt) ) {
225                timer_cnt = value->i;
226        }
227
228        /* Allocate Common Timer Description, size depends on how many timers
229         * are present.
230         */
231        size = sizeof(struct gptimer_priv) +
232                timer_cnt*sizeof(struct gptimer_timer);
233        priv = dev->priv = (struct gptimer_priv *)malloc(size);
234        if ( !priv )
235                return DRVMGR_NOMEM;
236        memset(priv, 0, size);
237        priv->dev = dev;
238        priv->regs = regs;
239
240        /* The Base Frequency of the GPTIMER core is the same as the
241         * frequency of the AMBA bus it is situated on.
242         */
243        drvmgr_freq_get(dev, DEV_APB_SLV, &priv->base_clk);
244
245        /* This core will may provide important Timer functionality
246         * to other drivers and the RTEMS kernel, the Clock driver
247         * may for example use this device. So the Timer driver must be
248         * initialized in the first iiitialization stage.
249         */
250
251        /*** Initialize Hardware ***/
252
253        /* If user request to set prescaler, we will do that. However, note
254         * that doing so for the Root-Bus GPTIMER may affect the RTEMS Clock
255         * so that Clock frequency is wrong.
256         */
257        value = drvmgr_dev_key_get(priv->dev, "prescaler", DRVMGR_KT_INT);
258        if ( value )
259                regs->scaler_reload = value->i;
260
261        /* Get Frequency that the timers are operating in (after prescaler) */
262        priv->base_freq = priv->base_clk / (priv->regs->scaler_reload + 1);
263
264        /* Stop Timer and probe Pending bit. In newer hardware the
265         * timer has pending bit is cleared by writing a one to it,
266         * whereas older versions it is cleared with a zero.
267         */
268        priv->regs->timer[timer_start].ctrl = GPTIMER_CTRL_IP;
269        if ((priv->regs->timer[timer_start].ctrl & GPTIMER_CTRL_IP) != 0)
270                irq_ack_mask = ~GPTIMER_CTRL_IP;
271        else
272                irq_ack_mask = ~0;
273
274        /* Probe timer register width mask */
275        priv->regs->timer[timer_start].value = 0xffffffff;
276        priv->widthmask = priv->regs->timer[timer_start].value;
277
278        priv->timer_cnt = timer_cnt;
279        for (i=0; i<timer_cnt; i++) {
280                timer = &priv->timers[i];
281                timer->index = i;
282                timer->tindex = i + timer_start;
283                timer->tregs = &regs->timer[(int)timer->tindex];
284                timer->tdev.drv = &gptimer_tlib_drv;
285                timer->irq_ack_mask = irq_ack_mask;
286
287                /* Register Timer at Timer Library */
288                tlib_dev_reg(&timer->tdev);
289        }
290
291        /* Check Interrupt support implementation, two cases:
292         *  A. All Timers share one IRQ
293         *  B. Each Timer have an individual IRQ. The number is:
294         *        BASE_IRQ + timer_index
295         */
296        priv->separate_interrupt = (regs->cfg & GPTIMER_CFG_SI) != 0;
297
298        return DRVMGR_OK;
299}
300
301#ifdef GPTIMER_INFO_AVAIL
302static int gptimer_info(
303        struct drvmgr_dev *dev,
304        void (*print_line)(void *p, char *str),
305        void *p, int argc, char *argv[])
306{
307        struct gptimer_priv *priv = dev->priv;
308        struct gptimer_timer *timer;
309        char buf[64];
310        int i;
311
312        if (priv == NULL || argc != 0)
313                return -DRVMGR_EINVAL;
314
315        sprintf(buf, "Timer Count: %d", priv->timer_cnt);
316        print_line(p, buf);
317        sprintf(buf, "REGS:        0x%08x", (unsigned int)priv->regs);
318        print_line(p, buf);
319        sprintf(buf, "BASE SCALER: %d", priv->regs->scaler_reload);
320        print_line(p, buf);
321        sprintf(buf, "BASE FREQ:   %dkHz", priv->base_freq / 1000);
322        print_line(p, buf);
323        sprintf(buf, "SeparateIRQ: %s", priv->separate_interrupt ? "YES":"NO");
324        print_line(p, buf);
325
326        for (i=0; i<priv->timer_cnt; i++) {
327                timer = &priv->timers[i];
328                sprintf(buf, " - TIMER HW Index %d -", timer->tindex);
329                print_line(p, buf);
330                sprintf(buf, " TLIB Index: %d", timer->index);
331                print_line(p, buf);
332                sprintf(buf, " RELOAD REG: %d", timer->tregs->reload);
333                print_line(p, buf);
334                sprintf(buf, " CTRL REG:   %d", timer->tregs->ctrl);
335                print_line(p, buf);
336        }
337
338        return DRVMGR_OK;
339}
340#endif
341
342static inline struct gptimer_priv *priv_from_timer(struct gptimer_timer *t)
343{
344        return (struct gptimer_priv *)
345                ((unsigned int)t -
346                sizeof(struct gptimer_priv) -
347                t->index * sizeof(struct gptimer_timer));
348}
349
350static int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack)
351{
352        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
353        unsigned int ctrl = timer->tregs->ctrl;
354
355        if ((ctrl & (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) ==
356                (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) {
357                /* clear Pending IRQ ? */
358                if (ack)
359                        timer->tregs->ctrl = ctrl & timer->irq_ack_mask;
360                return 1; /* timer generated IRQ */
361        } else
362                return 0; /* was not timer causing IRQ */
363}
364
365void gptimer_isr(void *data)
366{
367        struct gptimer_priv *priv = data;
368        int i;
369
370        /* Check all timers for IRQ */
371        for (i=0;i<priv->timer_cnt; i++) {
372                if (gptimer_tlib_int_pend((void *)&priv->timers[i], 0)) {
373                        /* IRQ Was generated by Timer and Pending flag has *not*
374                         * yet been cleared, this is to allow ISR to look at
375                         * pending bit. Call ISR registered. Clear pending bit.
376                         */
377                        if (priv->timers[i].tdev.isr_func) {
378                                priv->timers[i].tdev.isr_func(
379                                        priv->timers[i].tdev.isr_data);
380                        }
381                        gptimer_tlib_int_pend((void *)&priv->timers[i], 1);
382                }
383        }
384}
385
386static void gptimer_tlib_reset(struct tlib_dev *hand)
387{
388        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
389
390        timer->tregs->ctrl = (timer->tregs->ctrl & timer->irq_ack_mask) &
391                             GPTIMER_CTRL_IP;
392        timer->tregs->reload = 0xffffffff;
393        timer->tregs->ctrl = GPTIMER_CTRL_LD;
394}
395
396static void gptimer_tlib_get_freq(
397        struct tlib_dev *hand,
398        unsigned int *basefreq,
399        unsigned int *tickrate)
400{
401        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
402        struct gptimer_priv *priv = priv_from_timer(timer);
403
404        /* Calculate base frequency from Timer Clock and Prescaler */
405        if ( basefreq )
406                *basefreq = priv->base_freq;
407        if ( tickrate )
408                *tickrate = timer->tregs->reload + 1;
409}
410
411static int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate)
412{
413        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
414
415        timer->tregs->reload = tickrate - 1;
416
417        /*Check that value was allowed (Timer may not be as wide as expected)*/
418        if ( timer->tregs->reload != (tickrate - 1) )
419                return -1;
420        else
421                return 0;
422}
423
424static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags)
425{
426        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
427        struct gptimer_priv *priv = priv_from_timer(timer);
428
429        if ( priv->separate_interrupt ) {
430                drvmgr_interrupt_register(priv->dev, timer->tindex,
431                                                "gptimer", func, data);
432        } else {
433                if (priv->isr_installed == 0) {
434                        /* Shared IRQ handler */
435                        drvmgr_interrupt_register(
436                                priv->dev,
437                                0,
438                                "gptimer_shared",
439                                gptimer_isr,
440                                priv);
441                }
442                priv->isr_installed++;
443        }
444
445#if RTEMS_SMP
446        if (flags & TLIB_FLAGS_BROADCAST) {
447                int tindex = 0;
448
449                if (priv->separate_interrupt) {
450                        /* Offset interrupt number with HW subtimer index */
451                        tindex = timer->tindex;
452                }
453                drvmgr_interrupt_set_affinity(priv->dev, tindex,
454                                              _SMP_Get_online_processors());
455        }
456#endif
457
458        timer->tregs->ctrl |= GPTIMER_CTRL_IE;
459}
460
461static void gptimer_tlib_irq_unreg(struct tlib_dev *hand, tlib_isr_t func, void *data)
462{
463        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
464        struct gptimer_priv *priv = priv_from_timer(timer);
465
466        /* Turn off IRQ at source, unregister IRQ handler */
467        timer->tregs->ctrl &= ~GPTIMER_CTRL_IE;
468
469        if ( priv->separate_interrupt ) {
470                drvmgr_interrupt_unregister(priv->dev, timer->tindex,
471                                                func, data);
472        } else {
473                timer->tdev.isr_func = NULL;
474                priv->isr_installed--;
475                if (priv->isr_installed == 0) {
476                        drvmgr_interrupt_unregister(priv->dev, 0,
477                                                        gptimer_isr, priv);
478                }
479        }
480}
481
482static void gptimer_tlib_start(struct tlib_dev *hand, int once)
483{
484        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
485        unsigned int ctrl;
486
487        /* Load the selected frequency before starting Frequency */
488        ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_EN;
489        if ( once == 0 )
490                ctrl |= GPTIMER_CTRL_RS; /* Restart Timer */
491        timer->tregs->ctrl = ctrl | (timer->tregs->ctrl & timer->irq_ack_mask &
492                             ~GPTIMER_CTRL_RS);
493}
494
495static void gptimer_tlib_stop(struct tlib_dev *hand)
496{
497        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
498
499        /* Load the selected Frequency */
500        timer->tregs->ctrl &= ~(GPTIMER_CTRL_EN|GPTIMER_CTRL_IP);
501}
502
503static void gptimer_tlib_restart(struct tlib_dev *hand)
504{
505        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
506
507        timer->tregs->ctrl |= GPTIMER_CTRL_LD | GPTIMER_CTRL_EN;
508}
509
510static void gptimer_tlib_get_counter(
511        struct tlib_dev *hand,
512        unsigned int *counter)
513{
514        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
515
516        *counter = timer->tregs->value;
517}
518
519static void gptimer_tlib_get_widthmask(
520        struct tlib_dev *hand,
521        unsigned int *widthmask)
522{
523        struct gptimer_timer *timer = (struct gptimer_timer *)hand;
524        struct gptimer_priv *priv = priv_from_timer(timer);
525
526        *widthmask = priv->widthmask;
527}
528
529static struct tlib_drv gptimer_tlib_drv =
530{
531        .reset = gptimer_tlib_reset,
532        .get_freq = gptimer_tlib_get_freq,
533        .set_freq = gptimer_tlib_set_freq,
534        .irq_reg = gptimer_tlib_irq_reg,
535        .irq_unreg = gptimer_tlib_irq_unreg,
536        .start = gptimer_tlib_start,
537        .stop = gptimer_tlib_stop,
538        .restart = gptimer_tlib_restart,
539        .get_counter = gptimer_tlib_get_counter,
540        .custom = NULL,
541        .int_pend = gptimer_tlib_int_pend,
542        .get_widthmask = gptimer_tlib_get_widthmask,
543};
Note: See TracBrowser for help on using the repository browser.