sdio_cis.c 6.3 KB
Newer Older
N
Nicolas Pitre 已提交
1 2 3 4 5 6 7
/*
 * linux/drivers/mmc/core/sdio_cis.c
 *
 * Author:	Nicolas Pitre
 * Created:	June 11, 2007
 * Copyright:	MontaVista Software Inc.
 *
8 9
 * Copyright 2007 Pierre Ossman
 *
N
Nicolas Pitre 已提交
10 11 12 13 14 15 16 17 18
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 */

#include <linux/kernel.h>

#include <linux/mmc/host.h>
19
#include <linux/mmc/card.h>
N
Nicolas Pitre 已提交
20 21 22 23 24 25
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>

#include "sdio_cis.h"
#include "sdio_ops.h"

26 27
static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
			 const unsigned char *buf, unsigned size)
N
Nicolas Pitre 已提交
28
{
29 30
	unsigned int vendor, device;

N
Nicolas Pitre 已提交
31
	/* TPLMID_MANF */
32
	vendor = buf[0] | (buf[1] << 8);
N
Nicolas Pitre 已提交
33 34

	/* TPLMID_CARD */
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
	device = buf[2] | (buf[3] << 8);

	if (func) {
		func->vendor = vendor;
		func->device = device;
	} else {
		card->cis.vendor = vendor;
		card->cis.device = device;
	}

	return 0;
}

static const unsigned char speed_val[16] =
	{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static const unsigned int speed_unit[8] =
	{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };

static int cistpl_funce_common(struct mmc_card *card,
			       const unsigned char *buf, unsigned size)
{
	if (size < 0x04 || buf[0] != 0)
		return -EINVAL;

	/* TPLFE_FN0_BLK_SIZE */
	card->cis.blksize = buf[1] | (buf[2] << 8);

	/* TPLFE_MAX_TRAN_SPEED */
	card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
			    speed_unit[buf[3] & 7];

	return 0;
}

static int cistpl_funce_func(struct sdio_func *func,
			     const unsigned char *buf, unsigned size)
{
	unsigned vsn;
	unsigned min_size;

	vsn = func->card->cccr.sdio_vsn;
	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;

	if (size < min_size || buf[0] != 1)
		return -EINVAL;

	/* TPLFE_MAX_BLK_SIZE */
	func->blksize = buf[12] | (buf[13] << 8);
N
Nicolas Pitre 已提交
83 84 85 86

	return 0;
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
			const unsigned char *buf, unsigned size)
{
	int ret;

	/*
	 * There should be two versions of the CISTPL_FUNCE tuple,
	 * one for the common CIS (function 0) and a version used by
	 * the individual function's CIS (1-7). Yet, the later has a
	 * different length depending on the SDIO spec version.
	 */
	if (func)
		ret = cistpl_funce_func(func, buf, size);
	else
		ret = cistpl_funce_common(card, buf, size);

	if (ret) {
		printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
		       "type %u\n", mmc_hostname(card->host), size, buf[0]);
		return ret;
	}

	return 0;
}

typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
			   const unsigned char *, unsigned);

N
Nicolas Pitre 已提交
115 116 117
struct cis_tpl {
	unsigned char code;
	unsigned char min_size;
118
	tpl_parse_t *parse;
N
Nicolas Pitre 已提交
119 120 121 122 123 124
};

static const struct cis_tpl cis_tpl_list[] = {
	{	0x15,	3,	/* cistpl_vers_1 */	},
	{	0x20,	4,	cistpl_manfid		},
	{	0x21,	2,	/* cistpl_funcid */	},
125
	{	0x22,	0,	cistpl_funce		},
N
Nicolas Pitre 已提交
126 127
};

