brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · 165de15 Raw
387 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for s390 eadm subchannels4 *5 * Copyright IBM Corp. 20126 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>7 */8 9#include <linux/kernel_stat.h>10#include <linux/completion.h>11#include <linux/workqueue.h>12#include <linux/spinlock.h>13#include <linux/device.h>14#include <linux/module.h>15#include <linux/timer.h>16#include <linux/slab.h>17#include <linux/list.h>18#include <linux/io.h>19 20#include <asm/css_chars.h>21#include <asm/debug.h>22#include <asm/isc.h>23#include <asm/cio.h>24#include <asm/scsw.h>25#include <asm/eadm.h>26 27#include "eadm_sch.h"28#include "ioasm.h"29#include "cio.h"30#include "css.h"31#include "orb.h"32 33MODULE_DESCRIPTION("driver for s390 eadm subchannels");34MODULE_LICENSE("GPL");35 36#define EADM_TIMEOUT (7 * HZ)37static DEFINE_SPINLOCK(list_lock);38static LIST_HEAD(eadm_list);39 40static debug_info_t *eadm_debug;41 42#define EADM_LOG(imp, txt) do {					\43		debug_text_event(eadm_debug, imp, txt);		\44	} while (0)45 46static void EADM_LOG_HEX(int level, void *data, int length)47{48	debug_event(eadm_debug, level, data, length);49}50 51static void orb_init(union orb *orb)52{53	memset(orb, 0, sizeof(union orb));54	orb->eadm.compat1 = 1;55	orb->eadm.compat2 = 1;56	orb->eadm.fmt = 1;57	orb->eadm.x = 1;58}59 60static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob)61{62	union orb *orb = &get_eadm_private(sch)->orb;63	int cc;64 65	orb_init(orb);66	orb->eadm.aob = virt_to_dma32(aob);67	orb->eadm.intparm = (u32)virt_to_phys(sch);68	orb->eadm.key = PAGE_DEFAULT_KEY >> 4;69 70	EADM_LOG(6, "start");71	EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid));72 73	cc = ssch(sch->schid, orb);74	switch (cc) {75	case 0:76		sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND;77		break;78	case 1:		/* status pending */79	case 2:		/* busy */80		return -EBUSY;81	case 3:		/* not operational */82		return -ENODEV;83	}84	return 0;85}86 87static int eadm_subchannel_clear(struct subchannel *sch)88{89	int cc;90 91	cc = csch(sch->schid);92	if (cc)93		return -ENODEV;94 95	sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND;96	return 0;97}98 99static void eadm_subchannel_timeout(struct timer_list *t)100{101	struct eadm_private *private = from_timer(private, t, timer);102	struct subchannel *sch = private->sch;103 104	spin_lock_irq(&sch->lock);105	EADM_LOG(1, "timeout");106	EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));107	if (eadm_subchannel_clear(sch))108		EADM_LOG(0, "clear failed");109	spin_unlock_irq(&sch->lock);110}111 112static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)113{114	struct eadm_private *private = get_eadm_private(sch);115 116	if (expires == 0)117		del_timer(&private->timer);118	else119		mod_timer(&private->timer, jiffies + expires);120}121 122static void eadm_subchannel_irq(struct subchannel *sch)123{124	struct eadm_private *private = get_eadm_private(sch);125	struct eadm_scsw *scsw = &sch->schib.scsw.eadm;126	struct irb *irb = this_cpu_ptr(&cio_irb);127	blk_status_t error = BLK_STS_OK;128 129	EADM_LOG(6, "irq");130	EADM_LOG_HEX(6, irb, sizeof(*irb));131 132	inc_irq_stat(IRQIO_ADM);133 134	if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))135	    && scsw->eswf == 1 && irb->esw.eadm.erw.r)136		error = BLK_STS_IOERR;137 138	if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC)139		error = BLK_STS_TIMEOUT;140 141	eadm_subchannel_set_timeout(sch, 0);142 143	if (private->state != EADM_BUSY) {144		EADM_LOG(1, "irq unsol");145		EADM_LOG_HEX(1, irb, sizeof(*irb));146		private->state = EADM_NOT_OPER;147		css_sched_sch_todo(sch, SCH_TODO_EVAL);148		return;149	}150	scm_irq_handler(dma32_to_virt(scsw->aob), error);151	private->state = EADM_IDLE;152 153	if (private->completion)154		complete(private->completion);155}156 157static struct subchannel *eadm_get_idle_sch(void)158{159	struct eadm_private *private;160	struct subchannel *sch;161	unsigned long flags;162 163	spin_lock_irqsave(&list_lock, flags);164	list_for_each_entry(private, &eadm_list, head) {165		sch = private->sch;166		spin_lock(&sch->lock);167		if (private->state == EADM_IDLE) {168			private->state = EADM_BUSY;169			list_move_tail(&private->head, &eadm_list);170			spin_unlock(&sch->lock);171			spin_unlock_irqrestore(&list_lock, flags);172 173			return sch;174		}175		spin_unlock(&sch->lock);176	}177	spin_unlock_irqrestore(&list_lock, flags);178 179	return NULL;180}181 182int eadm_start_aob(struct aob *aob)183{184	struct eadm_private *private;185	struct subchannel *sch;186	unsigned long flags;187	int ret;188 189	sch = eadm_get_idle_sch();190	if (!sch)191		return -EBUSY;192 193	spin_lock_irqsave(&sch->lock, flags);194	eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);195	ret = eadm_subchannel_start(sch, aob);196	if (!ret)197		goto out_unlock;198 199	/* Handle start subchannel failure. */200	eadm_subchannel_set_timeout(sch, 0);201	private = get_eadm_private(sch);202	private->state = EADM_NOT_OPER;203	css_sched_sch_todo(sch, SCH_TODO_EVAL);204 205out_unlock:206	spin_unlock_irqrestore(&sch->lock, flags);207 208	return ret;209}210EXPORT_SYMBOL_GPL(eadm_start_aob);211 212static int eadm_subchannel_probe(struct subchannel *sch)213{214	struct eadm_private *private;215	int ret;216 217	private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);218	if (!private)219		return -ENOMEM;220 221	INIT_LIST_HEAD(&private->head);222	timer_setup(&private->timer, eadm_subchannel_timeout, 0);223 224	spin_lock_irq(&sch->lock);225	set_eadm_private(sch, private);226	private->state = EADM_IDLE;227	private->sch = sch;228	sch->isc = EADM_SCH_ISC;229	ret = cio_enable_subchannel(sch, (u32)virt_to_phys(sch));230	if (ret) {231		set_eadm_private(sch, NULL);232		spin_unlock_irq(&sch->lock);233		kfree(private);234		goto out;235	}236	spin_unlock_irq(&sch->lock);237 238	spin_lock_irq(&list_lock);239	list_add(&private->head, &eadm_list);240	spin_unlock_irq(&list_lock);241out:242	return ret;243}244 245static void eadm_quiesce(struct subchannel *sch)246{247	struct eadm_private *private = get_eadm_private(sch);248	DECLARE_COMPLETION_ONSTACK(completion);249	int ret;250 251	spin_lock_irq(&sch->lock);252	if (private->state != EADM_BUSY)253		goto disable;254 255	if (eadm_subchannel_clear(sch))256		goto disable;257 258	private->completion = &completion;259	spin_unlock_irq(&sch->lock);260 261	wait_for_completion_io(&completion);262 263	spin_lock_irq(&sch->lock);264	private->completion = NULL;265 266disable:267	eadm_subchannel_set_timeout(sch, 0);268	do {269		ret = cio_disable_subchannel(sch);270	} while (ret == -EBUSY);271 272	spin_unlock_irq(&sch->lock);273}274 275static void eadm_subchannel_remove(struct subchannel *sch)276{277	struct eadm_private *private = get_eadm_private(sch);278 279	spin_lock_irq(&list_lock);280	list_del(&private->head);281	spin_unlock_irq(&list_lock);282 283	eadm_quiesce(sch);284 285	spin_lock_irq(&sch->lock);286	set_eadm_private(sch, NULL);287	spin_unlock_irq(&sch->lock);288 289	kfree(private);290}291 292static void eadm_subchannel_shutdown(struct subchannel *sch)293{294	eadm_quiesce(sch);295}296 297/**298 * eadm_subchannel_sch_event - process subchannel event299 * @sch: subchannel300 * @process: non-zero if function is called in process context301 *302 * An unspecified event occurred for this subchannel. Adjust data according303 * to the current operational state of the subchannel. Return zero when the304 * event has been handled sufficiently or -EAGAIN when this function should305 * be called again in process context.306 */307static int eadm_subchannel_sch_event(struct subchannel *sch, int process)308{309	struct eadm_private *private;310	unsigned long flags;311 312	spin_lock_irqsave(&sch->lock, flags);313	if (!device_is_registered(&sch->dev))314		goto out_unlock;315 316	if (work_pending(&sch->todo_work))317		goto out_unlock;318 319	if (cio_update_schib(sch)) {320		css_sched_sch_todo(sch, SCH_TODO_UNREG);321		goto out_unlock;322	}323	private = get_eadm_private(sch);324	if (private->state == EADM_NOT_OPER)325		private->state = EADM_IDLE;326 327out_unlock:328	spin_unlock_irqrestore(&sch->lock, flags);329 330	return 0;331}332 333static struct css_device_id eadm_subchannel_ids[] = {334	{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, },335	{ /* end of list */ },336};337MODULE_DEVICE_TABLE(css, eadm_subchannel_ids);338 339static struct css_driver eadm_subchannel_driver = {340	.drv = {341		.name = "eadm_subchannel",342		.owner = THIS_MODULE,343	},344	.subchannel_type = eadm_subchannel_ids,345	.irq = eadm_subchannel_irq,346	.probe = eadm_subchannel_probe,347	.remove = eadm_subchannel_remove,348	.shutdown = eadm_subchannel_shutdown,349	.sch_event = eadm_subchannel_sch_event,350};351 352static int __init eadm_sch_init(void)353{354	int ret;355 356	if (!css_general_characteristics.eadm)357		return -ENXIO;358 359	eadm_debug = debug_register("eadm_log", 16, 1, 16);360	if (!eadm_debug)361		return -ENOMEM;362 363	debug_register_view(eadm_debug, &debug_hex_ascii_view);364	debug_set_level(eadm_debug, 2);365 366	isc_register(EADM_SCH_ISC);367	ret = css_driver_register(&eadm_subchannel_driver);368	if (ret)369		goto cleanup;370 371	return ret;372 373cleanup:374	isc_unregister(EADM_SCH_ISC);375	debug_unregister(eadm_debug);376	return ret;377}378 379static void __exit eadm_sch_exit(void)380{381	css_driver_unregister(&eadm_subchannel_driver);382	isc_unregister(EADM_SCH_ISC);383	debug_unregister(eadm_debug);384}385module_init(eadm_sch_init);386module_exit(eadm_sch_exit);387