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