brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · afaa6ca Raw
433 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/drivers/mmc/core/sdio_cis.c4 *5 * Author:	Nicolas Pitre6 * Created:	June 11, 20077 * Copyright:	MontaVista Software Inc.8 *9 * Copyright 2007 Pierre Ossman10 */11 12#include <linux/kernel.h>13#include <linux/slab.h>14 15#include <linux/mmc/host.h>16#include <linux/mmc/card.h>17#include <linux/mmc/sdio.h>18#include <linux/mmc/sdio_func.h>19 20#include "sdio_cis.h"21#include "sdio_ops.h"22 23#define SDIO_READ_CIS_TIMEOUT_MS  (10 * 1000) /* 10s */24 25static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,26			 const unsigned char *buf, unsigned size)27{28	u8 major_rev, minor_rev;29	unsigned i, nr_strings;30	char **buffer, *string;31 32	if (size < 2)33		return 0;34 35	major_rev = buf[0];36	minor_rev = buf[1];37 38	/* Find all null-terminated (including zero length) strings in39	   the TPLLV1_INFO field. Trailing garbage is ignored. */40	buf += 2;41	size -= 2;42 43	nr_strings = 0;44	for (i = 0; i < size; i++) {45		if (buf[i] == 0xff)46			break;47		if (buf[i] == 0)48			nr_strings++;49	}50	if (nr_strings == 0)51		return 0;52 53	size = i;54 55	buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);56	if (!buffer)57		return -ENOMEM;58 59	string = (char*)(buffer + nr_strings);60 61	for (i = 0; i < nr_strings; i++) {62		buffer[i] = string;63		strcpy(string, buf);64		string += strlen(string) + 1;65		buf += strlen(buf) + 1;66	}67 68	if (func) {69		func->major_rev = major_rev;70		func->minor_rev = minor_rev;71		func->num_info = nr_strings;72		func->info = (const char**)buffer;73	} else {74		card->major_rev = major_rev;75		card->minor_rev = minor_rev;76		card->num_info = nr_strings;77		card->info = (const char**)buffer;78	}79 80	return 0;81}82 83static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,84			 const unsigned char *buf, unsigned size)85{86	unsigned int vendor, device;87 88	/* TPLMID_MANF */89	vendor = buf[0] | (buf[1] << 8);90 91	/* TPLMID_CARD */92	device = buf[2] | (buf[3] << 8);93 94	if (func) {95		func->vendor = vendor;96		func->device = device;97	} else {98		card->cis.vendor = vendor;99		card->cis.device = device;100	}101 102	return 0;103}104 105static const unsigned char speed_val[16] =106	{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };107static const unsigned int speed_unit[8] =108	{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };109 110 111typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,112			   const unsigned char *, unsigned);113 114struct cis_tpl {115	unsigned char code;116	unsigned char min_size;117	tpl_parse_t *parse;118};119 120static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,121			 const char *tpl_descr,122			 const struct cis_tpl *tpl, int tpl_count,123			 unsigned char code,124			 const unsigned char *buf, unsigned size)125{126	int i, ret;127 128	/* look for a matching code in the table */129	for (i = 0; i < tpl_count; i++, tpl++) {130		if (tpl->code == code)131			break;132	}133	if (i < tpl_count) {134		if (size >= tpl->min_size) {135			if (tpl->parse)136				ret = tpl->parse(card, func, buf, size);137			else138				ret = -EILSEQ;	/* known tuple, not parsed */139		} else {140			/* invalid tuple */141			ret = -EINVAL;142		}143		if (ret && ret != -EILSEQ && ret != -ENOENT) {144			pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",145			       mmc_hostname(card->host), tpl_descr, code, size);146		}147	} else {148		/* unknown tuple */149		ret = -ENOENT;150	}151 152	return ret;153}154 155static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,156			       const unsigned char *buf, unsigned size)157{158	/* Only valid for the common CIS (function 0) */159	if (func)160		return -EINVAL;161 162	/* TPLFE_FN0_BLK_SIZE */163	card->cis.blksize = buf[1] | (buf[2] << 8);164 165	/* TPLFE_MAX_TRAN_SPEED */166	card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *167			    speed_unit[buf[3] & 7];168 169	return 0;170}171 172static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,173			     const unsigned char *buf, unsigned size)174{175	unsigned vsn;176	unsigned min_size;177 178	/* Only valid for the individual function's CIS (1-7) */179	if (!func)180		return -EINVAL;181 182	/*183	 * This tuple has a different length depending on the SDIO spec184	 * version.185	 */186	vsn = func->card->cccr.sdio_vsn;187	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;188 189	if (size == 28 && vsn == SDIO_SDIO_REV_1_10) {190		pr_warn("%s: card has broken SDIO 1.1 CIS, forcing SDIO 1.0\n",191			mmc_hostname(card->host));192		vsn = SDIO_SDIO_REV_1_00;193	} else if (size < min_size) {194		return -EINVAL;195	}196 197	/* TPLFE_MAX_BLK_SIZE */198	func->max_blksize = buf[12] | (buf[13] << 8);199 200	/* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */201	if (vsn > SDIO_SDIO_REV_1_00)202		func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10;203	else204		func->enable_timeout = jiffies_to_msecs(HZ);205 206	return 0;207}208 209/*210 * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.211 *212 * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending213 * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO214 * TPLFID_FUNCTION is always hardcoded to 0x0C.215 */216static const struct cis_tpl cis_tpl_funce_list[] = {217	{	0x00,	4,	cistpl_funce_common		},218	{	0x01,	0,	cistpl_funce_func		},219	{	0x04,	1+1+6,	/* CISTPL_FUNCE_LAN_NODE_ID */	},220};221 222static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,223			const unsigned char *buf, unsigned size)224{225	if (size < 1)226		return -EINVAL;227 228	return cis_tpl_parse(card, func, "CISTPL_FUNCE",229			     cis_tpl_funce_list,230			     ARRAY_SIZE(cis_tpl_funce_list),231			     buf[0], buf, size);232}233 234/* Known TPL_CODEs table for CIS tuples */235static const struct cis_tpl cis_tpl_list[] = {236	{	0x15,	3,	cistpl_vers_1		},237	{	0x20,	4,	cistpl_manfid		},238	{	0x21,	2,	/* cistpl_funcid */	},239	{	0x22,	0,	cistpl_funce		},240	{	0x91,	2,	/* cistpl_sdio_std */	},241};242 243static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)244{245	int ret;246	struct sdio_func_tuple *this, **prev;247	unsigned i, ptr = 0;248 249	/*250	 * Note that this works for the common CIS (function number 0) as251	 * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS252	 * have the same offset.253	 */254	for (i = 0; i < 3; i++) {255		unsigned char x, fn;256 257		if (func)258			fn = func->num;259		else260			fn = 0;261 262		ret = mmc_io_rw_direct(card, 0, 0,263			SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);264		if (ret)265			return ret;266		ptr |= x << (i * 8);267	}268 269	if (func)270		prev = &func->tuples;271	else272		prev = &card->tuples;273 274	if (*prev)275		return -EINVAL;276 277	do {278		unsigned char tpl_code, tpl_link;279		unsigned long timeout = jiffies +280			msecs_to_jiffies(SDIO_READ_CIS_TIMEOUT_MS);281 282		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);283		if (ret)284			break;285 286		/* 0xff means we're done */287		if (tpl_code == 0xff)288			break;289 290		/* null entries have no link field or data */291		if (tpl_code == 0x00)292			continue;293 294		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);295		if (ret)296			break;297 298		/* a size of 0xff also means we're done */299		if (tpl_link == 0xff)300			break;301 302		this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);303		if (!this)304			return -ENOMEM;305 306		for (i = 0; i < tpl_link; i++) {307			ret = mmc_io_rw_direct(card, 0, 0,308					       ptr + i, 0, &this->data[i]);309			if (ret)310				break;311		}312		if (ret) {313			kfree(this);314			break;315		}316 317		/* Try to parse the CIS tuple */318		ret = cis_tpl_parse(card, func, "CIS",319				    cis_tpl_list, ARRAY_SIZE(cis_tpl_list),320				    tpl_code, this->data, tpl_link);321		if (ret == -EILSEQ || ret == -ENOENT) {322			/*323			 * The tuple is unknown or known but not parsed.324			 * Queue the tuple for the function driver.325			 */326			this->next = NULL;327			this->code = tpl_code;328			this->size = tpl_link;329			*prev = this;330			prev = &this->next;331 332			if (ret == -ENOENT) {333 334				if (time_after(jiffies, timeout))335					break;336 337#define FMT(type) "%s: queuing " type " CIS tuple 0x%02x [%*ph] (%u bytes)\n"338				/*339				 * Tuples in this range are reserved for340				 * vendors, so don't warn about them341				 */342				if (tpl_code >= 0x80 && tpl_code <= 0x8f)343					pr_debug_ratelimited(FMT("vendor"),344						mmc_hostname(card->host),345						tpl_code, tpl_link, this->data,346						tpl_link);347				else348					pr_warn_ratelimited(FMT("unknown"),349						mmc_hostname(card->host),350						tpl_code, tpl_link, this->data,351						tpl_link);352			}353 354			/* keep on analyzing tuples */355			ret = 0;356		} else {357			/*358			 * We don't need the tuple anymore if it was359			 * successfully parsed by the SDIO core or if it is360			 * not going to be queued for a driver.361			 */362			kfree(this);363		}364 365		ptr += tpl_link;366	} while (!ret);367 368	/*369	 * Link in all unknown tuples found in the common CIS so that370	 * drivers don't have to go digging in two places.371	 */372	if (func)373		*prev = card->tuples;374 375	return ret;376}377 378int sdio_read_common_cis(struct mmc_card *card)379{380	return sdio_read_cis(card, NULL);381}382 383void sdio_free_common_cis(struct mmc_card *card)384{385	struct sdio_func_tuple *tuple, *victim;386 387	tuple = card->tuples;388 389	while (tuple) {390		victim = tuple;391		tuple = tuple->next;392		kfree(victim);393	}394 395	card->tuples = NULL;396}397 398int sdio_read_func_cis(struct sdio_func *func)399{400	int ret;401 402	ret = sdio_read_cis(func->card, func);403	if (ret)404		return ret;405 406	/*407	 * Vendor/device id is optional for function CIS, so408	 * copy it from the card structure as needed.409	 */410	if (func->vendor == 0) {411		func->vendor = func->card->cis.vendor;412		func->device = func->card->cis.device;413	}414 415	return 0;416}417 418void sdio_free_func_cis(struct sdio_func *func)419{420	struct sdio_func_tuple *tuple, *victim;421 422	tuple = func->tuples;423 424	while (tuple && tuple != func->card->tuples) {425		victim = tuple;426		tuple = tuple->next;427		kfree(victim);428	}429 430	func->tuples = NULL;431}432 433