[cd64fbf] | 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 |
---|
[4a7d1026] | 31 | * http://www.rtems.org/license/LICENSE. |
---|
[cd64fbf] | 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> |
---|
[5823bae8] | 40 | #include <bsp/gptimer.h> |
---|
| 41 | #include <bsp/tlib.h> |
---|
[cd64fbf] | 42 | |
---|
[c77e3cb] | 43 | #if defined(LEON3) |
---|
[cd64fbf] | 44 | #include <leon.h> |
---|
| 45 | #endif |
---|
| 46 | |
---|
| 47 | #ifdef GPTIMER_INFO_AVAIL |
---|
| 48 | #include <stdio.h> |
---|
| 49 | #endif |
---|
| 50 | |
---|
[f600458d] | 51 | #ifdef RTEMS_SMP |
---|
| 52 | #include <rtems/score/processormask.h> |
---|
| 53 | #include <rtems/score/smpimpl.h> |
---|
| 54 | #endif |
---|
| 55 | |
---|
[cd64fbf] | 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 */ |
---|
| 87 | struct 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 */ |
---|
[16d6e42] | 92 | unsigned char irq_ack_mask; |
---|
[cd64fbf] | 93 | }; |
---|
| 94 | |
---|
| 95 | /* GPTIMER Core private */ |
---|
| 96 | struct gptimer_priv { |
---|
| 97 | struct drvmgr_dev *dev; |
---|
| 98 | struct gptimer_regs *regs; |
---|
| 99 | unsigned int base_clk; |
---|
| 100 | unsigned int base_freq; |
---|
[78e56096] | 101 | unsigned int widthmask; |
---|
[e3c9937] | 102 | char separate_interrupt; |
---|
| 103 | char isr_installed; |
---|
[cd64fbf] | 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 | |
---|
| 110 | void gptimer_isr(void *data); |
---|
| 111 | |
---|
| 112 | #if 0 |
---|
| 113 | void 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 */ |
---|
[bf6fe958] | 127 | static struct tlib_drv gptimer_tlib_drv; |
---|
[cd64fbf] | 128 | int gptimer_device_init(struct gptimer_priv *priv); |
---|
| 129 | |
---|
| 130 | int gptimer_init1(struct drvmgr_dev *dev); |
---|
| 131 | #ifdef GPTIMER_INFO_AVAIL |
---|
| 132 | static 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 | |
---|
| 141 | struct drvmgr_drv_ops gptimer_ops = |
---|
| 142 | { |
---|
| 143 | .init = {gptimer_init1, NULL, NULL, NULL}, |
---|
| 144 | .remove = NULL, |
---|
| 145 | .info = GTIMER_INFO_FUNC, |
---|
| 146 | }; |
---|
| 147 | |
---|
| 148 | struct amba_dev_id gptimer_ids[] = |
---|
| 149 | { |
---|
| 150 | {VENDOR_GAISLER, GAISLER_GPTIMER}, |
---|
[fa12bb43] | 151 | {VENDOR_GAISLER, GAISLER_GRTIMER}, |
---|
[cd64fbf] | 152 | {0, 0} /* Mark end of table */ |
---|
| 153 | }; |
---|
| 154 | |
---|
| 155 | struct 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 | |
---|
| 172 | void gptimer_register_drv (void) |
---|
| 173 | { |
---|
| 174 | DBG("Registering GPTIMER driver\n"); |
---|
| 175 | drvmgr_drv_register(&gptimer_drv_info.general); |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | int 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; |
---|
[7ebc28cd] | 188 | unsigned char irq_ack_mask; |
---|
[cd64fbf] | 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 |
---|
[4d3e70f4] | 218 | value = drvmgr_dev_key_get(dev, "timerStart", DRVMGR_KT_INT); |
---|
[cd64fbf] | 219 | if ( value) { |
---|
| 220 | timer_start = value->i; |
---|
| 221 | timer_cnt = timer_hw_cnt - timer_start; |
---|
| 222 | } |
---|
[4d3e70f4] | 223 | value = drvmgr_dev_key_get(dev, "timerCnt", DRVMGR_KT_INT); |
---|
[cd64fbf] | 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 | |
---|
[c77e3cb] | 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); |
---|
[cd64fbf] | 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 | */ |
---|
[4d3e70f4] | 257 | value = drvmgr_dev_key_get(priv->dev, "prescaler", DRVMGR_KT_INT); |
---|
[cd64fbf] | 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 | |
---|
[7ebc28cd] | 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 | */ |
---|
[acf7047e] | 268 | priv->regs->timer[timer_start].ctrl = GPTIMER_CTRL_IP; |
---|
| 269 | if ((priv->regs->timer[timer_start].ctrl & GPTIMER_CTRL_IP) != 0) |
---|
[7ebc28cd] | 270 | irq_ack_mask = ~GPTIMER_CTRL_IP; |
---|
| 271 | else |
---|
| 272 | irq_ack_mask = ~0; |
---|
| 273 | |
---|
[78e56096] | 274 | /* Probe timer register width mask */ |
---|
| 275 | priv->regs->timer[timer_start].value = 0xffffffff; |
---|
| 276 | priv->widthmask = priv->regs->timer[timer_start].value; |
---|
| 277 | |
---|
[cd64fbf] | 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 = ®s->timer[(int)timer->tindex]; |
---|
| 284 | timer->tdev.drv = &gptimer_tlib_drv; |
---|
[7ebc28cd] | 285 | timer->irq_ack_mask = irq_ack_mask; |
---|
[cd64fbf] | 286 | |
---|
| 287 | /* Register Timer at Timer Library */ |
---|
[c77e3cb] | 288 | tlib_dev_reg(&timer->tdev); |
---|
[cd64fbf] | 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 | */ |
---|
[256e083f] | 296 | priv->separate_interrupt = (regs->cfg & GPTIMER_CFG_SI) != 0; |
---|
[cd64fbf] | 297 | |
---|
| 298 | return DRVMGR_OK; |
---|
| 299 | } |
---|
| 300 | |
---|
| 301 | #ifdef GPTIMER_INFO_AVAIL |
---|
| 302 | static 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 | |
---|
| 342 | static 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 | |
---|
[f37a3c2] | 350 | static int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack) |
---|
[16d6e42] | 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 | |
---|
[cd64fbf] | 365 | void 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++) { |
---|
[4d249b9f] | 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. |
---|
[cd64fbf] | 376 | */ |
---|
[16d6e42] | 377 | if (priv->timers[i].tdev.isr_func) { |
---|
[cd64fbf] | 378 | priv->timers[i].tdev.isr_func( |
---|
| 379 | priv->timers[i].tdev.isr_data); |
---|
| 380 | } |
---|
[4d249b9f] | 381 | gptimer_tlib_int_pend((void *)&priv->timers[i], 1); |
---|
[cd64fbf] | 382 | } |
---|
| 383 | } |
---|
| 384 | } |
---|
| 385 | |
---|
[f37a3c2] | 386 | static void gptimer_tlib_reset(struct tlib_dev *hand) |
---|
[cd64fbf] | 387 | { |
---|
| 388 | struct gptimer_timer *timer = (struct gptimer_timer *)hand; |
---|
| 389 | |
---|
[61cc024a] | 390 | timer->tregs->ctrl = (timer->tregs->ctrl & timer->irq_ack_mask) & |
---|
| 391 | GPTIMER_CTRL_IP; |
---|
[cd64fbf] | 392 | timer->tregs->reload = 0xffffffff; |
---|
| 393 | timer->tregs->ctrl = GPTIMER_CTRL_LD; |
---|
| 394 | } |
---|
| 395 | |
---|
[bf6fe958] | 396 | static void gptimer_tlib_get_freq( |
---|
[cd64fbf] | 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 | |
---|
[bf6fe958] | 411 | static int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate) |
---|
[cd64fbf] | 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 | |
---|
[f600458d] | 424 | static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags) |
---|
[cd64fbf] | 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); |
---|
[e3c9937] | 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++; |
---|
[cd64fbf] | 443 | } |
---|
| 444 | |
---|
[f600458d] | 445 | #if RTEMS_SMP |
---|
| 446 | if (flags & TLIB_FLAGS_BROADCAST) { |
---|
[8927c0f] | 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, |
---|
[76d1198] | 454 | _SMP_Get_online_processors()); |
---|
[f600458d] | 455 | } |
---|
| 456 | #endif |
---|
| 457 | |
---|
[cd64fbf] | 458 | timer->tregs->ctrl |= GPTIMER_CTRL_IE; |
---|
| 459 | } |
---|
| 460 | |
---|
[bf6fe958] | 461 | static void gptimer_tlib_irq_unreg(struct tlib_dev *hand, tlib_isr_t func, void *data) |
---|
[cd64fbf] | 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; |
---|
[e3c9937] | 474 | priv->isr_installed--; |
---|
| 475 | if (priv->isr_installed == 0) { |
---|
| 476 | drvmgr_interrupt_unregister(priv->dev, 0, |
---|
| 477 | gptimer_isr, priv); |
---|
| 478 | } |
---|
[cd64fbf] | 479 | } |
---|
| 480 | } |
---|
| 481 | |
---|
[bf6fe958] | 482 | static void gptimer_tlib_start(struct tlib_dev *hand, int once) |
---|
[cd64fbf] | 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 */ |
---|
[61cc024a] | 491 | timer->tregs->ctrl = ctrl | (timer->tregs->ctrl & timer->irq_ack_mask & |
---|
| 492 | ~GPTIMER_CTRL_RS); |
---|
[cd64fbf] | 493 | } |
---|
| 494 | |
---|
[bf6fe958] | 495 | static void gptimer_tlib_stop(struct tlib_dev *hand) |
---|
[cd64fbf] | 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 | |
---|
[bf6fe958] | 503 | static void gptimer_tlib_restart(struct tlib_dev *hand) |
---|
[cd64fbf] | 504 | { |
---|
| 505 | struct gptimer_timer *timer = (struct gptimer_timer *)hand; |
---|
| 506 | |
---|
| 507 | timer->tregs->ctrl |= GPTIMER_CTRL_LD | GPTIMER_CTRL_EN; |
---|
| 508 | } |
---|
| 509 | |
---|
[f37a3c2] | 510 | static void gptimer_tlib_get_counter( |
---|
| 511 | struct tlib_dev *hand, |
---|
| 512 | unsigned int *counter) |
---|
[cd64fbf] | 513 | { |
---|
| 514 | struct gptimer_timer *timer = (struct gptimer_timer *)hand; |
---|
| 515 | |
---|
| 516 | *counter = timer->tregs->value; |
---|
| 517 | } |
---|
| 518 | |
---|
[78e56096] | 519 | static 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 | |
---|
[bf6fe958] | 529 | static struct tlib_drv gptimer_tlib_drv = |
---|
[cd64fbf] | 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, |
---|
[16d6e42] | 541 | .int_pend = gptimer_tlib_int_pend, |
---|
[78e56096] | 542 | .get_widthmask = gptimer_tlib_get_widthmask, |
---|
[cd64fbf] | 543 | }; |
---|