diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-02-05 15:18:19 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-02-05 15:18:19 -0500 |
commit | d54853ef8cb17296ac7bce9c77430fb7c80532d0 (patch) | |
tree | 649e14d532e17231225a042a7c9a3d9207ad91ee /drivers | |
parent | c1821c2e9711adc3cd298a16b7237c92a2cee78d (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')
-rw-r--r-- | drivers/s390/block/dasd.c | 13 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 44 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 17 | ||||
-rw-r--r-- | drivers/s390/s390mach.c | 17 | ||||
-rw-r--r-- | drivers/s390/s390mach.h | 3 |
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 | ||
207 | static inline void | 207 | static inline int |
208 | check_XRC (struct ccw1 *de_ccw, | 208 | check_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 | ||
232 | static inline void | 233 | static inline int |
233 | define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | 234 | define_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 | ||
317 | static inline void | 320 | static 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 | |||
855 | static 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 | |||
853 | static inline int | 866 | static 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 | |||
504 | machine_check_init(void) | 517 | machine_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 */ |