diff options
author | Ivan Kokshaysky <ink@jurassic.park.msu.ru> | 2009-01-15 16:51:19 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-15 19:39:40 -0500 |
commit | 5f7dc5d75076fd1c1fc6bc09f2467509d20db24a (patch) | |
tree | c105f8463607381acd7d02bdda75641b3f497e37 | |
parent | 2f88d151cb8e73587983d7feccd70672ff6730fe (diff) |
alpha: fix RTC on marvel
Unlike other alphas, marvel doesn't have real PC-style CMOS clock hardware
- RTC accesses are emulated via PAL calls. Unfortunately, for unknown
reason these calls work only on CPU #0. So current implementation for
arbitrary CPU makes CMOS_READ/WRITE to be executed on CPU #0 via IPI.
However, for obvious reason this doesn't work with standard
get/set_rtc_time() functions, where a bunch of CMOS accesses is done with
disabled interrupts.
Solved by making the IPI calls for entire get/set_rtc_time() functions,
not for individual CMOS accesses. Which is also a lot more effective
performance-wise.
The patch is largely based on the code from Jay Estabrook.
My changes:
- tweak asm-generic/rtc.h by adding a couple of #defines to
avoid a massive code duplication in arch/alpha/include/asm/rtc.h;
- sys_marvel.c: fix get/set_rtc_time() return values (Jay's FIXMEs).
NOTE: this fixes *only* LIB_RTC drivers. Legacy (CONFIG_RTC) driver
wont't work on marvel. Actually I think that we should just disable
CONFIG_RTC on alpha (maybe in 2.6.30?), like most other arches - AFAIK,
all modern distributions use LIB_RTC anyway.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Richard Henderson <rth@twiddle.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/alpha/include/asm/machvec.h | 4 | ||||
-rw-r--r-- | arch/alpha/include/asm/rtc.h | 12 | ||||
-rw-r--r-- | arch/alpha/kernel/core_marvel.c | 10 | ||||
-rw-r--r-- | arch/alpha/kernel/machvec_impl.h | 5 | ||||
-rw-r--r-- | arch/alpha/kernel/proto.h | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/sys_jensen.c | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/sys_marvel.c | 56 | ||||
-rw-r--r-- | arch/alpha/kernel/time.c | 10 | ||||
-rw-r--r-- | include/asm-generic/rtc.h | 14 |
9 files changed, 98 insertions, 17 deletions
diff --git a/arch/alpha/include/asm/machvec.h b/arch/alpha/include/asm/machvec.h index a86c083cdf7f..fea4ea75b79d 100644 --- a/arch/alpha/include/asm/machvec.h +++ b/arch/alpha/include/asm/machvec.h | |||
@@ -21,6 +21,7 @@ struct pci_dev; | |||
21 | struct pci_ops; | 21 | struct pci_ops; |
22 | struct pci_controller; | 22 | struct pci_controller; |
23 | struct _alpha_agp_info; | 23 | struct _alpha_agp_info; |
24 | struct rtc_time; | ||
24 | 25 | ||
25 | struct alpha_machine_vector | 26 | struct alpha_machine_vector |
26 | { | 27 | { |
@@ -94,6 +95,9 @@ struct alpha_machine_vector | |||
94 | 95 | ||
95 | struct _alpha_agp_info *(*agp_info)(void); | 96 | struct _alpha_agp_info *(*agp_info)(void); |
96 | 97 | ||
98 | unsigned int (*rtc_get_time)(struct rtc_time *); | ||
99 | int (*rtc_set_time)(struct rtc_time *); | ||
100 | |||
97 | const char *vector_name; | 101 | const char *vector_name; |
98 | 102 | ||
99 | /* NUMA information */ | 103 | /* NUMA information */ |
diff --git a/arch/alpha/include/asm/rtc.h b/arch/alpha/include/asm/rtc.h index 4e854b1333eb..1f7fba671ae6 100644 --- a/arch/alpha/include/asm/rtc.h +++ b/arch/alpha/include/asm/rtc.h | |||
@@ -1,9 +1,15 @@ | |||
1 | #ifndef _ALPHA_RTC_H | 1 | #ifndef _ALPHA_RTC_H |
2 | #define _ALPHA_RTC_H | 2 | #define _ALPHA_RTC_H |
3 | 3 | ||
4 | /* | 4 | #if defined(CONFIG_ALPHA_GENERIC) |
5 | * Alpha uses the default access methods for the RTC. | 5 | # define get_rtc_time alpha_mv.rtc_get_time |
6 | */ | 6 | # define set_rtc_time alpha_mv.rtc_set_time |
7 | #else | ||
8 | # if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP) | ||
9 | # define get_rtc_time marvel_get_rtc_time | ||
10 | # define set_rtc_time marvel_set_rtc_time | ||
11 | # endif | ||
12 | #endif | ||
7 | 13 | ||
8 | #include <asm-generic/rtc.h> | 14 | #include <asm-generic/rtc.h> |
9 | 15 | ||
diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c index 9cd8dca742a7..e302daecbe56 100644 --- a/arch/alpha/kernel/core_marvel.c +++ b/arch/alpha/kernel/core_marvel.c | |||
@@ -658,16 +658,8 @@ __marvel_rtc_io(u8 b, unsigned long addr, int write) | |||
658 | rtc_access.data = bcd2bin(b); | 658 | rtc_access.data = bcd2bin(b); |
659 | rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */ | 659 | rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */ |
660 | 660 | ||
661 | #ifdef CONFIG_SMP | ||
662 | if (smp_processor_id() != boot_cpuid) | ||
663 | smp_call_function_single(boot_cpuid, | ||
664 | __marvel_access_rtc, | ||
665 | &rtc_access, 1); | ||
666 | else | ||
667 | __marvel_access_rtc(&rtc_access); | ||
668 | #else | ||
669 | __marvel_access_rtc(&rtc_access); | 661 | __marvel_access_rtc(&rtc_access); |
670 | #endif | 662 | |
671 | ret = bin2bcd(rtc_access.data); | 663 | ret = bin2bcd(rtc_access.data); |
672 | break; | 664 | break; |
673 | 665 | ||
diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h index 466c9dff8181..512685f78097 100644 --- a/arch/alpha/kernel/machvec_impl.h +++ b/arch/alpha/kernel/machvec_impl.h | |||
@@ -40,7 +40,10 @@ | |||
40 | #define CAT1(x,y) x##y | 40 | #define CAT1(x,y) x##y |
41 | #define CAT(x,y) CAT1(x,y) | 41 | #define CAT(x,y) CAT1(x,y) |
42 | 42 | ||
43 | #define DO_DEFAULT_RTC .rtc_port = 0x70 | 43 | #define DO_DEFAULT_RTC \ |
44 | .rtc_port = 0x70, \ | ||
45 | .rtc_get_time = common_get_rtc_time, \ | ||
46 | .rtc_set_time = common_set_rtc_time | ||
44 | 47 | ||
45 | #define DO_EV4_MMU \ | 48 | #define DO_EV4_MMU \ |
46 | .max_asn = EV4_MAX_ASN, \ | 49 | .max_asn = EV4_MAX_ASN, \ |
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 708d5ca87782..fe14c6747cd6 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h | |||
@@ -145,6 +145,8 @@ extern void smp_percpu_timer_interrupt(struct pt_regs *); | |||
145 | extern irqreturn_t timer_interrupt(int irq, void *dev); | 145 | extern irqreturn_t timer_interrupt(int irq, void *dev); |
146 | extern void common_init_rtc(void); | 146 | extern void common_init_rtc(void); |
147 | extern unsigned long est_cycle_freq; | 147 | extern unsigned long est_cycle_freq; |
148 | extern unsigned int common_get_rtc_time(struct rtc_time *time); | ||
149 | extern int common_set_rtc_time(struct rtc_time *time); | ||
148 | 150 | ||
149 | /* smc37c93x.c */ | 151 | /* smc37c93x.c */ |
150 | extern void SMC93x_Init(void); | 152 | extern void SMC93x_Init(void); |
diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c index 2c3de97de46c..e2516f9a8967 100644 --- a/arch/alpha/kernel/sys_jensen.c +++ b/arch/alpha/kernel/sys_jensen.c | |||
@@ -261,6 +261,8 @@ struct alpha_machine_vector jensen_mv __initmv = { | |||
261 | .machine_check = jensen_machine_check, | 261 | .machine_check = jensen_machine_check, |
262 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | 262 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, |
263 | .rtc_port = 0x170, | 263 | .rtc_port = 0x170, |
264 | .rtc_get_time = common_get_rtc_time, | ||
265 | .rtc_set_time = common_set_rtc_time, | ||
264 | 266 | ||
265 | .nr_irqs = 16, | 267 | .nr_irqs = 16, |
266 | .device_interrupt = jensen_device_interrupt, | 268 | .device_interrupt = jensen_device_interrupt, |
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c index 828449cd2636..c5a1a2438c67 100644 --- a/arch/alpha/kernel/sys_marvel.c +++ b/arch/alpha/kernel/sys_marvel.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <asm/hwrpb.h> | 23 | #include <asm/hwrpb.h> |
24 | #include <asm/tlbflush.h> | 24 | #include <asm/tlbflush.h> |
25 | #include <asm/vga.h> | 25 | #include <asm/vga.h> |
26 | #include <asm/rtc.h> | ||
26 | 27 | ||
27 | #include "proto.h" | 28 | #include "proto.h" |
28 | #include "err_impl.h" | 29 | #include "err_impl.h" |
@@ -426,6 +427,57 @@ marvel_init_rtc(void) | |||
426 | init_rtc_irq(); | 427 | init_rtc_irq(); |
427 | } | 428 | } |
428 | 429 | ||
430 | struct marvel_rtc_time { | ||
431 | struct rtc_time *time; | ||
432 | int retval; | ||
433 | }; | ||
434 | |||
435 | #ifdef CONFIG_SMP | ||
436 | static void | ||
437 | smp_get_rtc_time(void *data) | ||
438 | { | ||
439 | struct marvel_rtc_time *mrt = data; | ||
440 | mrt->retval = __get_rtc_time(mrt->time); | ||
441 | } | ||
442 | |||
443 | static void | ||
444 | smp_set_rtc_time(void *data) | ||
445 | { | ||
446 | struct marvel_rtc_time *mrt = data; | ||
447 | mrt->retval = __set_rtc_time(mrt->time); | ||
448 | } | ||
449 | #endif | ||
450 | |||
451 | static unsigned int | ||
452 | marvel_get_rtc_time(struct rtc_time *time) | ||
453 | { | ||
454 | #ifdef CONFIG_SMP | ||
455 | struct marvel_rtc_time mrt; | ||
456 | |||
457 | if (smp_processor_id() != boot_cpuid) { | ||
458 | mrt.time = time; | ||
459 | smp_call_function_single(boot_cpuid, smp_get_rtc_time, &mrt, 1); | ||
460 | return mrt.retval; | ||
461 | } | ||
462 | #endif | ||
463 | return __get_rtc_time(time); | ||
464 | } | ||
465 | |||
466 | static int | ||
467 | marvel_set_rtc_time(struct rtc_time *time) | ||
468 | { | ||
469 | #ifdef CONFIG_SMP | ||
470 | struct marvel_rtc_time mrt; | ||
471 | |||
472 | if (smp_processor_id() != boot_cpuid) { | ||
473 | mrt.time = time; | ||
474 | smp_call_function_single(boot_cpuid, smp_set_rtc_time, &mrt, 1); | ||
475 | return mrt.retval; | ||
476 | } | ||
477 | #endif | ||
478 | return __set_rtc_time(time); | ||
479 | } | ||
480 | |||
429 | static void | 481 | static void |
430 | marvel_smp_callin(void) | 482 | marvel_smp_callin(void) |
431 | { | 483 | { |
@@ -466,7 +518,9 @@ marvel_smp_callin(void) | |||
466 | struct alpha_machine_vector marvel_ev7_mv __initmv = { | 518 | struct alpha_machine_vector marvel_ev7_mv __initmv = { |
467 | .vector_name = "MARVEL/EV7", | 519 | .vector_name = "MARVEL/EV7", |
468 | DO_EV7_MMU, | 520 | DO_EV7_MMU, |
469 | DO_DEFAULT_RTC, | 521 | .rtc_port = 0x70, |
522 | .rtc_get_time = marvel_get_rtc_time, | ||
523 | .rtc_set_time = marvel_set_rtc_time, | ||
470 | DO_MARVEL_IO, | 524 | DO_MARVEL_IO, |
471 | .machine_check = marvel_machine_check, | 525 | .machine_check = marvel_machine_check, |
472 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | 526 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, |
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index e6a231435cba..b04e2cbf23a4 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <asm/io.h> | 46 | #include <asm/io.h> |
47 | #include <asm/hwrpb.h> | 47 | #include <asm/hwrpb.h> |
48 | #include <asm/8253pit.h> | 48 | #include <asm/8253pit.h> |
49 | #include <asm/rtc.h> | ||
49 | 50 | ||
50 | #include <linux/mc146818rtc.h> | 51 | #include <linux/mc146818rtc.h> |
51 | #include <linux/time.h> | 52 | #include <linux/time.h> |
@@ -180,6 +181,15 @@ common_init_rtc(void) | |||
180 | init_rtc_irq(); | 181 | init_rtc_irq(); |
181 | } | 182 | } |
182 | 183 | ||
184 | unsigned int common_get_rtc_time(struct rtc_time *time) | ||
185 | { | ||
186 | return __get_rtc_time(time); | ||
187 | } | ||
188 | |||
189 | int common_set_rtc_time(struct rtc_time *time) | ||
190 | { | ||
191 | return __set_rtc_time(time); | ||
192 | } | ||
183 | 193 | ||
184 | /* Validate a computed cycle counter result against the known bounds for | 194 | /* Validate a computed cycle counter result against the known bounds for |
185 | the given processor core. There's too much brokenness in the way of | 195 | the given processor core. There's too much brokenness in the way of |
diff --git a/include/asm-generic/rtc.h b/include/asm-generic/rtc.h index 89061c1a67d4..763e3b060f43 100644 --- a/include/asm-generic/rtc.h +++ b/include/asm-generic/rtc.h | |||
@@ -42,7 +42,7 @@ static inline unsigned char rtc_is_updating(void) | |||
42 | return uip; | 42 | return uip; |
43 | } | 43 | } |
44 | 44 | ||
45 | static inline unsigned int get_rtc_time(struct rtc_time *time) | 45 | static inline unsigned int __get_rtc_time(struct rtc_time *time) |
46 | { | 46 | { |
47 | unsigned char ctrl; | 47 | unsigned char ctrl; |
48 | unsigned long flags; | 48 | unsigned long flags; |
@@ -108,8 +108,12 @@ static inline unsigned int get_rtc_time(struct rtc_time *time) | |||
108 | return RTC_24H; | 108 | return RTC_24H; |
109 | } | 109 | } |
110 | 110 | ||
111 | #ifndef get_rtc_time | ||
112 | #define get_rtc_time __get_rtc_time | ||
113 | #endif | ||
114 | |||
111 | /* Set the current date and time in the real time clock. */ | 115 | /* Set the current date and time in the real time clock. */ |
112 | static inline int set_rtc_time(struct rtc_time *time) | 116 | static inline int __set_rtc_time(struct rtc_time *time) |
113 | { | 117 | { |
114 | unsigned long flags; | 118 | unsigned long flags; |
115 | unsigned char mon, day, hrs, min, sec; | 119 | unsigned char mon, day, hrs, min, sec; |
@@ -190,11 +194,15 @@ static inline int set_rtc_time(struct rtc_time *time) | |||
190 | return 0; | 194 | return 0; |
191 | } | 195 | } |
192 | 196 | ||
197 | #ifndef set_rtc_time | ||
198 | #define set_rtc_time __set_rtc_time | ||
199 | #endif | ||
200 | |||
193 | static inline unsigned int get_rtc_ss(void) | 201 | static inline unsigned int get_rtc_ss(void) |
194 | { | 202 | { |
195 | struct rtc_time h; | 203 | struct rtc_time h; |
196 | 204 | ||
197 | get_rtc_time(&h); | 205 | __get_rtc_time(&h); |
198 | return h.tm_sec; | 206 | return h.tm_sec; |
199 | } | 207 | } |
200 | 208 | ||