int
sda_slot_power_on(sda_slot_t *slot)
{
	int		rv;
	uint32_t	ocr;

	sda_slot_enter(slot);

	/*
	 * Get the voltage supplied by the host.  Note that we expect
	 * hosts will include a range of 2.7-3.7 in their supported
	 * voltage ranges.  The spec does not allow for hosts that
	 * cannot supply a voltage in this range, yet.
	 */
	if ((rv = sda_getprop(slot, SDA_PROP_OCR, &ocr)) != 0) {
		sda_slot_err(slot, "Failed to get host OCR (%d)", rv);
		goto done;
	}
	if ((ocr & OCR_HI_MASK) == 0) {
		sda_slot_err(slot, "Host does not support standard voltages.");
		rv = ENOTSUP;
		goto done;
	}

	/*
	 * We prefer 3.3V, 3.0V, and failing that, just use the
	 * maximum that the host supports.  3.3V is preferable,
	 * because it is the typical common voltage that just about
	 * everything supports.  Otherwise we just pick the highest
	 * supported voltage.  This facilitates initial power up.
	 */
	if (ocr & OCR_32_33V) {
		slot->s_cur_ocr = OCR_32_33V;
	} else if (ocr & OCR_29_30V) {
		slot->s_cur_ocr = OCR_29_30V;
	} else {
		slot->s_cur_ocr = (1U << (ddi_fls(ocr) - 1));
	}

	/*
	 * Turn on the power.
	 */
	if ((rv = sda_setprop(slot, SDA_PROP_OCR, slot->s_cur_ocr)) != 0) {
		sda_slot_err(slot, "Failed to set OCR %x (%d)",
		    slot->s_cur_ocr, rv);
		goto done;
	}

	sda_slot_exit(slot);

	/*
	 * Wait 250 msec (per spec) for power ramp to complete.
	 */
	delay(drv_usectohz(250000));
	return (0);

done:
	sda_slot_exit(slot);
	return (rv);
}
Exemple #2
0
void
sda_slot_insert(void *arg)
{
	sda_slot_t	*slot = arg;

	if (sda_init_card(slot) != SDA_EOK) {
		/*
		 * Remove power from the slot.  If a more severe fault
		 * occurred, then a manual reset with cfgadm will be needed.
		 */
		sda_slot_err(slot, "Unable to initialize card!");
		sda_slot_enter(slot);
		sda_slot_power_off(slot);
		sda_slot_abort(slot, SDA_ENODEV);
		sda_slot_exit(slot);

	} else if ((slot->s_flags & SLOTF_MEMORY) == 0) {
		/*
		 * SDIO: For SDIO, we can write the card's
		 * MANFID tuple in CIS to the UUID.  Until we
		 * support SDIO, we just suppress creating
		 * devinfo nodes.
		 */
		sda_slot_err(slot, "Non-memory target not supported");
	} else {

		sda_slot_enter(slot);
		if (sda_mem_parse_cid_csd(slot) != DDI_SUCCESS) {
			sda_slot_err(slot,
			    "Unable to parse card identification");
		} else {
			slot->s_warn = B_FALSE;
			slot->s_ready = B_TRUE;
		}
		sda_slot_exit(slot);
	}

	slot->s_stamp = ddi_get_time();
	slot->s_intransit = 0;
	bd_state_change(slot->s_bdh);
}
void
sda_slot_handle_fault(sda_slot_t *slot, sda_fault_t fault)
{
	const char	*msg;
	int		i;

	sda_slot_enter(slot);

	if ((fault == SDA_FAULT_TIMEOUT) && (slot->s_init)) {
		/*
		 * Timeouts during initialization are quite normal.
		 */
		sda_slot_exit(slot);
		return;
	}

	slot->s_failed = B_TRUE;
	sda_slot_abort(slot, SDA_EFAULT);

	msg = "Unknown fault (%d)";
	for (i = 0; sda_slot_faults[i].msg != NULL; i++) {
		if (sda_slot_faults[i].fault == fault) {
			msg = sda_slot_faults[i].msg;
			break;
		}
	}

	/*
	 * FMA would be a better choice here.
	 */
	sda_slot_err(slot, msg, fault);

	/*
	 * Shut down the slot.  Interaction from userland via cfgadm
	 * can revive it.
	 *
	 * FMA can help here.
	 */
	sda_slot_halt(slot);

	sda_slot_exit(slot);
}
void
sda_slot_insert(void *arg)
{
	sda_slot_t	*slot = arg;

	if (sda_init_card(slot) != SDA_EOK) {
		/*
		 * Remove power from the slot.  If a more severe fault
		 * occurred, then a manual reset with cfgadm will be needed.
		 */
		sda_slot_err(slot, "Unable to initialize card!");
		sda_slot_enter(slot);
		sda_slot_power_off(slot);
		sda_slot_abort(slot, SDA_ENODEV);
		sda_slot_exit(slot);
		sda_nexus_remove(slot);

	} else {
		sda_nexus_insert(slot);
	}

	slot->s_stamp = ddi_get_time();
	slot->s_intransit = 0;
}
void
sda_slot_thread(void *arg)
{
	sda_slot_t	*slot = arg;

	for (;;) {
		sda_cmd_t	*cmdp;
		boolean_t	datline;
		sda_err_t	rv;

		mutex_enter(&slot->s_evlock);

		/*
		 * Process any abort list first.
		 */
		if ((cmdp = list_head(&slot->s_abortlist)) != NULL) {
			list_remove(&slot->s_abortlist, cmdp);
			mutex_exit(&slot->s_evlock);
			/*
			 * EOK used here, to avoid clobbering previous
			 * error code.
			 */
			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT,
			    SDA_EOK);
			continue;
		}

		if (slot->s_detach) {
			/* Parent is detaching the slot, bail out. */
			break;
		}

		if ((slot->s_suspend) && (slot->s_xfrp == NULL)) {
			/*
			 * Host wants to suspend, but don't do it if
			 * we have a transfer outstanding.
			 */
			break;
		}

		if (slot->s_detect) {
			slot->s_detect = B_FALSE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_detect(slot);
			continue;
		}

		if (slot->s_xfrdone) {
			sda_err_t	errno;

			errno = slot->s_errno;
			slot->s_errno = SDA_EOK;
			slot->s_xfrdone = B_FALSE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_transfer(slot, errno);
			continue;
		}

		if (slot->s_fault != SDA_FAULT_NONE) {
			sda_fault_t	fault;

			fault = slot->s_fault;
			slot->s_fault = SDA_FAULT_NONE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_fault(slot, fault);
			continue;
		}

		if (slot->s_reap) {
			/*
			 * Do not sleep while holding the evlock.  If this
			 * fails, we'll just try again the next cycle.
			 */
			(void) ddi_taskq_dispatch(slot->s_hp_tq,
			    sda_nexus_reap, slot, DDI_NOSLEEP);
		}

		if ((slot->s_xfrp != NULL) && (gethrtime() > slot->s_xfrtmo)) {
			/*
			 * The device stalled processing the data request.
			 * At this point, we really have no choice but to
			 * nuke the request, and flag a fault.
			 */
			mutex_exit(&slot->s_evlock);
			sda_slot_handle_transfer(slot, SDA_ETIME);
			sda_slot_fault(slot, SDA_FAULT_TIMEOUT);
			continue;
		}

		/*
		 * If the slot has suspended, then we can't process
		 * any new commands yet.
		 */
		if ((slot->s_suspend) || (!slot->s_wake)) {

			/*
			 * We use a timed wait if we are waiting for a
			 * data transfer to complete, or if we might
			 * need to reap child nodes.  Otherwise we
			 * avoid the timed wait to avoid waking CPU
			 * (power savings.)
			 */

			if ((slot->s_xfrp != NULL) || (slot->s_reap)) {
				/* Wait 3 sec (reap attempts). */
				(void) cv_reltimedwait(&slot->s_evcv,
				    &slot->s_evlock, drv_usectohz(3000000),
				    TR_CLOCK_TICK);
			} else {
				(void) cv_wait(&slot->s_evcv, &slot->s_evlock);
			}

			mutex_exit(&slot->s_evlock);
			continue;
		}

		slot->s_wake = B_FALSE;

		/*
		 * Possibly reap child nodes.
		 */
		if (slot->s_reap) {
			slot->s_reap = B_FALSE;
			mutex_exit(&slot->s_evlock);
			sda_nexus_reap(slot);
		} else {
			mutex_exit(&slot->s_evlock);
		}

		/*
		 * We're awake now, so look for work to do.  First
		 * acquire access to the slot.
		 */
		sda_slot_enter(slot);


		/*
		 * If no more commands to process, go back to sleep.
		 */
		if ((cmdp = list_head(&slot->s_cmdlist)) == NULL) {
			sda_slot_exit(slot);
			continue;
		}

		/*
		 * If the current command is not an initialization
		 * command, but we are initializing, go back to sleep.
		 * (This happens potentially during a card reset or
		 * suspend/resume cycle, where the card has not been
		 * removed, but a reset is in progress.)
		 */
		if (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT)) {
			sda_slot_exit(slot);
			continue;
		}

		datline = ((cmdp->sc_flags & SDA_CMDF_DAT) != 0);

		if (datline) {
			/*
			 * If the current command has a data phase
			 * while a transfer is in progress, then go
			 * back to sleep.
			 */
			if (slot->s_xfrp != NULL) {
				sda_slot_exit(slot);
				continue;
			}

			/*
			 * Note that APP_CMD doesn't have a data phase,
			 * although the associated ACMD might.
			 */
			if (cmdp->sc_index != CMD_APP_CMD) {
				slot->s_xfrp = cmdp;
				/*
				 * All commands should complete in
				 * less than 5 seconds.  The worst
				 * case is actually somewhere around 4
				 * seconds, but that is when the clock
				 * is only 100 kHz.
				 */
				slot->s_xfrtmo = gethrtime() +
				    5000000000ULL;
				(void) sda_setprop(slot, SDA_PROP_LED, 1);
			}
		}

		/*
		 * We're committed to dispatching this command now,
		 * so remove it from the list.
		 */
		list_remove(&slot->s_cmdlist, cmdp);

		/*
		 * There could be more commands after this one, so we
		 * mark ourself so we stay awake for another cycle.
		 */
		sda_slot_wakeup(slot);

		/*
		 * Submit the command.  Note that we are holding the
		 * slot lock here, so it is critical that the caller
		 * *not* call back up into the framework.  The caller
		 * must break context.  But doing it this way prevents
		 * a critical race on card removal.
		 *
		 * Note that we don't resubmit memory to the device if
		 * it isn't flagged as ready (e.g. if the wrong device
		 * was inserted!)
		 */
		if ((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) {
			rv = SDA_ENODEV;
			if (!slot->s_warn) {
				sda_slot_err(slot,
				    "Device removed while in use.  "
				    "Please reinsert!");
				slot->s_warn = B_TRUE;
			}
		} else {
			rv = slot->s_ops.so_cmd(slot->s_prv, cmdp);
		}
		if (rv == SDA_EOK)
			rv = sda_slot_check_response(cmdp);

		if (rv == SDA_EOK) {
			/*
			 * If APP_CMD completed properly, then
			 * resubmit with ACMD index.  Note wake was
			 * already set above.
			 */
			if (cmdp->sc_index == CMD_APP_CMD) {
				if ((cmdp->sc_response[0] & R1_APP_CMD) == 0) {
					sda_slot_log(slot, "APP_CMD not set!");
				}
				sda_cmd_resubmit_acmd(slot, cmdp);
				sda_slot_exit(slot);

				continue;
			}

		} else if (datline) {
			/*
			 * If an error occurred and we were expecting
			 * a transfer phase, we have to clean up.
			 */
			(void) sda_setprop(slot, SDA_PROP_LED, 0);
			slot->s_xfrp = NULL;
			slot->s_xfrtmo = 0;

			/*
			 * And notify any waiter.
			 */
			sda_slot_exit(slot);
			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, rv);
			continue;
		}

		/*
		 * Wake any waiter.
		 */
		sda_slot_exit(slot);
		sda_cmd_notify(cmdp, SDA_CMDF_BUSY, rv);
	}

	mutex_exit(&slot->s_evlock);
}
void
sda_slot_attach(sda_slot_t *slot)
{
	sda_host_t	*h = slot->s_hostp;
	char		name[16];
	uint32_t	cap;

	/*
	 * We have two taskqs.  The first taskq is used for
	 * card initialization.
	 *
	 * The second is used for the main processing loop.
	 *
	 * The reason for a separate taskq is that initialization
	 * needs to acquire locks which may be held by the slot
	 * thread, or by device driver context... use of the separate
	 * taskq breaks the deadlock.  Additionally, the
	 * initialization task may need to sleep quite a while during
	 * card initialization.
	 */

	sda_slot_enter(slot);

	(void) snprintf(name, sizeof (name), "slot_%d_hp_tq",
	    slot->s_slot_num);
	slot->s_hp_tq = ddi_taskq_create(h->h_dip, name, 1,
	    TASKQ_DEFAULTPRI, 0);
	if (slot->s_hp_tq == NULL) {
		/* Generally, this failure should never occur */
		sda_slot_err(slot, "Unable to create hotplug slot taskq");
		sda_slot_exit(slot);
		return;
	}

	/* create the main processing thread */
	(void) snprintf(name, sizeof (name), "slot_%d_main_tq",
	    slot->s_slot_num);
	slot->s_main_tq = ddi_taskq_create(h->h_dip, name, 1,
	    TASKQ_DEFAULTPRI, 0);
	if (slot->s_main_tq == NULL) {
		/* Generally, this failure should never occur */
		sda_slot_err(slot, "Unable to create main slot taskq");
		sda_slot_exit(slot);
		return;
	}
	(void) ddi_taskq_dispatch(slot->s_main_tq, sda_slot_thread, slot,
	    DDI_SLEEP);

	/*
	 * Determine slot capabilities.
	 */
	slot->s_caps = 0;

	if ((sda_getprop(slot, SDA_PROP_CAP_NOPIO, &cap) == 0) && (cap != 0)) {
		slot->s_caps |= SLOT_CAP_NOPIO;
	}
	if ((sda_getprop(slot, SDA_PROP_CAP_4BITS, &cap) == 0) && (cap != 0)) {
		slot->s_caps |= SLOT_CAP_4BITS;
	}
	if ((sda_getprop(slot, SDA_PROP_CAP_HISPEED, &cap) == 0) &&
	    (cap != 0)) {
		slot->s_caps |= SLOT_CAP_HISPEED;
	}

	/* make sure that the host is started up */
	if (slot->s_ops.so_reset(slot->s_prv) != 0) {
		sda_slot_fault(slot, SDA_FAULT_RESET);
	}

	sda_slot_exit(slot);
}
Exemple #7
0
int
sda_mem_parse_cid_csd(sda_slot_t *slot)
{
	uint32_t	*rcid;
	uint32_t	*rcsd;
	int		csdver;
	uint16_t	rblen;
	uint16_t	bshift;
	uint32_t	cmult;
	uint32_t	csize;

	rcid = slot->s_rcid;
	rcsd = slot->s_rcsd;

	csdver = sda_mem_getbits(rcsd, 127, 2);

	if (slot->s_flags & SLOTF_SDMEM) {
		switch (csdver) {
		case 0:
			csize = sda_mem_getbits(rcsd, 73, 12);
			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
			bshift = 9;
			break;
		case 1:
			rblen = 512;
			csize = sda_mem_getbits(rcsd, 69, 22);
			cmult = 1024;
			bshift = 0;
			break;
		default:
			sda_slot_err(slot, "Unknown SD CSD version (%d)",
			    csdver);
			return (DDI_FAILURE);
		}

		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
		slot->s_month = sda_mem_getbits(rcid, 11, 4);

	} else if (slot->s_flags & SLOTF_MMC) {
		if ((csdver < 1) || (csdver > 2)) {
			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
			    csdver);
			return (DDI_FAILURE);
		}

		switch (sda_mem_getbits(rcsd, 125, 4)) {
		case 0:	/* MMC 1.0 - 1.2 */
		case 1:	/* MMC 1.4 */
			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
			slot->s_oem[0] = 0;
			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
			break;

		case 2:	/* MMC 2.0 - 2.2 */
		case 3:	/* MMC 3.1 - 3.3 */
		case 4:	/* MMC 4.x */
			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
			break;

		default:
			/* this error isn't fatal to us */
			sda_slot_err(slot, "Unknown MMCA version (%d)",
			    sda_mem_getbits(rcsd, 125, 4));
			break;
		}

		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
		slot->s_month = sda_mem_getbits(rcid, 15, 4);

		csize = sda_mem_getbits(rcsd, 73, 12);
		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
		bshift = 9;

	} else {

		sda_slot_err(slot, "Card type unknown");
		return (DDI_FAILURE);
	}

	/*
	 * These fields are common to all known MMC/SDcard memory cards.
	 *
	 * The spec requires that block size 512 be supported.
	 * The media may have a different native size, but 512
	 * byte blocks will always work.  This is true for SDcard,
	 * and apparently for MMC as well.
	 */
	rblen = max(rblen, 512);	/* paranoia */
	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
	slot->s_bshift = bshift;
	slot->s_blksz = 512;

	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);

	if (((slot->s_ccc & (1 << 4)) == 0) ||
	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
		slot->s_flags &= ~SLOTF_WRITABLE;
	}

	return (DDI_SUCCESS);
}