128
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
N
Nicolas Pitre 已提交
129 130
{
	int ret;
131
	struct sdio_func_tuple *this, **prev;
N
Nicolas Pitre 已提交
132 133
	unsigned i, ptr = 0;

134 135 136 137 138
	/*
	 * Note that this works for the common CIS (function number 0) as
	 * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
	 * have the same offset.
	 */
N
Nicolas Pitre 已提交
139
	for (i = 0; i < 3; i++) {
140 141 142 143 144 145 146 147
		unsigned char x, fn;

		if (func)
			fn = func->num;
		else
			fn = 0;

		ret = mmc_io_rw_direct(card, 0, 0,
D
David Vrabel 已提交
148
			SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
N
Nicolas Pitre 已提交
149 150 151 152 153
		if (ret)
			return ret;
		ptr |= x << (i * 8);
	}

154 155 156 157 158 159
	if (func)
		prev = &func->tuples;
	else
		prev = &card->tuples;

	BUG_ON(*prev);
N
Nicolas Pitre 已提交
160 161 162 163

	do {
		unsigned char tpl_code, tpl_link;

164
		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
N
Nicolas Pitre 已提交
165 166 167 168 169 170 171
		if (ret)
			break;

		/* 0xff means we're done */
		if (tpl_code == 0xff)
			break;

172
		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
N
Nicolas Pitre 已提交
173 174 175
		if (ret)
			break;

176 177 178 179 180
		this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
		if (!this)
			return -ENOMEM;

		for (i = 0; i < tpl_link; i++) {
181
			ret = mmc_io_rw_direct(card, 0, 0,
182 183
					       ptr + i, 0, &this->data[i]);
			if (ret)
N
Nicolas Pitre 已提交
184 185
				break;
		}
186 187
		if (ret) {
			kfree(this);
N
Nicolas Pitre 已提交
188 189 190
			break;
		}

191 192
		for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
			if (cis_tpl_list[i].code == tpl_code)
N
Nicolas Pitre 已提交
193
				break;
194 195 196 197 198 199 200 201 202
		if (i >= ARRAY_SIZE(cis_tpl_list)) {
			/* this tuple is unknown to the core */
			this->next = NULL;
			this->code = tpl_code;
			this->size = tpl_link;
			*prev = this;
			prev = &this->next;
			printk(KERN_DEBUG
			       "%s: queuing CIS tuple 0x%02x length %u\n",
203
			       mmc_hostname(card->host), tpl_code, tpl_link);
204 205 206 207 208
		} else {
			const struct cis_tpl *tpl = cis_tpl_list + i;
			if (tpl_link < tpl->min_size) {
				printk(KERN_ERR
				       "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
209 210
				       mmc_hostname(card->host),
				       tpl_code, tpl_link, tpl->min_size);
211
				ret = -EINVAL;
212 213 214 215
			} else if (tpl->parse) {
				ret = tpl->parse(card, func,
						 this->data, tpl_link);
			}
216
			kfree(this);
N
Nicolas Pitre 已提交
217 218
		}

219
		ptr += tpl_link;
N
Nicolas Pitre 已提交
220 221
	} while (!ret);

222 223 224 225 226 227 228
	/*
	 * Link in all unknown tuples found in the common CIS so that
	 * drivers don't have to go digging in two places.
	 */
	if (func)
		*prev = card->tuples;

N
Nicolas Pitre 已提交
229 230
	return ret;
}
231

232 233 234 235 236 237
int sdio_read_common_cis(struct mmc_card *card)
{
	return sdio_read_cis(card, NULL);
}

void sdio_free_common_cis(struct mmc_card *card)
238 239 240
{
	struct sdio_func_tuple *tuple, *victim;

241
	tuple = card->tuples;
242 243 244 245 246 247 248

	while (tuple) {
		victim = tuple;
		tuple = tuple->next;
		kfree(victim);
	}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	card->tuples = NULL;
}

int sdio_read_func_cis(struct sdio_func *func)
{
	int ret;

	ret = sdio_read_cis(func->card, func);
	if (ret)
		return ret;

	/*
	 * Since we've linked to tuples in the card structure,
	 * we must make sure we have a reference to it.
	 */
	get_device(&func->card->dev);

	/*
	 * Vendor/device id is optional for function CIS, so
	 * copy it from the card structure as needed.
	 */
	if (func->vendor == 0) {
		func->vendor = func->card->cis.vendor;
		func->device = func->card->cis.device;
	}

	return 0;
}

void sdio_free_func_cis(struct sdio_func *func)
{
	struct sdio_func_tuple *tuple, *victim;

	tuple = func->tuples;

	while (tuple && tuple != func->card->tuples) {
		victim = tuple;
		tuple = tuple->next;
		kfree(victim);
	}

290
	func->tuples = NULL;
291 292 293 294 295 296

	/*
	 * We have now removed the link to the tuples in the
	 * card structure, so remove the reference.
	 */
	put_device(&func->card->dev);
297 298
}