mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
[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 <[email protected]>
This commit is contained in:
parent
c1821c2e97
commit
d54853ef8c
13 changed files with 1488 additions and 109 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue