forked from Minki/linux
[S390] ETR support.
This patch adds support for clock synchronization to an external time reference (ETR). The external time reference sends an oscillator signal and a synchronization signal every 2^20 microseconds to keep the TOD clocks of all connected servers in sync. For availability two ETR units can be connected to a machine. If the clock deviates for more than the sync-check tolerance all cpus get a machine check that indicates that the clock is out of sync. For the lovely details how to get the clock back in sync see the code below. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
c1821c2e97
commit
d54853ef8c
@ -125,14 +125,12 @@ void do_extint(struct pt_regs *regs, unsigned short code)
|
||||
* Make sure that the i/o interrupt did not "overtake"
|
||||
* the last HZ timer interrupt.
|
||||
*/
|
||||
account_ticks();
|
||||
account_ticks(S390_lowcore.int_clock);
|
||||
kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++;
|
||||
index = ext_hash(code);
|
||||
for (p = ext_int_hash[index]; p; p = p->next) {
|
||||
if (likely(p->code == code)) {
|
||||
if (likely(p->handler))
|
||||
p->handler(code);
|
||||
}
|
||||
if (likely(p->code == code))
|
||||
p->handler(code);
|
||||
}
|
||||
irq_exit();
|
||||
set_irq_regs(old_regs);
|
||||
|
@ -457,9 +457,10 @@ int __devinit start_secondary(void *cpuvoid)
|
||||
/* Setup the cpu */
|
||||
cpu_init();
|
||||
preempt_disable();
|
||||
/* init per CPU timer */
|
||||
/* Enable TOD clock interrupts on the secondary cpu. */
|
||||
init_cpu_timer();
|
||||
#ifdef CONFIG_VIRT_TIMER
|
||||
/* Enable cpu timer interrupts on the secondary cpu. */
|
||||
init_cpu_vtimer();
|
||||
#endif
|
||||
/* Enable pfault pseudo page faults on this cpu. */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -524,16 +524,15 @@ EXPORT_SYMBOL(del_virt_timer);
|
||||
void init_cpu_vtimer(void)
|
||||
{
|
||||
struct vtimer_queue *vt_list;
|
||||
unsigned long cr0;
|
||||
|
||||
/* kick the virtual timer */
|
||||
S390_lowcore.exit_timer = VTIMER_MAX_SLICE;
|
||||
S390_lowcore.last_update_timer = VTIMER_MAX_SLICE;
|
||||
asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer));
|
||||
asm volatile ("STCK %0" : "=m" (S390_lowcore.last_update_clock));
|
||||
__ctl_store(cr0, 0, 0);
|
||||
cr0 |= 0x400;
|
||||
__ctl_load(cr0, 0, 0);
|
||||
|
||||
/* enable cpu timer interrupts */
|
||||
__ctl_set_bit(0,10);
|
||||
|
||||
vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
|
||||
INIT_LIST_HEAD(&vt_list->list);
|
||||
@ -572,6 +571,7 @@ void __init vtime_init(void)
|
||||
if (register_idle_notifier(&vtimer_idle_nb))
|
||||
panic("Couldn't register idle notifier");
|
||||
|
||||
/* Enable cpu timer interrupts on the boot cpu. */
|
||||
init_cpu_vtimer();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/s390/kernel/delay.c
|
||||
* arch/s390/lib/delay.c
|
||||
* Precise Delay Loops for S390
|
||||
*
|
||||
* S390 version
|
||||
@ -13,10 +13,8 @@
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#include <asm/smp.h>
|
||||
#endif
|
||||
#include <linux/timex.h>
|
||||
#include <linux/irqflags.h>
|
||||
|
||||
void __delay(unsigned long loops)
|
||||
{
|
||||
@ -31,17 +29,39 @@ void __delay(unsigned long loops)
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for 'usecs' microseconds using the tod clock, giving up the time slice
|
||||
* of the virtual PU inbetween to avoid congestion.
|
||||
* Waits for 'usecs' microseconds using the TOD clock comparator.
|
||||
*/
|
||||
void __udelay(unsigned long usecs)
|
||||
{
|
||||
uint64_t start_cc;
|
||||
u64 end, time, jiffy_timer = 0;
|
||||
unsigned long flags, cr0, mask, dummy;
|
||||
|
||||
if (usecs == 0)
|
||||
return;
|
||||
start_cc = get_clock();
|
||||
do {
|
||||
cpu_relax();
|
||||
} while (((get_clock() - start_cc)/4096) < usecs);
|
||||
local_irq_save(flags);
|
||||
if (raw_irqs_disabled_flags(flags)) {
|
||||
jiffy_timer = S390_lowcore.jiffy_timer;
|
||||
S390_lowcore.jiffy_timer = -1ULL - (4096 << 12);
|
||||
__ctl_store(cr0, 0, 0);
|
||||
dummy = (cr0 & 0xffff00e0) | 0x00000800;
|
||||
__ctl_load(dummy , 0, 0);
|
||||
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT;
|
||||
} else
|
||||
mask = psw_kernel_bits | PSW_MASK_WAIT |
|
||||
PSW_MASK_EXT | PSW_MASK_IO;
|
||||
|
||||
end = get_clock() + ((u64) usecs << 12);
|
||||
do {
|
||||
time = end < S390_lowcore.jiffy_timer ?
|
||||
end : S390_lowcore.jiffy_timer;
|
||||
set_clock_comparator(time);
|
||||
trace_hardirqs_on();
|
||||
__load_psw_mask(mask);
|
||||
local_irq_disable();
|
||||
} while (get_clock() < end);
|
||||
|
||||
if (raw_irqs_disabled_flags(flags)) {
|
||||
__ctl_load(cr0, 0, 0);
|
||||
S390_lowcore.jiffy_timer = jiffy_timer;
|
||||
}
|
||||
set_clock_comparator(S390_lowcore.jiffy_timer);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
@ -1232,6 +1232,19 @@ __dasd_process_blk_queue(struct dasd_device * device)
|
||||
if (IS_ERR(cqr)) {
|
||||
if (PTR_ERR(cqr) == -ENOMEM)
|
||||
break; /* terminate request queue loop */
|
||||
if (PTR_ERR(cqr) == -EAGAIN) {
|
||||
/*
|
||||
* The current request cannot be build right
|
||||
* now, we have to try later. If this request
|
||||
* is the head-of-queue we stop the device
|
||||
* for 1/2 second.
|
||||
*/
|
||||
if (!list_empty(&device->ccw_queue))
|
||||
break;
|
||||
device->stopped |= DASD_STOPPED_PENDING;
|
||||
dasd_set_timer(device, HZ/2);
|
||||
break;
|
||||
}
|
||||
DBF_DEV_EVENT(DBF_ERR, device,
|
||||
"CCW creation failed (rc=%ld) "
|
||||
"on request %p",
|
||||
|
@ -204,37 +204,39 @@ recs_per_track(struct dasd_eckd_characteristics * rdc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
static inline int
|
||||
check_XRC (struct ccw1 *de_ccw,
|
||||
struct DE_eckd_data *data,
|
||||
struct dasd_device *device)
|
||||
{
|
||||
struct dasd_eckd_private *private;
|
||||
int rc;
|
||||
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
if (!private->rdc_data.facilities.XRC_supported)
|
||||
return 0;
|
||||
|
||||
/* switch on System Time Stamp - needed for XRC Support */
|
||||
if (private->rdc_data.facilities.XRC_supported) {
|
||||
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
|
||||
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
|
||||
|
||||
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
|
||||
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
|
||||
rc = get_sync_clock(&data->ep_sys_time);
|
||||
/* Ignore return code if sync clock is switched off. */
|
||||
if (rc == -ENOSYS || rc == -EACCES)
|
||||
rc = 0;
|
||||
|
||||
data->ep_sys_time = get_clock ();
|
||||
de_ccw->count = sizeof (struct DE_eckd_data);
|
||||
de_ccw->flags |= CCW_FLAG_SLI;
|
||||
return rc;
|
||||
}
|
||||
|
||||
de_ccw->count = sizeof (struct DE_eckd_data);
|
||||
de_ccw->flags |= CCW_FLAG_SLI;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
} /* end check_XRC */
|
||||
|
||||
static inline void
|
||||
static inline int
|
||||
define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
|
||||
int totrk, int cmd, struct dasd_device * device)
|
||||
{
|
||||
struct dasd_eckd_private *private;
|
||||
struct ch_t geo, beg, end;
|
||||
int rc = 0;
|
||||
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
|
||||
@ -263,12 +265,12 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
|
||||
case DASD_ECKD_CCW_WRITE_KD_MT:
|
||||
data->mask.perm = 0x02;
|
||||
data->attributes.operation = private->attrib.operation;
|
||||
check_XRC (ccw, data, device);
|
||||
rc = check_XRC (ccw, data, device);
|
||||
break;
|
||||
case DASD_ECKD_CCW_WRITE_CKD:
|
||||
case DASD_ECKD_CCW_WRITE_CKD_MT:
|
||||
data->attributes.operation = DASD_BYPASS_CACHE;
|
||||
check_XRC (ccw, data, device);
|
||||
rc = check_XRC (ccw, data, device);
|
||||
break;
|
||||
case DASD_ECKD_CCW_ERASE:
|
||||
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
|
||||
@ -276,7 +278,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
|
||||
data->mask.perm = 0x3;
|
||||
data->mask.auth = 0x1;
|
||||
data->attributes.operation = DASD_BYPASS_CACHE;
|
||||
check_XRC (ccw, data, device);
|
||||
rc = check_XRC (ccw, data, device);
|
||||
break;
|
||||
default:
|
||||
DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
|
||||
@ -312,6 +314,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
|
||||
data->beg_ext.head = beg.head;
|
||||
data->end_ext.cyl = end.cyl;
|
||||
data->end_ext.head = end.head;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1200,7 +1203,12 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
|
||||
return cqr;
|
||||
ccw = cqr->cpaddr;
|
||||
/* First ccw is define extent. */
|
||||
define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device);
|
||||
if (define_extent(ccw++, cqr->data, first_trk,
|
||||
last_trk, cmd, device) == -EAGAIN) {
|
||||
/* Clock not in sync and XRC is enabled. Try again later. */
|
||||
dasd_sfree_request(cqr, device);
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
/* Build locate_record+read/write/ccws. */
|
||||
idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
|
||||
LO_data = (struct LO_eckd_data *) (idaws + cidaw);
|
||||
|
@ -646,7 +646,7 @@ do_IRQ (struct pt_regs *regs)
|
||||
* Make sure that the i/o interrupt did not "overtake"
|
||||
* the last HZ timer interrupt.
|
||||
*/
|
||||
account_ticks();
|
||||
account_ticks(S390_lowcore.int_clock);
|
||||
/*
|
||||
* Get interrupt information from lowcore
|
||||
*/
|
||||
@ -850,6 +850,19 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
|
||||
return -EBUSY; /* uhm... */
|
||||
}
|
||||
|
||||
/* we can't use the normal udelay here, since it enables external interrupts */
|
||||
|
||||
static void udelay_reset(unsigned long usecs)
|
||||
{
|
||||
uint64_t start_cc, end_cc;
|
||||
|
||||
asm volatile ("STCK %0" : "=m" (start_cc));
|
||||
do {
|
||||
cpu_relax();
|
||||
asm volatile ("STCK %0" : "=m" (end_cc));
|
||||
} while (((end_cc - start_cc)/4096) < usecs);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__clear_subchannel_easy(struct subchannel_id schid)
|
||||
{
|
||||
@ -865,7 +878,7 @@ __clear_subchannel_easy(struct subchannel_id schid)
|
||||
if (schid_equal(&ti.schid, &schid))
|
||||
return 0;
|
||||
}
|
||||
udelay(100);
|
||||
udelay_reset(100);
|
||||
}
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/time.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include <asm/etr.h>
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/cio.h>
|
||||
#include "cio/cio.h"
|
||||
@ -466,6 +466,19 @@ s390_do_machine_check(struct pt_regs *regs)
|
||||
s390_handle_damage("unable to revalidate registers.");
|
||||
}
|
||||
|
||||
if (mci->cd) {
|
||||
/* Timing facility damage */
|
||||
s390_handle_damage("TOD clock damaged");
|
||||
}
|
||||
|
||||
if (mci->ed && mci->ec) {
|
||||
/* External damage */
|
||||
if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
|
||||
etr_sync_check();
|
||||
if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
|
||||
etr_switch_to_local();
|
||||
}
|
||||
|
||||
if (mci->se)
|
||||
/* Storage error uncorrected */
|
||||
s390_handle_damage("received storage error uncorrected "
|
||||
@ -504,7 +517,7 @@ static int
|
||||
machine_check_init(void)
|
||||
{
|
||||
init_MUTEX_LOCKED(&m_sem);
|
||||
ctl_clear_bit(14, 25); /* disable external damage MCH */
|
||||
ctl_set_bit(14, 25); /* enable external damage MCH */
|
||||
ctl_set_bit(14, 27); /* enable system recovery MCH */
|
||||
#ifdef CONFIG_MACHCHK_WARNING
|
||||
ctl_set_bit(14, 24); /* enable warning MCH */
|
||||
|
@ -102,4 +102,7 @@ static inline int stcrw(struct crw *pcrw )
|
||||
return ccode;
|
||||
}
|
||||
|
||||
#define ED_ETR_SYNC 12 /* External damage ETR sync check */
|
||||
#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
|
||||
|
||||
#endif /* __s390mach */
|
||||
|
219
include/asm-s390/etr.h
Normal file
219
include/asm-s390/etr.h
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* include/asm-s390/etr.h
|
||||
*
|
||||
* Copyright IBM Corp. 2006
|
||||
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
|
||||
*/
|
||||
#ifndef __S390_ETR_H
|
||||
#define __S390_ETR_H
|
||||
|
||||
/* ETR attachment control register */
|
||||
struct etr_eacr {
|
||||
unsigned int e0 : 1; /* port 0 stepping control */
|
||||
unsigned int e1 : 1; /* port 1 stepping control */
|
||||
unsigned int _pad0 : 5; /* must be 00100 */
|
||||
unsigned int dp : 1; /* data port control */
|
||||
unsigned int p0 : 1; /* port 0 change recognition control */
|
||||
unsigned int p1 : 1; /* port 1 change recognition control */
|
||||
unsigned int _pad1 : 3; /* must be 000 */
|
||||
unsigned int ea : 1; /* ETR alert control */
|
||||
unsigned int es : 1; /* ETR sync check control */
|
||||
unsigned int sl : 1; /* switch to local control */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Port state returned by steai */
|
||||
enum etr_psc {
|
||||
etr_psc_operational = 0,
|
||||
etr_psc_semi_operational = 1,
|
||||
etr_psc_protocol_error = 4,
|
||||
etr_psc_no_symbols = 8,
|
||||
etr_psc_no_signal = 12,
|
||||
etr_psc_pps_mode = 13
|
||||
};
|
||||
|
||||
/* Logical port state returned by stetr */
|
||||
enum etr_lpsc {
|
||||
etr_lpsc_operational_step = 0,
|
||||
etr_lpsc_operational_alt = 1,
|
||||
etr_lpsc_semi_operational = 2,
|
||||
etr_lpsc_protocol_error = 4,
|
||||
etr_lpsc_no_symbol_sync = 8,
|
||||
etr_lpsc_no_signal = 12,
|
||||
etr_lpsc_pps_mode = 13
|
||||
};
|
||||
|
||||
/* ETR status words */
|
||||
struct etr_esw {
|
||||
struct etr_eacr eacr; /* attachment control register */
|
||||
unsigned int y : 1; /* stepping mode */
|
||||
unsigned int _pad0 : 5; /* must be 00000 */
|
||||
unsigned int p : 1; /* stepping port number */
|
||||
unsigned int q : 1; /* data port number */
|
||||
unsigned int psc0 : 4; /* port 0 state code */
|
||||
unsigned int psc1 : 4; /* port 1 state code */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Second level data register status word */
|
||||
struct etr_slsw {
|
||||
unsigned int vv1 : 1; /* copy of validity bit data frame 1 */
|
||||
unsigned int vv2 : 1; /* copy of validity bit data frame 2 */
|
||||
unsigned int vv3 : 1; /* copy of validity bit data frame 3 */
|
||||
unsigned int vv4 : 1; /* copy of validity bit data frame 4 */
|
||||
unsigned int _pad0 : 19; /* must by all zeroes */
|
||||
unsigned int n : 1; /* EAF port number */
|
||||
unsigned int v1 : 1; /* validity bit ETR data frame 1 */
|
||||
unsigned int v2 : 1; /* validity bit ETR data frame 2 */
|
||||
unsigned int v3 : 1; /* validity bit ETR data frame 3 */
|
||||
unsigned int v4 : 1; /* validity bit ETR data frame 4 */
|
||||
unsigned int _pad1 : 4; /* must be 0000 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ETR data frames */
|
||||
struct etr_edf1 {
|
||||
unsigned int u : 1; /* untuned bit */
|
||||
unsigned int _pad0 : 1; /* must be 0 */
|
||||
unsigned int r : 1; /* service request bit */
|
||||
unsigned int _pad1 : 4; /* must be 0000 */
|
||||
unsigned int a : 1; /* time adjustment bit */
|
||||
unsigned int net_id : 8; /* ETR network id */
|
||||
unsigned int etr_id : 8; /* id of ETR which sends data frames */
|
||||
unsigned int etr_pn : 8; /* port number of ETR output port */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct etr_edf2 {
|
||||
unsigned int etv : 32; /* Upper 32 bits of TOD. */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct etr_edf3 {
|
||||
unsigned int rc : 8; /* failure reason code */
|
||||
unsigned int _pad0 : 3; /* must be 000 */
|
||||
unsigned int c : 1; /* ETR coupled bit */
|
||||
unsigned int tc : 4; /* ETR type code */
|
||||
unsigned int blto : 8; /* biased local time offset */
|
||||
/* (blto - 128) * 15 = minutes */
|
||||
unsigned int buo : 8; /* biased utc offset */
|
||||
/* (buo - 128) = leap seconds */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct etr_edf4 {
|
||||
unsigned int ed : 8; /* ETS device dependent data */
|
||||
unsigned int _pad0 : 1; /* must be 0 */
|
||||
unsigned int buc : 5; /* biased ut1 correction */
|
||||
/* (buc - 16) * 0.1 seconds */
|
||||
unsigned int em : 6; /* ETS error magnitude */
|
||||
unsigned int dc : 6; /* ETS drift code */
|
||||
unsigned int sc : 6; /* ETS steering code */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* ETR attachment information block, two formats
|
||||
* format 1 has 4 reserved words with a size of 64 bytes
|
||||
* format 2 has 16 reserved words with a size of 96 bytes
|
||||
*/
|
||||
struct etr_aib {
|
||||
struct etr_esw esw;
|
||||
struct etr_slsw slsw;
|
||||
unsigned long long tsp;
|
||||
struct etr_edf1 edf1;
|
||||
struct etr_edf2 edf2;
|
||||
struct etr_edf3 edf3;
|
||||
struct etr_edf4 edf4;
|
||||
unsigned int reserved[16];
|
||||
} __attribute__ ((packed,aligned(8)));
|
||||
|
||||
/* ETR interruption parameter */
|
||||
struct etr_interruption_parameter {
|
||||
unsigned int _pad0 : 8;
|
||||
unsigned int pc0 : 1; /* port 0 state change */
|
||||
unsigned int pc1 : 1; /* port 1 state change */
|
||||
unsigned int _pad1 : 3;
|
||||
unsigned int eai : 1; /* ETR alert indication */
|
||||
unsigned int _pad2 : 18;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Query TOD offset result */
|
||||
struct etr_ptff_qto {
|
||||
unsigned long long physical_clock;
|
||||
unsigned long long tod_offset;
|
||||
unsigned long long logical_tod_offset;
|
||||
unsigned long long tod_epoch_difference;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Inline assembly helper functions */
|
||||
static inline int etr_setr(struct etr_eacr *ctrl)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2160000,0(%2)\n"
|
||||
"0: la %0,0\n"
|
||||
"1:\n"
|
||||
EX_TABLE(0b,1b)
|
||||
: "+d" (rc) : "m" (*ctrl), "a" (ctrl));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Stores a format 1 aib with 64 bytes */
|
||||
static inline int etr_stetr(struct etr_aib *aib)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2170000,0(%2)\n"
|
||||
"0: la %0,0\n"
|
||||
"1:\n"
|
||||
EX_TABLE(0b,1b)
|
||||
: "+d" (rc) : "m" (*aib), "a" (aib));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Stores a format 2 aib with 96 bytes for specified port */
|
||||
static inline int etr_steai(struct etr_aib *aib, unsigned int func)
|
||||
{
|
||||
register unsigned int reg0 asm("0") = func;
|
||||
int rc = -ENOSYS;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2b30000,0(%2)\n"
|
||||
"0: la %0,0\n"
|
||||
"1:\n"
|
||||
EX_TABLE(0b,1b)
|
||||
: "+d" (rc) : "m" (*aib), "a" (aib), "d" (reg0));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Function codes for the steai instruction. */
|
||||
#define ETR_STEAI_STEPPING_PORT 0x10
|
||||
#define ETR_STEAI_ALTERNATE_PORT 0x11
|
||||
#define ETR_STEAI_PORT_0 0x12
|
||||
#define ETR_STEAI_PORT_1 0x13
|
||||
|
||||
static inline int etr_ptff(void *ptff_block, unsigned int func)
|
||||
{
|
||||
register unsigned int reg0 asm("0") = func;
|
||||
register unsigned long reg1 asm("1") = (unsigned long) ptff_block;
|
||||
int rc = -ENOSYS;
|
||||
|
||||
asm volatile(
|
||||
" .word 0x0104\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (rc), "=m" (ptff_block)
|
||||
: "d" (reg0), "d" (reg1), "m" (ptff_block) : "cc");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Function codes for the ptff instruction. */
|
||||
#define ETR_PTFF_QAF 0x00 /* query available functions */
|
||||
#define ETR_PTFF_QTO 0x01 /* query tod offset */
|
||||
#define ETR_PTFF_QSI 0x02 /* query steering information */
|
||||
#define ETR_PTFF_ATO 0x40 /* adjust tod offset */
|
||||
#define ETR_PTFF_STO 0x41 /* set tod offset */
|
||||
#define ETR_PTFF_SFS 0x42 /* set fine steering rate */
|
||||
#define ETR_PTFF_SGS 0x43 /* set gross steering rate */
|
||||
|
||||
/* Functions needed by the machine check handler */
|
||||
extern void etr_switch_to_local(void);
|
||||
extern void etr_sync_check(void);
|
||||
|
||||
#endif /* __S390_ETR_H */
|
@ -32,6 +32,6 @@ typedef struct {
|
||||
|
||||
#define HARDIRQ_BITS 8
|
||||
|
||||
extern void account_ticks(void);
|
||||
extern void account_ticks(u64 time);
|
||||
|
||||
#endif /* __ASM_HARDIRQ_H */
|
||||
|
@ -11,6 +11,41 @@
|
||||
#ifndef _ASM_S390_TIMEX_H
|
||||
#define _ASM_S390_TIMEX_H
|
||||
|
||||
/* Inline functions for clock register access. */
|
||||
static inline int set_clock(__u64 time)
|
||||
{
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" sck 0(%2)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc) : "m" (time), "a" (&time) : "cc");
|
||||
return cc;
|
||||
}
|
||||
|
||||
static inline int store_clock(__u64 *time)
|
||||
{
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" stck 0(%2)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc), "=m" (*time) : "a" (time) : "cc");
|
||||
return cc;
|
||||
}
|
||||
|
||||
static inline void set_clock_comparator(__u64 time)
|
||||
{
|
||||
asm volatile("sckc 0(%1)" : : "m" (time), "a" (&time));
|
||||
}
|
||||
|
||||
static inline void store_clock_comparator(__u64 *time)
|
||||
{
|
||||
asm volatile("stckc 0(%1)" : "=m" (*time) : "a" (time));
|
||||
}
|
||||
|
||||
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
|
||||
|
||||
typedef unsigned long long cycles_t;
|
||||
@ -32,6 +67,7 @@ static inline cycles_t get_cycles(void)
|
||||
return (cycles_t) get_clock() >> 2;
|
||||
}
|
||||
|
||||
int get_sync_clock(unsigned long long *clock);
|
||||
void init_cpu_timer(void);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user