OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [arch/] [x86/] [kernel/] [mfgpt_32.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
2
 * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
3
 *
4
 * Copyright (C) 2006, Advanced Micro Devices, Inc.
5
 * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of version 2 of the GNU General Public License
9
 * as published by the Free Software Foundation.
10
 *
11
 * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
12
 */
13
 
14
/*
15
 * We are using the 32Khz input clock - its the only one that has the
16
 * ranges we find desirable.  The following table lists the suitable
17
 * divisors and the associated hz, minimum interval
18
 * and the maximum interval:
19
 *
20
 *  Divisor   Hz      Min Delta (S) Max Delta (S)
21
 *   1        32000     .0005          2.048
22
 *   2        16000      .001          4.096
23
 *   4         8000      .002          8.192
24
 *   8         4000      .004         16.384
25
 *   16        2000      .008         32.768
26
 *   32        1000      .016         65.536
27
 *   64         500      .032        131.072
28
 *  128         250      .064        262.144
29
 *  256         125      .128        524.288
30
 */
31
 
32
#include <linux/kernel.h>
33
#include <linux/interrupt.h>
34
#include <linux/module.h>
35
#include <asm/geode.h>
36
 
37
#define F_AVAIL    0x01
38
 
39
static struct mfgpt_timer_t {
40
        int flags;
41
        struct module *owner;
42
} mfgpt_timers[MFGPT_MAX_TIMERS];
43
 
44
/* Selected from the table above */
45
 
46
#define MFGPT_DIVISOR 16
47
#define MFGPT_SCALE  4     /* divisor = 2^(scale) */
48
#define MFGPT_HZ  (32000 / MFGPT_DIVISOR)
49
#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
50
 
51
#ifdef CONFIG_GEODE_MFGPT_TIMER
52
static int __init mfgpt_timer_setup(void);
53
#else
54
#define mfgpt_timer_setup() (0)
55
#endif
56
 
57
/* Allow for disabling of MFGPTs */
58
static int disable;
59
static int __init mfgpt_disable(char *s)
60
{
61
        disable = 1;
62
        return 1;
63
}
64
__setup("nomfgpt", mfgpt_disable);
65
 
66
/*
67
 * Check whether any MFGPTs are available for the kernel to use.  In most
68
 * cases, firmware that uses AMD's VSA code will claim all timers during
69
 * bootup; we certainly don't want to take them if they're already in use.
70
 * In other cases (such as with VSAless OpenFirmware), the system firmware
71
 * leaves timers available for us to use.
72
 */
73
int __init geode_mfgpt_detect(void)
74
{
75
        int count = 0, i;
76
        u16 val;
77
 
78
        if (disable) {
79
                printk(KERN_INFO "geode-mfgpt:  Skipping MFGPT setup\n");
80
                return 0;
81
        }
82
 
83
        for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
84
                val = geode_mfgpt_read(i, MFGPT_REG_SETUP);
85
                if (!(val & MFGPT_SETUP_SETUP)) {
86
                        mfgpt_timers[i].flags = F_AVAIL;
87
                        count++;
88
                }
89
        }
90
 
91
        /* set up clock event device, if desired */
92
        i = mfgpt_timer_setup();
93
 
94
        return count;
95
}
96
 
97
int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
98
{
99
        u32 msr, mask, value, dummy;
100
        int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
101
 
102
        if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
103
                return -EIO;
104
 
105
        /*
106
         * The register maps for these are described in sections 6.17.1.x of
107
         * the AMD Geode CS5536 Companion Device Data Book.
108
         */
109
        switch (event) {
110
        case MFGPT_EVENT_RESET:
111
                /*
112
                 * XXX: According to the docs, we cannot reset timers above
113
                 * 6; that is, resets for 7 and 8 will be ignored.  Is this
114
                 * a problem?   -dilinger
115
                 */
116
                msr = MFGPT_NR_MSR;
117
                mask = 1 << (timer + 24);
118
                break;
119
 
120
        case MFGPT_EVENT_NMI:
121
                msr = MFGPT_NR_MSR;
122
                mask = 1 << (timer + shift);
123
                break;
124
 
125
        case MFGPT_EVENT_IRQ:
126
                msr = MFGPT_IRQ_MSR;
127
                mask = 1 << (timer + shift);
128
                break;
129
 
130
        default:
131
                return -EIO;
132
        }
133
 
134
        rdmsr(msr, value, dummy);
135
 
136
        if (enable)
137
                value |= mask;
138
        else
139
                value &= ~mask;
140
 
141
        wrmsr(msr, value, dummy);
142
        return 0;
143
}
144
 
145
int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable)
146
{
147
        u32 val, dummy;
148
        int offset;
149
 
150
        if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
151
                return -EIO;
152
 
153
        if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
154
                return -EIO;
155
 
156
        rdmsr(MSR_PIC_ZSEL_LOW, val, dummy);
157
 
158
        offset = (timer % 4) * 4;
159
 
160
        val &= ~((0xF << offset) | (0xF << (offset + 16)));
161
 
162
        if (enable) {
163
                val |= (irq & 0x0F) << (offset);
164
                val |= (irq & 0x0F) << (offset + 16);
165
        }
166
 
167
        wrmsr(MSR_PIC_ZSEL_LOW, val, dummy);
168
        return 0;
169
}
170
 
171
static int mfgpt_get(int timer, struct module *owner)
172
{
173
        mfgpt_timers[timer].flags &= ~F_AVAIL;
174
        mfgpt_timers[timer].owner = owner;
175
        printk(KERN_INFO "geode-mfgpt:  Registered timer %d\n", timer);
176
        return timer;
177
}
178
 
179
int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner)
180
{
181
        int i;
182
 
183
        if (!geode_get_dev_base(GEODE_DEV_MFGPT))
184
                return -ENODEV;
185
        if (timer >= MFGPT_MAX_TIMERS)
186
                return -EIO;
187
 
188
        if (timer < 0) {
189
                /* Try to find an available timer */
190
                for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
191
                        if (mfgpt_timers[i].flags & F_AVAIL)
192
                                return mfgpt_get(i, owner);
193
 
194
                        if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
195
                                break;
196
                }
197
        } else {
198
                /* If they requested a specific timer, try to honor that */
199
                if (mfgpt_timers[timer].flags & F_AVAIL)
200
                        return mfgpt_get(timer, owner);
201
        }
202
 
203
        /* No timers available - too bad */
204
        return -1;
205
}
206
 
207
 
208
#ifdef CONFIG_GEODE_MFGPT_TIMER
209
 
210
/*
211
 * The MFPGT timers on the CS5536 provide us with suitable timers to use
212
 * as clock event sources - not as good as a HPET or APIC, but certainly
213
 * better then the PIT.  This isn't a general purpose MFGPT driver, but
214
 * a simplified one designed specifically to act as a clock event source.
215
 * For full details about the MFGPT, please consult the CS5536 data sheet.
216
 */
217
 
218
#include <linux/clocksource.h>
219
#include <linux/clockchips.h>
220
 
221
static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
222
static u16 mfgpt_event_clock;
223
 
224
static int irq = 7;
225
static int __init mfgpt_setup(char *str)
226
{
227
        get_option(&str, &irq);
228
        return 1;
229
}
230
__setup("mfgpt_irq=", mfgpt_setup);
231
 
232
static inline void mfgpt_disable_timer(u16 clock)
233
{
234
        u16 val = geode_mfgpt_read(clock, MFGPT_REG_SETUP);
235
        geode_mfgpt_write(clock, MFGPT_REG_SETUP, val & ~MFGPT_SETUP_CNTEN);
236
}
237
 
238
static int mfgpt_next_event(unsigned long, struct clock_event_device *);
239
static void mfgpt_set_mode(enum clock_event_mode, struct clock_event_device *);
240
 
