/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include "sdhost.h"
struct sdstats {
};
/*
* Per slot state.
*/
struct sdslot {
int ss_num;
/*
* Command in progress
*/
int ss_blksz;
int ss_rcnt;
/* scratch buffer, to receive extra PIO data */
};
/*
* This allocates a rather large chunk of contiguous memory for DMA.
* But doing so means that we'll almost never have to resort to PIO.
*/
/*
* Per controller state.
*/
struct sdhost {
int sh_numslots;
/*
* Interrupt related information.
*/
int sh_icap;
};
#define PROPSET(x) \
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, x, 0) != 0)
static int sdhost_quiesce(dev_info_t *);
static int sdhost_suspend(dev_info_t *);
static int sdhost_resume(dev_info_t *);
static void sdhost_enable_interrupts(sdslot_t *);
static void sdhost_disable_interrupts(sdslot_t *);
static void sdhost_uninit_slot(sdhost_t *, int);
static sda_err_t sdhost_poll(void *);
static sda_err_t sdhost_reset(void *);
static sda_err_t sdhost_halt(void *);
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
ddi_no_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
sdhost_attach, /* devo_attach */
sdhost_detach, /* devo_detach */
nodev, /* devo_reset */
NULL, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
sdhost_quiesce, /* devo_quiesce */
};
&mod_driverops, /* drv_modops */
"Standard SD Host Controller", /* drv_linkinfo */
&sdhost_dev_ops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
sdhost_cmd, /* so_cmd */
sdhost_getprop, /* so_getprop */
sdhost_setprop, /* so_setprop */
sdhost_poll, /* so_poll */
sdhost_reset, /* so_reset */
sdhost_halt, /* so_halt */
};
DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
DDI_STRUCTURE_LE_ACC, /* devacc_attr_endian_flags */
DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
DDI_DEFAULT_ACC, /* devacc_attr_access */
};
DDI_DEVICE_ATTR_V0, /* devacc_attr_version */
DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */
DDI_STRICTORDER_ACC, /* devacc_attr_dataorder */
DDI_DEFAULT_ACC, /* devacc_attr_access */
};
/*
* If ever anyone uses PIO on SPARC, we have to endian-swap. But we
* think that SD Host Controllers are likely to be uncommon on SPARC,
* and hopefully when they exist at all they will be able to use DMA.
*/
#ifdef _BIG_ENDIAN
#else
#define sw32(x) (x)
#define sw16(x) (x)
#endif
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
int
{
int i;
int rv;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (sdhost_resume(dip));
default:
return (DDI_FAILURE);
}
/*
* Soft state allocation.
*/
/*
* Reset the "slot number", so uninit slot works properly.
*/
for (i = 0; i < SDHOST_MAXSLOTS; i++) {
}
/*
* Initialize DMA attributes. For now we initialize as for
* SDMA. If we add ADMA support we can improve this.
*/
/*
* PCI configuration access to figure out number of slots present.
*/
goto failed;
}
shp->sh_numslots);
goto failed;
}
/*
* Enable master accesses and DMA.
*/
/*
* Figure out which BAR to use. Note that we number BARs from
* 1, although PCI and SD Host numbers from 0. (We number
* from 1, because register number 0 means PCI configuration
* space in Solaris.)
*/
/*
* Setup interrupts ... supports the new DDI interrupt API. This
* will support MSI or MSI-X interrupts if a device is found to
* support it.
*/
goto failed;
}
NULL);
goto failed;
}
/*
* Configure slots, this also maps registers, enables
* interrupts, etc. Most of the hardware setup is done here.
*/
for (i = 0; i < shp->sh_numslots; i++) {
goto failed;
}
}
/*
* Enable device interrupts at the DDI layer.
*/
} else {
}
if (rv != DDI_SUCCESS) {
goto failed;
}
/*
* Mark the slots online with the framework. This will cause
* the framework to probe them for the presence of cards.
*/
} else {
}
goto failed;
}
return (DDI_SUCCESS);
}
for (i = 0; i < shp->sh_numslots; i++)
sdhost_uninit_slot(shp, i);
return (DDI_FAILURE);
}
int
{
int i;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (sdhost_suspend(dip));
default:
return (DDI_FAILURE);
}
/*
* Take host offline with the framework.
*/
/*
* Tear down interrupts.
*/
} else {
}
}
/*
* Tear down register mappings, etc.
*/
for (i = 0; i < shp->sh_numslots; i++)
sdhost_uninit_slot(shp, i);
return (DDI_SUCCESS);
}
int
{
/* reset each slot separately */
for (int i = 0; i < shp->sh_numslots; i++) {
continue;
}
return (DDI_SUCCESS);
}
int
{
int i;
for (i = 0; i < shp->sh_numslots; i++) {
}
return (DDI_SUCCESS);
}
int
{
int i;
for (i = 0; i < shp->sh_numslots; i++) {
}
return (DDI_SUCCESS);
}
{
int count;
/*
* Shut off the clock to begin.
*/
ss->ss_cardclk = 0;
if (hz == 0) {
return (SDA_EOK);
}
if (ss->ss_baseclk == 0) {
"Base clock frequency not established.");
return (SDA_EINVAL);
}
/* this clock requires high speed timings! */
} else {
/* don't allow clock to run faster than 25MHz */
}
/* figure out the divider */
div = 1;
if (div > 0x80)
break;
}
/*
* Set the internal clock divider first, without enabling the
* card clock yet.
*/
/*
* Wait up to 100 msec for the internal clock to stabilize.
* (The spec does not seem to indicate a maximum timeout, but
* it also suggests that an infinite loop be used, which is
* not appropriate for hardened Solaris drivers.)
*/
if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
/* if clock is stable, enable the SD clock pin */
return (SDA_EOK);
}
drv_usecwait(10);
}
return (SDA_ETIME);
}
{
int count;
/*
* There appears to be a bug where Ricoh hosts might have a
* problem if the host frequency is not set. If the card
* isn't present, or we are doing a master reset, just enable
* the internal clock at its native speed. (No dividers, and
* not exposed to card.).
*/
/* simple 1msec wait, don't wait for clock to stabilize */
drv_usecwait(1000);
/*
* reset the card clock & width -- master reset also
* resets these
*/
ss->ss_cardclk = 0;
}
return (SDA_EOK);
}
drv_usecwait(10);
}
return (SDA_ETIME);
}
void
{
/* disable slot interrupts for card insert and remove */
/* disable error interrupts */
}
void
{
/*
* Note that we want to enable reading of the CMD related
* bits, but we do not want them to generate an interrupt.
* (The busy wait for typical CMD stuff will normally be less
* the worst case of 100 kHz, the poll is at worst 2 msec.)
*/
/* enable slot interrupts for card insert and remove */
/* enable error interrupts */
}
int
{
int itypes;
int itype;
/*
* Set up interrupt handler.
*/
return (DDI_FAILURE);
}
/*
* It turns out that some controllers don't properly implement MSI,
* but advertise MSI capability in their PCI config space.
*
* While this is really a chip-specific bug, the simplest solution
* is to just suppress MSI for now by default -- every device seen
* so far can use FIXED interrupts.
*
* We offer an override property, though, just in case someone really
* wants to force it.
*
* We don't do this if the FIXED type isn't supported though!
*/
if (itypes & DDI_INTR_TYPE_FIXED) {
if (!PROPSET(SDHOST_PROP_ENABLE_MSI)) {
itypes &= ~DDI_INTR_TYPE_MSI;
}
if (!PROPSET(SDHOST_PROP_ENABLE_MSIX)) {
itypes &= ~DDI_INTR_TYPE_MSIX;
}
}
/*
* Interrupt types are bits in a mask. We know about these ones:
* FIXED = 1
* MSI = 2
* MSIX = 4
*/
int count;
/* this type is not supported on this device! */
continue;
}
(count == 0)) {
continue;
}
/*
* We have not seen a host device with multiple
* interrupts (one per slot?), and the spec does not
* indicate that they exist. But if one ever occurs,
* efforts.
*/
if (count > 1) {
"but driver only supports one", count);
continue;
}
(count != 1)) {
continue;
}
DDI_SUCCESS) {
continue;
}
continue;
}
DDI_SUCCESS) {
continue;
}
continue;
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
void
{
/* an unexpected partial transfer was found */
errno = SDA_ERESID;
}
/* send a STOP command if necessary */
(CMD_STOP_TRANSMIT << 8) |
}
}
}
{
int count;
if (ss->ss_suspended) {
return (DDI_INTR_UNCLAIMED);
}
if (intr == 0) {
return (DDI_INTR_UNCLAIMED);
}
/* no further interrupt processing this cycle */
return (DDI_INTR_CLAIMED);
}
/*
*/
/*
* Apparently some sdhost controllers issue a final
* DMA interrupt if the DMA completes on a boundary,
* even though there is no further data to transfer.
*
* There might be a risk here of the controller
* continuing to access the same data over and over
* again, but we accept the risk.
*/
}
/*
* PIO read! PIO is quite suboptimal, but we expect
* performance critical applications to use DMA
* whenever possible. We have to stage this through
* the bounce buffer to meet alignment considerations.
*/
}
}
}
}
}
/*
* PIO write! PIO is quite suboptimal, but we expect
* performance critical applications to use DMA
* whenever possible. We have to stage this through
* the bounce buffer to meet alignment considerations.
*/
}
}
}
}
}
(XFR_MODE_READ | XFR_MODE_DMA_EN)) {
}
}
} else {
}
} else if (errs & ERR_ACMD12) {
/*
* Generally, this is bad news. we need a full
* reset to recover properly.
*/
}
/*
* This asynchronous error leaves the slot more or less
* useless. Report it to the framework.
*/
if (errs & ERR_CURRENT) {
}
}
return (DDI_INTR_CLAIMED);
}
/*ARGSUSED1*/
{
int num;
/* interrupt for each of the slots present in the system */
}
}
return (rv);
}
int
{
unsigned ndmac;
int rv;
/*
* Register the private state.
*/
/*
* Initialize core data structure, locks, etc.
*/
/*
* Set up DMA.
*/
if (rv != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (rv != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Set up virtual kstats.
*/
/* counters are 64 bits wide */
/* these aren't counters -- leave them at 32 bits */
}
if (PROPSET(SDHOST_PROP_FORCE_PIO)) {
}
if (PROPSET(SDHOST_PROP_FORCE_DMA)) {
}
return (DDI_FAILURE);
}
/* reset before reading capabilities */
return (DDI_FAILURE);
/* host voltages in OCR format */
/* base clock */
ss->ss_baseclk =
/*
* Timeout clock. We can calculate this using the following
* formula:
*
*
* Clock time is the length of the base clock in usecs.
*
* Our base factor is 2^13, which is the shortest clock we
* can count.
*
* To simplify the math and avoid overflow, we cancel out the
* zeros for kHz or MHz. Since we want to wait more clocks, not
* less, on error, we truncate the result rather than rounding
* up.
*/
return (DDI_FAILURE);
}
if (capab & CAPAB_TIMEOUT_UNITS) {
/* MHz */
clk *= 1000000;
} else {
/* kHz */
clk *= 1000;
}
/*
* Calculation of the timeout.
*
* SDIO cards use a 1sec timeout, and SDHC cards use fixed
* 100msec for read and 250 msec for write.
*
* Legacy cards running at 375kHz have a worst case of about
* 15 seconds. Running at 25MHz (the standard speed) it is
* about 100msec for read, and about 3.2 sec for write.
* Typical values are 1/100th that, or about 1msec for read,
* and 32 msec for write.
*
* No transaction at full speed should ever take more than 4
* seconds. (Some slow legacy cards might have trouble, but
* we'll worry about them if they ever are seen. Nobody wants
* to wait 4 seconds to access a single block anyway!)
*
* To get to 4 seconds, we continuously double usec until we
* get to the maximum value, or a timeout greater than 4
* seconds.
*
* Note that for high-speed timeout clocks, we might not be
* able to get to the full 4 seconds. E.g. with a 48MHz
* timeout clock, we can only get to about 2.8 seconds. Its
* possible that there could be some slow MMC cards that will
* timeout at this clock rate, but it seems unlikely. (The
* device would have to be pressing the very worst times,
* against the 100-fold "permissive" window allowed, and
* running at only 12.5MHz.)
*
* XXX: this could easily be a tunable. Someone dealing with only
* reasonable cards could set this to just 1 second.
*/
break;
}
}
/*
* Enable slot interrupts.
*/
return (DDI_SUCCESS);
}
void
{
}
void
{
int i;
/*
* Response 2 is goofy because the host drops the low
* order CRC bits. This makes it a bit awkward, so we
* have to shift the bits to make it work out right.
*
* Note that the framework expects the 32 bit
* words to be ordered in LE fashion. (The
* bits within the words are in native order).
*/
for (i = 3; i > 0; i--) {
resp[i] <<= 8;
}
resp[0] <<= 8;
}
}
{
int i;
/*
* Worst case for 100kHz timeout is 2msec (200 clocks), we add
* a tiny bit for safety. (Generally timeout will be far, far
* less than that.)
*
* Note that at more typical 12MHz (and normally it will be
* even faster than that!) that the device timeout is only
* 16.67 usec. We could be smarter and reduce the delay time,
* but that would require putting more intelligence into the
* code, and we don't expect CMD timeout to normally occur
* except during initialization. (At which time we need the
* full timeout anyway.)
*
* Checking the ERR_STAT will normally cause the timeout to
* terminate to finish early if the device is healthy, anyway.
*/
for (i = 3000; i > 0; i -= 5) {
/* command completed */
return (SDA_EOK);
}
/* command timeout isn't a host failure */
} else {
rv = SDA_EPROTO;
}
goto error;
}
drv_usecwait(5);
}
/*
* NB: We need to soft reset the CMD and DAT
* lines after a failure of this sort.
*/
return (rv);
}
{
(void) sdhost_slot_intr(ss);
return (SDA_EOK);
}
{
/*
* Command register:
* bit 13-8 = command index
* bit 7-6 = command type (always zero for us!)
* bit 5 = data present select
* bit 4 = command index check (always on!)
* bit 3 = command CRC check enable
* bit 2 = reserved
* bit 1-0 = response type
*/
command |= COMMAND_TYPE_NORM |
case R0:
break;
case R1:
case R5:
case R6:
case R7:
break;
case R1b:
case R5b:
break;
case R2:
break;
case R3:
case R4:
break;
default:
return (SDA_EINVAL);
}
if (ss->ss_suspended) {
return (SDA_ESUSPENDED);
}
/*
* Ensure that we have good data.
*/
return (SDA_EINVAL);
}
/*
* Only SDMA for now. We can investigate ADMA2 later.
* (Right now we don't have ADMA2 capable hardware.)
* We always use a bounce buffer, which solves weird
* problems with certain controllers. Doing this with
* a large contiguous buffer may be faster than
* servicing all the little per-page interrupts
* anyway. (Bcopy of 64 K vs. 16 interrupts.)
*/
/*
* if we're writing, prepare initial round
* of data
*/
} else {
}
} else {
mode = 0;
}
if (nblks > 1) {
}
mode |= XFR_MODE_READ;
} else {
}
}
return (rv);
}
{
if (ss->ss_suspended) {
return (SDA_ESUSPENDED);
}
switch (prop) {
case SDA_PROP_INSERTED:
} else {
}
break;
case SDA_PROP_WPROTECT:
} else {
}
break;
case SDA_PROP_OCR:
break;
case SDA_PROP_CLOCK:
break;
case SDA_PROP_CAP_HISPEED:
} else {
}
break;
case SDA_PROP_CAP_4BITS:
break;
case SDA_PROP_CAP_NOPIO:
/*
* We might have to use PIO for buffers that don't
* have reasonable alignments. A few controllers seem
* not to deal with granularity or alignments of
* something other 32-bits.
*/
break;
case SDA_PROP_CAP_INTR:
case SDA_PROP_CAP_8BITS:
break;
default:
rv = SDA_ENOTSUP;
break;
}
return (rv);
}
{
if (ss->ss_suspended) {
return (SDA_ESUSPENDED);
}
switch (prop) {
case SDA_PROP_LED:
if (val) {
} else {
}
break;
case SDA_PROP_CLOCK:
break;
case SDA_PROP_BUSWIDTH:
switch (val) {
case 1:
break;
case 4:
break;
default:
rv = SDA_EINVAL;
}
break;
case SDA_PROP_OCR:
if (val & OCR_17_18V) {
} else if (val & OCR_29_30V) {
} else if (val & OCR_32_33V) {
} else if (val == 0) {
/* turn off power */
} else {
rv = SDA_EINVAL;
}
break;
case SDA_PROP_HISPEED:
if (val) {
} else {
}
/* give clocks time to settle */
drv_usecwait(10);
break;
default:
rv = SDA_ENOTSUP;
break;
}
/*
* Apparently some controllers (ENE) have issues with changing
* certain parameters (bus width seems to be one), requiring
* a reset of the DAT and CMD lines.
*/
}
return (rv);
}
{
if (!ss->ss_suspended) {
return (SDA_ETIME);
}
}
return (SDA_EOK);
}
{
if (!ss->ss_suspended) {
/* this has the side effect of removing power from the card */
return (SDA_ETIME);
}
}
return (SDA_EOK);
}