source: mainline/kernel/genarch/src/drivers/am335x/timer.c@ bdae198

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bdae198 was a940f1d, checked in by Maurizio Lombardi <m.lombardi85@…>, 12 years ago

am335x: the NEWIRQAGR bit of the ISR must be set *after* calling the interrupt handler which
deasserts the interrupt condition at the peripheral side.

Fixes the problem with ghost interrupts in the timer driver.

  • Property mode set to 100644
File size: 4.9 KB
Line 
1/*
2 * Copyright (c) 2012 Maurizio Lombardi
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup genarch
29 * @{
30 */
31/**
32 * @file
33 * @brief Texas Instruments AM335x timer driver.
34 */
35
36#include <genarch/drivers/am335x/timer.h>
37#include <mm/km.h>
38#include <errno.h>
39
40typedef enum {
41 REG_TCLR = 0x00,
42 REG_TCRR = 0x01,
43 REG_TLDR = 0x02,
44 REG_TTGR = 0x04
45} timer_reg_t;
46
47typedef struct timer_regs_mmap {
48 uintptr_t base;
49 size_t size;
50} timer_regs_mmap_t;
51
52static const timer_regs_mmap_t regs_map[TIMERS_MAX] = {
53 { .base = AM335x_DMTIMER0_BASE_ADDRESS, .size = AM335x_DMTIMER0_SIZE },
54 {0, 0}, /* DMTIMER1 is not supported by this driver */
55 { .base = AM335x_DMTIMER2_BASE_ADDRESS, .size = AM335x_DMTIMER2_SIZE },
56 { .base = AM335x_DMTIMER3_BASE_ADDRESS, .size = AM335x_DMTIMER3_SIZE },
57 { .base = AM335x_DMTIMER4_BASE_ADDRESS, .size = AM335x_DMTIMER4_SIZE },
58 { .base = AM335x_DMTIMER5_BASE_ADDRESS, .size = AM335x_DMTIMER5_SIZE },
59 { .base = AM335x_DMTIMER6_BASE_ADDRESS, .size = AM335x_DMTIMER6_SIZE },
60 { .base = AM335x_DMTIMER7_BASE_ADDRESS, .size = AM335x_DMTIMER7_SIZE },
61};
62
63static void
64write_register_posted(am335x_timer_t *timer, timer_reg_t reg, uint32_t value)
65{
66 am335x_timer_regs_t *regs = timer->regs;
67
68 while (regs->twps & reg);
69
70 switch (reg) {
71 default:
72 return;
73 case REG_TCLR:
74 regs->tclr = value;
75 break;
76 case REG_TCRR:
77 regs->tcrr = value;
78 break;
79 case REG_TLDR:
80 regs->tldr = value;
81 break;
82 }
83}
84
85int
86am335x_timer_init(am335x_timer_t *timer, am335x_timer_id_t id, unsigned hz,
87 unsigned srcclk_hz)
88{
89 uintptr_t base_addr;
90 size_t size;
91
92 ASSERT(id < TIMERS_MAX);
93 ASSERT(timer != NULL);
94
95 if (id == DMTIMER1_1MS)
96 return ENOTSUP; /* Not supported yet */
97
98 base_addr = regs_map[id].base;
99 size = regs_map[id].size;
100
101 timer->regs = (void *) km_map(base_addr, size, PAGE_NOT_CACHEABLE);
102 ASSERT(timer->regs != NULL);
103
104 timer->id = id;
105
106 am335x_timer_regs_t *regs = timer->regs;
107
108 /* Enable the posted mode of operation */
109 regs->tsicr |= AM335x_TIMER_TSICR_POSTED_FLAG;
110
111 /* Stop the timer */
112 am335x_timer_stop(timer);
113
114 /* Perform a soft reset */
115 am335x_timer_reset(timer);
116
117 unsigned tclr = regs->tclr;
118
119 /* Disable compare mode */
120 tclr &= ~AM335x_TIMER_TCLR_CE_FLAG;
121
122 /* Enable auto-reload mode */
123 tclr |= AM335x_TIMER_TCLR_AR_FLAG;
124
125 write_register_posted(timer, REG_TCLR, tclr);
126
127 /* Disable the emulation mode */
128 regs->tiocp_cfg |= AM335x_TIMER_TIOCPCFG_EMUFREE_FLAG;
129
130 unsigned const count = 0xFFFFFFFF - (srcclk_hz / hz + 1);
131 write_register_posted(timer, REG_TCRR, count);
132 write_register_posted(timer, REG_TLDR, count);
133
134 return EOK;
135}
136
137void
138am335x_timer_intr_ack(am335x_timer_t *timer)
139{
140 /* Clear pending OVF event */
141 timer->regs->irqstatus |= AM335x_TIMER_IRQSTATUS_OVF_FLAG;
142}
143
144void
145am335x_timer_reset(am335x_timer_t *timer)
146{
147 /* Initiate soft reset */
148 timer->regs->tiocp_cfg |= AM335x_TIMER_TIOCPCFG_SOFTRESET_FLAG;
149 /* Wait until the reset is done */
150 while (timer->regs->tiocp_cfg & AM335x_TIMER_TIOCPCFG_SOFTRESET_FLAG);
151}
152
153void
154am335x_timer_stop(am335x_timer_t *timer)
155{
156 /* Disable the interrupt */
157 timer->regs->irqenable_clr |= AM335x_TIMER_IRQENABLE_CLR_OVF_FLAG;
158 timer->regs->irqwakeen &= ~AM335x_TIMER_IRQWAKEEN_OVF_FLAG;
159 /* Stop the timer */
160 write_register_posted(timer, REG_TCLR,
161 timer->regs->tclr & ~AM335x_TIMER_TCLR_ST_FLAG);
162}
163
164void
165am335x_timer_start(am335x_timer_t *timer)
166{
167 /* Enable the interrupt */
168 timer->regs->irqenable_set |= AM335x_TIMER_IRQENABLE_SET_OVF_FLAG;
169 timer->regs->irqwakeen |= AM335x_TIMER_IRQWAKEEN_OVF_FLAG;
170 /* Start the clock */
171 write_register_posted(timer, REG_TCLR,
172 timer->regs->tclr | AM335x_TIMER_TCLR_ST_FLAG);
173}
174
175/**
176 * @}
177 */
178
Note: See TracBrowser for help on using the repository browser.