241
static struct clock_event_device mfgpt_clockevent = {
242
        .name = "mfgpt-timer",
243
        .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
244
        .set_mode = mfgpt_set_mode,
245
        .set_next_event = mfgpt_next_event,
246
        .rating = 250,
247
        .cpumask = CPU_MASK_ALL,
248
        .shift = 32
249
};
250
 
251
static inline void mfgpt_start_timer(u16 clock, u16 delta)
252
{
253
        geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta);
254
        geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
255
 
256
        geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
257
                          MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
258
}
259
 
260
static void mfgpt_set_mode(enum clock_event_mode mode,
261
                           struct clock_event_device *evt)
262
{
263
        mfgpt_disable_timer(mfgpt_event_clock);
264
 
265
        if (mode == CLOCK_EVT_MODE_PERIODIC)
266
                mfgpt_start_timer(mfgpt_event_clock, MFGPT_PERIODIC);
267
 
268
        mfgpt_tick_mode = mode;
269
}
270
 
271
static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
272
{
273
        mfgpt_start_timer(mfgpt_event_clock, delta);
274
        return 0;
275
}
276
 
277
/* Assume (foolishly?), that this interrupt was due to our tick */
278
 
279
static irqreturn_t mfgpt_tick(int irq, void *dev_id)
280
{
281
        /* Turn off the clock (and clear the event) */
282
        mfgpt_disable_timer(mfgpt_event_clock);
283
 
284
        if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
285
                return IRQ_HANDLED;
286
 
287
        /* Clear the counter */
288
        geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
289
 
290
        /* Restart the clock in periodic mode */
291
 
292
        if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) {
293
                geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
294
                                  MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
295
        }
296
 
297
        mfgpt_clockevent.event_handler(&mfgpt_clockevent);
298
        return IRQ_HANDLED;
299
}
300
 
301
static struct irqaction mfgptirq  = {
302
        .handler = mfgpt_tick,
303
        .flags = IRQF_DISABLED | IRQF_NOBALANCING,
304
        .mask = CPU_MASK_NONE,
305
        .name = "mfgpt-timer"
306
};
307
 
308
static int __init mfgpt_timer_setup(void)
309
{
310
        int timer, ret;
311
        u16 val;
312
 
313
        timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING,
314
                        THIS_MODULE);
315
        if (timer < 0) {
316
                printk(KERN_ERR
317
                       "mfgpt-timer:  Could not allocate a MFPGT timer\n");
318
                return -ENODEV;
319
        }
320
 
321
        mfgpt_event_clock = timer;
322
 
323
        /* Set up the IRQ on the MFGPT side */
324
        if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) {
325
                printk(KERN_ERR "mfgpt-timer:  Could not set up IRQ %d\n", irq);
326
                return -EIO;
327
        }
328
 
329
        /* And register it with the kernel */
330
        ret = setup_irq(irq, &mfgptirq);
331
 
332
        if (ret) {
333
                printk(KERN_ERR
334
                       "mfgpt-timer:  Unable to set up the interrupt.\n");
335
                goto err;
336
        }
337
 
338
        /* Set the clock scale and enable the event mode for CMP2 */
339
        val = MFGPT_SCALE | (3 << 8);
340
 
341
        geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val);
342
 
343
        /* Set up the clock event */
344
        mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC, 32);
345
        mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
346
                        &mfgpt_clockevent);
347
        mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
348
                        &mfgpt_clockevent);
349
 
350
        printk(KERN_INFO
351
               "mfgpt-timer:  registering the MFGT timer as a clock event.\n");
352
        clockevents_register_device(&mfgpt_clockevent);
353
 
354
        return 0;
355
 
356
err:
357
        geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq);
358
        printk(KERN_ERR
359
               "mfgpt-timer:  Unable to set up the MFGPT clock source\n");
360
        return -EIO;
361
}
362
 
363
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.