SDIO driver
Ben Gray
ben.r.gray at gmail.com
Fri Jun 1 20:14:18 UTC 2012
Hi Warner,
Thanks very much for your reply and sorry for my rather tardy reply.
>
> On May 24, 2012, at 2:01 PM, Ben Gray wrote:
>
>> Hi all,
>>
>> I've being doing some work on hacking SDIO support into the kernel. My changes build on top of the current MMC/MMCBR module. Expanding the diagram from Warner Losh's pdf (http://www.bsdcan.org/2007/schedule/attachments/23-freebsd-sd-warner-losh.pdf), my hacks have the following logic layout
>>
>>
>> +------------+ +------------+
>> | ti_mmchs0 | | ti_mmchs1 |
>> +------------+ +------------+
>> | |
>> | |
>> +--------+ +--------+
>> | mmc0 | | mmc1 |
>> +--------+ +--------+
>> | |
>> +------+-----+ |
>> | | |
>> +---------+ +---------+ +---------+
>> | mmcsd0 | | mmcsd1 | | sdio0 |
>> +---------+ +---------+ +---------+
>> |
>> |
>> +------+-------+------- .... -----+
>> | | |
>> function#1 function#2 function#7
>> +----------+ +-----------+ +----------+
>> | wifi | | bluetooth | .... | whatever |
>> +----------+ +-----------+ +----------+
>>
>>
>> So my sdio module sits at the same level as the current mmcsd module and uses the same interface to talk to the mmc0 module (notably MMCBUS_WAIT_FOR_REQUEST). The sdio part does the card setup and CIS scanning, then enables any child functions and probes for a suitable child driver.
>
> I think this is one layer too low in the tree. This is bus code, and should be in the mmc bus. sdio drivers can then attach, and multiple functions can attach multiple drivers more easily.
I tended to agree, the issue was I didn't want to change too much at the
mmc level. IMO there are some advantages by having an SDIO layer; it's
a place to put the sdio specific stuff (CIS parsing, etc) and it means
child devices are based off 'sdio' rather than 'mmc' which seems to make
more sense to me. It also means the sdio layer can expose
functions/interfaces for reading/writing data blocks, something that
would be used commonly across all child sdio devices.
However I take your point and I guess it's probably best to do it
properly rather than just tack something on.
So I might go back to the drawing board and see how to cleanly integrate
the sdio code.
>
>
>> The problem with this approach is that currently a driver can target only one logic function on the card - I don't know if any driver will ever need to span more than one function. But perhaps a bigger problem is that it doesn't support 'combo cards' (cards with both I/O and standard SD memory). However supporting combo cards, I think, would require a lot more changes to the current mmc/mmcsd drivers which I'm reluctant to do.
>
> Yes. That's one of many problems, since SD I/O cards also need some additional stuff in the enumeration, which may be tricky to reliably do in the lower layers like you've done. In addition, there would need to be some bus/bridge interfaces because some mmc/sd controllers can't do SD I/O transactions.
>
>> Anyway I'm interested in whether anyone thinks this is useful in it's current form? or whether the above limitations are too much of a problem? If others are happy and would like it in the tree then perhaps I can commit it to the armv6 branch for review?
>
> I'd be interested in taking a look.
Once again sorry I've been a bit slow to respond, but I've now attached
a patch for my code in it's current state. I apologise in advance, it's
a bit messy and there parts of the standard missing (interrupts for
one), but it does work well enough to detect an sdio card and probe a
child driver.
The patch doesn't include my wifi driver, because that's even more of a
mess.
Oh and the patch is based off the armv6 project branch, my version may
be a month or so old.
>
>
>> Lastly the code is not quite complete as there are still corner cases that need to be fixed, however in it's current form it works with a basic wifi driver I've written for the Pandaboard (can load f/w, read registers, etc).
>
> Which wifi card?
It's not actually a card rather a solder-on-module that supports wifi,
bt and (on the newer devices) gps. It's made by TI, called wilink
(http://www.ti.com/general/docs/wtbu/wtbuproductcontent.tsp?templateId=6123&navigationId=12859&contentId=67453).
It comes on all Pandaboards.
>
>
> Warner
Cheers,
Ben.
-------------- next part --------------
Index: mmcreg.h
===================================================================
--- mmcreg.h (revision 234394)
+++ mmcreg.h (working copy)
@@ -85,6 +85,8 @@
#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
+#define MMC_RSP_R4 (MMC_RSP_PRESENT)
+#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC)
#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC)
#define MMC_RSP(x) ((x) & MMC_RSP_MASK)
@@ -151,6 +153,31 @@
#define R1_STATE_PRG 7
#define R1_STATE_DIS 8
+/*
+ * R5 responses
+ *
+ * Types (per SD 2.0 standard)
+ * e : error bit
+ * s : status bit
+ * r : detected and set for the actual command response
+ * x : Detected and set during command execution. The host can get
+ * the status by issuing a command with R1 response.
+ *
+ * Clear Condition (per SD 2.0 standard)
+ * a : according to the card current state.
+ * b : always related to the previous command. reception of a valid
+ * command will clear it (with a delay of one command).
+ * c : clear by read
+ */
+#define R5_COM_CRC_ERROR (1u << 15) /* er, b */
+#define R5_ILLEGAL_COMMAND (1u << 14) /* er, b */
+#define R5_IO_CURRENT_STATE_MASK (3u << 12) /* s, b */
+#define R5_IO_CURRENT_STATE(x) (((x) & R5_IO_CURRENT_STATE_MASK) >> 12)
+#define R5_ERROR (1u << 11) /* erx, c */
+#define R5_FUNCTION_NUMBER (1u << 9) /* er, c */
+#define R5_OUT_OF_RANGE (1u << 8) /* er, c */
+
+
struct mmc_data {
size_t len; /* size of the data */
size_t xfer_len;
@@ -181,7 +208,7 @@
#define MMC_SET_RELATIVE_ADDR 3
#define SD_SEND_RELATIVE_ADDR 3
#define MMC_SET_DSR 4
- /* reserved: 5 */
+#define SD_IO_SEND_OP_COND 5
#define MMC_SWITCH_FUNC 6
#define MMC_SWITCH_FUNC_CMDS 0
#define MMC_SWITCH_FUNC_SET 1
@@ -332,6 +359,22 @@
#define SD_MAX_HS 50000000
+/*
+ * SDIO Direct & Extended I/O
+ */
+#define SD_IO_RW_WR (1u << 31)
+#define SD_IO_RW_FUNC(x) (((x) & 0x7) << 28)
+#define SD_IO_RW_RAW (1u << 27)
+#define SD_IO_RW_INCR (1u << 26)
+#define SD_IO_RW_ADR(x) (((x) & 0x1FFFF) << 9)
+#define SD_IO_RW_DAT(x) (((x) & 0xFF) << 0)
+#define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0)
+
+#define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0)
+#define SD_IOE_RW_BLK (1u << 27)
+
+
+
/* OCR bits */
/*
Index: bridge.h
===================================================================
--- bridge.h (revision 234394)
+++ bridge.h (working copy)
@@ -105,7 +105,7 @@
};
enum mmc_bus_timing {
- bus_timing_normal = 0, bus_timing_hs
+ bus_timing_ls = 0, bus_timing_normal, bus_timing_hs
};
struct mmc_ios {
@@ -119,7 +119,7 @@
};
enum mmc_card_mode {
- mode_mmc, mode_sd
+ mode_mmc, mode_sd, mode_sdio
};
struct mmc_host {
Index: mmcvar.h
===================================================================
--- mmcvar.h (revision 234394)
+++ mmcvar.h (working copy)
@@ -69,6 +69,7 @@
MMC_IVAR_BUS_WIDTH,
MMC_IVAR_ERASE_SECTOR,
MMC_IVAR_MAX_DATA,
+ MMC_IVAR_IO_FUNCTIONS,
// MMC_IVAR_,
};
@@ -89,5 +90,6 @@
MMC_ACCESSOR(bus_width, BUS_WIDTH, int)
MMC_ACCESSOR(erase_sector, ERASE_SECTOR, int)
MMC_ACCESSOR(max_data, MAX_DATA, int)
+MMC_ACCESSOR(io_functions, IO_FUNCTIONS, int)
#endif /* DEV_MMC_MMCVAR_H */
Index: mmc.c
===================================================================
--- mmc.c (revision 234394)
+++ mmc.c (working copy)
@@ -70,6 +70,11 @@
#include "mmcbr_if.h"
#include "mmcbus_if.h"
+#define MMC_SDIO_SUPPORT
+#if defined(MMC_SDIO_SUPPORT)
+#include <dev/mmc/mmcioreg.h>
+#endif
+
struct mmc_softc {
device_t dev;
struct mtx sc_mtx;
@@ -100,7 +105,9 @@
uint32_t sec_count; /* Card capacity in 512byte blocks */
uint32_t tran_speed; /* Max speed in normal mode */
uint32_t hs_tran_speed; /* Max speed in high speed mode */
+ uint32_t ls_tran_speed; /* Max speed in low speed mode (SDIO only) */
uint32_t erase_sector; /* Card native erase sector size */
+ uint32_t io_functions; /* Number of IO functions supported (SDIO only) */
};
#define CMD_RETRIES 3
@@ -506,6 +513,86 @@
return (err);
}
+/* [BRG] - Added for SDIO */
+#if defined(MMC_SDIO_SUPPORT)
+
+static int
+mmc_send_io_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr,
+ uint32_t *rfns, uint32_t *rmem)
+{
+ struct mmc_command cmd;
+ int err;
+ int i;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = SD_IO_SEND_OP_COND;
+ cmd.arg = ocr;
+ cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
+ cmd.data = NULL;
+
+ printf("[BRG] mmc_send_io_op_cond : cmd.opcode = 0x%08x : cmd.arg = 0x%08x\n", cmd.opcode, cmd.arg);
+
+ for (i = 0; i < 1000; i++) {
+ err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+ if (err != MMC_ERR_NONE)
+ break;
+ if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+ (ocr & MMC_OCR_VOLTAGE) == 0)
+ break;
+ err = MMC_ERR_TIMEOUT;
+ mmc_ms_delay(10);
+ }
+
+ printf("[BRG] mmc_send_io_op_cond : cmd.resp[0] = 0x%08x (err %d)\n", cmd.resp[0], err);
+
+ if (err == MMC_ERR_NONE) {
+ if (rocr)
+ *rocr = (cmd.resp[0] & 0x00ffffff);
+ if (rfns)
+ *rfns = (cmd.resp[0] >> 28) & 0x7;
+ if (rmem)
+ *rmem = (cmd.resp[0] >> 27) & 0x1;
+ }
+ return (err);
+}
+
+/* [BRG] - Added for SDIO */
+static int
+mmc_io_rw_direct(struct mmc_softc *sc, int wr, uint32_t fn, uint32_t adr,
+ uint8_t *data)
+{
+ struct mmc_command cmd;
+ int err;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = SD_IO_RW_FUNC(fn) | SD_IO_RW_ADR(adr);
+ if (wr)
+ cmd.arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+ cmd.data = NULL;
+
+ printf("[BRG] mmc_io_rw_direct : cmd.opcode = 0x%08x : cmd.arg = 0x%08x\n", cmd.opcode, cmd.arg);
+
+ err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+ if (err)
+ return (err);
+ if (cmd.error)
+ return (cmd.error);
+
+ if (cmd.resp[0] & R5_COM_CRC_ERROR)
+ return (MMC_ERR_BADCRC);
+ if (cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
+ return (MMC_ERR_INVALID);
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return (MMC_ERR_FAILED);
+
+ *data = (uint8_t) (cmd.resp[0] & 0xff);
+ return (MMC_ERR_NONE);
+}
+
+#endif /* MMC_SDIO_SUPPORT */
+
static void
mmc_power_up(struct mmc_softc *sc)
{
@@ -597,14 +684,68 @@
return (err);
}
+#if defined(MMC_SDIO_SUPPORT)
static int
+mmc_sdio_set_timing(struct mmc_softc *sc, int timing)
+{
+ int err;
+ uint8_t reg;
+
+ err = mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_HIGH_SPEED, ®);
+ if (err) {
+ device_printf(sc->dev, "failed to read SD_IO_CCCR_HIGH_SPEED register\n");
+ return (err);
+ }
+
+ if (timing == bus_timing_hs) {
+ if (!(reg & CCCR_HIGH_SPEED_SHS))
+ return (MMC_ERR_INVALID);
+ reg |= CCCR_HIGH_SPEED_EHS;
+ } else {
+ reg &= ~CCCR_HIGH_SPEED_EHS;
+ }
+
+ err = mmc_io_rw_direct(sc, 1, 0, SD_IO_CCCR_HIGH_SPEED, ®);
+ if (err)
+ return (err);
+
+ if ((timing == bus_timing_hs) &&
+ ((reg & CCCR_HIGH_SPEED_EHS) != CCCR_HIGH_SPEED_EHS)) {
+ return (MMC_ERR_FAILED);
+ }
+
+ return (MMC_ERR_NONE);
+}
+#endif /* MMC_SDIO_SUPPORT */
+
+static int
mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width)
{
struct mmc_command cmd;
int err;
uint8_t value;
+ int mode = mmcbr_get_mode(sc->dev);
- if (mmcbr_get_mode(sc->dev) == mode_sd) {
+#if defined(MMC_SDIO_SUPPORT)
+ if (mode == mode_sdio) {
+ err = mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_BUS_WIDTH, &value);
+ if (err == 0) {
+ value &= ~CCCR_BUS_WIDTH_MASK;
+ switch (width) {
+ case bus_width_1:
+ value |= CCCR_BUS_WIDTH_1;
+ break;
+ case bus_width_4:
+ value |= CCCR_BUS_WIDTH_4;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
+ err = mmc_io_rw_direct(sc, 1, 0, SD_IO_CCCR_BUS_WIDTH, &value);
+ }
+ } else
+#endif /* MMC_SDIO_SUPPORT */
+ if (mode == mode_sd) {
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = ACMD_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -656,12 +797,24 @@
default:
return (MMC_ERR_INVALID);
}
- if (mmcbr_get_mode(sc->dev) == mode_sd)
+
+ switch (mmcbr_get_mode(sc->dev)) {
+#if defined(MMC_SDIO_SUPPORT)
+ case mode_sdio:
+ err = mmc_sdio_set_timing(sc, timing);
+ break;
+#endif
+ case mode_sd:
err = mmc_sd_switch(sc, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1,
value, switch_res);
- else
+ break;
+ case mode_mmc:
+ default:
err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, value);
+ break;
+ }
+
return (err);
}
@@ -1082,7 +1235,7 @@
ivar->rca, newcard ? " added" : "");
device_printf(dev, " card: %s%s (0x%x/0x%x/\"%s\" rev %d.%d "
"m/d %02d.%04d s/n %08x)\n",
- ivar->mode == mode_sd ? "SD" : "MMC",
+ ivar->mode == mode_sdio ? "SDIO" : ivar->mode == mode_sd ? "SD" : "MMC",
ivar->high_cap ? " High Capacity" : "",
ivar->cid.mid, ivar->cid.oid,
ivar->cid.pnm, ivar->cid.prv >> 4, ivar->cid.prv & 0x0f,
@@ -1098,7 +1251,136 @@
ivar->read_only ? ", read-only" : "");
}
+#if defined(MMC_SDIO_SUPPORT)
+/**
+ * mmc_io_discover_cards - found an SDIO card on the bus so do discover
+ * @sc: device soft context
+ *
+ * What has been done:
+ * - CMD5 (IO_SEND_OP_COND) has been sent and a valid response received
+ * - voltage on the bus has been set to match the returned OC values
+ *
+ * What hasn't been done
+ * -
+ */
static void
+mmc_io_discover_cards(struct mmc_softc *sc)
+{
+ int err;
+ uint32_t resp;
+ uint32_t fns;
+ uint32_t mem;
+ uint16_t rca;
+ uint8_t reg;
+ struct mmc_ivars *ivar = NULL;
+ int i, devcount, newcard = 1;
+ device_t *devlist;
+ device_t child;
+
+ if (bootverbose || mmc_debug)
+ device_printf(sc->dev, "Probing SDIO cards\n");
+
+
+ /* Issue (another) CMD5 to get the number of functions */
+ err = mmc_send_io_op_cond(sc, MMC_OCR_CCS, NULL, &fns, &mem);
+ if (err) {
+ printf("mmc_send_io_op_cond failed\n");
+ return;
+ }
+
+ /* We currently don't support SDIO cards that have both memory and I/O */
+ if (mem) {
+ device_printf(sc->dev, "SDIO driver doesn't currently support mixed memory and I/O cards\n");
+ return;
+ }
+
+
+ /* Send a CMD3 which moves the card state machine from 'init' to 'standby' */
+ err = mmc_send_relative_addr(sc, &resp);
+ rca = resp >> 16;
+
+ /* Verify that the RCA has been set by selecting the card. */
+ err = mmc_select_card(sc, rca);
+ if (err) {
+ device_printf(sc->dev, "couldn't select I/O RCA %d\n", rca);
+ return;
+ }
+
+
+ /*
+ * Now things get a bit hacky ... the original driver uses ivars stored in
+ * the child device (mmcsd memory card) to calculate things like the bus
+ * width, timing requirements, etc. It's all based on the unique card id,
+ * however this is not relevant to SDIO cards which don't have the
+ * same bus scan procedure and don't support the card id cmd (CID).
+ *
+ * Regardless (for the moment) I've tried to provide the same ivars for
+ * SDIO cards that would be for SD/MMC memory cards.
+ */
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return;
+
+ for (i = 0; i < devcount; i++) {
+ ivar = device_get_ivars(devlist[i]);
+ if (ivar->mode == mode_sdio) {
+ newcard = 0;
+ break;
+ }
+ }
+ free(devlist, M_TEMP);
+
+ if (bootverbose || mmc_debug) {
+ device_printf(sc->dev, "%sSDIO card detected\n", newcard ? "New " : "");
+ }
+
+ if (newcard) {
+ ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!ivar)
+ return;
+ }
+
+ ivar->rca = rca;
+ ivar->read_only = 0;
+ ivar->timing = bus_timing_normal;
+ ivar->mode = mmcbr_get_mode(sc->dev);
+ ivar->io_functions = fns;
+
+ /* Find max supported bus width. */
+ mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_CAPABILITY, ®);
+ if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) &&
+ (!(reg & CCCR_CAPS_LSC) || (reg & CCCR_CAPS_4BLS))) {
+ ivar->bus_width = bus_width_4;
+ }
+
+ /* For SDIO set the maximum speeds in either low or full speed modes */
+ ivar->tran_speed = 25 * 1000 * 1000;
+ ivar->hs_tran_speed = 50 * 1000 * 1000;
+ ivar->ls_tran_speed = 400 * 1000;
+ if (reg & CCCR_CAPS_LSC)
+ ivar->timing = bus_timing_ls;
+ else {
+ mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_HIGH_SPEED, ®);
+ if (reg & CCCR_HIGH_SPEED_SHS)
+ ivar->timing = bus_timing_hs;
+ else
+ ivar->timing = bus_timing_normal;
+ }
+
+
+ if (bootverbose || mmc_debug)
+ mmc_log_card(sc->dev, ivar, newcard);
+
+
+ /* Add device. */
+ child = device_add_child(sc->dev, "sdio", -1);
+ device_set_ivars(child, ivar);
+
+ return;
+
+}
+#endif /* MMC_SDIO_SUPPORT */
+
+static void
mmc_discover_cards(struct mmc_softc *sc)
{
struct mmc_ivars *ivar = NULL;
@@ -1192,7 +1474,7 @@
mmc_log_card(sc->dev, ivar, newcard);
if (newcard) {
/* Add device. */
- child = device_add_child(sc->dev, NULL, -1);
+ child = device_add_child(sc->dev, "mmcsd", -1);
device_set_ivars(child, ivar);
}
return;
@@ -1249,7 +1531,7 @@
mmc_log_card(sc->dev, ivar, newcard);
if (newcard) {
/* Add device. */
- child = device_add_child(sc->dev, NULL, -1);
+ child = device_add_child(sc->dev, "mmcsd", -1);
device_set_ivars(child, ivar);
}
}
@@ -1308,36 +1590,55 @@
dev = sc->dev;
if (mmcbr_get_power_mode(dev) != power_on) {
+
+#if defined(MMC_SDIO_SUPPORT)
+//mmc_debug = 10;
+
/*
- * First, try SD modes
+ * First, try SDIO mode
*/
- mmcbr_set_mode(dev, mode_sd);
+ mmcbr_set_mode(dev, mode_sdio);
mmc_power_up(sc);
mmcbr_set_bus_mode(dev, pushpull);
if (bootverbose || mmc_debug)
device_printf(sc->dev, "Probing bus\n");
mmc_idle_cards(sc);
- err = mmc_send_if_cond(sc, 1);
- if ((bootverbose || mmc_debug) && err == 0)
- device_printf(sc->dev, "SD 2.0 interface conditions: OK\n");
- if (mmc_send_app_op_cond(sc, err ? 0 : MMC_OCR_CCS, &ocr) !=
- MMC_ERR_NONE) {
- if (bootverbose || mmc_debug)
- device_printf(sc->dev, "SD probe: failed\n");
+
+ err = mmc_send_io_op_cond(sc, 0, &ocr, NULL, NULL);
+ if (err == MMC_ERR_NONE) {
+ if (bootverbose || mmc_debug) {
+ device_printf(sc->dev, "SDIO 2.0 interface conditions: OK\n");
+ device_printf(sc->dev, "SDIO probe: OK (OCR: 0x%08x)\n", ocr);
+ }
+ } else
+#endif /* MMC_SDIO_SUPPORT */
+ {
/*
- * Failed, try MMC
+ * Next, try SD modes
*/
- mmcbr_set_mode(dev, mode_mmc);
- if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
+ mmcbr_set_mode(dev, mode_sd);
+ err = mmc_send_if_cond(sc, 1);
+ if ((bootverbose || mmc_debug) && err == 0)
+ device_printf(sc->dev, "SD 2.0 interface conditions: OK\n");
+ if (mmc_send_app_op_cond(sc, err ? 0 : MMC_OCR_CCS, &ocr) !=
+ MMC_ERR_NONE) {
if (bootverbose || mmc_debug)
- device_printf(sc->dev, "MMC probe: failed\n");
- ocr = 0; /* Failed both, powerdown. */
+ device_printf(sc->dev, "SD probe: failed\n");
+ /*
+ * Failed, try MMC
+ */
+ mmcbr_set_mode(dev, mode_mmc);
+ if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
+ if (bootverbose || mmc_debug)
+ device_printf(sc->dev, "MMC probe: failed\n");
+ ocr = 0; /* Failed both, powerdown. */
+ } else if (bootverbose || mmc_debug)
+ device_printf(sc->dev,
+ "MMC probe: OK (OCR: 0x%08x)\n", ocr);
} else if (bootverbose || mmc_debug)
- device_printf(sc->dev,
- "MMC probe: OK (OCR: 0x%08x)\n", ocr);
- } else if (bootverbose || mmc_debug)
- device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr);
-
+ device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr);
+ }
+
mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr));
if (mmcbr_get_ocr(dev) != 0)
mmc_idle_cards(sc);
@@ -1358,7 +1659,16 @@
mmc_power_down(sc);
return;
}
+#if defined(MMC_SDIO_SUPPORT)
/*
+ * If SDIO card in the slot perform SDIO init only
+ */
+ if (mmcbr_get_mode(dev) == mode_sdio) {
+ mmc_io_discover_cards(sc);
+
+ } else {
+#endif
+ /*
* Reselect the cards after we've idled them above.
*/
if (mmcbr_get_mode(dev) == mode_sd) {
@@ -1369,18 +1679,46 @@
mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL);
mmc_discover_cards(sc);
mmc_rescan_cards(sc);
-
+#if defined(MMC_SDIO_SUPPORT)
+ }
+#endif
+
mmcbr_set_bus_mode(dev, pushpull);
mmcbr_update_ios(dev);
mmc_calculate_clock(sc);
+#if !defined(MMC_SDIO_SUPPORT)
bus_generic_attach(dev);
+#else
+ /* The SDIO driver attempts to scan the functions on the card in
+ * device_attach, therefore we need to be able to access the card
+ * and it should be selected.
+ *
+ * So replace the generic bus_attach with a loop that selects the
+ * card before calling the probing and attaching the child drivers.
+ */
+ {
+ int nkid, i;
+ device_t *kids;
+ struct mmc_ivars *ivar;
+
+ if ((err = device_get_children(sc->dev, &kids, &nkid)) != 0)
+ return;
+ for (i = 0; i < nkid; i++) {
+ ivar = device_get_ivars(kids[i]);
+ mmc_select_card(sc, ivar->rca);
+ device_probe_and_attach(kids[i]);
+ }
+ mmc_select_card(sc, 0);
+ }
+#endif
+
/* mmc_update_children_sysctl(dev);*/
}
static int
mmc_calculate_clock(struct mmc_softc *sc)
{
- int max_dtr, max_hs_dtr, max_timing;
+ int max_ls_dtr, max_dtr, max_hs_dtr, max_timing;
int nkid, i, f_min, f_max;
device_t *kids;
struct mmc_ivars *ivar;
@@ -1398,6 +1736,8 @@
ivar = device_get_ivars(kids[i]);
if (ivar->timing < max_timing)
max_timing = ivar->timing;
+ if (ivar->ls_tran_speed < max_ls_dtr)
+ max_ls_dtr = ivar->ls_tran_speed;
if (ivar->tran_speed < max_dtr)
max_dtr = ivar->tran_speed;
if (ivar->hs_tran_speed < max_hs_dtr)
@@ -1405,7 +1745,7 @@
}
for (i = 0; i < nkid; i++) {
ivar = device_get_ivars(kids[i]);
- if (ivar->timing == bus_timing_normal)
+ if (ivar->timing == max_timing)
continue;
mmc_select_card(sc, ivar->rca);
mmc_set_timing(sc, max_timing);
@@ -1414,6 +1754,8 @@
free(kids, M_TEMP);
if (max_timing == bus_timing_hs)
max_dtr = max_hs_dtr;
+ else if (max_timing == bus_timing_ls)
+ max_dtr = max_ls_dtr;
if (bootverbose || mmc_debug) {
device_printf(sc->dev,
"setting transfer rate to %d.%03dMHz%s\n",
@@ -1477,6 +1819,9 @@
case MMC_IVAR_MAX_DATA:
*result = mmcbr_get_max_data(bus);
break;
+ case MMC_IVAR_IO_FUNCTIONS:
+ *result = ivar->io_functions;
+ break;
}
return (0);
}
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ sdio.c 2012-06-01 17:28:05.000000000 +0100
@@ -0,0 +1,910 @@
+/*-
+ * Copyright (c) 2011 Ben Gray. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SDIO Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the SD simplified
+ * specification ("Simplified Specification") by the SD Card Association.
+ * The Simplified Specification is a subset of the complete SD Specification
+ * which is owned by the SD Card Association.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented
+ * only as a standard specification for SD Cards and SD Host/Ancillary products
+ * and is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Card Association for any
+ * damages, any infringements of patents or other right of the SD Card
+ * Association or any third parties, which may result from its use. No
+ * license is granted by implication, estoppel or otherwise under any patent
+ * or other rights of the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Card Association to
+ * disclose or distribute any technical information, know-how or other
+ * confidential information to any third party.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <dev/mmc/mmcvar.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcioreg.h>
+#include <dev/mmc/sdio.h>
+#include <dev/mmc/sdiovar.h>
+
+#include "mmcbus_if.h"
+#include "sdio_if.h"
+
+
+#define SDIO_FLAG_SUPPORT_MULTI_BLOCK (0x1UL << 0)
+
+
+
+struct sdio_softc {
+ device_t dev;
+ struct mtx sc_mtx;
+ int num_funcs;
+
+ unsigned int flags;
+ uint8_t cccr_rev;
+ uint8_t sdio_rev;
+
+ uint16_t vendor_id;
+ uint16_t product_id;
+};
+
+/* bus entry points */
+static int sdio_probe(device_t dev);
+static int sdio_attach(device_t dev);
+static int sdio_detach(device_t dev);
+
+/*
+ * Per-function data
+ */
+struct sdio_ivars {
+ u_int fn; /* Function number */
+ u_short blk_sz;
+};
+
+
+#define SDIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define SDIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define SDIO_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
+ "sdio", MTX_DEF)
+#define SDIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+#define SDIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define SDIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+
+/**
+ * sdio_rw_direct - reads or writes a single byte
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * LOCKING:
+ * The driver context must be locked before calling this function. Internally
+ * the function sleeps, releasing the lock as it does so, however the lock is
+ * always retaken before this function returns.
+ *
+ * RETURNS:
+ * 0 if the event(s) were tripped within timeout period
+ * -EBUSY if timedout waiting for the events
+ * -ENXIO if a NACK event was received
+ */
+int
+sdio_rw_direct(struct sdio_softc *sc, int wr, uint32_t fn, uint32_t adr,
+ uint8_t *data)
+{
+ device_t dev = sc->dev;
+ struct mmc_request req;
+ struct mmc_command cmd;
+
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ req.cmd = &cmd;
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = SD_IO_RW_FUNC(fn) | SD_IO_RW_ADR(adr);
+ if (wr)
+ cmd.arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+ cmd.data = NULL;
+
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, &req);
+ if (req.cmd->error != MMC_ERR_NONE)
+ return (req.cmd->error);
+
+ if (cmd.resp[0] & R5_COM_CRC_ERROR)
+ return (MMC_ERR_BADCRC);
+ if (cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
+ return (MMC_ERR_INVALID);
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return (MMC_ERR_FAILED);
+
+ *data = (uint8_t) (cmd.resp[0] & 0xff);
+ return (MMC_ERR_NONE);
+}
+
+
+/**
+ * sdio_rw_extended - reads or writes multiple bytes
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+int
+sdio_rw_extended(struct sdio_softc *sc, int wr, int incr, uint32_t fn,
+ uint32_t adr, uint8_t *datap, size_t datalen, unsigned blks)
+{
+ device_t dev = sc->dev;
+ struct mmc_request req;
+ struct mmc_command cmd;
+ struct mmc_data data;
+
+#if 0
+ /* TODO: block mode not yet supported */
+ if (len > 512) {
+ device_printf(sc->dev, "warning - block mode not yet supported\n");
+ return MMC_ERR_MAX;
+ }
+#endif
+
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
+ req.cmd = &cmd;
+
+ cmd.opcode = SD_IO_RW_EXTENDED;
+ cmd.arg = SD_IO_RW_FUNC(fn);
+ cmd.arg |= SD_IO_RW_ADR(adr);
+ if (blks)
+ cmd.arg |= SD_IOE_RW_BLK | SD_IOE_RW_LEN(blks);
+ else
+ cmd.arg |= SD_IOE_RW_LEN(datalen);
+ if (wr)
+ cmd.arg |= SD_IO_RW_WR;
+ if (incr)
+ cmd.arg |= SD_IO_RW_INCR;
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+ cmd.data = &data;
+
+ data.data = datap;
+ data.len = datalen;
+ data.flags = wr ? MMC_DATA_WRITE : MMC_DATA_READ;
+
+ // printf("[BRG] sdio_rw_extended : cmd.opcode = 0x%08x : cmd.arg = 0x%08x : data.len = %u\n", cmd.opcode, cmd.arg, data.len);
+//sc->flags |= SDIO_FLAG_SUPPORT_MULTI_BLOCK
+
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, &req);
+ if (req.cmd->error != MMC_ERR_NONE)
+ return (req.cmd->error);
+
+ return (MMC_ERR_NONE);
+}
+
+
+/**
+ * sdio_rw_extended_ex - reads or writes multiple bytes
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_rw_extended_ex(struct sdio_softc *sc, int wr, int incr,
+ struct sdio_ivars *ivar, uint32_t adr, uint8_t *datap, size_t len)
+{
+ int ret;
+ size_t nblks;
+ size_t nbytes;
+
+ /* If the device supports block transfers and the size is larger than
+ * the block size then do the intial transfer as a block.
+ */
+ if ((sc->flags & SDIO_FLAG_SUPPORT_MULTI_BLOCK) &&
+ (ivar->blk_sz > 0) && (len > ivar->blk_sz)) {
+
+ while (len > ivar->blk_sz) {
+
+ /* The maximum number of blocks that can be transferred according
+ * to the spec if 511.
+ */
+ nblks = (len / ivar->blk_sz);
+ if (nblks > 511)
+ nblks = 511;
+
+ nbytes = (nblks * ivar->blk_sz);
+
+ /* Write the data as blocks */
+ ret = sdio_rw_extended(sc, wr, incr, ivar->fn, adr, datap, nbytes, nblks);
+ if (ret != 0)
+ return (ret);
+
+ len -= nbytes;
+ datap += nbytes;
+ if (incr)
+ adr += nbytes;
+ }
+ }
+
+ /* If there is any data remaining use standard byte transfers */
+ while (len > 0) {
+
+ nbytes = min(len, 512);
+
+ /* Do the write in byte mode */
+ ret = sdio_rw_extended(sc, wr, incr, ivar->fn, adr, datap, nbytes, 0);
+ if (ret != 0)
+ return (ret);
+
+ len -= nbytes;
+ datap += nbytes;
+ if (incr)
+ adr += nbytes;
+ }
+
+ return 0;
+}
+
+
+/**
+ * sdio_readb - reads a single byte
+ * sdio_reads - reads a 16-bit value
+ * sdio_readl - reads a 32-bit value
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_f0_readb(device_t brdev, u_int reg, uint8_t *val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ return sdio_rw_direct(sc, 0, 0, reg, val);
+}
+
+static int
+sdio_readb(device_t brdev, device_t reqdev, u_int reg, uint8_t *val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_direct(sc, 0, ivar->fn, reg, val);
+}
+
+static int
+sdio_reads(device_t brdev, device_t reqdev, u_int reg, uint16_t *val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended(sc, 0, 1, ivar->fn, reg, (uint8_t*)val, 2, 0);
+}
+
+static int
+sdio_readl(device_t brdev, device_t reqdev, u_int reg, uint32_t *val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended(sc, 0, 1, ivar->fn, reg, (uint8_t*)val, 4, 0);
+}
+
+
+/**
+ * sdio_writeb - writes a single byte
+ * sdio_writes - writes a 16-bit value
+ * sdio_writel - writes a 32-bit value
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_f0_writeb(device_t brdev, u_int reg, uint8_t val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ return sdio_rw_direct(sc, 1, 0, reg, &val);
+}
+
+static int
+sdio_writeb(device_t brdev, device_t reqdev, u_int reg, uint8_t val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_direct(sc, 1, ivar->fn, reg, &val);
+}
+
+static int
+sdio_writes(device_t brdev, device_t reqdev, u_int reg, uint16_t val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended(sc, 1, 1, ivar->fn, reg, (uint8_t*)&val, 2, 0);
+}
+
+static int
+sdio_writel(device_t brdev, device_t reqdev, u_int reg, uint32_t val)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended(sc, 1, 1, ivar->fn, reg, (uint8_t*)&val, 4, 0);
+}
+
+
+
+/**
+ * sdio_memcpy_from - writes a single byte
+ * sdio_memcpy_to - writes a 16-bit value
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_memcpy_from(device_t brdev, device_t reqdev, u_int reg, u_char *datap,
+ size_t len)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended_ex(sc, 0, 1, ivar, reg, datap, len);
+}
+
+static int
+sdio_memcpy_to(device_t brdev, device_t reqdev, u_int reg, u_char *datap,
+ size_t len)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended_ex(sc, 1, 1, ivar, reg, datap, len);
+}
+
+/**
+ * sdio_stream_from - writes a single byte
+ * sdio_stream_to - writes a 16-bit value
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_stream_from(device_t brdev, device_t reqdev, u_int reg, u_char *datap,
+ size_t len)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended_ex(sc, 0, 0, ivar, reg, datap, len);
+}
+
+static int
+sdio_stream_to(device_t brdev, device_t reqdev, u_int reg, u_char *datap,
+ size_t len)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+
+ return sdio_rw_extended_ex(sc, 1, 0, ivar, reg, datap, len);
+}
+
+
+/**
+ * sdio_set_blk_sz - sets the block size for the function
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_set_blk_sz(device_t brdev, device_t reqdev, uint16_t size)
+{
+ struct sdio_softc *sc = device_get_softc(brdev);
+ struct sdio_ivars *ivar = device_get_ivars(reqdev);
+ int err;
+ uint32_t cccr_adr;
+ uint8_t data[2];
+
+ /* block size is 16-bit in little endian order */
+ data[0] = (size & 0xff);
+ data[1] = ((size >> 8) & 0xff);
+
+ cccr_adr = (ivar->fn * 0x100) + SD_IO_CCCR_FN0_BLKSIZ;
+
+ err = sdio_rw_direct(sc, 1, 0, cccr_adr, &data[0]);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ err = sdio_rw_direct(sc, 1, 0, cccr_adr + 1, &data[1]);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ ivar->blk_sz = size;
+ return 0;
+}
+
+static int
+mmc_io_dump_cccr(struct sdio_softc *sc)
+{
+ uint8_t buf[1024];
+ uint8_t *cccr;
+ int err;
+ unsigned int i, j;
+
+
+ cccr = (uint8_t*) (((unsigned int)&buf[0] + 512) & ~0xff);
+
+ for (j = 0; j < 7; j++) {
+ err = sdio_rw_extended(sc, 0, 1, 0, (0x100 * j), cccr, 512, 0);
+ if (err == MMC_ERR_NONE) {
+ for (i = 0; i < 32; i++) {
+ printf("[CCCR] 0x%02x : %d%d%d%d%d%d%d%d\n", ((0x100 * j) + i),
+ (cccr[i] >> 7) & 0x1,
+ (cccr[i] >> 6) & 0x1,
+ (cccr[i] >> 5) & 0x1,
+ (cccr[i] >> 4) & 0x1,
+ (cccr[i] >> 3) & 0x1,
+ (cccr[i] >> 2) & 0x1,
+ (cccr[i] >> 1) & 0x1,
+ (cccr[i] >> 0) & 0x1);
+ }
+ }
+ }
+
+ return (err);
+}
+
+
+
+/**
+ * sdio_function_disable - enables a given function
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ * Scans the SDIO card and gets the functions ...
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_function_disable(device_t dev, int fn)
+{
+ struct sdio_softc *sc = device_get_softc(dev);
+ int err;
+ uint8_t ioe;
+
+ err = sdio_rw_direct(sc, 0, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ ioe &= ~(0x1 << fn);
+
+ err = sdio_rw_direct(sc, 1, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ return (0);
+}
+
+/**
+ * sdio_function_enable - enables a given function
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ * Scans the SDIO card and gets the functions ...
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_function_enable(device_t dev, int fn, unsigned long timeout_ms)
+{
+ int err;
+ struct sdio_softc *sc = device_get_softc(dev);
+ unsigned long elapsed_ms;
+ unsigned int ticks_10ms;
+ uint8_t ioe;
+ uint8_t ior;
+
+device_printf(dev, "sdio_function_enable : fn %d : timeout_ms = %lu\n", fn, timeout_ms);
+ /* set the enable bit */
+ err = sdio_rw_direct(sc, 0, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ ioe |= (0x1 << fn);
+
+ err = sdio_rw_direct(sc, 1, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ ticks_10ms = (hz / 100);
+ if (!ticks_10ms)
+ ticks_10ms = 1;
+
+ /* poll on the ready bit */
+ elapsed_ms = 0;
+ do {
+ pause("sdio_fn_en", ticks_10ms);
+ elapsed_ms += 10;
+
+ err = sdio_rw_direct(sc, 0, 0, SD_IO_CCCR_FN_IOREADY, &ior);
+device_printf(dev, "sdio_function_enable : err = %d : elapsed_ms = %lu : ior 0x%02x\n", err, elapsed_ms, ior);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ } while (!(ior & (0x1 << fn)) && (elapsed_ms < timeout_ms));
+
+ if (!(ior & (0x1 << fn)))
+ return (MMC_ERR_TIMEOUT);
+
+ return (0);
+}
+
+
+/**
+ * sdio_probe_and_attach_child - loops over the
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ * Scans the SDIO card and gets the functions ...
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_probe_and_attach_child(device_t dev, device_t child, int fn,
+ const struct sdio_cis *cis)
+{
+ int err;
+ struct sdio_softc *sc = device_get_softc(dev);
+ unsigned long timeout_ms;
+
+
+ err = device_probe(child);
+ if (err != 0)
+ goto out;
+
+ timeout_ms = cis->funce.fns.startup_timeout_ms;
+ if (timeout_ms < 100)
+ timeout_ms = 1000;
+
+ if (sdio_function_enable(dev, fn, timeout_ms) == 0 &&
+ device_attach(child) == 0) {
+ device_printf(dev, "function %d \n", fn);
+ return (0);
+ }
+ err = ENXIO;
+
+out:;
+ sdio_function_disable(dev, fn);
+ return err;
+}
+
+
+/**
+ * sdio_scan_funcs - loops over the
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ * Scans the SDIO card and gets the functions ...
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_scan_funcs(struct sdio_softc *sc)
+{
+ device_t dev = sc->dev;
+ int err;
+ int fn;
+ uint8_t val[4];
+ uint32_t cisptr;
+ struct sdio_cis cis;
+ struct sdio_ivars *ivar = NULL;
+ device_t child;
+
+ /* Read the CCCR registers */
+ err = sdio_rw_direct(sc, 0, 0, SD_IO_CCCR_CCCR_SDIO_REV, val);
+ if (err != MMC_ERR_NONE)
+ goto done;
+ sc->cccr_rev = val[0] & 0xf;
+ sc->sdio_rev = (val[0] >> 4) & 0xf;
+
+
+ /* Read the card's capablities */
+ err = sdio_rw_direct(sc, 0, 0, SD_IO_CCCR_CAPABILITY, val);
+ if (err != MMC_ERR_NONE)
+ goto done;
+ if (val[0] & CCCR_CAPS_SMB)
+ sc->flags |= SDIO_FLAG_SUPPORT_MULTI_BLOCK;
+
+
+
+ /* First off read the common CIS area */
+ err = sdio_rw_extended(sc, 0, 1, 0, SD_IO_CCCR_CISPTR, val, 4, 0);
+ if (err != MMC_ERR_NONE)
+ goto done;
+ cisptr = val[0] | ((uint32_t)val[1] << 8) | ((uint32_t)val[2] << 8);
+
+ sdio_cis_read(dev, cisptr, &cis);
+ sdio_cis_print(dev, &cis);
+
+ sc->vendor_id = cis.manufacturer;
+ sc->product_id = cis.product;
+
+
+ /* Next the individual functions */
+ for (fn = 1; fn <= sc->num_funcs; fn++) {
+ /* Read the function basic registers to get the CIS pointer */
+ err = sdio_rw_extended(sc, 0, 1, 0,
+ ((SD_IO_FBR_SIZE * fn) + SD_IO_CCCR_CISPTR), val, 4, 0);
+ if (err != MMC_ERR_NONE)
+ continue;
+
+ cisptr = val[0] | ((uint32_t)val[1] << 8) | ((uint32_t)val[2] << 8);
+
+ sdio_cis_read(dev, cisptr, &cis);
+ sdio_cis_print(dev, &cis);
+
+ ivar = malloc(sizeof(struct sdio_ivars), M_DEVBUF, M_WAITOK | M_ZERO);
+ ivar->fn = fn;
+ ivar->blk_sz = 0;
+
+ /* Add device. */
+ child = device_add_child(dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ sdio_probe_and_attach_child(dev, child, fn, &cis);
+ }
+
+done:
+ return (err);
+}
+
+
+
+static int
+sdio_probe(device_t dev)
+{
+
+// device_quiet(dev);
+ device_set_desc(dev, "SDIO Card");
+ return (0);
+}
+
+/**
+ * sdio_attach - reads or writes multiple bytes
+ * @sc: i2c driver context
+ * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags
+ * @statp: if not null will contain the status flags upon return
+ * @timo: the number of ticks to wait
+ *
+ *
+ *
+ * RETURNS:
+ * MMC_ERR_NONE if successiful
+ */
+static int
+sdio_attach(device_t dev)
+{
+ struct sdio_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ SDIO_LOCK_INIT(sc);
+
+ device_printf(dev, "SDIO Card with %d Functions at %s %dMHz/%dbit\n",
+ mmc_get_io_functions(dev),
+ device_get_nameunit(device_get_parent(dev)),
+ mmc_get_tran_speed(dev) / 1000000,
+ (mmc_get_bus_width(dev) == bus_width_1) ? 1 : 4);
+
+ sc->num_funcs = mmc_get_io_functions(dev);
+
+ sdio_scan_funcs(sc);
+
+ return (0);
+}
+
+static int
+sdio_detach(device_t dev)
+{
+ struct sdio_softc *sc = device_get_softc(dev);
+
+ SDIO_LOCK(sc);
+#if 0
+ sc->suspend = 0;
+ if (sc->running > 0) {
+ /* kill thread */
+ sc->running = 0;
+ wakeup(sc);
+ /* wait for thread to finish. */
+ while (sc->running != -1)
+ msleep(sc, &sc->sc_mtx, 0, "detach", 0);
+ }
+#endif
+ SDIO_UNLOCK(sc);
+
+
+ SDIO_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+
+static int
+sdio_acquire_bus(device_t carddev, device_t reqdev)
+{
+ MMCBUS_ACQUIRE_BUS(device_get_parent(carddev), carddev);
+ return (0);
+}
+
+static int
+sdio_release_bus(device_t carddev, device_t reqdev)
+{
+ MMCBUS_RELEASE_BUS(device_get_parent(carddev), carddev);
+ return (0);
+}
+
+static int
+sdio_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
+{
+ struct sdio_ivars *ivar = device_get_ivars(child);
+ struct sdio_softc *sc = device_get_softc(bus);
+
+ switch (which) {
+ default:
+ return (EINVAL);
+ case SDIO_IVAR_VENDOR:
+ *(unsigned int *)result = sc->vendor_id;
+ break;
+ case SDIO_IVAR_PRODUCT:
+ *(unsigned int *)result = sc->product_id;
+ break;
+ case SDIO_IVAR_FUNCTION_NUMBER:
+ *(unsigned int *)result = ivar->fn;
+ break;
+ case SDIO_IVAR_BLOCK_SIZE:
+ *(unsigned int *)result = ivar->blk_sz;
+ break;
+ }
+ return (0);
+}
+
+static int
+sdio_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+{
+ /*
+ * None are writable ATM
+ */
+ return (EINVAL);
+}
+
+
+
+static device_method_t sdio_methods[] = {
+ DEVMETHOD(device_probe, sdio_probe),
+ DEVMETHOD(device_attach, sdio_attach),
+ DEVMETHOD(device_detach, sdio_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdio_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdio_write_ivar),
+
+ /* SDIO interface */
+ DEVMETHOD(sdio_acquire_card, sdio_acquire_bus),
+ DEVMETHOD(sdio_release_card, sdio_release_bus),
+ DEVMETHOD(sdio_set_block_size, sdio_set_blk_sz),
+
+ DEVMETHOD(sdio_func0_read_1, sdio_f0_readb),
+ DEVMETHOD(sdio_func0_write_1, sdio_f0_writeb),
+ DEVMETHOD(sdio_read_1, sdio_readb),
+ DEVMETHOD(sdio_read_2, sdio_reads),
+ DEVMETHOD(sdio_read_4, sdio_readl),
+ DEVMETHOD(sdio_write_1, sdio_writeb),
+ DEVMETHOD(sdio_write_2, sdio_writes),
+ DEVMETHOD(sdio_write_4, sdio_writel),
+ DEVMETHOD(sdio_read_multi, sdio_memcpy_from),
+ DEVMETHOD(sdio_read_stream_multi, sdio_stream_from),
+ DEVMETHOD(sdio_write_multi, sdio_memcpy_to),
+ DEVMETHOD(sdio_write_stream_multi, sdio_stream_to),
+
+// DEVMETHOD(sdio_setup_intr, mmc_sdio_setup_intr),
+// DEVMETHOD(sdio_teardown_intr, mmc_sdio_teardown_intr),
+
+ {0, 0},
+};
+
+static driver_t sdio_driver = {
+ "sdio",
+ sdio_methods,
+ sizeof(struct sdio_softc),
+};
+static devclass_t sdio_devclass;
+
+DRIVER_MODULE(sdio, mmc, sdio_driver, sdio_devclass, 0, 0);
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ sdio.h 2012-06-01 17:28:40.000000000 +0100
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2011 Ben Gray. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * "$FreeBSD$"
+ */
+
+#ifndef DEV_MMC_SDIO_H
+#define DEV_MMC_SDIO_H
+
+
+struct sdio_softc;
+
+
+int
+sdio_rw_direct(struct sdio_softc *sc, int wr, uint32_t fn, uint32_t adr,
+ uint8_t *data);
+int
+sdio_rw_extended(struct sdio_softc *sc, int wr, int incr, uint32_t fn,
+ uint32_t adr, uint8_t *datap, size_t len, unsigned blks);
+
+
+
+
+/*
+ * Decoded PC Card 16 based Card Information Structure (CIS),
+ * per card (function 0) and per function (1 and greater).
+ */
+struct sdio_cis {
+ uint16_t manufacturer;
+#define SDIO_VENDOR_INVALID 0xffff
+ uint16_t product;
+#define SDIO_PRODUCT_INVALID 0xffff
+
+ u_char cis1_major;
+ u_char cis1_minor;
+
+ union {
+ struct {
+ uint16_t blk_sz;
+ uint32_t max_spd;
+
+ } fn0;
+
+ struct {
+ uint32_t serial_num;
+ uint16_t max_blk_sz;
+ uint32_t startup_timeout_ms;
+
+ uint32_t op_min_pwr, op_avg_pwr, op_max_pwr;
+ uint32_t sb_min_pwr, sb_avg_pwr, sb_max_pwr;
+ uint32_t hp_avg_pwr, hp_max_pwr;
+ uint32_t lp_avg_pwr, lp_max_pwr;
+ } fns;
+ } funce;
+};
+
+int
+sdio_cis_read(device_t dev, uint32_t cisptr, struct sdio_cis *cis);
+void
+sdio_cis_print(device_t dev, struct sdio_cis *cis);
+
+
+
+#endif
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ mmcioreg.h 2012-01-16 11:48:36.000000000 +0000
@@ -0,0 +1,136 @@
+/* $NetBSD: sdmmc_ioreg.h,v 1.2 2010/10/07 12:40:34 kiyohara Exp $ */
+/* $OpenBSD: sdmmc_ioreg.h,v 1.4 2007/06/02 01:48:37 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DEV_MMCIOREG_H
+#define DEV_MMCIOREG_H
+
+/* SDIO commands */ /* response type */
+#define SD_IO_SEND_OP_COND 5 /* R4 */
+#define SD_IO_RW_DIRECT 52 /* R5 */
+#define SD_IO_RW_EXTENDED 53 /* R5? */
+
+/* CMD52 arguments */
+#define SD_ARG_CMD52_READ (0<<31)
+#define SD_ARG_CMD52_WRITE (1<<31)
+#define SD_ARG_CMD52_FUNC_SHIFT 28
+#define SD_ARG_CMD52_FUNC_MASK 0x7
+#define SD_ARG_CMD52_EXCHANGE (1<<27)
+#define SD_ARG_CMD52_REG_SHIFT 9
+#define SD_ARG_CMD52_REG_MASK 0x1ffff
+#define SD_ARG_CMD52_DATA_SHIFT 0
+#define SD_ARG_CMD52_DATA_MASK 0xff
+#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
+
+/* CMD53 arguments */
+#define SD_ARG_CMD53_READ (0<<31)
+#define SD_ARG_CMD53_WRITE (1<<31)
+#define SD_ARG_CMD53_FUNC_SHIFT 28
+#define SD_ARG_CMD53_FUNC_MASK 0x7
+#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
+#define SD_ARG_CMD53_INCREMENT (1<<26)
+#define SD_ARG_CMD53_REG_SHIFT 9
+#define SD_ARG_CMD53_REG_MASK 0x1ffff
+#define SD_ARG_CMD53_LENGTH_SHIFT 0
+#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
+#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */
+
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R4(resp) ((resp)[0])
+#define MMC_R5(resp) ((resp)[0])
+
+/* SD R4 response (IO OCR) */
+#define SD_IO_OCR_MEM_READY (1<<31)
+#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x7)
+#define SD_IO_OCR_MEM_PRESENT (1<<27)
+#define SD_IO_OCR_MASK 0x00fffff0
+
+/* Card Common Control Registers (CCCR) */
+#define SD_IO_CCCR_START 0x00000
+#define SD_IO_CCCR_SIZE 0x100
+#define SD_IO_CCCR_CCCR_SDIO_REV 0x00
+#define SD_IO_CCCR_CCCR_REV(r) ((r) & 0xf)
+#define CCCR_CCCR_REV_1_00 0
+#define CCCR_CCCR_REV_1_10 1
+#define CCCR_CCCR_REV_1_20 2
+#define SD_IO_CCCR_SDIO_REV(r) (((r) >> 4) & 0xf)
+#define CCCR_SDIO_REV_1_00 0
+#define CCCR_SDIO_REV_1_10 1
+#define CCCR_SDIO_REV_1_20 2 /* (unreleased) */
+#define CCCR_SDIO_REV_2_00 3
+#define SD_IO_CCCR_SPEC_REV 0x01
+#define SD_IO_CCCR_SD_PHYS_SPEC_VER(r) ((r) & 0xf)
+#define CCCR_SD_PHYS_SPEC_VER_1_01 0
+#define CCCR_SD_PHYS_SPEC_VER_1_10 1
+#define CCCR_SD_PHYS_SPEC_VER_2_00 2
+#define SD_IO_CCCR_FN_ENABLE 0x02
+#define SD_IO_CCCR_FN_IOREADY 0x03
+#define SD_IO_CCCR_FN_INTEN 0x04
+#define CCCR_INTEN_INTM (1<<0)
+#define SD_IO_CCCR_FN_INTPENDING 0x05
+#define SD_IO_CCCR_CTL 0x06
+#define CCCR_CTL_RES (1<<3)
+#define SD_IO_CCCR_BUS_WIDTH 0x07
+#define CCCR_BUS_WIDTH_4 (2<<0)
+#define CCCR_BUS_WIDTH_1 (0<<0)
+#define CCCR_BUS_WIDTH_MASK (3<<0)
+#define SD_IO_CCCR_CAPABILITY 0x08
+#define CCCR_CAPS_SDC (1<<0)
+#define CCCR_CAPS_SMB (1<<1) /* Multi-Block support */
+#define CCCR_CAPS_SRB (1<<2) /* Read Wait support */
+#define CCCR_CAPS_SBS (1<<3) /* Suspend/Resume support */
+#define CCCR_CAPS_S4MI (1<<4) /* intr support in 4-bit mode */
+#define CCCR_CAPS_E4MI (1<<5) /* enable intr in 4-bit mode */
+#define CCCR_CAPS_LSC (1<<6) /* Low speed card */
+#define CCCR_CAPS_4BLS (1<<7) /* 4-bit support for low speed */
+#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */
+#define SD_IO_CCCR_BUS_SUSPEND 0x0c
+#define SD_IO_CCCR_FUNC_SELECT 0x0d
+#define CCCR_FUNC_FS(r) ((r) & 0xf)
+#define CCCR_FUNC_FS_FN(fn) ((fn) & 0x7)
+#define CCCR_FUNC_FS_MEM 8
+#define SD_IO_CCCR_FN_EXEC_FLG 0x0e
+#define SD_IO_CCCR_FN_READY_FLG 0x0f
+#define SD_IO_CCCR_FN0_BLKSIZ 0x10 /* 0x10-0x11 */
+#define SD_IO_CCCR_POWER_CTL 0x12
+#define SD_IO_CCCR_HIGH_SPEED 0x13
+#define CCCR_HIGH_SPEED_SHS (1<<0) /* Support High-Speed */
+#define CCCR_HIGH_SPEED_EHS (1<<1) /* Enable High-Speed */
+
+/* Function Basic Registers (FBR) */
+#define SD_IO_FBR_START 0x00100
+#define SD_IO_FBR_SIZE 0x100
+#define SD_IO_FBR(func) ((((func) - 1) * SD_IO_FBR_SIZE) + SD_IO_FBR_START)
+#define FBR_STD_FUNC_IF_CODE(v) ((v) & 0x0f)
+
+/* Card Information Structure (CIS) */
+#define SD_IO_CIS_START 0x01000
+#define SD_IO_CIS_SIZE 0x17000
+
+/* SDIO Standard Function Interface code */
+#define SD_IO_SFIC_NO_STANDARD 0x0
+#define SD_IO_SFIC_UART 0x1
+#define SD_IO_SFIC_TYPEA_BLUETOOTH 0x2 /* Type-A Bluetooth */
+#define SD_IO_SFIC_TYPEB_BLUETOOTH 0x3 /* Type-B Bluetooth */
+#define SD_IO_SFIC_GPS 0x4
+#define SD_IO_SFIC_CAMERA 0x5
+#define SD_IO_SFIC_PHS 0x6
+#define SD_IO_SFIC_WLAN 0x7
+#define SD_IO_SFIC_ATA 0x8 /* Embedded SDIO-ATA */
+
+#endif /* DEV_MMCREG_H */
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ sdiovar.h 2012-06-01 17:28:29.000000000 +0100
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2011 Ben Gray. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * "$FreeBSD$"
+ */
+
+#ifndef DEV_MMC_SDIOVAR_H
+#define DEV_MMC_SDIOVAR_H
+
+#include <dev/mmc/bridge.h>
+
+enum sdio_card_ivars {
+ SDIO_IVAR_VENDOR,
+ SDIO_IVAR_PRODUCT,
+ SDIO_IVAR_FUNCTION_NUMBER,
+ SDIO_IVAR_BLOCK_SIZE,
+// SDIO_IVAR_,
+};
+
+/*
+ * Simplified accessors for pci devices
+ */
+#define SDIO_ACCESSOR(var, ivar, type) \
+ __BUS_ACCESSOR(sdio, var, SDIO, ivar, type)
+
+SDIO_ACCESSOR(vendor, VENDOR, unsigned int)
+SDIO_ACCESSOR(product, PRODUCT, unsigned int)
+SDIO_ACCESSOR(function_number, FUNCTION_NUMBER, unsigned int)
+SDIO_ACCESSOR(block_size, BLOCK_SIZE, unsigned int)
+
+
+/*
+ * Convenience functions
+ */
+#include "sdio_if.h"
+
+static inline int
+sdio_read_1(device_t dev, uint32_t offset, uint8_t *val)
+{
+ return (SDIO_READ_MULTI(device_get_parent(dev), dev, offset, val, 1));
+}
+
+static inline int
+sdio_write_1(device_t dev, uint32_t offset, uint8_t val)
+{
+ return (SDIO_WRITE_MULTI(device_get_parent(dev), dev, offset, &val, 1));
+}
+
+static inline int
+sdio_read_2(device_t dev, uint32_t offset, uint16_t *val)
+{
+ return (SDIO_READ_MULTI(device_get_parent(dev), dev, offset, (uint8_t*)val, 2));
+}
+
+static inline int
+sdio_write_2(device_t dev, uint32_t offset, uint16_t val)
+{
+ return (SDIO_WRITE_MULTI(device_get_parent(dev), dev, offset, (uint8_t*)&val, 2));
+}
+
+static inline int
+sdio_read_4(device_t dev, uint32_t offset, uint32_t *val)
+{
+ return (SDIO_READ_MULTI(device_get_parent(dev), dev, offset, (uint8_t*)val, 4));
+}
+
+static inline int
+sdio_write_4(device_t dev, uint32_t offset, uint32_t val)
+{
+ return (SDIO_WRITE_MULTI(device_get_parent(dev), dev, offset, (uint8_t*)&val, 4));
+}
+
+
+static inline int
+sdio_func0_read_1(device_t dev, uint32_t offset, uint8_t *val)
+{
+ return (SDIO_FUNC0_READ_1(device_get_parent(dev), offset, val));
+}
+
+static inline int
+sdio_func0_write_1(device_t dev, uint32_t offset, uint8_t val)
+{
+ return (SDIO_FUNC0_WRITE_1(device_get_parent(dev), offset, val));
+}
+
+
+
+
+#endif /* DEV_MMC_MMCVAR_H */
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ sdio_if.m 2012-05-07 22:27:32.000000000 +0100
@@ -0,0 +1,232 @@
+#-
+# Copyright (c) 2012 Ben Gray
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Portions of this software may have been developed with reference to
+# the SD Simplified Specification. The following disclaimer may apply:
+#
+# The following conditions apply to the release of the simplified
+# specification ("Simplified Specification") by the SD Card Association and
+# the SD Group. The Simplified Specification is a subset of the complete SD
+# Specification which is owned by the SD Card Association and the SD
+# Group. This Simplified Specification is provided on a non-confidential
+# basis subject to the disclaimers below. Any implementation of the
+# Simplified Specification may require a license from the SD Card
+# Association, SD Group, SD-3C LLC or other third parties.
+#
+# Disclaimers:
+#
+# The information contained in the Simplified Specification is presented only
+# as a standard specification for SD Cards and SD Host/Ancillary products and
+# is provided "AS-IS" without any representations or warranties of any
+# kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+# Card Association for any damages, any infringements of patents or other
+# right of the SD Group, SD-3C LLC, the SD Card Association or any third
+# parties, which may result from its use. No license is granted by
+# implication, estoppel or otherwise under any patent or other rights of the
+# SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+# herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+# or the SD Card Association to disclose or distribute any technical
+# information, know-how or other confidential information to any third party.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/bridge.h>
+
+#
+# This is the set of callbacks that sdio card drivers call to make requests.
+#
+
+INTERFACE sdio;
+
+#
+# Claim the current card, blocking the current thread until the host
+# is no longer busy.
+#
+METHOD int acquire_card {
+ device_t carddev;
+ device_t reqdev;
+}
+
+#
+# Release the current card.
+#
+METHOD int release_card {
+ device_t carddev;
+ device_t reqdev;
+}
+
+
+#
+# Release the current card.
+#
+METHOD int set_block_size {
+ device_t carddev;
+ device_t reqdev;
+ uint16_t size;
+}
+
+
+#
+# Read a single byte from a register in function 0 area on the SDIO card.
+#
+METHOD int func0_read_1 {
+ device_t brdev;
+ u_int reg;
+ uint8_t *val;
+};
+
+#
+# Write a single byte to a register in function 0 area on the SDIO card.
+#
+METHOD int func0_write_1 {
+ device_t brdev;
+ u_int reg;
+ uint8_t val;
+};
+
+
+
+
+#
+# Read a single byte from a register on the SDIO card.
+#
+METHOD int read_1 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint8_t *val;
+};
+
+METHOD int read_2 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint16_t *val;
+};
+
+METHOD int read_4 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint32_t *val;
+};
+
+#
+# Write a single byte from a register on the SDIO card.
+#
+METHOD int write_1 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint8_t val;
+};
+
+METHOD int write_2 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint16_t val;
+};
+
+METHOD int write_4 {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ uint32_t val;
+};
+
+
+
+#
+# Read multiple bytes from the SDIO card's registers
+#
+METHOD int read_multi {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ u_char *datap;
+ size_t len;
+};
+
+#
+# Reads one or more bytes from a fifo type device (doesn't increment
+# the address on read)
+#
+METHOD int read_stream_multi {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ u_char *datap;
+ size_t len;
+};
+
+#
+# Write multiple bytes to the SDIO card's registers
+#
+METHOD int write_multi {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ u_char *datap;
+ size_t len;
+};
+
+#
+# Writes one or more bytes into a fifo type device (doesn't increment
+# the address after each write)
+#
+METHOD int write_stream_multi {
+ device_t brdev;
+ device_t reqdev;
+ u_int reg;
+ u_char *datap;
+ size_t len;
+};
+
+
+
+#
+# Activate and install an interrupt handler
+#
+METHOD int setup_intr {
+ device_t brdev;
+ device_t reqdev;
+ driver_intr_t fun;
+};
+
+#
+# Activate and install an interrupt handler
+#
+METHOD int teardown_intr {
+ device_t brdev;
+ device_t reqdev;
+};
+
+
+
+
--- /dev/null 2012-06-01 16:58:34.000000000 +0100
+++ sdio_cis.c 2012-01-26 17:35:24.000000000 +0000
@@ -0,0 +1,271 @@
+/* $FreeBSD$ */
+/* $NetBSD: sdmmc_cis.c,v 1.3 2010/10/07 16:26:37 kiyohara Exp $ */
+/* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <dev/pccard/pccard_cis.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcioreg.h>
+#include <dev/mmc/sdio.h>
+
+
+
+#define SDMMCCISDEBUG
+
+#ifdef SDMMCCISDEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+
+
+static uint8_t
+sdio_cis_readb(device_t dev, uint32_t adr)
+{
+ struct sdio_softc *sc = device_get_softc(dev);
+ uint8_t val;
+
+ if (sdio_rw_direct(sc, 0, 0, adr, &val) != MMC_ERR_NONE) {
+ device_printf(dev, "SDIO read failed!\n");
+ return 0xff;
+ }
+
+ return val;
+}
+
+
+static void
+decode_funce_common(device_t dev, struct sdio_cis *cis, int tpllen,
+ uint32_t reg)
+{
+ static const int speed_val[] =
+ { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
+ static const int speed_unit[] = { 10, 100, 1000, 10000, };
+ uint8_t max_tran_speed;
+
+ if (tpllen < 4) {
+ device_printf(dev, "CISTPL_FUNCE(common) too short\n");
+ return;
+ }
+
+ cis->funce.fn0.blk_sz = sdio_cis_readb(dev, reg++);
+ cis->funce.fn0.blk_sz |= sdio_cis_readb(dev, reg++) << 8;
+ max_tran_speed = sdio_cis_readb(dev, reg++);
+ cis->funce.fn0.max_spd =
+ speed_val[max_tran_speed >> 3] * speed_unit[max_tran_speed & 7];
+
+ DPRINTF(
+ ("CISTPL_FUNCE: FN0_BLK_SIZE=0x%x, MAX_TRAN_SPEED=0x%x(%dkHz)\n",
+ cis->funce.fn0.blk_sz, max_tran_speed, cis->funce.fn0.max_spd));
+}
+
+static void
+decode_funce_function(device_t dev, struct sdio_cis *cis,
+ int tpllen, uint32_t reg)
+{
+ int sdiox_cccrx, sdiox;
+
+ sdiox_cccrx = sdio_cis_readb(dev, SD_IO_CCCR_CCCR_SDIO_REV);
+ sdiox = SD_IO_CCCR_SDIO_REV(sdiox_cccrx);
+
+ if (sdiox == CCCR_SDIO_REV_1_00 && tpllen < 0x1c) {
+ device_printf(dev,
+ "CISTPL_FUNCE(function) too short (v1.00)\n");
+ return;
+ } else if (sdiox != CCCR_SDIO_REV_1_00 && tpllen < 0x2a) {
+ device_printf(dev, "CISTPL_FUNCE(function) too short\n");
+ return;
+ }
+
+ cis->funce.fns.serial_num = sdio_cis_readb(dev, reg + 2);
+ cis->funce.fns.serial_num |= sdio_cis_readb(dev, reg + 3) << 8;
+ cis->funce.fns.serial_num |= sdio_cis_readb(dev, reg + 4) << 16;
+ cis->funce.fns.serial_num |= sdio_cis_readb(dev, reg + 5) << 24;
+
+ cis->funce.fns.max_blk_sz = sdio_cis_readb(dev, reg + 11);
+ cis->funce.fns.max_blk_sz |= sdio_cis_readb(dev, reg + 12) << 8;
+
+ cis->funce.fns.startup_timeout_ms |= sdio_cis_readb(dev, reg + 27);
+ cis->funce.fns.startup_timeout_ms |= sdio_cis_readb(dev, reg + 28) << 8;
+ cis->funce.fns.startup_timeout_ms *= 10;
+
+ cis->funce.fns.op_min_pwr = sdio_cis_readb(dev, reg + 17);
+ cis->funce.fns.op_avg_pwr = sdio_cis_readb(dev, reg + 18);
+ cis->funce.fns.op_max_pwr = sdio_cis_readb(dev, reg + 19);
+ cis->funce.fns.sb_min_pwr = sdio_cis_readb(dev, reg + 20);
+ cis->funce.fns.sb_avg_pwr = sdio_cis_readb(dev, reg + 21);
+ cis->funce.fns.sb_max_pwr = sdio_cis_readb(dev, reg + 22);
+
+ cis->funce.fns.hp_avg_pwr = sdio_cis_readb(dev, reg + 33);
+ cis->funce.fns.hp_max_pwr |= sdio_cis_readb(dev, reg + 34) << 8;
+ cis->funce.fns.lp_avg_pwr = sdio_cis_readb(dev, reg + 35);
+ cis->funce.fns.lp_max_pwr |= sdio_cis_readb(dev, reg + 36) << 8;
+
+ DPRINTF(("CISTPL_FUNCE: MAX_BLK_SIZE=0x%x\n", cis->funce.fns.max_blk_sz));
+}
+
+static void
+decode_vers_1(device_t dev, struct sdio_cis *cis, int tpllen,
+ uint32_t reg)
+{
+ if (tpllen < 2) {
+ device_printf(dev, "CISTPL_VERS_1 too short\n");
+ return;
+ }
+
+ cis->cis1_major = sdio_cis_readb(dev, reg++);
+ cis->cis1_minor = sdio_cis_readb(dev, reg++);
+
+ DPRINTF(("CISTPL_VERS_1\n"));
+}
+
+int
+sdio_cis_read(device_t dev, uint32_t cisptr, struct sdio_cis *cis)
+{
+ uint32_t reg;
+ uint8_t tplcode, tpllen;
+
+ memset(cis, 0, sizeof(struct sdio_cis));
+ cis->manufacturer = SDIO_VENDOR_INVALID;
+ cis->product = SDIO_PRODUCT_INVALID;
+
+ reg = cisptr;
+ if ((reg < SD_IO_CIS_START) ||
+ (reg >= (SD_IO_CIS_START + SD_IO_CIS_SIZE - 16))) {
+ device_printf(dev, "bad CIS ptr %#x\n", reg);
+ return 1;
+ }
+
+ DPRINTF(("READING CISTPL AT 0x%06x\n", reg));
+
+ for (;;) {
+ tplcode = sdio_cis_readb(dev, reg++);
+
+ if (tplcode == CISTPL_NULL) {
+ DPRINTF((" 00\nCISTPL_NONE\n"));
+ continue;
+ }
+
+ tpllen = sdio_cis_readb(dev, reg++);
+ if (tplcode == CISTPL_END || tpllen == 0) {
+ if (tplcode != 0xff)
+ device_printf(dev, "CIS parse error at %d, "
+ "tuple code %#x, length %d\n",
+ reg, tplcode, tpllen);
+ else {
+ DPRINTF((" ff\nCISTPL_END\n"));
+ }
+ break;
+ }
+
+#ifdef SDMMCCISDEBUG
+ {
+ int i;
+
+ /* print the tuple */
+ DPRINTF((" %02x %02x", tplcode, tpllen));
+
+ for (i = 0; i < tpllen; i++) {
+ DPRINTF((" %02x",
+ sdio_cis_readb(dev, reg + i)));
+ if ((i % 16) == 13)
+ DPRINTF(("\n"));
+ }
+ if ((i % 16) != 14)
+ DPRINTF(("\n"));
+ }
+#endif
+
+ switch (tplcode) {
+ case CISTPL_FUNCE:
+ if (sdio_cis_readb(dev, reg++) == 0)
+ decode_funce_common(dev, cis, tpllen, reg);
+ else
+ decode_funce_function(dev, cis, tpllen, reg);
+ reg += (tpllen - 1);
+ break;
+
+ case CISTPL_FUNCID:
+ if (tpllen < 2) {
+ device_printf(dev,
+ "bad CISTPL_FUNCID length\n");
+ reg += tpllen;
+ break;
+ }
+ DPRINTF(("CISTPL_FUNCID\n"));
+ reg += tpllen;
+ break;
+
+ case CISTPL_MANFID:
+ if (tpllen < 4) {
+ device_printf(dev,
+ "bad CISTPL_MANFID length\n");
+ reg += tpllen;
+ break;
+ }
+ cis->manufacturer = sdio_cis_readb(dev, reg++);
+ cis->manufacturer |= sdio_cis_readb(dev, reg++) << 8;
+ cis->product = sdio_cis_readb(dev, reg++);
+ cis->product |= sdio_cis_readb(dev, reg++) << 8;
+ DPRINTF(("CISTPL_MANFID\n"));
+ break;
+
+ case CISTPL_VERS_1:
+ decode_vers_1(dev, cis, tpllen, reg);
+ reg += tpllen;
+ break;
+
+ default:
+ device_printf(dev,
+ "unknown tuple code %#x, length %d\n",
+ tplcode, tpllen);
+ reg += tpllen;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void
+sdio_cis_print(device_t dev, struct sdio_cis *cis)
+{
+ device_printf(dev, "CIS version %u.%u\n", cis->cis1_major,
+ cis->cis1_minor);
+
+ device_printf(dev, "Manufacturer code 0x%x, product 0x%x\n",
+ cis->manufacturer, cis->product);
+
+ printf("\n");
+}
+
+
More information about the freebsd-arm
mailing list