aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/chsc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r--drivers/s390/cio/chsc.c379
1 files changed, 150 insertions, 229 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 5de86908b0d0..65264a38057d 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,8 +2,7 @@
2 * drivers/s390/cio/chsc.c 2 * drivers/s390/cio/chsc.c
3 * S/390 common I/O routines -- channel subsystem call 3 * S/390 common I/O routines -- channel subsystem call
4 * 4 *
5 * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, 5 * Copyright IBM Corp. 1999,2008
6 * IBM Corporation
7 * Author(s): Ingo Adlung (adlung@de.ibm.com) 6 * Author(s): Ingo Adlung (adlung@de.ibm.com)
8 * Cornelia Huck (cornelia.huck@de.ibm.com) 7 * Cornelia Huck (cornelia.huck@de.ibm.com)
9 * Arnd Bergmann (arndb@de.ibm.com) 8 * Arnd Bergmann (arndb@de.ibm.com)
@@ -16,7 +15,9 @@
16 15
17#include <asm/cio.h> 16#include <asm/cio.h>
18#include <asm/chpid.h> 17#include <asm/chpid.h>
18#include <asm/chsc.h>
19 19
20#include "../s390mach.h"
20#include "css.h" 21#include "css.h"
21#include "cio.h" 22#include "cio.h"
22#include "cio_debug.h" 23#include "cio_debug.h"
@@ -127,77 +128,12 @@ out_free:
127 return ret; 128 return ret;
128} 129}
129 130
130static int check_for_io_on_path(struct subchannel *sch, int mask)
131{
132 int cc;
133
134 cc = stsch(sch->schid, &sch->schib);
135 if (cc)
136 return 0;
137 if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
138 return 1;
139 return 0;
140}
141
142static void terminate_internal_io(struct subchannel *sch)
143{
144 if (cio_clear(sch)) {
145 /* Recheck device in case clear failed. */
146 sch->lpm = 0;
147 if (device_trigger_verify(sch) != 0)
148 css_schedule_eval(sch->schid);
149 return;
150 }
151 /* Request retry of internal operation. */
152 device_set_intretry(sch);
153 /* Call handler. */
154 if (sch->driver && sch->driver->termination)
155 sch->driver->termination(sch);
156}
157
158static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) 131static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
159{ 132{
160 int j;
161 int mask;
162 struct chp_id *chpid = data;
163 struct schib schib;
164
165 for (j = 0; j < 8; j++) {
166 mask = 0x80 >> j;
167 if ((sch->schib.pmcw.pim & mask) &&
168 (sch->schib.pmcw.chpid[j] == chpid->id))
169 break;
170 }
171 if (j >= 8)
172 return 0;
173
174 spin_lock_irq(sch->lock); 133 spin_lock_irq(sch->lock);
175 134 if (sch->driver && sch->driver->chp_event)
176 stsch(sch->schid, &schib); 135 if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
177 if (!css_sch_is_valid(&schib))
178 goto out_unreg;
179 memcpy(&sch->schib, &schib, sizeof(struct schib));
180 /* Check for single path devices. */
181 if (sch->schib.pmcw.pim == 0x80)
182 goto out_unreg;
183
184 if (check_for_io_on_path(sch, mask)) {
185 if (device_is_online(sch))
186 device_kill_io(sch);
187 else {
188 terminate_internal_io(sch);
189 /* Re-start path verification. */
190 if (sch->driver && sch->driver->verify)
191 sch->driver->verify(sch);
192 }
193 } else {
194 /* trigger path verification. */
195 if (sch->driver && sch->driver->verify)
196 sch->driver->verify(sch);
197 else if (sch->lpm == mask)
198 goto out_unreg; 136 goto out_unreg;
199 }
200
201 spin_unlock_irq(sch->lock); 137 spin_unlock_irq(sch->lock);
202 return 0; 138 return 0;
203 139
@@ -211,15 +147,18 @@ out_unreg:
211void chsc_chp_offline(struct chp_id chpid) 147void chsc_chp_offline(struct chp_id chpid)
212{ 148{
213 char dbf_txt[15]; 149 char dbf_txt[15];
150 struct chp_link link;
214 151
215 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); 152 sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
216 CIO_TRACE_EVENT(2, dbf_txt); 153 CIO_TRACE_EVENT(2, dbf_txt);
217 154
218 if (chp_get_status(chpid) <= 0) 155 if (chp_get_status(chpid) <= 0)
219 return; 156 return;
157 memset(&link, 0, sizeof(struct chp_link));
158 link.chpid = chpid;
220 /* Wait until previous actions have settled. */ 159 /* Wait until previous actions have settled. */
221 css_wait_for_slow_path(); 160 css_wait_for_slow_path();
222 for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); 161 for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link);
223} 162}
224 163
225static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) 164static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
@@ -242,67 +181,25 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
242 return 0; 181 return 0;
243} 182}
244 183
245struct res_acc_data {
246 struct chp_id chpid;
247 u32 fla_mask;
248 u16 fla;
249};
250
251static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
252 struct res_acc_data *data)
253{
254 int i;
255 int mask;
256
257 for (i = 0; i < 8; i++) {
258 mask = 0x80 >> i;
259 if (!(ssd->path_mask & mask))
260 continue;
261 if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
262 continue;
263 if ((ssd->fla_valid_mask & mask) &&
264 ((ssd->fla[i] & data->fla_mask) != data->fla))
265 continue;
266 return mask;
267 }
268 return 0;
269}
270
271static int __s390_process_res_acc(struct subchannel *sch, void *data) 184static int __s390_process_res_acc(struct subchannel *sch, void *data)
272{ 185{
273 int chp_mask, old_lpm;
274 struct res_acc_data *res_data = data;
275
276 spin_lock_irq(sch->lock); 186 spin_lock_irq(sch->lock);
277 chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); 187 if (sch->driver && sch->driver->chp_event)
278 if (chp_mask == 0) 188 sch->driver->chp_event(sch, data, CHP_ONLINE);
279 goto out;
280 if (stsch(sch->schid, &sch->schib))
281 goto out;
282 old_lpm = sch->lpm;
283 sch->lpm = ((sch->schib.pmcw.pim &
284 sch->schib.pmcw.pam &
285 sch->schib.pmcw.pom)
286 | chp_mask) & sch->opm;
287 if (!old_lpm && sch->lpm)
288 device_trigger_reprobe(sch);
289 else if (sch->driver && sch->driver->verify)
290 sch->driver->verify(sch);
291out:
292 spin_unlock_irq(sch->lock); 189 spin_unlock_irq(sch->lock);
293 190
294 return 0; 191 return 0;
295} 192}
296 193
297static void s390_process_res_acc (struct res_acc_data *res_data) 194static void s390_process_res_acc(struct chp_link *link)
298{ 195{
299 char dbf_txt[15]; 196 char dbf_txt[15];
300 197
301 sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, 198 sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid,
302 res_data->chpid.id); 199 link->chpid.id);
303 CIO_TRACE_EVENT( 2, dbf_txt); 200 CIO_TRACE_EVENT( 2, dbf_txt);
304 if (res_data->fla != 0) { 201 if (link->fla != 0) {
305 sprintf(dbf_txt, "fla%x", res_data->fla); 202 sprintf(dbf_txt, "fla%x", link->fla);
306 CIO_TRACE_EVENT( 2, dbf_txt); 203 CIO_TRACE_EVENT( 2, dbf_txt);
307 } 204 }
308 /* Wait until previous actions have settled. */ 205 /* Wait until previous actions have settled. */
@@ -315,7 +212,7 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
315 * will we have to do. 212 * will we have to do.
316 */ 213 */
317 for_each_subchannel_staged(__s390_process_res_acc, 214 for_each_subchannel_staged(__s390_process_res_acc,
318 s390_process_res_acc_new_sch, res_data); 215 s390_process_res_acc_new_sch, link);
319} 216}
320 217
321static int 218static int
@@ -388,7 +285,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
388 285
389static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) 286static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
390{ 287{
391 struct res_acc_data res_data; 288 struct chp_link link;
392 struct chp_id chpid; 289 struct chp_id chpid;
393 int status; 290 int status;
394 291
@@ -404,18 +301,18 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
404 chp_new(chpid); 301 chp_new(chpid);
405 else if (!status) 302 else if (!status)
406 return; 303 return;
407 memset(&res_data, 0, sizeof(struct res_acc_data)); 304 memset(&link, 0, sizeof(struct chp_link));
408 res_data.chpid = chpid; 305 link.chpid = chpid;
409 if ((sei_area->vf & 0xc0) != 0) { 306 if ((sei_area->vf & 0xc0) != 0) {
410 res_data.fla = sei_area->fla; 307 link.fla = sei_area->fla;
411 if ((sei_area->vf & 0xc0) == 0xc0) 308 if ((sei_area->vf & 0xc0) == 0xc0)
412 /* full link address */ 309 /* full link address */
413 res_data.fla_mask = 0xffff; 310 link.fla_mask = 0xffff;
414 else 311 else
415 /* link address */ 312 /* link address */
416 res_data.fla_mask = 0xff00; 313 link.fla_mask = 0xff00;
417 } 314 }
418 s390_process_res_acc(&res_data); 315 s390_process_res_acc(&link);
419} 316}
420 317
421struct chp_config_data { 318struct chp_config_data {
@@ -480,17 +377,25 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
480 } 377 }
481} 378}
482 379
483void chsc_process_crw(void) 380static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
484{ 381{
485 struct chsc_sei_area *sei_area; 382 struct chsc_sei_area *sei_area;
486 383
384 if (overflow) {
385 css_schedule_eval_all();
386 return;
387 }
388 CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
389 "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
390 crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
391 crw0->erc, crw0->rsid);
487 if (!sei_page) 392 if (!sei_page)
488 return; 393 return;
489 /* Access to sei_page is serialized through machine check handler 394 /* Access to sei_page is serialized through machine check handler
490 * thread, so no need for locking. */ 395 * thread, so no need for locking. */
491 sei_area = sei_page; 396 sei_area = sei_page;
492 397
493 CIO_TRACE_EVENT( 2, "prcss"); 398 CIO_TRACE_EVENT(2, "prcss");
494 do { 399 do {
495 memset(sei_area, 0, sizeof(*sei_area)); 400 memset(sei_area, 0, sizeof(*sei_area));
496 sei_area->request.length = 0x0010; 401 sei_area->request.length = 0x0010;
@@ -509,114 +414,36 @@ void chsc_process_crw(void)
509 } while (sei_area->flags & 0x80); 414 } while (sei_area->flags & 0x80);
510} 415}
511 416
512static int __chp_add_new_sch(struct subchannel_id schid, void *data)
513{
514 struct schib schib;
515
516 if (stsch_err(schid, &schib))
517 /* We're through */
518 return -ENXIO;
519
520 /* Put it on the slow path. */
521 css_schedule_eval(schid);
522 return 0;
523}
524
525
526static int __chp_add(struct subchannel *sch, void *data)
527{
528 int i, mask;
529 struct chp_id *chpid = data;
530
531 spin_lock_irq(sch->lock);
532 for (i=0; i<8; i++) {
533 mask = 0x80 >> i;
534 if ((sch->schib.pmcw.pim & mask) &&
535 (sch->schib.pmcw.chpid[i] == chpid->id))
536 break;
537 }
538 if (i==8) {
539 spin_unlock_irq(sch->lock);
540 return 0;
541 }
542 if (stsch(sch->schid, &sch->schib)) {
543 spin_unlock_irq(sch->lock);
544 css_schedule_eval(sch->schid);
545 return 0;
546 }
547 sch->lpm = ((sch->schib.pmcw.pim &
548 sch->schib.pmcw.pam &
549 sch->schib.pmcw.pom)
550 | mask) & sch->opm;
551
552 if (sch->driver && sch->driver->verify)
553 sch->driver->verify(sch);
554
555 spin_unlock_irq(sch->lock);
556
557 return 0;
558}
559
560void chsc_chp_online(struct chp_id chpid) 417void chsc_chp_online(struct chp_id chpid)
561{ 418{
562 char dbf_txt[15]; 419 char dbf_txt[15];
420 struct chp_link link;
563 421
564 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); 422 sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
565 CIO_TRACE_EVENT(2, dbf_txt); 423 CIO_TRACE_EVENT(2, dbf_txt);
566 424
567 if (chp_get_status(chpid) != 0) { 425 if (chp_get_status(chpid) != 0) {
426 memset(&link, 0, sizeof(struct chp_link));
427 link.chpid = chpid;
568 /* Wait until previous actions have settled. */ 428 /* Wait until previous actions have settled. */
569 css_wait_for_slow_path(); 429 css_wait_for_slow_path();
570 for_each_subchannel_staged(__chp_add, __chp_add_new_sch, 430 for_each_subchannel_staged(__s390_process_res_acc, NULL,
571 &chpid); 431 &link);
572 } 432 }
573} 433}
574 434
575static void __s390_subchannel_vary_chpid(struct subchannel *sch, 435static void __s390_subchannel_vary_chpid(struct subchannel *sch,
576 struct chp_id chpid, int on) 436 struct chp_id chpid, int on)
577{ 437{
578 int chp, old_lpm;
579 int mask;
580 unsigned long flags; 438 unsigned long flags;
439 struct chp_link link;
581 440
441 memset(&link, 0, sizeof(struct chp_link));
442 link.chpid = chpid;
582 spin_lock_irqsave(sch->lock, flags); 443 spin_lock_irqsave(sch->lock, flags);
583 old_lpm = sch->lpm; 444 if (sch->driver && sch->driver->chp_event)
584 for (chp = 0; chp < 8; chp++) { 445 sch->driver->chp_event(sch, &link,
585 mask = 0x80 >> chp; 446 on ? CHP_VARY_ON : CHP_VARY_OFF);
586 if (!(sch->ssd_info.path_mask & mask))
587 continue;
588 if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
589 continue;
590
591 if (on) {
592 sch->opm |= mask;
593 sch->lpm |= mask;
594 if (!old_lpm)
595 device_trigger_reprobe(sch);
596 else if (sch->driver && sch->driver->verify)
597 sch->driver->verify(sch);
598 break;
599 }
600 sch->opm &= ~mask;
601 sch->lpm &= ~mask;
602 if (check_for_io_on_path(sch, mask)) {
603 if (device_is_online(sch))
604 /* Path verification is done after killing. */
605 device_kill_io(sch);
606 else {
607 /* Kill and retry internal I/O. */
608 terminate_internal_io(sch);
609 /* Re-start path verification. */
610 if (sch->driver && sch->driver->verify)
611 sch->driver->verify(sch);
612 }
613 } else if (!sch->lpm) {
614 if (device_trigger_verify(sch) != 0)
615 css_schedule_eval(sch->schid);
616 } else if (sch->driver && sch->driver->verify)
617 sch->driver->verify(sch);
618 break;
619 }
620 spin_unlock_irqrestore(sch->lock, flags); 447 spin_unlock_irqrestore(sch->lock, flags);
621} 448}
622 449
@@ -656,6 +483,10 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
656 */ 483 */
657int chsc_chp_vary(struct chp_id chpid, int on) 484int chsc_chp_vary(struct chp_id chpid, int on)
658{ 485{
486 struct chp_link link;
487
488 memset(&link, 0, sizeof(struct chp_link));
489 link.chpid = chpid;
659 /* Wait until previous actions have settled. */ 490 /* Wait until previous actions have settled. */
660 css_wait_for_slow_path(); 491 css_wait_for_slow_path();
661 /* 492 /*
@@ -664,10 +495,10 @@ int chsc_chp_vary(struct chp_id chpid, int on)
664 495
665 if (on) 496 if (on)
666 for_each_subchannel_staged(s390_subchannel_vary_chpid_on, 497 for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
667 __s390_vary_chpid_on, &chpid); 498 __s390_vary_chpid_on, &link);
668 else 499 else
669 for_each_subchannel_staged(s390_subchannel_vary_chpid_off, 500 for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
670 NULL, &chpid); 501 NULL, &link);
671 502
672 return 0; 503 return 0;
673} 504}
@@ -797,23 +628,33 @@ chsc_secm(struct channel_subsystem *css, int enable)
797 return ret; 628 return ret;
798} 629}
799 630
800int chsc_determine_channel_path_description(struct chp_id chpid, 631int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
801 struct channel_path_desc *desc) 632 int c, int m,
633 struct chsc_response_struct *resp)
802{ 634{
803 int ccode, ret; 635 int ccode, ret;
804 636
805 struct { 637 struct {
806 struct chsc_header request; 638 struct chsc_header request;
807 u32 : 24; 639 u32 : 2;
640 u32 m : 1;
641 u32 c : 1;
642 u32 fmt : 4;
643 u32 cssid : 8;
644 u32 : 4;
645 u32 rfmt : 4;
808 u32 first_chpid : 8; 646 u32 first_chpid : 8;
809 u32 : 24; 647 u32 : 24;
810 u32 last_chpid : 8; 648 u32 last_chpid : 8;
811 u32 zeroes1; 649 u32 zeroes1;
812 struct chsc_header response; 650 struct chsc_header response;
813 u32 zeroes2; 651 u8 data[PAGE_SIZE - 20];
814 struct channel_path_desc desc;
815 } __attribute__ ((packed)) *scpd_area; 652 } __attribute__ ((packed)) *scpd_area;
816 653
654 if ((rfmt == 1) && !css_general_characteristics.fcs)
655 return -EINVAL;
656 if ((rfmt == 2) && !css_general_characteristics.cib)
657 return -EINVAL;
817 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 658 scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
818 if (!scpd_area) 659 if (!scpd_area)
819 return -ENOMEM; 660 return -ENOMEM;
@@ -821,8 +662,13 @@ int chsc_determine_channel_path_description(struct chp_id chpid,
821 scpd_area->request.length = 0x0010; 662 scpd_area->request.length = 0x0010;
822 scpd_area->request.code = 0x0002; 663 scpd_area->request.code = 0x0002;
823 664
665 scpd_area->cssid = chpid.cssid;
824 scpd_area->first_chpid = chpid.id; 666 scpd_area->first_chpid = chpid.id;
825 scpd_area->last_chpid = chpid.id; 667 scpd_area->last_chpid = chpid.id;
668 scpd_area->m = m;
669 scpd_area->c = c;
670 scpd_area->fmt = fmt;
671 scpd_area->rfmt = rfmt;
826 672
827 ccode = chsc(scpd_area); 673 ccode = chsc(scpd_area);
828 if (ccode > 0) { 674 if (ccode > 0) {
@@ -833,8 +679,7 @@ int chsc_determine_channel_path_description(struct chp_id chpid,
833 ret = chsc_error_from_response(scpd_area->response.code); 679 ret = chsc_error_from_response(scpd_area->response.code);
834 if (ret == 0) 680 if (ret == 0)
835 /* Success. */ 681 /* Success. */
836 memcpy(desc, &scpd_area->desc, 682 memcpy(resp, &scpd_area->response, scpd_area->response.length);
837 sizeof(struct channel_path_desc));
838 else 683 else
839 CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", 684 CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
840 scpd_area->response.code); 685 scpd_area->response.code);
@@ -842,6 +687,25 @@ out:
842 free_page((unsigned long)scpd_area); 687 free_page((unsigned long)scpd_area);
843 return ret; 688 return ret;
844} 689}
690EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
691
692int chsc_determine_base_channel_path_desc(struct chp_id chpid,
693 struct channel_path_desc *desc)
694{
695 struct chsc_response_struct *chsc_resp;
696 int ret;
697
698 chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
699 if (!chsc_resp)
700 return -ENOMEM;
701 ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
702 if (ret)
703 goto out_free;
704 memcpy(desc, &chsc_resp->data, chsc_resp->length);
705out_free:
706 kfree(chsc_resp);
707 return ret;
708}
845 709
846static void 710static void
847chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, 711chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
@@ -937,15 +801,23 @@ out:
937 801
938int __init chsc_alloc_sei_area(void) 802int __init chsc_alloc_sei_area(void)
939{ 803{
804 int ret;
805
940 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 806 sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
941 if (!sei_page) 807 if (!sei_page) {
942 CIO_MSG_EVENT(0, "Can't allocate page for processing of " 808 CIO_MSG_EVENT(0, "Can't allocate page for processing of "
943 "chsc machine checks!\n"); 809 "chsc machine checks!\n");
944 return (sei_page ? 0 : -ENOMEM); 810 return -ENOMEM;
811 }
812 ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
813 if (ret)
814 kfree(sei_page);
815 return ret;
945} 816}
946 817
947void __init chsc_free_sei_area(void) 818void __init chsc_free_sei_area(void)
948{ 819{
820 s390_unregister_crw_handler(CRW_RSC_CSS);
949 kfree(sei_page); 821 kfree(sei_page);
950} 822}
951 823
@@ -1043,3 +915,52 @@ exit:
1043 915
1044EXPORT_SYMBOL_GPL(css_general_characteristics); 916EXPORT_SYMBOL_GPL(css_general_characteristics);
1045EXPORT_SYMBOL_GPL(css_chsc_characteristics); 917EXPORT_SYMBOL_GPL(css_chsc_characteristics);
918
919int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
920{
921 struct {
922 struct chsc_header request;
923 unsigned int rsvd0;
924 unsigned int op : 8;
925 unsigned int rsvd1 : 8;
926 unsigned int ctrl : 16;
927 unsigned int rsvd2[5];
928 struct chsc_header response;
929 unsigned int rsvd3[7];
930 } __attribute__ ((packed)) *rr;
931 int rc;
932
933 memset(page, 0, PAGE_SIZE);
934 rr = page;
935 rr->request.length = 0x0020;
936 rr->request.code = 0x0033;
937 rr->op = op;
938 rr->ctrl = ctrl;
939 rc = chsc(rr);
940 if (rc)
941 return -EIO;
942 rc = (rr->response.code == 0x0001) ? 0 : -EIO;
943 return rc;
944}
945
946int chsc_sstpi(void *page, void *result, size_t size)
947{
948 struct {
949 struct chsc_header request;
950 unsigned int rsvd0[3];
951 struct chsc_header response;
952 char data[size];
953 } __attribute__ ((packed)) *rr;
954 int rc;
955
956 memset(page, 0, PAGE_SIZE);
957 rr = page;
958 rr->request.length = 0x0010;
959 rr->request.code = 0x0038;
960 rc = chsc(rr);
961 if (rc)
962 return -EIO;
963 memcpy(result, &rr->data, size);
964 return (rr->response.code == 0x0001) ? 0 : -EIO;
965}
966