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