2007-06-16 10:04:16 +04:00
/*
* linux / drivers / mmc / core / sdio_cis . c
*
* Author : Nicolas Pitre
* Created : June 11 , 2007
* Copyright : MontaVista Software Inc .
*
2007-06-16 10:06:47 +04:00
* Copyright 2007 Pierre Ossman
*
2007-06-16 10:04:16 +04:00
* 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>
2007-07-30 17:15:30 +04:00
# include <linux/mmc/card.h>
2007-06-16 10:04:16 +04:00
# include <linux/mmc/sdio.h>
# include <linux/mmc/sdio_func.h>
# include "sdio_cis.h"
# include "sdio_ops.h"
2007-07-30 17:15:30 +04:00
static int cistpl_manfid ( struct mmc_card * card , struct sdio_func * func ,
const unsigned char * buf , unsigned size )
2007-06-16 10:04:16 +04:00
{
2007-07-30 17:15:30 +04:00
unsigned int vendor , device ;
2007-06-16 10:04:16 +04:00
/* TPLMID_MANF */
2007-07-30 17:15:30 +04:00
vendor = buf [ 0 ] | ( buf [ 1 ] < < 8 ) ;
2007-06-16 10:04:16 +04:00
/* TPLMID_CARD */
2007-07-30 17:15:30 +04:00
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 */
2007-08-08 17:23:48 +04:00
func - > max_blksize = buf [ 12 ] | ( buf [ 13 ] < < 8 ) ;
2007-06-16 10:04:16 +04:00
return 0 ;
}
2007-07-30 17:15:30 +04:00
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 ) ;
2007-06-16 10:04:16 +04:00
struct cis_tpl {
unsigned char code ;
unsigned char min_size ;
2007-07-30 17:15:30 +04:00
tpl_parse_t * parse ;
2007-06-16 10:04:16 +04:00
} ;
static const struct cis_tpl cis_tpl_list [ ] = {
{ 0x15 , 3 , /* cistpl_vers_1 */ } ,
{ 0x20 , 4 , cistpl_manfid } ,
{ 0x21 , 2 , /* cistpl_funcid */ } ,
2007-07-30 17:15:30 +04:00
{ 0x22 , 0 , cistpl_funce } ,
2007-06-16 10:04:16 +04:00
} ;
2007-07-30 17:15:30 +04:00
static int sdio_read_cis ( struct mmc_card * card , struct sdio_func * func )
2007-06-16 10:04:16 +04:00
{
int ret ;
2007-06-16 10:06:47 +04:00
struct sdio_func_tuple * this , * * prev ;
2007-06-16 10:04:16 +04:00
unsigned i , ptr = 0 ;
2007-07-30 17:15:30 +04:00
/*
* 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 .
*/
2007-06-16 10:04:16 +04:00
for ( i = 0 ; i < 3 ; i + + ) {
2007-07-30 17:15:30 +04:00
unsigned char x , fn ;
if ( func )
fn = func - > num ;
else
fn = 0 ;
ret = mmc_io_rw_direct ( card , 0 , 0 ,
2007-08-08 17:23:05 +04:00
SDIO_FBR_BASE ( fn ) + SDIO_FBR_CIS + i , 0 , & x ) ;
2007-06-16 10:04:16 +04:00
if ( ret )
return ret ;
ptr | = x < < ( i * 8 ) ;
}
2007-07-30 17:15:30 +04:00
if ( func )
prev = & func - > tuples ;
else
prev = & card - > tuples ;
BUG_ON ( * prev ) ;
2007-06-16 10:04:16 +04:00
do {
unsigned char tpl_code , tpl_link ;
2007-07-30 17:15:30 +04:00
ret = mmc_io_rw_direct ( card , 0 , 0 , ptr + + , 0 , & tpl_code ) ;
2007-06-16 10:04:16 +04:00
if ( ret )
break ;
/* 0xff means we're done */
if ( tpl_code = = 0xff )
break ;
2007-07-30 17:15:30 +04:00
ret = mmc_io_rw_direct ( card , 0 , 0 , ptr + + , 0 , & tpl_link ) ;
2007-06-16 10:04:16 +04:00
if ( ret )
break ;
2007-06-16 10:06:47 +04:00
this = kmalloc ( sizeof ( * this ) + tpl_link , GFP_KERNEL ) ;
if ( ! this )
return - ENOMEM ;
for ( i = 0 ; i < tpl_link ; i + + ) {
2007-07-30 17:15:30 +04:00
ret = mmc_io_rw_direct ( card , 0 , 0 ,
2007-06-16 10:06:47 +04:00
ptr + i , 0 , & this - > data [ i ] ) ;
if ( ret )
2007-06-16 10:04:16 +04:00
break ;
}
2007-06-16 10:06:47 +04:00
if ( ret ) {
kfree ( this ) ;
2007-06-16 10:04:16 +04:00
break ;
}
2007-06-16 10:06:47 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( cis_tpl_list ) ; i + + )
if ( cis_tpl_list [ i ] . code = = tpl_code )
2007-06-16 10:04:16 +04:00
break ;
2007-06-16 10:06:47 +04:00
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 " ,
2007-07-30 17:15:30 +04:00
mmc_hostname ( card - > host ) , tpl_code , tpl_link ) ;
2007-06-16 10:06:47 +04:00
} 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 " ,
2007-07-30 17:15:30 +04:00
mmc_hostname ( card - > host ) ,
tpl_code , tpl_link , tpl - > min_size ) ;
2007-06-16 10:06:47 +04:00
ret = - EINVAL ;
2007-07-30 17:15:30 +04:00
} else if ( tpl - > parse ) {
ret = tpl - > parse ( card , func ,
this - > data , tpl_link ) ;
}
2007-06-16 10:06:47 +04:00
kfree ( this ) ;
2007-06-16 10:04:16 +04:00
}
2007-06-16 10:06:47 +04:00
ptr + = tpl_link ;
2007-06-16 10:04:16 +04:00
} while ( ! ret ) ;
2007-07-30 17:15:30 +04:00
/*
* 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 ;
2007-06-16 10:04:16 +04:00
return ret ;
}
2007-06-16 10:06:47 +04:00
2007-07-30 17:15:30 +04:00
int sdio_read_common_cis ( struct mmc_card * card )
{
return sdio_read_cis ( card , NULL ) ;
}
void sdio_free_common_cis ( struct mmc_card * card )
2007-06-16 10:06:47 +04:00
{
struct sdio_func_tuple * tuple , * victim ;
2007-07-30 17:15:30 +04:00
tuple = card - > tuples ;
2007-06-16 10:06:47 +04:00
while ( tuple ) {
victim = tuple ;
tuple = tuple - > next ;
kfree ( victim ) ;
}
2007-07-30 17:15:30 +04:00
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 ) ;
}
2007-06-16 10:06:47 +04:00
func - > tuples = NULL ;
2007-07-30 17:15:30 +04:00
/*
* We have now removed the link to the tuples in the
* card structure , so remove the reference .
*/
put_device ( & func - > card - > dev ) ;
2007-06-16 10:06:47 +04:00
}