Commit 5f7dc5d7 authored by Ivan Kokshaysky's avatar Ivan Kokshaysky Committed by Linus Torvalds

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: default avatarIvan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Richard Henderson <rth@twiddle.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 2f88d151
...@@ -21,6 +21,7 @@ struct pci_dev; ...@@ -21,6 +21,7 @@ struct pci_dev;
struct pci_ops; struct pci_ops;
struct pci_controller; struct pci_controller;
struct _alpha_agp_info; struct _alpha_agp_info;
struct rtc_time;
struct alpha_machine_vector struct alpha_machine_vector
{ {
...@@ -94,6 +95,9 @@ struct alpha_machine_vector ...@@ -94,6 +95,9 @@ struct alpha_machine_vector
struct _alpha_agp_info *(*agp_info)(void); struct _alpha_agp_info *(*agp_info)(void);
unsigned int (*rtc_get_time)(struct rtc_time *);
int (*rtc_set_time)(struct rtc_time *);
const char *vector_name; const char *vector_name;
/* NUMA information */ /* NUMA information */
......
#ifndef _ALPHA_RTC_H #ifndef _ALPHA_RTC_H
#define _ALPHA_RTC_H #define _ALPHA_RTC_H
/* #if defined(CONFIG_ALPHA_GENERIC)
* Alpha uses the default access methods for the RTC. # define get_rtc_time alpha_mv.rtc_get_time
*/ # define set_rtc_time alpha_mv.rtc_set_time
#else
# if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP)
# define get_rtc_time marvel_get_rtc_time
# define set_rtc_time marvel_set_rtc_time
# endif
#endif
#include <asm-generic/rtc.h> #include <asm-generic/rtc.h>
......
...@@ -658,16 +658,8 @@ __marvel_rtc_io(u8 b, unsigned long addr, int write) ...@@ -658,16 +658,8 @@ __marvel_rtc_io(u8 b, unsigned long addr, int write)
rtc_access.data = bcd2bin(b); rtc_access.data = bcd2bin(b);
rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */ rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */
#ifdef CONFIG_SMP
if (smp_processor_id() != boot_cpuid)
smp_call_function_single(boot_cpuid,
__marvel_access_rtc,
&rtc_access, 1);
else
__marvel_access_rtc(&rtc_access);
#else
__marvel_access_rtc(&rtc_access); __marvel_access_rtc(&rtc_access);
#endif
ret = bin2bcd(rtc_access.data); ret = bin2bcd(rtc_access.data);
break; break;
......
...@@ -40,7 +40,10 @@ ...@@ -40,7 +40,10 @@
#define CAT1(x,y) x##y #define CAT1(x,y) x##y
#define CAT(x,y) CAT1(x,y) #define CAT(x,y) CAT1(x,y)
#define DO_DEFAULT_RTC .rtc_port = 0x70 #define DO_DEFAULT_RTC \
.rtc_port = 0x70, \
.rtc_get_time = common_get_rtc_time, \
.rtc_set_time = common_set_rtc_time
#define DO_EV4_MMU \ #define DO_EV4_MMU \
.max_asn = EV4_MAX_ASN, \ .max_asn = EV4_MAX_ASN, \
......
...@@ -145,6 +145,8 @@ extern void smp_percpu_timer_interrupt(struct pt_regs *); ...@@ -145,6 +145,8 @@ extern void smp_percpu_timer_interrupt(struct pt_regs *);
extern irqreturn_t timer_interrupt(int irq, void *dev); extern irqreturn_t timer_interrupt(int irq, void *dev);
extern void common_init_rtc(void); extern void common_init_rtc(void);
extern unsigned long est_cycle_freq; extern unsigned long est_cycle_freq;
extern unsigned int common_get_rtc_time(struct rtc_time *time);
extern int common_set_rtc_time(struct rtc_time *time);
/* smc37c93x.c */ /* smc37c93x.c */
extern void SMC93x_Init(void); extern void SMC93x_Init(void);
......
...@@ -261,6 +261,8 @@ struct alpha_machine_vector jensen_mv __initmv = { ...@@ -261,6 +261,8 @@ struct alpha_machine_vector jensen_mv __initmv = {
.machine_check = jensen_machine_check, .machine_check = jensen_machine_check,
.max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS,
.rtc_port = 0x170, .rtc_port = 0x170,
.rtc_get_time = common_get_rtc_time,
.rtc_set_time = common_set_rtc_time,
.nr_irqs = 16, .nr_irqs = 16,
.device_interrupt = jensen_device_interrupt, .device_interrupt = jensen_device_interrupt,
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <asm/hwrpb.h> #include <asm/hwrpb.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/vga.h> #include <asm/vga.h>
#include <asm/rtc.h>
#include "proto.h" #include "proto.h"
#include "err_impl.h" #include "err_impl.h"
...@@ -426,6 +427,57 @@ marvel_init_rtc(void) ...@@ -426,6 +427,57 @@ marvel_init_rtc(void)
init_rtc_irq(); init_rtc_irq();
} }
struct marvel_rtc_time {
struct rtc_time *time;
int retval;
};
#ifdef CONFIG_SMP
static void
smp_get_rtc_time(void *data)
{
struct marvel_rtc_time *mrt = data;
mrt->retval = __get_rtc_time(mrt->time);
}
static void
smp_set_rtc_time(void *data)
{
struct marvel_rtc_time *mrt = data;
mrt->retval = __set_rtc_time(mrt->time);
}
#endif
static unsigned int
marvel_get_rtc_time(struct rtc_time *time)
{
#ifdef CONFIG_SMP
struct marvel_rtc_time mrt;
if (smp_processor_id() != boot_cpuid) {
mrt.time = time;
smp_call_function_single(boot_cpuid, smp_get_rtc_time, &mrt, 1);
return mrt.retval;
}
#endif
return __get_rtc_time(time);
}
static int
marvel_set_rtc_time(struct rtc_time *time)
{
#ifdef CONFIG_SMP
struct marvel_rtc_time mrt;
if (smp_processor_id() != boot_cpuid) {
mrt.time = time;
smp_call_function_single(boot_cpuid, smp_set_rtc_time, &mrt, 1);
return mrt.retval;
}
#endif
return __set_rtc_time(time);
}
static void static void
marvel_smp_callin(void) marvel_smp_callin(void)
{ {
...@@ -466,7 +518,9 @@ marvel_smp_callin(void) ...@@ -466,7 +518,9 @@ marvel_smp_callin(void)
struct alpha_machine_vector marvel_ev7_mv __initmv = { struct alpha_machine_vector marvel_ev7_mv __initmv = {
.vector_name = "MARVEL/EV7", .vector_name = "MARVEL/EV7",
DO_EV7_MMU, DO_EV7_MMU,
DO_DEFAULT_RTC, .rtc_port = 0x70,
.rtc_get_time = marvel_get_rtc_time,
.rtc_set_time = marvel_set_rtc_time,
DO_MARVEL_IO, DO_MARVEL_IO,
.machine_check = marvel_machine_check, .machine_check = marvel_machine_check,
.max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS,
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/hwrpb.h> #include <asm/hwrpb.h>
#include <asm/8253pit.h> #include <asm/8253pit.h>
#include <asm/rtc.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/time.h> #include <linux/time.h>
...@@ -180,6 +181,15 @@ common_init_rtc(void) ...@@ -180,6 +181,15 @@ common_init_rtc(void)
init_rtc_irq(); init_rtc_irq();
} }
unsigned int common_get_rtc_time(struct rtc_time *time)
{
return __get_rtc_time(time);
}
int common_set_rtc_time(struct rtc_time *time)
{
return __set_rtc_time(time);
}
/* Validate a computed cycle counter result against the known bounds for /* Validate a computed cycle counter result against the known bounds for
the given processor core. There's too much brokenness in the way of the given processor core. There's too much brokenness in the way of
......
...@@ -42,7 +42,7 @@ static inline unsigned char rtc_is_updating(void) ...@@ -42,7 +42,7 @@ static inline unsigned char rtc_is_updating(void)
return uip; return uip;
} }
static inline unsigned int get_rtc_time(struct rtc_time *time) static inline unsigned int __get_rtc_time(struct rtc_time *time)
{ {
unsigned char ctrl; unsigned char ctrl;
unsigned long flags; unsigned long flags;
...@@ -108,8 +108,12 @@ static inline unsigned int get_rtc_time(struct rtc_time *time) ...@@ -108,8 +108,12 @@ static inline unsigned int get_rtc_time(struct rtc_time *time)
return RTC_24H; return RTC_24H;
} }
#ifndef get_rtc_time
#define get_rtc_time __get_rtc_time
#endif
/* Set the current date and time in the real time clock. */ /* Set the current date and time in the real time clock. */
static inline int set_rtc_time(struct rtc_time *time) static inline int __set_rtc_time(struct rtc_time *time)
{ {
unsigned long flags; unsigned long flags;
unsigned char mon, day, hrs, min, sec; unsigned char mon, day, hrs, min, sec;
...@@ -190,11 +194,15 @@ static inline int set_rtc_time(struct rtc_time *time) ...@@ -190,11 +194,15 @@ static inline int set_rtc_time(struct rtc_time *time)
return 0; return 0;
} }
#ifndef set_rtc_time
#define set_rtc_time __set_rtc_time
#endif
static inline unsigned int get_rtc_ss(void) static inline unsigned int get_rtc_ss(void)
{ {
struct rtc_time h; struct rtc_time h;
get_rtc_time(&h); __get_rtc_time(&h);
return h.tm_sec; return h.tm_sec;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment