aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2007-02-05 15:18:19 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-02-05 15:18:19 -0500
commitd54853ef8cb17296ac7bce9c77430fb7c80532d0 (patch)
tree649e14d532e17231225a042a7c9a3d9207ad91ee /drivers/s390
parentc1821c2e9711adc3cd298a16b7237c92a2cee78d (diff)
[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>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c13
-rw-r--r--drivers/s390/block/dasd_eckd.c44
-rw-r--r--drivers/s390/cio/cio.c17
-rw-r--r--drivers/s390/s390mach.c17
-rw-r--r--drivers/s390/s390mach.h3
5 files changed, 72 insertions, 22 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index f208940c463c..555e18a6b781 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1232,6 +1232,19 @@ __dasd_process_blk_queue(struct dasd_device * device)
1232 if (IS_ERR(cqr)) { 1232 if (IS_ERR(cqr)) {
1233 if (PTR_ERR(cqr) == -ENOMEM) 1233 if (PTR_ERR(cqr) == -ENOMEM)
1234 break; /* terminate request queue loop */ 1234 break; /* terminate request queue loop */
1235 if (PTR_ERR(cqr) == -EAGAIN) {
1236 /*
1237 * The current request cannot be build right
1238 * now, we have to try later. If this request
1239 * is the head-of-queue we stop the device
1240 * for 1/2 second.
1241 */
1242 if (!list_empty(&device->ccw_queue))
1243 break;
1244 device->stopped |= DASD_STOPPED_PENDING;
1245 dasd_set_timer(device, HZ/2);
1246 break;
1247 }
1235 DBF_DEV_EVENT(DBF_ERR, device, 1248 DBF_DEV_EVENT(DBF_ERR, device,
1236 "CCW creation failed (rc=%ld) " 1249 "CCW creation failed (rc=%ld) "
1237 "on request %p", 1250 "on request %p",
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index d59115cce6dc..a17d73193aab 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -204,37 +204,39 @@ recs_per_track(struct dasd_eckd_characteristics * rdc,
204 return 0; 204 return 0;
205} 205}
206 206
207static inline void 207static inline int
208check_XRC (struct ccw1 *de_ccw, 208check_XRC (struct ccw1 *de_ccw,
209 struct DE_eckd_data *data, 209 struct DE_eckd_data *data,
210 struct dasd_device *device) 210 struct dasd_device *device)
211{ 211{
212 struct dasd_eckd_private *private; 212 struct dasd_eckd_private *private;
213 int rc;
213 214
214 private = (struct dasd_eckd_private *) device->private; 215 private = (struct dasd_eckd_private *) device->private;
216 if (!private->rdc_data.facilities.XRC_supported)
217 return 0;
215 218
216 /* switch on System Time Stamp - needed for XRC Support */ 219 /* switch on System Time Stamp - needed for XRC Support */
217 if (private->rdc_data.facilities.XRC_supported) { 220 data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
218 221 data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
219 data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
220 data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
221
222 data->ep_sys_time = get_clock ();
223
224 de_ccw->count = sizeof (struct DE_eckd_data);
225 de_ccw->flags |= CCW_FLAG_SLI;
226 }
227 222
228 return; 223 rc = get_sync_clock(&data->ep_sys_time);
224 /* Ignore return code if sync clock is switched off. */
225 if (rc == -ENOSYS || rc == -EACCES)
226 rc = 0;
229 227
230} /* end check_XRC */ 228 de_ccw->count = sizeof (struct DE_eckd_data);
229 de_ccw->flags |= CCW_FLAG_SLI;
230 return rc;
231}
231 232
232static inline void 233static inline int
233define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, 234define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
234 int totrk, int cmd, struct dasd_device * device) 235 int totrk, int cmd, struct dasd_device * device)
235{ 236{
236 struct dasd_eckd_private *private; 237 struct dasd_eckd_private *private;
237 struct ch_t geo, beg, end; 238 struct ch_t geo, beg, end;
239 int rc = 0;
238 240
239 private = (struct dasd_eckd_private *) device->private; 241 private = (struct dasd_eckd_private *) device->private;
240 242
@@ -263,12 +265,12 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
263 case DASD_ECKD_CCW_WRITE_KD_MT: 265 case DASD_ECKD_CCW_WRITE_KD_MT:
264 data->mask.perm = 0x02; 266 data->mask.perm = 0x02;
265 data->attributes.operation = private->attrib.operation; 267 data->attributes.operation = private->attrib.operation;
266 check_XRC (ccw, data, device); 268 rc = check_XRC (ccw, data, device);
267 break; 269 break;
268 case DASD_ECKD_CCW_WRITE_CKD: 270 case DASD_ECKD_CCW_WRITE_CKD:
269 case DASD_ECKD_CCW_WRITE_CKD_MT: 271 case DASD_ECKD_CCW_WRITE_CKD_MT:
270 data->attributes.operation = DASD_BYPASS_CACHE; 272 data->attributes.operation = DASD_BYPASS_CACHE;
271 check_XRC (ccw, data, device); 273 rc = check_XRC (ccw, data, device);
272 break; 274 break;
273 case DASD_ECKD_CCW_ERASE: 275 case DASD_ECKD_CCW_ERASE:
274 case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 276 case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
@@ -276,7 +278,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
276 data->mask.perm = 0x3; 278 data->mask.perm = 0x3;
277 data->mask.auth = 0x1; 279 data->mask.auth = 0x1;
278 data->attributes.operation = DASD_BYPASS_CACHE; 280 data->attributes.operation = DASD_BYPASS_CACHE;
279 check_XRC (ccw, data, device); 281 rc = check_XRC (ccw, data, device);
280 break; 282 break;
281 default: 283 default:
282 DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); 284 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,
312 data->beg_ext.head = beg.head; 314 data->beg_ext.head = beg.head;
313 data->end_ext.cyl = end.cyl; 315 data->end_ext.cyl = end.cyl;
314 data->end_ext.head = end.head; 316 data->end_ext.head = end.head;
317 return rc;
315} 318}
316 319
317static inline void 320static inline void
@@ -1200,7 +1203,12 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
1200 return cqr; 1203 return cqr;
1201 ccw = cqr->cpaddr; 1204 ccw = cqr->cpaddr;
1202 /* First ccw is define extent. */ 1205 /* First ccw is define extent. */
1203 define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device); 1206 if (define_extent(ccw++, cqr->data, first_trk,
1207 last_trk, cmd, device) == -EAGAIN) {
1208 /* Clock not in sync and XRC is enabled. Try again later. */
1209 dasd_sfree_request(cqr, device);
1210 return ERR_PTR(-EAGAIN);
1211 }
1204 /* Build locate_record+read/write/ccws. */ 1212 /* Build locate_record+read/write/ccws. */
1205 idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); 1213 idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
1206 LO_data = (struct LO_eckd_data *) (idaws + cidaw); 1214 LO_data = (struct LO_eckd_data *) (idaws + cidaw);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 07a4cbfc2436..ad2b37929848 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -646,7 +646,7 @@ do_IRQ (struct pt_regs *regs)
646 * Make sure that the i/o interrupt did not "overtake" 646 * Make sure that the i/o interrupt did not "overtake"
647 * the last HZ timer interrupt. 647 * the last HZ timer interrupt.
648 */ 648 */
649 account_ticks(); 649 account_ticks(S390_lowcore.int_clock);
650 /* 650 /*
651 * Get interrupt information from lowcore 651 * Get interrupt information from lowcore
652 */ 652 */
@@ -850,6 +850,19 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
850 return -EBUSY; /* uhm... */ 850 return -EBUSY; /* uhm... */
851} 851}
852 852
853/* we can't use the normal udelay here, since it enables external interrupts */
854
855static void udelay_reset(unsigned long usecs)
856{
857 uint64_t start_cc, end_cc;
858
859 asm volatile ("STCK %0" : "=m" (start_cc));
860 do {
861 cpu_relax();
862 asm volatile ("STCK %0" : "=m" (end_cc));
863 } while (((end_cc - start_cc)/4096) < usecs);
864}
865
853static inline int 866static inline int
854__clear_subchannel_easy(struct subchannel_id schid) 867__clear_subchannel_easy(struct subchannel_id schid)
855{ 868{
@@ -865,7 +878,7 @@ __clear_subchannel_easy(struct subchannel_id schid)
865 if (schid_equal(&ti.schid, &schid)) 878 if (schid_equal(&ti.schid, &schid))
866 return 0; 879 return 0;
867 } 880 }
868 udelay(100); 881 udelay_reset(100);
869 } 882 }
870 return -EBUSY; 883 return -EBUSY;
871} 884}
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 442d63470428..806bb1a921eb 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -15,7 +15,7 @@
15#include <linux/time.h> 15#include <linux/time.h>
16#include <linux/device.h> 16#include <linux/device.h>
17#include <linux/kthread.h> 17#include <linux/kthread.h>
18 18#include <asm/etr.h>
19#include <asm/lowcore.h> 19#include <asm/lowcore.h>
20#include <asm/cio.h> 20#include <asm/cio.h>
21#include "cio/cio.h" 21#include "cio/cio.h"
@@ -466,6 +466,19 @@ s390_do_machine_check(struct pt_regs *regs)
466 s390_handle_damage("unable to revalidate registers."); 466 s390_handle_damage("unable to revalidate registers.");
467 } 467 }
468 468
469 if (mci->cd) {
470 /* Timing facility damage */
471 s390_handle_damage("TOD clock damaged");
472 }
473
474 if (mci->ed && mci->ec) {
475 /* External damage */
476 if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
477 etr_sync_check();
478 if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
479 etr_switch_to_local();
480 }
481
469 if (mci->se) 482 if (mci->se)
470 /* Storage error uncorrected */ 483 /* Storage error uncorrected */
471 s390_handle_damage("received storage error uncorrected " 484 s390_handle_damage("received storage error uncorrected "
@@ -504,7 +517,7 @@ static int
504machine_check_init(void) 517machine_check_init(void)
505{ 518{
506 init_MUTEX_LOCKED(&m_sem); 519 init_MUTEX_LOCKED(&m_sem);
507 ctl_clear_bit(14, 25); /* disable external damage MCH */ 520 ctl_set_bit(14, 25); /* enable external damage MCH */
508 ctl_set_bit(14, 27); /* enable system recovery MCH */ 521 ctl_set_bit(14, 27); /* enable system recovery MCH */
509#ifdef CONFIG_MACHCHK_WARNING 522#ifdef CONFIG_MACHCHK_WARNING
510 ctl_set_bit(14, 24); /* enable warning MCH */ 523 ctl_set_bit(14, 24); /* enable warning MCH */
diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h
index 7abb42a09ae2..d3ca4281a494 100644
--- a/drivers/s390/s390mach.h
+++ b/drivers/s390/s390mach.h
@@ -102,4 +102,7 @@ static inline int stcrw(struct crw *pcrw )
102 return ccode; 102 return ccode;
103} 103}
104 104
105#define ED_ETR_SYNC 12 /* External damage ETR sync check */
106#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
107
105#endif /* __s390mach */ 108#endif /* __s390mach */