| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  drivers/s390/cio/cio.c | 
 | 3 |  *   S/390 common I/O routines -- low level i/o calls | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4 |  * | 
| Michael Holzheu | aa77015 | 2006-12-28 00:35:36 +0100 | [diff] [blame] | 5 |  *    Copyright (C) IBM Corp. 1999,2006 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 |  *    Author(s): Ingo Adlung (adlung@de.ibm.com) | 
| Cornelia Huck | 4ce3b30 | 2006-01-14 13:21:04 -0800 | [diff] [blame] | 7 |  *		 Cornelia Huck (cornelia.huck@de.ibm.com) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  *		 Arnd Bergmann (arndb@de.ibm.com) | 
 | 9 |  *		 Martin Schwidefsky (schwidefsky@de.ibm.com) | 
 | 10 |  */ | 
 | 11 |  | 
 | 12 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/init.h> | 
 | 14 | #include <linux/slab.h> | 
 | 15 | #include <linux/device.h> | 
 | 16 | #include <linux/kernel_stat.h> | 
 | 17 | #include <linux/interrupt.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | #include <asm/cio.h> | 
 | 19 | #include <asm/delay.h> | 
 | 20 | #include <asm/irq.h> | 
| Heiko Carstens | 5a489b9 | 2006-10-06 16:38:35 +0200 | [diff] [blame] | 21 | #include <asm/irq_regs.h> | 
| Heiko Carstens | e87bfe5 | 2006-09-20 15:59:15 +0200 | [diff] [blame] | 22 | #include <asm/setup.h> | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 23 | #include <asm/reset.h> | 
| Michael Holzheu | 46b05d2 | 2007-02-21 10:55:21 +0100 | [diff] [blame] | 24 | #include <asm/ipl.h> | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 25 | #include <asm/chpid.h> | 
| Peter Oberparleiter | 4e8e56c | 2008-01-26 14:10:44 +0100 | [diff] [blame] | 26 | #include <asm/airq.h> | 
| Heiko Carstens | 43ca5c3 | 2008-04-17 07:46:23 +0200 | [diff] [blame] | 27 | #include <asm/cpu.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | #include "cio.h" | 
 | 29 | #include "css.h" | 
 | 30 | #include "chsc.h" | 
 | 31 | #include "ioasm.h" | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 32 | #include "io_sch.h" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 | #include "blacklist.h" | 
 | 34 | #include "cio_debug.h" | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 35 | #include "chp.h" | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 36 | #include "../s390mach.h" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 |  | 
 | 38 | debug_info_t *cio_debug_msg_id; | 
 | 39 | debug_info_t *cio_debug_trace_id; | 
 | 40 | debug_info_t *cio_debug_crw_id; | 
 | 41 |  | 
 | 42 | int cio_show_msg; | 
 | 43 |  | 
 | 44 | static int __init | 
 | 45 | cio_setup (char *parm) | 
 | 46 | { | 
 | 47 | 	if (!strcmp (parm, "yes")) | 
 | 48 | 		cio_show_msg = 1; | 
 | 49 | 	else if (!strcmp (parm, "no")) | 
 | 50 | 		cio_show_msg = 0; | 
 | 51 | 	else | 
| Cornelia Huck | e556bbb | 2007-07-27 12:29:19 +0200 | [diff] [blame] | 52 | 		printk(KERN_ERR "cio: cio_setup: " | 
 | 53 | 		       "invalid cio_msg parameter '%s'", parm); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 | 	return 1; | 
 | 55 | } | 
 | 56 |  | 
 | 57 | __setup ("cio_msg=", cio_setup); | 
 | 58 |  | 
 | 59 | /* | 
 | 60 |  * Function: cio_debug_init | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 61 |  * Initializes three debug logs for common I/O: | 
 | 62 |  * - cio_msg logs generic cio messages | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 |  * - cio_trace logs the calling of different functions | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 64 |  * - cio_crw logs machine check related cio messages | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 |  */ | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 66 | static int __init cio_debug_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | { | 
| Peter Tiedemann | 361f494 | 2008-01-26 14:11:30 +0100 | [diff] [blame] | 68 | 	cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 69 | 	if (!cio_debug_msg_id) | 
 | 70 | 		goto out_unregister; | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 71 | 	debug_register_view(cio_debug_msg_id, &debug_sprintf_view); | 
 | 72 | 	debug_set_level(cio_debug_msg_id, 2); | 
| Peter Tiedemann | 361f494 | 2008-01-26 14:11:30 +0100 | [diff] [blame] | 73 | 	cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | 	if (!cio_debug_trace_id) | 
 | 75 | 		goto out_unregister; | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 76 | 	debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view); | 
 | 77 | 	debug_set_level(cio_debug_trace_id, 2); | 
| Peter Tiedemann | 361f494 | 2008-01-26 14:11:30 +0100 | [diff] [blame] | 78 | 	cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | 	if (!cio_debug_crw_id) | 
 | 80 | 		goto out_unregister; | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 81 | 	debug_register_view(cio_debug_crw_id, &debug_sprintf_view); | 
 | 82 | 	debug_set_level(cio_debug_crw_id, 4); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | 	return 0; | 
 | 84 |  | 
 | 85 | out_unregister: | 
 | 86 | 	if (cio_debug_msg_id) | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 87 | 		debug_unregister(cio_debug_msg_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | 	if (cio_debug_trace_id) | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 89 | 		debug_unregister(cio_debug_trace_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | 	if (cio_debug_crw_id) | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 91 | 		debug_unregister(cio_debug_crw_id); | 
| Cornelia Huck | e556bbb | 2007-07-27 12:29:19 +0200 | [diff] [blame] | 92 | 	printk(KERN_WARNING"cio: could not initialize debugging\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | 	return -1; | 
 | 94 | } | 
 | 95 |  | 
 | 96 | arch_initcall (cio_debug_init); | 
 | 97 |  | 
 | 98 | int | 
 | 99 | cio_set_options (struct subchannel *sch, int flags) | 
 | 100 | { | 
 | 101 |        sch->options.suspend = (flags & DOIO_ALLOW_SUSPEND) != 0; | 
 | 102 |        sch->options.prefetch = (flags & DOIO_DENY_PREFETCH) != 0; | 
 | 103 |        sch->options.inter = (flags & DOIO_SUPPRESS_INTER) != 0; | 
 | 104 |        return 0; | 
 | 105 | } | 
 | 106 |  | 
 | 107 | /* FIXME: who wants to use this? */ | 
 | 108 | int | 
 | 109 | cio_get_options (struct subchannel *sch) | 
 | 110 | { | 
 | 111 |        int flags; | 
 | 112 |  | 
 | 113 |        flags = 0; | 
 | 114 |        if (sch->options.suspend) | 
 | 115 | 		flags |= DOIO_ALLOW_SUSPEND; | 
 | 116 |        if (sch->options.prefetch) | 
 | 117 | 		flags |= DOIO_DENY_PREFETCH; | 
 | 118 |        if (sch->options.inter) | 
 | 119 | 		flags |= DOIO_SUPPRESS_INTER; | 
 | 120 |        return flags; | 
 | 121 | } | 
 | 122 |  | 
 | 123 | /* | 
 | 124 |  * Use tpi to get a pending interrupt, call the interrupt handler and | 
 | 125 |  * return a pointer to the subchannel structure. | 
 | 126 |  */ | 
| Heiko Carstens | 4d284ca | 2007-02-05 21:18:53 +0100 | [diff] [blame] | 127 | static int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 128 | cio_tpi(void) | 
 | 129 | { | 
 | 130 | 	struct tpi_info *tpi_info; | 
 | 131 | 	struct subchannel *sch; | 
 | 132 | 	struct irb *irb; | 
 | 133 |  | 
 | 134 | 	tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; | 
 | 135 | 	if (tpi (NULL) != 1) | 
 | 136 | 		return 0; | 
 | 137 | 	irb = (struct irb *) __LC_IRB; | 
 | 138 | 	/* Store interrupt response block to lowcore. */ | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 139 | 	if (tsch (tpi_info->schid, irb) != 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 | 		/* Not status pending or not operational. */ | 
 | 141 | 		return 1; | 
 | 142 | 	sch = (struct subchannel *)(unsigned long)tpi_info->intparm; | 
 | 143 | 	if (!sch) | 
 | 144 | 		return 1; | 
 | 145 | 	local_bh_disable(); | 
 | 146 | 	irq_enter (); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 147 | 	spin_lock(sch->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 148 | 	memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); | 
 | 149 | 	if (sch->driver && sch->driver->irq) | 
| Cornelia Huck | 602b20f | 2008-01-26 14:10:39 +0100 | [diff] [blame] | 150 | 		sch->driver->irq(sch); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 151 | 	spin_unlock(sch->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | 	irq_exit (); | 
| Heiko Carstens | 1f194a4 | 2006-07-03 00:24:46 -0700 | [diff] [blame] | 153 | 	_local_bh_enable(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 154 | 	return 1; | 
 | 155 | } | 
 | 156 |  | 
| Heiko Carstens | 4d284ca | 2007-02-05 21:18:53 +0100 | [diff] [blame] | 157 | static int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) | 
 | 159 | { | 
 | 160 | 	char dbf_text[15]; | 
 | 161 |  | 
 | 162 | 	if (lpm != 0) | 
 | 163 | 		sch->lpm &= ~lpm; | 
 | 164 | 	else | 
 | 165 | 		sch->lpm = 0; | 
 | 166 |  | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 167 | 	stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 |  | 
 | 169 | 	CIO_MSG_EVENT(0, "cio_start: 'not oper' status for " | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 170 | 		      "subchannel 0.%x.%04x!\n", sch->schid.ssid, | 
 | 171 | 		      sch->schid.sch_no); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 172 | 	sprintf(dbf_text, "no%s", sch->dev.bus_id); | 
 | 173 | 	CIO_TRACE_EVENT(0, dbf_text); | 
 | 174 | 	CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); | 
 | 175 |  | 
 | 176 | 	return (sch->lpm ? -EACCES : -ENODEV); | 
 | 177 | } | 
 | 178 |  | 
 | 179 | int | 
 | 180 | cio_start_key (struct subchannel *sch,	/* subchannel structure */ | 
 | 181 | 	       struct ccw1 * cpa,	/* logical channel prog addr */ | 
 | 182 | 	       __u8 lpm,		/* logical path mask */ | 
 | 183 | 	       __u8 key)                /* storage key */ | 
 | 184 | { | 
 | 185 | 	char dbf_txt[15]; | 
 | 186 | 	int ccode; | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 187 | 	struct orb *orb; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 |  | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 189 | 	CIO_TRACE_EVENT(4, "stIO"); | 
 | 190 | 	CIO_TRACE_EVENT(4, sch->dev.bus_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 |  | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 192 | 	orb = &to_io_private(sch)->orb; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 193 | 	/* sch is always under 2G. */ | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 194 | 	orb->intparm = (u32)(addr_t)sch; | 
 | 195 | 	orb->fmt = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 196 |  | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 197 | 	orb->pfch = sch->options.prefetch == 0; | 
 | 198 | 	orb->spnd = sch->options.suspend; | 
 | 199 | 	orb->ssic = sch->options.suspend && sch->options.inter; | 
 | 200 | 	orb->lpm = (lpm != 0) ? lpm : sch->lpm; | 
| Martin Schwidefsky | 347a8dc | 2006-01-06 00:19:28 -0800 | [diff] [blame] | 201 | #ifdef CONFIG_64BIT | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 202 | 	/* | 
 | 203 | 	 * for 64 bit we always support 64 bit IDAWs with 4k page size only | 
 | 204 | 	 */ | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 205 | 	orb->c64 = 1; | 
 | 206 | 	orb->i2k = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | #endif | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 208 | 	orb->key = key >> 4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 209 | 	/* issue "Start Subchannel" */ | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 210 | 	orb->cpa = (__u32) __pa(cpa); | 
 | 211 | 	ccode = ssch(sch->schid, orb); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 |  | 
 | 213 | 	/* process condition code */ | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 214 | 	sprintf(dbf_txt, "ccode:%d", ccode); | 
 | 215 | 	CIO_TRACE_EVENT(4, dbf_txt); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 |  | 
 | 217 | 	switch (ccode) { | 
 | 218 | 	case 0: | 
 | 219 | 		/* | 
 | 220 | 		 * initialize device status information | 
 | 221 | 		 */ | 
 | 222 | 		sch->schib.scsw.actl |= SCSW_ACTL_START_PEND; | 
 | 223 | 		return 0; | 
 | 224 | 	case 1:		/* status pending */ | 
 | 225 | 	case 2:		/* busy */ | 
 | 226 | 		return -EBUSY; | 
 | 227 | 	default:		/* device/path not operational */ | 
 | 228 | 		return cio_start_handle_notoper(sch, lpm); | 
 | 229 | 	} | 
 | 230 | } | 
 | 231 |  | 
 | 232 | int | 
 | 233 | cio_start (struct subchannel *sch, struct ccw1 *cpa, __u8 lpm) | 
 | 234 | { | 
| Peter Oberparleiter | 0b642ed | 2005-05-01 08:58:58 -0700 | [diff] [blame] | 235 | 	return cio_start_key(sch, cpa, lpm, PAGE_DEFAULT_KEY); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | } | 
 | 237 |  | 
 | 238 | /* | 
 | 239 |  * resume suspended I/O operation | 
 | 240 |  */ | 
 | 241 | int | 
 | 242 | cio_resume (struct subchannel *sch) | 
 | 243 | { | 
 | 244 | 	char dbf_txt[15]; | 
 | 245 | 	int ccode; | 
 | 246 |  | 
 | 247 | 	CIO_TRACE_EVENT (4, "resIO"); | 
 | 248 | 	CIO_TRACE_EVENT (4, sch->dev.bus_id); | 
 | 249 |  | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 250 | 	ccode = rsch (sch->schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 |  | 
 | 252 | 	sprintf (dbf_txt, "ccode:%d", ccode); | 
 | 253 | 	CIO_TRACE_EVENT (4, dbf_txt); | 
 | 254 |  | 
 | 255 | 	switch (ccode) { | 
 | 256 | 	case 0: | 
 | 257 | 		sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND; | 
 | 258 | 		return 0; | 
 | 259 | 	case 1: | 
 | 260 | 		return -EBUSY; | 
 | 261 | 	case 2: | 
 | 262 | 		return -EINVAL; | 
 | 263 | 	default: | 
 | 264 | 		/* | 
 | 265 | 		 * useless to wait for request completion | 
 | 266 | 		 *  as device is no longer operational ! | 
 | 267 | 		 */ | 
 | 268 | 		return -ENODEV; | 
 | 269 | 	} | 
 | 270 | } | 
 | 271 |  | 
 | 272 | /* | 
 | 273 |  * halt I/O operation | 
 | 274 |  */ | 
 | 275 | int | 
 | 276 | cio_halt(struct subchannel *sch) | 
 | 277 | { | 
 | 278 | 	char dbf_txt[15]; | 
 | 279 | 	int ccode; | 
 | 280 |  | 
 | 281 | 	if (!sch) | 
 | 282 | 		return -ENODEV; | 
 | 283 |  | 
 | 284 | 	CIO_TRACE_EVENT (2, "haltIO"); | 
 | 285 | 	CIO_TRACE_EVENT (2, sch->dev.bus_id); | 
 | 286 |  | 
 | 287 | 	/* | 
 | 288 | 	 * Issue "Halt subchannel" and process condition code | 
 | 289 | 	 */ | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 290 | 	ccode = hsch (sch->schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 |  | 
 | 292 | 	sprintf (dbf_txt, "ccode:%d", ccode); | 
 | 293 | 	CIO_TRACE_EVENT (2, dbf_txt); | 
 | 294 |  | 
 | 295 | 	switch (ccode) { | 
 | 296 | 	case 0: | 
 | 297 | 		sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND; | 
 | 298 | 		return 0; | 
 | 299 | 	case 1:		/* status pending */ | 
 | 300 | 	case 2:		/* busy */ | 
 | 301 | 		return -EBUSY; | 
 | 302 | 	default:		/* device not operational */ | 
 | 303 | 		return -ENODEV; | 
 | 304 | 	} | 
 | 305 | } | 
 | 306 |  | 
 | 307 | /* | 
 | 308 |  * Clear I/O operation | 
 | 309 |  */ | 
 | 310 | int | 
 | 311 | cio_clear(struct subchannel *sch) | 
 | 312 | { | 
 | 313 | 	char dbf_txt[15]; | 
 | 314 | 	int ccode; | 
 | 315 |  | 
 | 316 | 	if (!sch) | 
 | 317 | 		return -ENODEV; | 
 | 318 |  | 
 | 319 | 	CIO_TRACE_EVENT (2, "clearIO"); | 
 | 320 | 	CIO_TRACE_EVENT (2, sch->dev.bus_id); | 
 | 321 |  | 
 | 322 | 	/* | 
 | 323 | 	 * Issue "Clear subchannel" and process condition code | 
 | 324 | 	 */ | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 325 | 	ccode = csch (sch->schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 326 |  | 
 | 327 | 	sprintf (dbf_txt, "ccode:%d", ccode); | 
 | 328 | 	CIO_TRACE_EVENT (2, dbf_txt); | 
 | 329 |  | 
 | 330 | 	switch (ccode) { | 
 | 331 | 	case 0: | 
 | 332 | 		sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND; | 
 | 333 | 		return 0; | 
 | 334 | 	default:		/* device not operational */ | 
 | 335 | 		return -ENODEV; | 
 | 336 | 	} | 
 | 337 | } | 
 | 338 |  | 
 | 339 | /* | 
 | 340 |  * Function: cio_cancel | 
 | 341 |  * Issues a "Cancel Subchannel" on the specified subchannel | 
 | 342 |  * Note: We don't need any fancy intparms and flags here | 
 | 343 |  *	 since xsch is executed synchronously. | 
 | 344 |  * Only for common I/O internal use as for now. | 
 | 345 |  */ | 
 | 346 | int | 
 | 347 | cio_cancel (struct subchannel *sch) | 
 | 348 | { | 
 | 349 | 	char dbf_txt[15]; | 
 | 350 | 	int ccode; | 
 | 351 |  | 
 | 352 | 	if (!sch) | 
 | 353 | 		return -ENODEV; | 
 | 354 |  | 
 | 355 | 	CIO_TRACE_EVENT (2, "cancelIO"); | 
 | 356 | 	CIO_TRACE_EVENT (2, sch->dev.bus_id); | 
 | 357 |  | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 358 | 	ccode = xsch (sch->schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 |  | 
 | 360 | 	sprintf (dbf_txt, "ccode:%d", ccode); | 
 | 361 | 	CIO_TRACE_EVENT (2, dbf_txt); | 
 | 362 |  | 
 | 363 | 	switch (ccode) { | 
 | 364 | 	case 0:		/* success */ | 
 | 365 | 		/* Update information in scsw. */ | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 366 | 		stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 367 | 		return 0; | 
 | 368 | 	case 1:		/* status pending */ | 
 | 369 | 		return -EBUSY; | 
 | 370 | 	case 2:		/* not applicable */ | 
 | 371 | 		return -EINVAL; | 
 | 372 | 	default:	/* not oper */ | 
 | 373 | 		return -ENODEV; | 
 | 374 | 	} | 
 | 375 | } | 
 | 376 |  | 
 | 377 | /* | 
 | 378 |  * Function: cio_modify | 
 | 379 |  * Issues a "Modify Subchannel" on the specified subchannel | 
 | 380 |  */ | 
 | 381 | int | 
 | 382 | cio_modify (struct subchannel *sch) | 
 | 383 | { | 
 | 384 | 	int ccode, retry, ret; | 
 | 385 |  | 
 | 386 | 	ret = 0; | 
 | 387 | 	for (retry = 0; retry < 5; retry++) { | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 388 | 		ccode = msch_err (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | 		if (ccode < 0)	/* -EIO if msch gets a program check. */ | 
 | 390 | 			return ccode; | 
 | 391 | 		switch (ccode) { | 
 | 392 | 		case 0: /* successfull */ | 
 | 393 | 			return 0; | 
 | 394 | 		case 1:	/* status pending */ | 
 | 395 | 			return -EBUSY; | 
 | 396 | 		case 2:	/* busy */ | 
 | 397 | 			udelay (100);	/* allow for recovery */ | 
 | 398 | 			ret = -EBUSY; | 
 | 399 | 			break; | 
 | 400 | 		case 3:	/* not operational */ | 
 | 401 | 			return -ENODEV; | 
 | 402 | 		} | 
 | 403 | 	} | 
 | 404 | 	return ret; | 
 | 405 | } | 
 | 406 |  | 
 | 407 | /* | 
 | 408 |  * Enable subchannel. | 
 | 409 |  */ | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 410 | int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, | 
 | 411 | 			  u32 intparm) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 412 | { | 
 | 413 | 	char dbf_txt[15]; | 
 | 414 | 	int ccode; | 
 | 415 | 	int retry; | 
 | 416 | 	int ret; | 
 | 417 |  | 
 | 418 | 	CIO_TRACE_EVENT (2, "ensch"); | 
 | 419 | 	CIO_TRACE_EVENT (2, sch->dev.bus_id); | 
 | 420 |  | 
| Cornelia Huck | d7b5a4c | 2006-12-08 15:54:28 +0100 | [diff] [blame] | 421 | 	if (sch_is_pseudo_sch(sch)) | 
 | 422 | 		return -EINVAL; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 423 | 	ccode = stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | 	if (ccode) | 
 | 425 | 		return -ENODEV; | 
 | 426 |  | 
 | 427 | 	for (retry = 5, ret = 0; retry > 0; retry--) { | 
 | 428 | 		sch->schib.pmcw.ena = 1; | 
 | 429 | 		sch->schib.pmcw.isc = isc; | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 430 | 		sch->schib.pmcw.intparm = intparm; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 431 | 		ret = cio_modify(sch); | 
 | 432 | 		if (ret == -ENODEV) | 
 | 433 | 			break; | 
 | 434 | 		if (ret == -EIO) | 
 | 435 | 			/* | 
 | 436 | 			 * Got a program check in cio_modify. Try without | 
 | 437 | 			 * the concurrent sense bit the next time. | 
 | 438 | 			 */ | 
 | 439 | 			sch->schib.pmcw.csense = 0; | 
 | 440 | 		if (ret == 0) { | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 441 | 			stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 | 			if (sch->schib.pmcw.ena) | 
 | 443 | 				break; | 
 | 444 | 		} | 
 | 445 | 		if (ret == -EBUSY) { | 
 | 446 | 			struct irb irb; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 447 | 			if (tsch(sch->schid, &irb) != 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 | 				break; | 
 | 449 | 		} | 
 | 450 | 	} | 
 | 451 | 	sprintf (dbf_txt, "ret:%d", ret); | 
 | 452 | 	CIO_TRACE_EVENT (2, dbf_txt); | 
 | 453 | 	return ret; | 
 | 454 | } | 
 | 455 |  | 
 | 456 | /* | 
 | 457 |  * Disable subchannel. | 
 | 458 |  */ | 
 | 459 | int | 
 | 460 | cio_disable_subchannel (struct subchannel *sch) | 
 | 461 | { | 
 | 462 | 	char dbf_txt[15]; | 
 | 463 | 	int ccode; | 
 | 464 | 	int retry; | 
 | 465 | 	int ret; | 
 | 466 |  | 
 | 467 | 	CIO_TRACE_EVENT (2, "dissch"); | 
 | 468 | 	CIO_TRACE_EVENT (2, sch->dev.bus_id); | 
 | 469 |  | 
| Cornelia Huck | d7b5a4c | 2006-12-08 15:54:28 +0100 | [diff] [blame] | 470 | 	if (sch_is_pseudo_sch(sch)) | 
 | 471 | 		return 0; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 472 | 	ccode = stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 | 	if (ccode == 3)		/* Not operational. */ | 
 | 474 | 		return -ENODEV; | 
 | 475 |  | 
 | 476 | 	if (sch->schib.scsw.actl != 0) | 
 | 477 | 		/* | 
 | 478 | 		 * the disable function must not be called while there are | 
 | 479 | 		 *  requests pending for completion ! | 
 | 480 | 		 */ | 
 | 481 | 		return -EBUSY; | 
 | 482 |  | 
 | 483 | 	for (retry = 5, ret = 0; retry > 0; retry--) { | 
 | 484 | 		sch->schib.pmcw.ena = 0; | 
 | 485 | 		ret = cio_modify(sch); | 
 | 486 | 		if (ret == -ENODEV) | 
 | 487 | 			break; | 
 | 488 | 		if (ret == -EBUSY) | 
 | 489 | 			/* | 
 | 490 | 			 * The subchannel is busy or status pending. | 
 | 491 | 			 * We'll disable when the next interrupt was delivered | 
 | 492 | 			 * via the state machine. | 
 | 493 | 			 */ | 
 | 494 | 			break; | 
 | 495 | 		if (ret == 0) { | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 496 | 			stsch (sch->schid, &sch->schib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 | 			if (!sch->schib.pmcw.ena) | 
 | 498 | 				break; | 
 | 499 | 		} | 
 | 500 | 	} | 
 | 501 | 	sprintf (dbf_txt, "ret:%d", ret); | 
 | 502 | 	CIO_TRACE_EVENT (2, dbf_txt); | 
 | 503 | 	return ret; | 
 | 504 | } | 
 | 505 |  | 
| Cornelia Huck | d7b5a4c | 2006-12-08 15:54:28 +0100 | [diff] [blame] | 506 | int cio_create_sch_lock(struct subchannel *sch) | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 507 | { | 
 | 508 | 	sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); | 
 | 509 | 	if (!sch->lock) | 
 | 510 | 		return -ENOMEM; | 
 | 511 | 	spin_lock_init(sch->lock); | 
 | 512 | 	return 0; | 
 | 513 | } | 
 | 514 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 515 | /* | 
 | 516 |  * cio_validate_subchannel() | 
 | 517 |  * | 
 | 518 |  * Find out subchannel type and initialize struct subchannel. | 
 | 519 |  * Return codes: | 
 | 520 |  *   SUBCHANNEL_TYPE_IO for a normal io subchannel | 
 | 521 |  *   SUBCHANNEL_TYPE_CHSC for a chsc subchannel | 
 | 522 |  *   SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel | 
 | 523 |  *   SUBCHANNEL_TYPE_ADM for a adm(?) subchannel | 
 | 524 |  *   -ENXIO for non-defined subchannels | 
 | 525 |  *   -ENODEV for subchannels with invalid device number or blacklisted devices | 
 | 526 |  */ | 
 | 527 | int | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 528 | cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 529 | { | 
 | 530 | 	char dbf_txt[15]; | 
 | 531 | 	int ccode; | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 532 | 	int err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 533 |  | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 534 | 	sprintf (dbf_txt, "valsch%x", schid.sch_no); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 535 | 	CIO_TRACE_EVENT (4, dbf_txt); | 
 | 536 |  | 
 | 537 | 	/* Nuke all fields. */ | 
 | 538 | 	memset(sch, 0, sizeof(struct subchannel)); | 
 | 539 |  | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 540 | 	sch->schid = schid; | 
 | 541 | 	if (cio_is_console(schid)) { | 
 | 542 | 		sch->lock = cio_get_console_lock(); | 
 | 543 | 	} else { | 
 | 544 | 		err = cio_create_sch_lock(sch); | 
 | 545 | 		if (err) | 
 | 546 | 			goto out; | 
 | 547 | 	} | 
| Cornelia Huck | 6ab4879 | 2006-07-12 16:39:50 +0200 | [diff] [blame] | 548 | 	mutex_init(&sch->reg_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 | 	/* Set a name for the subchannel */ | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 550 | 	snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid, | 
 | 551 | 		  schid.sch_no); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 552 |  | 
 | 553 | 	/* | 
 | 554 | 	 * The first subchannel that is not-operational (ccode==3) | 
 | 555 | 	 *  indicates that there aren't any more devices available. | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 556 | 	 * If stsch gets an exception, it means the current subchannel set | 
 | 557 | 	 *  is not valid. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 558 | 	 */ | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 559 | 	ccode = stsch_err (schid, &sch->schib); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 560 | 	if (ccode) { | 
 | 561 | 		err = (ccode == 3) ? -ENXIO : ccode; | 
 | 562 | 		goto out; | 
 | 563 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | 	/* Copy subchannel type from path management control word. */ | 
 | 565 | 	sch->st = sch->schib.pmcw.st; | 
 | 566 |  | 
 | 567 | 	/* | 
 | 568 | 	 * ... just being curious we check for non I/O subchannels | 
 | 569 | 	 */ | 
 | 570 | 	if (sch->st != 0) { | 
 | 571 | 		CIO_DEBUG(KERN_INFO, 0, | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 572 | 			  "Subchannel 0.%x.%04x reports " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 573 | 			  "non-I/O subchannel type %04X\n", | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 574 | 			  sch->schid.ssid, sch->schid.sch_no, sch->st); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 575 | 		/* We stop here for non-io subchannels. */ | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 576 | 		err = sch->st; | 
 | 577 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 578 | 	} | 
 | 579 |  | 
 | 580 | 	/* Initialization for io subchannels. */ | 
| Michael Ernst | 808e488 | 2008-01-26 14:10:51 +0100 | [diff] [blame] | 581 | 	if (!css_sch_is_valid(&sch->schib)) { | 
 | 582 | 		err = -ENODEV; | 
 | 583 | 		goto out; | 
 | 584 | 	} | 
 | 585 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 | 	/* Devno is valid. */ | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 587 | 	if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 | 		/* | 
 | 589 | 		 * This device must not be known to Linux. So we simply | 
 | 590 | 		 * say that there is no device and return ENODEV. | 
 | 591 | 		 */ | 
| Cornelia Huck | 347d59d | 2007-02-05 21:17:56 +0100 | [diff] [blame] | 592 | 		CIO_MSG_EVENT(4, "Blacklisted device detected " | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 593 | 			      "at devno %04X, subchannel set %x\n", | 
 | 594 | 			      sch->schib.pmcw.dev, sch->schid.ssid); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 595 | 		err = -ENODEV; | 
 | 596 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 597 | 	} | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 598 | 	if (cio_is_console(sch->schid)) | 
 | 599 | 		sch->opm = 0xff; | 
 | 600 | 	else | 
 | 601 | 		sch->opm = chp_get_sch_opm(sch); | 
| Peter Oberparleiter | 28bdc6f | 2006-09-20 15:59:59 +0200 | [diff] [blame] | 602 | 	sch->lpm = sch->schib.pmcw.pam & sch->opm; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 603 |  | 
 | 604 | 	CIO_DEBUG(KERN_INFO, 0, | 
| Cornelia Huck | bc698bc | 2008-01-26 14:10:42 +0100 | [diff] [blame] | 605 | 		  "Detected device %04x on subchannel 0.%x.%04X" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | 		  " - PIM = %02X, PAM = %02X, POM = %02X\n", | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 607 | 		  sch->schib.pmcw.dev, sch->schid.ssid, | 
 | 608 | 		  sch->schid.sch_no, sch->schib.pmcw.pim, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 | 		  sch->schib.pmcw.pam, sch->schib.pmcw.pom); | 
 | 610 |  | 
 | 611 | 	/* | 
 | 612 | 	 * We now have to initially ... | 
 | 613 | 	 *  ... set "interruption subclass" | 
 | 614 | 	 *  ... enable "concurrent sense" | 
 | 615 | 	 *  ... enable "multipath mode" if more than one | 
 | 616 | 	 *	  CHPID is available. This is done regardless | 
 | 617 | 	 *	  whether multiple paths are available for us. | 
 | 618 | 	 */ | 
 | 619 | 	sch->schib.pmcw.isc = 3;	/* could be smth. else */ | 
 | 620 | 	sch->schib.pmcw.csense = 1;	/* concurrent sense */ | 
 | 621 | 	sch->schib.pmcw.ena = 0; | 
 | 622 | 	if ((sch->lpm & (sch->lpm - 1)) != 0) | 
 | 623 | 		sch->schib.pmcw.mp = 1;	/* multipath mode */ | 
| Cornelia Huck | 1842f2b | 2007-10-12 16:11:22 +0200 | [diff] [blame] | 624 | 	/* clean up possible residual cmf stuff */ | 
 | 625 | 	sch->schib.pmcw.mme = 0; | 
 | 626 | 	sch->schib.pmcw.mbfc = 0; | 
 | 627 | 	sch->schib.pmcw.mbi = 0; | 
 | 628 | 	sch->schib.mba = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 629 | 	return 0; | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 630 | out: | 
 | 631 | 	if (!cio_is_console(schid)) | 
 | 632 | 		kfree(sch->lock); | 
 | 633 | 	sch->lock = NULL; | 
 | 634 | 	return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 635 | } | 
 | 636 |  | 
 | 637 | /* | 
 | 638 |  * do_IRQ() handles all normal I/O device IRQ's (the special | 
 | 639 |  *	    SMP cross-CPU interrupts have their own specific | 
 | 640 |  *	    handlers). | 
 | 641 |  * | 
 | 642 |  */ | 
 | 643 | void | 
 | 644 | do_IRQ (struct pt_regs *regs) | 
 | 645 | { | 
 | 646 | 	struct tpi_info *tpi_info; | 
 | 647 | 	struct subchannel *sch; | 
 | 648 | 	struct irb *irb; | 
| Heiko Carstens | 5a489b9 | 2006-10-06 16:38:35 +0200 | [diff] [blame] | 649 | 	struct pt_regs *old_regs; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 650 |  | 
| Heiko Carstens | 5a489b9 | 2006-10-06 16:38:35 +0200 | [diff] [blame] | 651 | 	old_regs = set_irq_regs(regs); | 
| Heiko Carstens | 9d0a57c | 2006-10-11 15:31:26 +0200 | [diff] [blame] | 652 | 	irq_enter(); | 
| Heiko Carstens | 43ca5c3 | 2008-04-17 07:46:23 +0200 | [diff] [blame] | 653 | 	s390_idle_check(); | 
| Heiko Carstens | 5a62b19 | 2008-04-17 07:46:25 +0200 | [diff] [blame] | 654 | 	if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | 
 | 655 | 		/* Serve timer interrupts first. */ | 
 | 656 | 		clock_comparator_work(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 657 | 	/* | 
 | 658 | 	 * Get interrupt information from lowcore | 
 | 659 | 	 */ | 
 | 660 | 	tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; | 
 | 661 | 	irb = (struct irb *) __LC_IRB; | 
 | 662 | 	do { | 
 | 663 | 		kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++; | 
 | 664 | 		/* | 
 | 665 | 		 * Non I/O-subchannel thin interrupts are processed differently | 
 | 666 | 		 */ | 
 | 667 | 		if (tpi_info->adapter_IO == 1 && | 
 | 668 | 		    tpi_info->int_type == IO_INTERRUPT_TYPE) { | 
 | 669 | 			do_adapter_IO(); | 
 | 670 | 			continue; | 
 | 671 | 		} | 
 | 672 | 		sch = (struct subchannel *)(unsigned long)tpi_info->intparm; | 
| Heiko Carstens | a806170 | 2008-04-17 07:46:26 +0200 | [diff] [blame] | 673 | 		if (!sch) { | 
 | 674 | 			/* Clear pending interrupt condition. */ | 
 | 675 | 			tsch(tpi_info->schid, irb); | 
 | 676 | 			continue; | 
 | 677 | 		} | 
 | 678 | 		spin_lock(sch->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 679 | 		/* Store interrupt response block to lowcore. */ | 
| Heiko Carstens | a806170 | 2008-04-17 07:46:26 +0200 | [diff] [blame] | 680 | 		if (tsch(tpi_info->schid, irb) == 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 681 | 			/* Keep subchannel information word up to date. */ | 
 | 682 | 			memcpy (&sch->schib.scsw, &irb->scsw, | 
 | 683 | 				sizeof (irb->scsw)); | 
 | 684 | 			/* Call interrupt handler if there is one. */ | 
 | 685 | 			if (sch->driver && sch->driver->irq) | 
| Cornelia Huck | 602b20f | 2008-01-26 14:10:39 +0100 | [diff] [blame] | 686 | 				sch->driver->irq(sch); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 687 | 		} | 
| Heiko Carstens | a806170 | 2008-04-17 07:46:26 +0200 | [diff] [blame] | 688 | 		spin_unlock(sch->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 689 | 		/* | 
 | 690 | 		 * Are more interrupts pending? | 
 | 691 | 		 * If so, the tpi instruction will update the lowcore | 
 | 692 | 		 * to hold the info for the next interrupt. | 
 | 693 | 		 * We don't do this for VM because a tpi drops the cpu | 
 | 694 | 		 * out of the sie which costs more cycles than it saves. | 
 | 695 | 		 */ | 
 | 696 | 	} while (!MACHINE_IS_VM && tpi (NULL) != 0); | 
| Heiko Carstens | 9d0a57c | 2006-10-11 15:31:26 +0200 | [diff] [blame] | 697 | 	irq_exit(); | 
| Heiko Carstens | 5a489b9 | 2006-10-06 16:38:35 +0200 | [diff] [blame] | 698 | 	set_irq_regs(old_regs); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 699 | } | 
 | 700 |  | 
 | 701 | #ifdef CONFIG_CCW_CONSOLE | 
 | 702 | static struct subchannel console_subchannel; | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 703 | static struct io_subchannel_private console_priv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 | static int console_subchannel_in_use; | 
 | 705 |  | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 706 | void *cio_get_console_priv(void) | 
 | 707 | { | 
 | 708 | 	return &console_priv; | 
 | 709 | } | 
 | 710 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 711 | /* | 
 | 712 |  * busy wait for the next interrupt on the console | 
 | 713 |  */ | 
| Heiko Carstens | a806170 | 2008-04-17 07:46:26 +0200 | [diff] [blame] | 714 | void wait_cons_dev(void) | 
 | 715 | 	__releases(console_subchannel.lock) | 
 | 716 | 	__acquires(console_subchannel.lock) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 717 | { | 
 | 718 | 	unsigned long cr6      __attribute__ ((aligned (8))); | 
 | 719 | 	unsigned long save_cr6 __attribute__ ((aligned (8))); | 
 | 720 |  | 
 | 721 | 	/*  | 
 | 722 | 	 * before entering the spinlock we may already have | 
 | 723 | 	 * processed the interrupt on a different CPU... | 
 | 724 | 	 */ | 
 | 725 | 	if (!console_subchannel_in_use) | 
 | 726 | 		return; | 
 | 727 |  | 
 | 728 | 	/* disable all but isc 7 (console device) */ | 
 | 729 | 	__ctl_store (save_cr6, 6, 6); | 
 | 730 | 	cr6 = 0x01000000; | 
 | 731 | 	__ctl_load (cr6, 6, 6); | 
 | 732 |  | 
 | 733 | 	do { | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 734 | 		spin_unlock(console_subchannel.lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 735 | 		if (!cio_tpi()) | 
 | 736 | 			cpu_relax(); | 
| Cornelia Huck | 2ec2298 | 2006-12-08 15:54:26 +0100 | [diff] [blame] | 737 | 		spin_lock(console_subchannel.lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 738 | 	} while (console_subchannel.schib.scsw.actl != 0); | 
 | 739 | 	/* | 
 | 740 | 	 * restore previous isc value | 
 | 741 | 	 */ | 
 | 742 | 	__ctl_load (save_cr6, 6, 6); | 
 | 743 | } | 
 | 744 |  | 
 | 745 | static int | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 746 | cio_test_for_console(struct subchannel_id schid, void *data) | 
 | 747 | { | 
| Cornelia Huck | fb6958a | 2006-01-06 00:19:25 -0800 | [diff] [blame] | 748 | 	if (stsch_err(schid, &console_subchannel.schib) != 0) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 749 | 		return -ENXIO; | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 750 | 	if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) && | 
 | 751 | 	    console_subchannel.schib.pmcw.dnv && | 
 | 752 | 	    (console_subchannel.schib.pmcw.dev == console_devno)) { | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 753 | 		console_irq = schid.sch_no; | 
 | 754 | 		return 1; /* found */ | 
 | 755 | 	} | 
 | 756 | 	return 0; | 
 | 757 | } | 
 | 758 |  | 
 | 759 |  | 
 | 760 | static int | 
 | 761 | cio_get_console_sch_no(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 762 | { | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 763 | 	struct subchannel_id schid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 764 | 	 | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 765 | 	init_subchannel_id(&schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | 	if (console_irq != -1) { | 
 | 767 | 		/* VM provided us with the irq number of the console. */ | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 768 | 		schid.sch_no = console_irq; | 
 | 769 | 		if (stsch(schid, &console_subchannel.schib) != 0 || | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 770 | 		    (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) || | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 771 | 		    !console_subchannel.schib.pmcw.dnv) | 
 | 772 | 			return -1; | 
 | 773 | 		console_devno = console_subchannel.schib.pmcw.dev; | 
 | 774 | 	} else if (console_devno != -1) { | 
 | 775 | 		/* At least the console device number is known. */ | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 776 | 		for_each_subchannel(cio_test_for_console, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 777 | 		if (console_irq == -1) | 
 | 778 | 			return -1; | 
 | 779 | 	} else { | 
 | 780 | 		/* unlike in 2.4, we cannot autoprobe here, since | 
 | 781 | 		 * the channel subsystem is not fully initialized. | 
 | 782 | 		 * With some luck, the HWC console can take over */ | 
| Cornelia Huck | e556bbb | 2007-07-27 12:29:19 +0200 | [diff] [blame] | 783 | 		printk(KERN_WARNING "cio: No ccw console found!\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 784 | 		return -1; | 
 | 785 | 	} | 
 | 786 | 	return console_irq; | 
 | 787 | } | 
 | 788 |  | 
 | 789 | struct subchannel * | 
 | 790 | cio_probe_console(void) | 
 | 791 | { | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 792 | 	int sch_no, ret; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 793 | 	struct subchannel_id schid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 794 |  | 
 | 795 | 	if (xchg(&console_subchannel_in_use, 1) != 0) | 
 | 796 | 		return ERR_PTR(-EBUSY); | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 797 | 	sch_no = cio_get_console_sch_no(); | 
 | 798 | 	if (sch_no == -1) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 799 | 		console_subchannel_in_use = 0; | 
 | 800 | 		return ERR_PTR(-ENODEV); | 
 | 801 | 	} | 
 | 802 | 	memset(&console_subchannel, 0, sizeof(struct subchannel)); | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 803 | 	init_subchannel_id(&schid); | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 804 | 	schid.sch_no = sch_no; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 805 | 	ret = cio_validate_subchannel(&console_subchannel, schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 806 | 	if (ret) { | 
 | 807 | 		console_subchannel_in_use = 0; | 
 | 808 | 		return ERR_PTR(-ENODEV); | 
 | 809 | 	} | 
 | 810 |  | 
 | 811 | 	/* | 
 | 812 | 	 * enable console I/O-interrupt subclass 7 | 
 | 813 | 	 */ | 
 | 814 | 	ctl_set_bit(6, 24); | 
 | 815 | 	console_subchannel.schib.pmcw.isc = 7; | 
 | 816 | 	console_subchannel.schib.pmcw.intparm = | 
| Cornelia Huck | cd6b4f2 | 2008-01-26 14:10:43 +0100 | [diff] [blame] | 817 | 		(u32)(addr_t)&console_subchannel; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 818 | 	ret = cio_modify(&console_subchannel); | 
 | 819 | 	if (ret) { | 
 | 820 | 		console_subchannel_in_use = 0; | 
 | 821 | 		return ERR_PTR(ret); | 
 | 822 | 	} | 
 | 823 | 	return &console_subchannel; | 
 | 824 | } | 
 | 825 |  | 
 | 826 | void | 
 | 827 | cio_release_console(void) | 
 | 828 | { | 
 | 829 | 	console_subchannel.schib.pmcw.intparm = 0; | 
 | 830 | 	cio_modify(&console_subchannel); | 
 | 831 | 	ctl_clear_bit(6, 24); | 
 | 832 | 	console_subchannel_in_use = 0; | 
 | 833 | } | 
 | 834 |  | 
 | 835 | /* Bah... hack to catch console special sausages. */ | 
 | 836 | int | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 837 | cio_is_console(struct subchannel_id schid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 838 | { | 
 | 839 | 	if (!console_subchannel_in_use) | 
 | 840 | 		return 0; | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 841 | 	return schid_equal(&schid, &console_subchannel.schid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 842 | } | 
 | 843 |  | 
 | 844 | struct subchannel * | 
 | 845 | cio_get_console_subchannel(void) | 
 | 846 | { | 
 | 847 | 	if (!console_subchannel_in_use) | 
| Heiko Carstens | d2c993d | 2006-07-12 16:41:55 +0200 | [diff] [blame] | 848 | 		return NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 849 | 	return &console_subchannel; | 
 | 850 | } | 
 | 851 |  | 
 | 852 | #endif | 
| Heiko Carstens | 4d284ca | 2007-02-05 21:18:53 +0100 | [diff] [blame] | 853 | static int | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 854 | __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 855 | { | 
 | 856 | 	int retry, cc; | 
 | 857 |  | 
 | 858 | 	cc = 0; | 
 | 859 | 	for (retry=0;retry<3;retry++) { | 
 | 860 | 		schib->pmcw.ena = 0; | 
 | 861 | 		cc = msch(schid, schib); | 
 | 862 | 		if (cc) | 
 | 863 | 			return (cc==3?-ENODEV:-EBUSY); | 
 | 864 | 		stsch(schid, schib); | 
 | 865 | 		if (!schib->pmcw.ena) | 
 | 866 | 			return 0; | 
 | 867 | 	} | 
 | 868 | 	return -EBUSY; /* uhm... */ | 
 | 869 | } | 
 | 870 |  | 
| Martin Schwidefsky | d54853e | 2007-02-05 21:18:19 +0100 | [diff] [blame] | 871 | /* we can't use the normal udelay here, since it enables external interrupts */ | 
 | 872 |  | 
 | 873 | static void udelay_reset(unsigned long usecs) | 
 | 874 | { | 
 | 875 | 	uint64_t start_cc, end_cc; | 
 | 876 |  | 
 | 877 | 	asm volatile ("STCK %0" : "=m" (start_cc)); | 
 | 878 | 	do { | 
 | 879 | 		cpu_relax(); | 
 | 880 | 		asm volatile ("STCK %0" : "=m" (end_cc)); | 
 | 881 | 	} while (((end_cc - start_cc)/4096) < usecs); | 
 | 882 | } | 
 | 883 |  | 
| Heiko Carstens | 4d284ca | 2007-02-05 21:18:53 +0100 | [diff] [blame] | 884 | static int | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 885 | __clear_subchannel_easy(struct subchannel_id schid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 886 | { | 
 | 887 | 	int retry; | 
 | 888 |  | 
 | 889 | 	if (csch(schid)) | 
 | 890 | 		return -ENODEV; | 
 | 891 | 	for (retry=0;retry<20;retry++) { | 
 | 892 | 		struct tpi_info ti; | 
 | 893 |  | 
 | 894 | 		if (tpi(&ti)) { | 
| Cornelia Huck | a8237fc | 2006-01-06 00:19:21 -0800 | [diff] [blame] | 895 | 			tsch(ti.schid, (struct irb *)__LC_IRB); | 
 | 896 | 			if (schid_equal(&ti.schid, &schid)) | 
| Cornelia Huck | 4c24da7 | 2005-09-03 15:58:01 -0700 | [diff] [blame] | 897 | 				return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 898 | 		} | 
| Martin Schwidefsky | d54853e | 2007-02-05 21:18:19 +0100 | [diff] [blame] | 899 | 		udelay_reset(100); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 900 | 	} | 
 | 901 | 	return -EBUSY; | 
 | 902 | } | 
 | 903 |  | 
| Michael Holzheu | a45e141 | 2006-12-15 17:18:22 +0100 | [diff] [blame] | 904 | static int pgm_check_occured; | 
 | 905 |  | 
 | 906 | static void cio_reset_pgm_check_handler(void) | 
 | 907 | { | 
 | 908 | 	pgm_check_occured = 1; | 
 | 909 | } | 
 | 910 |  | 
 | 911 | static int stsch_reset(struct subchannel_id schid, volatile struct schib *addr) | 
 | 912 | { | 
 | 913 | 	int rc; | 
 | 914 |  | 
 | 915 | 	pgm_check_occured = 0; | 
| Heiko Carstens | ab14de6 | 2007-02-05 21:18:37 +0100 | [diff] [blame] | 916 | 	s390_base_pgm_handler_fn = cio_reset_pgm_check_handler; | 
| Heiko Carstens | 6faf444 | 2007-01-09 10:18:41 +0100 | [diff] [blame] | 917 | 	rc = stsch(schid, addr); | 
| Heiko Carstens | ab14de6 | 2007-02-05 21:18:37 +0100 | [diff] [blame] | 918 | 	s390_base_pgm_handler_fn = NULL; | 
| Heiko Carstens | 6faf444 | 2007-01-09 10:18:41 +0100 | [diff] [blame] | 919 |  | 
| Heiko Carstens | ab14de6 | 2007-02-05 21:18:37 +0100 | [diff] [blame] | 920 | 	/* The program check handler could have changed pgm_check_occured. */ | 
| Heiko Carstens | 6faf444 | 2007-01-09 10:18:41 +0100 | [diff] [blame] | 921 | 	barrier(); | 
 | 922 |  | 
| Michael Holzheu | a45e141 | 2006-12-15 17:18:22 +0100 | [diff] [blame] | 923 | 	if (pgm_check_occured) | 
 | 924 | 		return -EIO; | 
 | 925 | 	else | 
 | 926 | 		return rc; | 
 | 927 | } | 
 | 928 |  | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 929 | static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 930 | { | 
 | 931 | 	struct schib schib; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 932 |  | 
| Michael Holzheu | a45e141 | 2006-12-15 17:18:22 +0100 | [diff] [blame] | 933 | 	if (stsch_reset(schid, &schib)) | 
| Cornelia Huck | f97a56f | 2006-01-06 00:19:22 -0800 | [diff] [blame] | 934 | 		return -ENXIO; | 
 | 935 | 	if (!schib.pmcw.ena) | 
 | 936 | 		return 0; | 
 | 937 | 	switch(__disable_subchannel_easy(schid, &schib)) { | 
 | 938 | 	case 0: | 
 | 939 | 	case -ENODEV: | 
 | 940 | 		break; | 
 | 941 | 	default: /* -EBUSY */ | 
 | 942 | 		if (__clear_subchannel_easy(schid)) | 
 | 943 | 			break; /* give up... */ | 
 | 944 | 		stsch(schid, &schib); | 
 | 945 | 		__disable_subchannel_easy(schid, &schib); | 
 | 946 | 	} | 
 | 947 | 	return 0; | 
 | 948 | } | 
 | 949 |  | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 950 | static atomic_t chpid_reset_count; | 
 | 951 |  | 
 | 952 | static void s390_reset_chpids_mcck_handler(void) | 
 | 953 | { | 
 | 954 | 	struct crw crw; | 
 | 955 | 	struct mci *mci; | 
 | 956 |  | 
 | 957 | 	/* Check for pending channel report word. */ | 
 | 958 | 	mci = (struct mci *)&S390_lowcore.mcck_interruption_code; | 
 | 959 | 	if (!mci->cp) | 
 | 960 | 		return; | 
 | 961 | 	/* Process channel report words. */ | 
 | 962 | 	while (stcrw(&crw) == 0) { | 
 | 963 | 		/* Check for responses to RCHP. */ | 
 | 964 | 		if (crw.slct && crw.rsc == CRW_RSC_CPATH) | 
 | 965 | 			atomic_dec(&chpid_reset_count); | 
 | 966 | 	} | 
 | 967 | } | 
 | 968 |  | 
 | 969 | #define RCHP_TIMEOUT (30 * USEC_PER_SEC) | 
 | 970 | static void css_reset(void) | 
 | 971 | { | 
 | 972 | 	int i, ret; | 
 | 973 | 	unsigned long long timeout; | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 974 | 	struct chp_id chpid; | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 975 |  | 
 | 976 | 	/* Reset subchannels. */ | 
 | 977 | 	for_each_subchannel(__shutdown_subchannel_easy,  NULL); | 
 | 978 | 	/* Reset channel paths. */ | 
| Heiko Carstens | ab14de6 | 2007-02-05 21:18:37 +0100 | [diff] [blame] | 979 | 	s390_base_mcck_handler_fn = s390_reset_chpids_mcck_handler; | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 980 | 	/* Enable channel report machine checks. */ | 
 | 981 | 	__ctl_set_bit(14, 28); | 
 | 982 | 	/* Temporarily reenable machine checks. */ | 
 | 983 | 	local_mcck_enable(); | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 984 | 	chp_id_init(&chpid); | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 985 | 	for (i = 0; i <= __MAX_CHPID; i++) { | 
| Peter Oberparleiter | f86635f | 2007-04-27 16:01:26 +0200 | [diff] [blame] | 986 | 		chpid.id = i; | 
 | 987 | 		ret = rchp(chpid); | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 988 | 		if ((ret == 0) || (ret == 2)) | 
 | 989 | 			/* | 
 | 990 | 			 * rchp either succeeded, or another rchp is already | 
 | 991 | 			 * in progress. In either case, we'll get a crw. | 
 | 992 | 			 */ | 
 | 993 | 			atomic_inc(&chpid_reset_count); | 
 | 994 | 	} | 
 | 995 | 	/* Wait for machine check for all channel paths. */ | 
 | 996 | 	timeout = get_clock() + (RCHP_TIMEOUT << 12); | 
 | 997 | 	while (atomic_read(&chpid_reset_count) != 0) { | 
 | 998 | 		if (get_clock() > timeout) | 
 | 999 | 			break; | 
 | 1000 | 		cpu_relax(); | 
 | 1001 | 	} | 
 | 1002 | 	/* Disable machine checks again. */ | 
 | 1003 | 	local_mcck_disable(); | 
 | 1004 | 	/* Disable channel report machine checks. */ | 
 | 1005 | 	__ctl_clear_bit(14, 28); | 
| Heiko Carstens | ab14de6 | 2007-02-05 21:18:37 +0100 | [diff] [blame] | 1006 | 	s390_base_mcck_handler_fn = NULL; | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 1007 | } | 
 | 1008 |  | 
 | 1009 | static struct reset_call css_reset_call = { | 
 | 1010 | 	.fn = css_reset, | 
 | 1011 | }; | 
 | 1012 |  | 
 | 1013 | static int __init init_css_reset_call(void) | 
 | 1014 | { | 
 | 1015 | 	atomic_set(&chpid_reset_count, 0); | 
 | 1016 | 	register_reset_call(&css_reset_call); | 
 | 1017 | 	return 0; | 
 | 1018 | } | 
 | 1019 |  | 
 | 1020 | arch_initcall(init_css_reset_call); | 
 | 1021 |  | 
 | 1022 | struct sch_match_id { | 
 | 1023 | 	struct subchannel_id schid; | 
 | 1024 | 	struct ccw_dev_id devid; | 
 | 1025 | 	int rc; | 
 | 1026 | }; | 
 | 1027 |  | 
 | 1028 | static int __reipl_subchannel_match(struct subchannel_id schid, void *data) | 
 | 1029 | { | 
 | 1030 | 	struct schib schib; | 
 | 1031 | 	struct sch_match_id *match_id = data; | 
 | 1032 |  | 
| Michael Holzheu | a45e141 | 2006-12-15 17:18:22 +0100 | [diff] [blame] | 1033 | 	if (stsch_reset(schid, &schib)) | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 1034 | 		return -ENXIO; | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 1035 | 	if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 1036 | 	    (schib.pmcw.dev == match_id->devid.devno) && | 
 | 1037 | 	    (schid.ssid == match_id->devid.ssid)) { | 
 | 1038 | 		match_id->schid = schid; | 
 | 1039 | 		match_id->rc = 0; | 
 | 1040 | 		return 1; | 
 | 1041 | 	} | 
 | 1042 | 	return 0; | 
 | 1043 | } | 
 | 1044 |  | 
 | 1045 | static int reipl_find_schid(struct ccw_dev_id *devid, | 
 | 1046 | 			    struct subchannel_id *schid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1047 | { | 
| Michael Holzheu | ff6b8ea | 2006-09-20 15:58:49 +0200 | [diff] [blame] | 1048 | 	struct sch_match_id match_id; | 
 | 1049 |  | 
 | 1050 | 	match_id.devid = *devid; | 
 | 1051 | 	match_id.rc = -ENODEV; | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 1052 | 	for_each_subchannel(__reipl_subchannel_match, &match_id); | 
| Michael Holzheu | ff6b8ea | 2006-09-20 15:58:49 +0200 | [diff] [blame] | 1053 | 	if (match_id.rc == 0) | 
 | 1054 | 		*schid = match_id.schid; | 
 | 1055 | 	return match_id.rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1056 | } | 
 | 1057 |  | 
| Michael Holzheu | ff6b8ea | 2006-09-20 15:58:49 +0200 | [diff] [blame] | 1058 | extern void do_reipl_asm(__u32 schid); | 
 | 1059 |  | 
 | 1060 | /* Make sure all subchannels are quiet before we re-ipl an lpar. */ | 
 | 1061 | void reipl_ccw_dev(struct ccw_dev_id *devid) | 
 | 1062 | { | 
 | 1063 | 	struct subchannel_id schid; | 
 | 1064 |  | 
| Heiko Carstens | 15e9b58 | 2006-12-04 15:40:26 +0100 | [diff] [blame] | 1065 | 	s390_reset_system(); | 
 | 1066 | 	if (reipl_find_schid(devid, &schid) != 0) | 
| Michael Holzheu | ff6b8ea | 2006-09-20 15:58:49 +0200 | [diff] [blame] | 1067 | 		panic("IPL Device not found\n"); | 
| Michael Holzheu | ff6b8ea | 2006-09-20 15:58:49 +0200 | [diff] [blame] | 1068 | 	do_reipl_asm(*((__u32*)&schid)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1069 | } | 
| Heiko Carstens | e87bfe5 | 2006-09-20 15:59:15 +0200 | [diff] [blame] | 1070 |  | 
| Heiko Carstens | 6fc321f | 2007-04-27 16:01:25 +0200 | [diff] [blame] | 1071 | int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) | 
| Heiko Carstens | e87bfe5 | 2006-09-20 15:59:15 +0200 | [diff] [blame] | 1072 | { | 
 | 1073 | 	struct subchannel_id schid; | 
| Heiko Carstens | 6fc321f | 2007-04-27 16:01:25 +0200 | [diff] [blame] | 1074 | 	struct schib schib; | 
| Heiko Carstens | e87bfe5 | 2006-09-20 15:59:15 +0200 | [diff] [blame] | 1075 |  | 
 | 1076 | 	schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; | 
 | 1077 | 	if (!schid.one) | 
| Heiko Carstens | 6fc321f | 2007-04-27 16:01:25 +0200 | [diff] [blame] | 1078 | 		return -ENODEV; | 
 | 1079 | 	if (stsch(schid, &schib)) | 
 | 1080 | 		return -ENODEV; | 
| Cornelia Huck | b279a4f | 2008-01-26 14:10:45 +0100 | [diff] [blame] | 1081 | 	if (schib.pmcw.st != SUBCHANNEL_TYPE_IO) | 
 | 1082 | 		return -ENODEV; | 
| Heiko Carstens | 6fc321f | 2007-04-27 16:01:25 +0200 | [diff] [blame] | 1083 | 	if (!schib.pmcw.dnv) | 
 | 1084 | 		return -ENODEV; | 
 | 1085 | 	iplinfo->devno = schib.pmcw.dev; | 
 | 1086 | 	iplinfo->is_qdio = schib.pmcw.qf; | 
 | 1087 | 	return 0; | 
| Heiko Carstens | e87bfe5 | 2006-09-20 15:59:15 +0200 | [diff] [blame] | 1088 | } |