Beispiel #1
0
static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid)
{
	struct subchannel *sch;
	int ret;

	sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA);
	if (sch == NULL)
		return ERR_PTR(-ENOMEM);
	ret = cio_validate_subchannel (sch, schid);
	if (ret < 0) {
		kfree(sch);
		return ERR_PTR(ret);
	}

	if (sch->st != SUBCHANNEL_TYPE_IO) {
		/* For now we ignore all non-io subchannels. */
		kfree(sch);
		return ERR_PTR(-EINVAL);
	}

	/* 
	 * Set intparm to subchannel address.
	 * This is fine even on 64bit since the subchannel is always located
	 * under 2G.
	 */
	sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
	ret = cio_modify(sch);
	if (ret) {
		kfree(sch);
		return ERR_PTR(ret);
	}
	return sch;
}
Beispiel #2
0
struct subchannel *
cio_probe_console(void)
{
	int irq, ret;

	if (xchg(&console_subchannel_in_use, 1) != 0)
		return ERR_PTR(-EBUSY);
	irq = cio_console_irq();
	if (irq == -1) {
		console_subchannel_in_use = 0;
		return ERR_PTR(-ENODEV);
	}
	memset(&console_subchannel, 0, sizeof(struct subchannel));
	ret = cio_validate_subchannel(&console_subchannel, irq);
	if (ret) {
		console_subchannel_in_use = 0;
		return ERR_PTR(-ENODEV);
	}

	/*
	 * enable console I/O-interrupt subclass 7
	 */
	ctl_set_bit(6, 24);
	console_subchannel.schib.pmcw.isc = 7;
	console_subchannel.schib.pmcw.intparm =
		(__u32)(unsigned long)&console_subchannel;
	ret = cio_modify(&console_subchannel);
	if (ret) {
		console_subchannel_in_use = 0;
		return ERR_PTR(ret);
	}
	return &console_subchannel;
}
Beispiel #3
0
void
cio_release_console(void)
{
	console_subchannel.schib.pmcw.intparm = 0;
	cio_modify(&console_subchannel);
	ctl_clear_bit(6, 24);
	console_subchannel_in_use = 0;
}
Beispiel #4
0
static void
css_free_subchannel(struct subchannel *sch)
{
    if (sch) {
        /* Reset intparm to zeroes. */
        sch->schib.pmcw.intparm = 0;
        cio_modify(sch);
        kfree(sch);
    }

}
Beispiel #5
0
/**
 * cio_disable_subchannel - disable a subchannel.
 * @sch: subchannel to disable
 */
int cio_disable_subchannel(struct subchannel *sch)
{
	char dbf_txt[15];
	int ccode;
	int retry;
	int ret;

	CIO_TRACE_EVENT (2, "dissch");
	CIO_TRACE_EVENT(2, dev_name(&sch->dev));

	if (sch_is_pseudo_sch(sch))
		return 0;
	ccode = stsch (sch->schid, &sch->schib);
	if (ccode == 3)		/* Not operational. */
		return -ENODEV;

	if (scsw_actl(&sch->schib.scsw) != 0)
		/*
		 * the disable function must not be called while there are
		 *  requests pending for completion !
		 */
		return -EBUSY;

	for (retry = 5, ret = 0; retry > 0; retry--) {
		sch->schib.pmcw.ena = 0;
		ret = cio_modify(sch);
		if (ret == -ENODEV)
			break;
		if (ret == -EBUSY)
			/*
			 * The subchannel is busy or status pending.
			 * We'll disable when the next interrupt was delivered
			 * via the state machine.
			 */
			break;
		if (ret == 0) {
			stsch (sch->schid, &sch->schib);
			if (!sch->schib.pmcw.ena)
				break;
		}
	}
	sprintf (dbf_txt, "ret:%d", ret);
	CIO_TRACE_EVENT (2, dbf_txt);
	return ret;
}
Beispiel #6
0
/**
 * cio_enable_subchannel - enable a subchannel.
 * @sch: subchannel to be enabled
 * @intparm: interruption parameter to set
 */
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
	char dbf_txt[15];
	int ccode;
	int retry;
	int ret;

	CIO_TRACE_EVENT (2, "ensch");
	CIO_TRACE_EVENT(2, dev_name(&sch->dev));

	if (sch_is_pseudo_sch(sch))
		return -EINVAL;
	ccode = stsch (sch->schid, &sch->schib);
	if (ccode)
		return -ENODEV;

	for (retry = 5, ret = 0; retry > 0; retry--) {
		sch->schib.pmcw.ena = 1;
		sch->schib.pmcw.isc = sch->isc;
		sch->schib.pmcw.intparm = intparm;
		ret = cio_modify(sch);
		if (ret == -ENODEV)
			break;
		if (ret == -EIO)
			/*
			 * Got a program check in cio_modify. Try without
			 * the concurrent sense bit the next time.
			 */
			sch->schib.pmcw.csense = 0;
		if (ret == 0) {
			stsch (sch->schid, &sch->schib);
			if (sch->schib.pmcw.ena)
				break;
		}
		if (ret == -EBUSY) {
			struct irb irb;
			if (tsch(sch->schid, &irb) != 0)
				break;
		}
	}
	sprintf (dbf_txt, "ret:%d", ret);
	CIO_TRACE_EVENT (2, dbf_txt);
	return ret;
}
Beispiel #7
0
/*
 * Enable subchannel.
 */
