~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/plat-orion/time.c

Version: ~ [ linux-6.1-rc7 ] ~ [ linux-6.0.10 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.80 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.156 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.225 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.267 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.300 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.334 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * arch/arm/plat-orion/time.c
  3  *
  4  * Marvell Orion SoC timer handling.
  5  *
  6  * This file is licensed under the terms of the GNU General Public
  7  * License version 2.  This program is licensed "as is" without any
  8  * warranty of any kind, whether express or implied.
  9  *
 10  * Timer 0 is used as free-running clocksource, while timer 1 is
 11  * used as clock_event_device.
 12  */
 13 
 14 #include <linux/kernel.h>
 15 #include <linux/timer.h>
 16 #include <linux/clockchips.h>
 17 #include <linux/interrupt.h>
 18 #include <linux/irq.h>
 19 #include <asm/sched_clock.h>
 20 
 21 /*
 22  * MBus bridge block registers.
 23  */
 24 #define BRIDGE_CAUSE_OFF        0x0110
 25 #define BRIDGE_MASK_OFF         0x0114
 26 #define  BRIDGE_INT_TIMER0       0x0002
 27 #define  BRIDGE_INT_TIMER1       0x0004
 28 
 29 
 30 /*
 31  * Timer block registers.
 32  */
 33 #define TIMER_CTRL_OFF          0x0000
 34 #define  TIMER0_EN               0x0001
 35 #define  TIMER0_RELOAD_EN        0x0002
 36 #define  TIMER1_EN               0x0004
 37 #define  TIMER1_RELOAD_EN        0x0008
 38 #define TIMER0_RELOAD_OFF       0x0010
 39 #define TIMER0_VAL_OFF          0x0014
 40 #define TIMER1_RELOAD_OFF       0x0018
 41 #define TIMER1_VAL_OFF          0x001c
 42 
 43 
 44 /*
 45  * SoC-specific data.
 46  */
 47 static void __iomem *bridge_base;
 48 static u32 bridge_timer1_clr_mask;
 49 static void __iomem *timer_base;
 50 
 51 
 52 /*
 53  * Number of timer ticks per jiffy.
 54  */
 55 static u32 ticks_per_jiffy;
 56 
 57 
 58 /*
 59  * Orion's sched_clock implementation. It has a resolution of
 60  * at least 7.5ns (133MHz TCLK).
 61  */
 62 
 63 static u32 notrace orion_read_sched_clock(void)
 64 {
 65         return ~readl(timer_base + TIMER0_VAL_OFF);
 66 }
 67 
 68 /*
 69  * Clockevent handling.
 70  */
 71 static int
 72 orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
 73 {
 74         unsigned long flags;
 75         u32 u;
 76 
 77         if (delta == 0)
 78                 return -ETIME;
 79 
 80         local_irq_save(flags);
 81 
 82         /*
 83          * Clear and enable clockevent timer interrupt.
 84          */
 85         writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
 86 
 87         u = readl(bridge_base + BRIDGE_MASK_OFF);
 88         u |= BRIDGE_INT_TIMER1;
 89         writel(u, bridge_base + BRIDGE_MASK_OFF);
 90 
 91         /*
 92          * Setup new clockevent timer value.
 93          */
 94         writel(delta, timer_base + TIMER1_VAL_OFF);
 95 
 96         /*
 97          * Enable the timer.
 98          */
 99         u = readl(timer_base + TIMER_CTRL_OFF);
100         u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
101         writel(u, timer_base + TIMER_CTRL_OFF);
102 
103         local_irq_restore(flags);
104 
105         return 0;
106 }
107 
108 static void
109 orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
110 {
111         unsigned long flags;
112         u32 u;
113 
114         local_irq_save(flags);
115         if (mode == CLOCK_EVT_MODE_PERIODIC) {
116                 /*
117                  * Setup timer to fire at 1/HZ intervals.
118                  */
119                 writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
120                 writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
121 
122                 /*
123                  * Enable timer interrupt.
124                  */
125                 u = readl(bridge_base + BRIDGE_MASK_OFF);
126                 writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
127 
128                 /*
129                  * Enable timer.
130                  */
131                 u = readl(timer_base + TIMER_CTRL_OFF);
132                 writel(u | TIMER1_EN | TIMER1_RELOAD_EN,
133                        timer_base + TIMER_CTRL_OFF);
134         } else {
135                 /*
136                  * Disable timer.
137                  */
138                 u = readl(timer_base + TIMER_CTRL_OFF);
139                 writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
140 
141                 /*
142                  * Disable timer interrupt.
143                  */
144                 u = readl(bridge_base + BRIDGE_MASK_OFF);
145                 writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
146 
147                 /*
148                  * ACK pending timer interrupt.
149                  */
150                 writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
151 
152         }
153         local_irq_restore(flags);
154 }
155 
156 static struct clock_event_device orion_clkevt = {
157         .name           = "orion_tick",
158         .features       = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
159         .rating         = 300,
160         .set_next_event = orion_clkevt_next_event,
161         .set_mode       = orion_clkevt_mode,
162 };
163 
164 static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
165 {
166         /*
167          * ACK timer interrupt and call event handler.
168          */
169         writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
170         orion_clkevt.event_handler(&orion_clkevt);
171 
172         return IRQ_HANDLED;
173 }
174 
175 static struct irqaction orion_timer_irq = {
176         .name           = "orion_tick",
177         .flags          = IRQF_DISABLED | IRQF_TIMER,
178         .handler        = orion_timer_interrupt
179 };
180 
181 void __init
182 orion_time_set_base(void __iomem *_timer_base)
183 {
184         timer_base = _timer_base;
185 }
186 
187 void __init
188 orion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask,
189                 unsigned int irq, unsigned int tclk)
190 {
191         u32 u;
192 
193         /*
194          * Set SoC-specific data.
195          */
196         bridge_base = _bridge_base;
197         bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
198 
199         ticks_per_jiffy = (tclk + HZ/2) / HZ;
200 
201         /*
202          * Set scale and timer for sched_clock.
203          */
204         setup_sched_clock(orion_read_sched_clock, 32, tclk);
205 
206         /*
207          * Setup free-running clocksource timer (interrupts
208          * disabled).
209          */
210         writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
211         writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
212         u = readl(bridge_base + BRIDGE_MASK_OFF);
213         writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
214         u = readl(timer_base + TIMER_CTRL_OFF);
215         writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
216         clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
217                 tclk, 300, 32, clocksource_mmio_readl_down);
218 
219         /*
220          * Setup clockevent timer (interrupt-driven).
221          */
222         setup_irq(irq, &orion_timer_irq);
223         orion_clkevt.cpumask = cpumask_of(0);
224         clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe);
225 }
226 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp