Home | History | Annotate | Line # | Download | only in sdmmc
sdmmc_cis.c revision 1.1
      1  1.1  nonaka /*	$NetBSD: sdmmc_cis.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $	*/
      2  1.1  nonaka /*	$OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $	*/
      3  1.1  nonaka 
      4  1.1  nonaka /*
      5  1.1  nonaka  * Copyright (c) 2006 Uwe Stuehler <uwe (at) openbsd.org>
      6  1.1  nonaka  *
      7  1.1  nonaka  * Permission to use, copy, modify, and distribute this software for any
      8  1.1  nonaka  * purpose with or without fee is hereby granted, provided that the above
      9  1.1  nonaka  * copyright notice and this permission notice appear in all copies.
     10  1.1  nonaka  *
     11  1.1  nonaka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  1.1  nonaka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  1.1  nonaka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  1.1  nonaka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  1.1  nonaka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  1.1  nonaka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  1.1  nonaka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  1.1  nonaka  */
     19  1.1  nonaka 
     20  1.1  nonaka /* Routines to decode the Card Information Structure of SD I/O cards */
     21  1.1  nonaka 
     22  1.1  nonaka #include <sys/cdefs.h>
     23  1.1  nonaka __KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
     24  1.1  nonaka 
     25  1.1  nonaka #include <sys/param.h>
     26  1.1  nonaka #include <sys/systm.h>
     27  1.1  nonaka 
     28  1.1  nonaka #include <dev/sdmmc/sdmmc_ioreg.h>
     29  1.1  nonaka #include <dev/sdmmc/sdmmcdevs.h>
     30  1.1  nonaka #include <dev/sdmmc/sdmmcvar.h>
     31  1.1  nonaka 
     32  1.1  nonaka #ifdef SDMMC_DEBUG
     33  1.1  nonaka #define DPRINTF(s)	printf s
     34  1.1  nonaka #else
     35  1.1  nonaka #define DPRINTF(s)	/**/
     36  1.1  nonaka #endif
     37  1.1  nonaka 
     38  1.1  nonaka static uint32_t sdmmc_cisptr(struct sdmmc_function *);
     39  1.1  nonaka static uint32_t
     40  1.1  nonaka sdmmc_cisptr(struct sdmmc_function *sf)
     41  1.1  nonaka {
     42  1.1  nonaka 	uint32_t cisptr = 0;
     43  1.1  nonaka 
     44  1.1  nonaka 	/* XXX where is the per-function CIS pointer register? */
     45  1.1  nonaka 	if (sf->number != 0)
     46  1.1  nonaka 		return SD_IO_CIS_START;
     47  1.1  nonaka 
     48  1.1  nonaka 	/* XXX is the CIS pointer stored in little-endian format? */
     49  1.1  nonaka 	cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 0) << 0;
     50  1.1  nonaka 	cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 1) << 8;
     51  1.1  nonaka 	cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 2) << 16;
     52  1.1  nonaka 	return cisptr;
     53  1.1  nonaka }
     54  1.1  nonaka 
     55  1.1  nonaka int
     56  1.1  nonaka sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis)
     57  1.1  nonaka {
     58  1.1  nonaka 	device_t dev = sf->sc->sc_dev;
     59  1.1  nonaka 	uint32_t reg;
     60  1.1  nonaka 	uint8_t tplcode;
     61  1.1  nonaka 	uint8_t tpllen;
     62  1.1  nonaka 	int start, ch, count;
     63  1.1  nonaka 	int i;
     64  1.1  nonaka 
     65  1.1  nonaka 	memset(cis, 0, sizeof *cis);
     66  1.1  nonaka 
     67  1.1  nonaka 	/* XXX read per-function CIS */
     68  1.1  nonaka 	if (sf->number != 0)
     69  1.1  nonaka 		return 1;
     70  1.1  nonaka 
     71  1.1  nonaka 	reg = sdmmc_cisptr(sf);
     72  1.1  nonaka 	if (reg < SD_IO_CIS_START ||
     73  1.1  nonaka 	    reg >= (SD_IO_CIS_START + SD_IO_CIS_SIZE - 16)) {
     74  1.1  nonaka 		aprint_error_dev(dev, "bad CIS ptr %#x\n", reg);
     75  1.1  nonaka 		return 1;
     76  1.1  nonaka 	}
     77  1.1  nonaka 
     78  1.1  nonaka 	for (;;) {
     79  1.1  nonaka 		tplcode = sdmmc_io_read_1(sf, reg++);
     80  1.1  nonaka 		tpllen = sdmmc_io_read_1(sf, reg++);
     81  1.1  nonaka 
     82  1.1  nonaka 		if (tplcode == 0xff || tpllen == 0) {
     83  1.1  nonaka 			if (tplcode != 0xff)
     84  1.1  nonaka 				aprint_error_dev(dev, "CIS parse error at %d, "
     85  1.1  nonaka 				    "tuple code %#x, length %d\n",
     86  1.1  nonaka 				    reg, tplcode, tpllen);
     87  1.1  nonaka 			break;
     88  1.1  nonaka 		}
     89  1.1  nonaka 
     90  1.1  nonaka 		switch (tplcode) {
     91  1.1  nonaka 		case SD_IO_CISTPL_FUNCID:
     92  1.1  nonaka 			if (tpllen < 2) {
     93  1.1  nonaka 				aprint_error_dev(dev,
     94  1.1  nonaka 				    "bad CISTPL_FUNCID length\n");
     95  1.1  nonaka 				reg += tpllen;
     96  1.1  nonaka 				break;
     97  1.1  nonaka 			}
     98  1.1  nonaka 			cis->function = sdmmc_io_read_1(sf, reg);
     99  1.1  nonaka 			reg += tpllen;
    100  1.1  nonaka 			break;
    101  1.1  nonaka 
    102  1.1  nonaka 		case SD_IO_CISTPL_MANFID:
    103  1.1  nonaka 			if (tpllen < 4) {
    104  1.1  nonaka 				aprint_error_dev(dev,
    105  1.1  nonaka 				    "bad CISTPL_MANFID length\n");
    106  1.1  nonaka 				reg += tpllen;
    107  1.1  nonaka 				break;
    108  1.1  nonaka 			}
    109  1.1  nonaka 			cis->manufacturer = sdmmc_io_read_1(sf, reg++);
    110  1.1  nonaka 			cis->manufacturer |= sdmmc_io_read_1(sf, reg++) << 8;
    111  1.1  nonaka 			cis->product = sdmmc_io_read_1(sf, reg++);
    112  1.1  nonaka 			cis->product |= sdmmc_io_read_1(sf, reg++) << 8;
    113  1.1  nonaka 			break;
    114  1.1  nonaka 
    115  1.1  nonaka 		case SD_IO_CISTPL_VERS_1:
    116  1.1  nonaka 			if (tpllen < 2) {
    117  1.1  nonaka 				aprint_error_dev(dev,
    118  1.1  nonaka 				    "CISTPL_VERS_1 too short\n");
    119  1.1  nonaka 				reg += tpllen;
    120  1.1  nonaka 				break;
    121  1.1  nonaka 			}
    122  1.1  nonaka 
    123  1.1  nonaka 			cis->cis1_major = sdmmc_io_read_1(sf, reg++);
    124  1.1  nonaka 			cis->cis1_minor = sdmmc_io_read_1(sf, reg++);
    125  1.1  nonaka 
    126  1.1  nonaka 			for (count = 0, start = 0, i = 0;
    127  1.1  nonaka 			     (count < 4) && ((i + 4) < 256); i++) {
    128  1.1  nonaka 				ch = sdmmc_io_read_1(sf, reg + i);
    129  1.1  nonaka 				if (ch == 0xff)
    130  1.1  nonaka 					break;
    131  1.1  nonaka 				cis->cis1_info_buf[i] = ch;
    132  1.1  nonaka 				if (ch == 0) {
    133  1.1  nonaka 					cis->cis1_info[count] =
    134  1.1  nonaka 					    cis->cis1_info_buf + start;
    135  1.1  nonaka 					start = i + 1;
    136  1.1  nonaka 					count++;
    137  1.1  nonaka 				}
    138  1.1  nonaka 			}
    139  1.1  nonaka 
    140  1.1  nonaka 			reg += tpllen - 2;
    141  1.1  nonaka 			break;
    142  1.1  nonaka 
    143  1.1  nonaka 		default:
    144  1.1  nonaka 			aprint_error_dev(dev,
    145  1.1  nonaka 			    "unknown tuple code %#x, length %d\n",
    146  1.1  nonaka 			    tplcode, tpllen);
    147  1.1  nonaka 			reg += tpllen;
    148  1.1  nonaka 			break;
    149  1.1  nonaka 		}
    150  1.1  nonaka 	}
    151  1.1  nonaka 
    152  1.1  nonaka 	return 0;
    153  1.1  nonaka }
    154  1.1  nonaka 
    155  1.1  nonaka void
    156  1.1  nonaka sdmmc_print_cis(struct sdmmc_function *sf)
    157  1.1  nonaka {
    158  1.1  nonaka 	device_t dev = sf->sc->sc_dev;
    159  1.1  nonaka 	struct sdmmc_cis *cis = &sf->cis;
    160  1.1  nonaka 	int i;
    161  1.1  nonaka 
    162  1.1  nonaka 	printf("%s: CIS version %u.%u\n", device_xname(dev), cis->cis1_major,
    163  1.1  nonaka 	    cis->cis1_minor);
    164  1.1  nonaka 
    165  1.1  nonaka 	printf("%s: CIS info: ", device_xname(dev));
    166  1.1  nonaka 	for (i = 0; i < 4; i++) {
    167  1.1  nonaka 		if (cis->cis1_info[i] == NULL)
    168  1.1  nonaka 			break;
    169  1.1  nonaka 		if (i != 0)
    170  1.1  nonaka 			aprint_verbose(", ");
    171  1.1  nonaka 		printf("%s", cis->cis1_info[i]);
    172  1.1  nonaka 	}
    173  1.1  nonaka 	printf("\n");
    174  1.1  nonaka 
    175  1.1  nonaka 	printf("%s: Manufacturer code 0x%x, product 0x%x\n", device_xname(dev),
    176  1.1  nonaka 	    cis->manufacturer, cis->product);
    177  1.1  nonaka 
    178  1.1  nonaka 	printf("%s: function %d: ", device_xname(dev), sf->number);
    179  1.1  nonaka 	switch (sf->cis.function) {
    180  1.1  nonaka 	case SDMMC_FUNCTION_WLAN:
    181  1.1  nonaka 		printf("wireless network adapter");
    182  1.1  nonaka 		break;
    183  1.1  nonaka 
    184  1.1  nonaka 	default:
    185  1.1  nonaka 		printf("unknown function (%d)", sf->cis.function);
    186  1.1  nonaka 		break;
    187  1.1  nonaka 	}
    188  1.1  nonaka 	printf("\n");
    189  1.1  nonaka }
    190  1.1  nonaka 
    191  1.1  nonaka void
    192  1.1  nonaka sdmmc_check_cis_quirks(struct sdmmc_function *sf)
    193  1.1  nonaka {
    194  1.1  nonaka 	char *p;
    195  1.1  nonaka 	int i;
    196  1.1  nonaka 
    197  1.1  nonaka 	if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC &&
    198  1.1  nonaka 	    sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) {
    199  1.1  nonaka 		/* This card lacks the VERS_1 tuple. */
    200  1.1  nonaka 		static const char cis1_info[] =
    201  1.1  nonaka 		    "Spectec\0SDIO WLAN Card\0SDW-820\0\0";
    202  1.1  nonaka 
    203  1.1  nonaka 		sf->cis.cis1_major = 0x01;
    204  1.1  nonaka 		sf->cis.cis1_minor = 0x00;
    205  1.1  nonaka 
    206  1.1  nonaka 		p = sf->cis.cis1_info_buf;
    207  1.1  nonaka 		strlcpy(p, cis1_info, sizeof(sf->cis.cis1_info_buf));
    208  1.1  nonaka 		for (i = 0; i < 4; i++) {
    209  1.1  nonaka 			sf->cis.cis1_info[i] = p;
    210  1.1  nonaka 			p += strlen(p) + 1;
    211  1.1  nonaka 		}
    212  1.1  nonaka 	}
    213  1.1  nonaka }
    214