int
cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
{
	char dbf_txt[15];
	int ccode;
	int retry;
	int ret;

	CIO_TRACE_EVENT (2, "ensch");
	CIO_TRACE_EVENT (2, sch->dev.bus_id);

	ccode = stsch (sch->irq, &sch->schib);
	if (ccode)
		return -ENODEV;

	for (retry = 5, ret = 0; retry > 0; retry--) {
		sch->schib.pmcw.ena = 1;
		sch->schib.pmcw.isc = isc;
		sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
		ret = cio_modify(sch);
		if (ret == -ENODEV)
			break;
		if (ret == -EIO)
			/*
			 * Got a program check in cio_modify. Try without
			 * the concurrent sense bit the next time.
			 */
			sch->schib.pmcw.csense = 0;
		if (ret == 0) {
			stsch (sch->irq, &sch->schib);
			if (sch->schib.pmcw.ena)
				break;
		}
		if (ret == -EBUSY) {
			struct irb irb;
			if (tsch(sch->irq, &irb) != 0)
				break;
		}
	}
	sprintf (dbf_txt, "ret:%d", ret);
	CIO_TRACE_EVENT (2, dbf_txt);
	return ret;
}
Beispiel #8
0
/*
 * Disable subchannel.
 */
int
cio_disable_subchannel (struct subchannel *sch)
{
	char dbf_txt[15];
	int ccode;
	int retry;
	int ret;

	sprintf (dbf_txt, "dissch%x", sch->irq);
	CIO_TRACE_EVENT (2, dbf_txt);

	ccode = stsch (sch->irq, &sch->schib);
	if (ccode == 3)		/* Not operational. */
		return -ENODEV;

	if (sch->schib.scsw.actl != 0)
		/*
		 * the disable function must not be called while there are
		 *  requests pending for completion !
		 */
		return -EBUSY;


	sch->schib.pmcw.ena = 0;
	for (retry = 5, ret = 0; retry > 0; retry--) {
		ret = cio_modify(sch);
		if (ret == -ENODEV)
			break;
		if (ret == 0) {
			stsch (sch->irq, &sch->schib);
			if (!sch->schib.pmcw.ena)
				break;
		}
	}
	sprintf (dbf_txt, "ret:%d", ret);
	CIO_TRACE_EVENT (2, dbf_txt);
	return ret;
}
Beispiel #9
0
struct subchannel *
cio_probe_console(void)
{
	int sch_no, ret;
	struct subchannel_id schid;

	if (xchg(&console_subchannel_in_use, 1) != 0)
		return ERR_PTR(-EBUSY);
	sch_no = cio_get_console_sch_no();
	if (sch_no == -1) {
		console_subchannel_in_use = 0;
		return ERR_PTR(-ENODEV);
	}
	memset(&console_subchannel, 0, sizeof(struct subchannel));
	init_subchannel_id(&schid);
	schid.sch_no = sch_no;
	ret = cio_validate_subchannel(&console_subchannel, schid);
	if (ret) {
		console_subchannel_in_use = 0;
		return ERR_PTR(-ENODEV);
	}

	/*
	 * enable console I/O-interrupt subclass 7
	 */
	ctl_set_bit(6, 24);
	console_subchannel.schib.pmcw.isc = 7;
	console_subchannel.schib.pmcw.intparm =
		(__u32)(unsigned long)&console_subchannel;
	ret = cio_modify(&console_subchannel);
	if (ret) {
		console_subchannel_in_use = 0;
		return ERR_PTR(ret);
	}
	return &console_subchannel;
}
Beispiel #10
0
static int
css_evaluate_subchannel(int irq, int slow)
{
    int event, ret, disc;
    struct subchannel *sch;
    unsigned long flags;

    sch = get_subchannel_by_schid(irq);
    disc = sch ? device_is_disconnected(sch) : 0;
    if (disc && slow) {
        if (sch)
            put_device(&sch->dev);
        return 0; /* Already processed. */
    }
    /*
     * We've got a machine check, so running I/O won't get an interrupt.
     * Kill any pending timers.
     */
    if (sch)
        device_kill_pending_timer(sch);
    if (!disc && !slow) {
        if (sch)
            put_device(&sch->dev);
        return -EAGAIN; /* Will be done on the slow path. */
    }
    event = css_get_subchannel_status(sch, irq);
    CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
                  irq, event, sch?(disc?"disconnected":"normal"):"unknown",
                  slow?"slow":"fast");
    switch (event) {
    case CIO_NO_PATH:
    case CIO_GONE:
        if (!sch) {
            /* Never used this subchannel. Ignore. */
            ret = 0;
            break;
        }
        if (disc && (event == CIO_NO_PATH)) {
            /*
             * Uargh, hack again. Because we don't get a machine
             * check on configure on, our path bookkeeping can
             * be out of date here (it's fine while we only do
             * logical varying or get chsc machine checks). We
             * need to force reprobing or we might miss devices
             * coming operational again. It won't do harm in real
             * no path situations.
             */
            spin_lock_irqsave(&sch->lock, flags);
            device_trigger_reprobe(sch);
            spin_unlock_irqrestore(&sch->lock, flags);
            ret = 0;
            break;
        }
        if (sch->driver && sch->driver->notify &&
                sch->driver->notify(&sch->dev, event)) {
            cio_disable_subchannel(sch);
            device_set_disconnected(sch);
            ret = 0;
            break;
        }
        /*
         * Unregister subchannel.
         * The device will be killed automatically.
         */
        cio_disable_subchannel(sch);
        device_unregister(&sch->dev);
        /* Reset intparm to zeroes. */
        sch->schib.pmcw.intparm = 0;
        cio_modify(sch);
        put_device(&sch->dev);
        ret = 0;
        break;
    case CIO_REVALIDATE:
        /*
         * Revalidation machine check. Sick.
         * We don't notify the driver since we have to throw the device
         * away in any case.
         */
        if (!disc) {
            device_unregister(&sch->dev);
            /* Reset intparm to zeroes. */
            sch->schib.pmcw.intparm = 0;
            cio_modify(sch);
            put_device(&sch->dev);
            ret = css_probe_device(irq);
        } else {
            /*
             * We can't immediately deregister the disconnected
             * device since it might block.
             */
            spin_lock_irqsave(&sch->lock, flags);
            device_trigger_reprobe(sch);
            spin_unlock_irqrestore(&sch->lock, flags);
            ret = 0;
        }
        break;
    case CIO_OPER:
        if (disc) {
            spin_lock_irqsave(&sch->lock, flags);
            /* Get device operational again. */
            device_trigger_reprobe(sch);
            spin_unlock_irqrestore(&sch->lock, flags);
        }
        ret = sch ? 0 : css_probe_device(irq);
        break;
    default:
        BUG();
        ret = 0;
    }
    return ret;
}