2007-09-15 12:54:08 -07:00
/*
* ricoh_mmc . c - Dummy driver to disable the Rioch MMC controller .
*
* Copyright ( C ) 2007 Philip Langdale , All Rights Reserved .
*
* 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 .
*/
/*
* This is a conceptually ridiculous driver , but it is required by the way
2008-11-30 20:27:50 -05:00
* the Ricoh multi - function chips ( R5CXXX ) work . These chips implement
* the four main memory card controllers ( SD , MMC , MS , xD ) and one or both
* of cardbus or firewire . It happens that they implement SD and MMC
* support as separate controllers ( and PCI functions ) . The linux SDHCI
2007-09-15 12:54:08 -07:00
* driver supports MMC cards but the chip detects MMC cards in hardware
* and directs them to the MMC controller - so the SDHCI driver never sees
* them . To get around this , we must disable the useless MMC controller .
* At that point , the SDHCI controller will start seeing them . As a bonus ,
* a detection event occurs immediately , even if the MMC card is already
* in the reader .
*
2008-11-30 20:27:50 -05:00
* It seems to be the case that the relevant PCI registers to deactivate the
* MMC controller live on PCI function 0 , which might be the cardbus controller
* or the firewire controller , depending on the particular chip in question . As
* such , it makes what this driver has to do unavoidably ugly . Such is life .
2007-09-15 12:54:08 -07:00
*/
# include <linux/pci.h>
# define DRIVER_NAME "ricoh-mmc"
static const struct pci_device_id pci_ids [ ] __devinitdata = {
{
. vendor = PCI_VENDOR_ID_RICOH ,
. device = PCI_DEVICE_ID_RICOH_R5C843 ,
. subvendor = PCI_ANY_ID ,
. subdevice = PCI_ANY_ID ,
} ,
{ /* end: all zeroes */ } ,
} ;
MODULE_DEVICE_TABLE ( pci , pci_ids ) ;
2007-12-29 00:11:42 -08:00
static int ricoh_mmc_disable ( struct pci_dev * fw_dev )
{
u8 write_enable ;
2008-02-04 19:25:42 +01:00
u8 write_target ;
2007-12-29 00:11:42 -08:00
u8 disable ;
2008-02-04 19:25:42 +01:00
if ( fw_dev - > device = = PCI_DEVICE_ID_RICOH_RL5C476 ) {
/* via RL5C476 */
2007-12-29 00:11:42 -08:00
2008-02-04 19:25:42 +01:00
pci_read_config_byte ( fw_dev , 0xB7 , & disable ) ;
if ( disable & 0x02 ) {
printk ( KERN_INFO DRIVER_NAME
" : Controller already disabled. " \
" Nothing to do. \n " ) ;
return - ENODEV ;
}
pci_read_config_byte ( fw_dev , 0x8E , & write_enable ) ;
pci_write_config_byte ( fw_dev , 0x8E , 0xAA ) ;
pci_read_config_byte ( fw_dev , 0x8D , & write_target ) ;
pci_write_config_byte ( fw_dev , 0x8D , 0xB7 ) ;
pci_write_config_byte ( fw_dev , 0xB7 , disable | 0x02 ) ;
pci_write_config_byte ( fw_dev , 0x8E , write_enable ) ;
pci_write_config_byte ( fw_dev , 0x8D , write_target ) ;
} else {
/* via R5C832 */
pci_read_config_byte ( fw_dev , 0xCB , & disable ) ;
if ( disable & 0x02 ) {
printk ( KERN_INFO DRIVER_NAME
" : Controller already disabled. " \
" Nothing to do. \n " ) ;
return - ENODEV ;
}
pci_read_config_byte ( fw_dev , 0xCA , & write_enable ) ;
pci_write_config_byte ( fw_dev , 0xCA , 0x57 ) ;
pci_write_config_byte ( fw_dev , 0xCB , disable | 0x02 ) ;
pci_write_config_byte ( fw_dev , 0xCA , write_enable ) ;
}
2007-12-29 00:11:42 -08:00
printk ( KERN_INFO DRIVER_NAME
" : Controller is now disabled. \n " ) ;
return 0 ;
}
static int ricoh_mmc_enable ( struct pci_dev * fw_dev )
{
u8 write_enable ;
2008-02-04 19:25:42 +01:00
u8 write_target ;
2007-12-29 00:11:42 -08:00
u8 disable ;
2008-02-04 19:25:42 +01:00
if ( fw_dev - > device = = PCI_DEVICE_ID_RICOH_RL5C476 ) {
/* via RL5C476 */
pci_read_config_byte ( fw_dev , 0x8E , & write_enable ) ;
pci_write_config_byte ( fw_dev , 0x8E , 0xAA ) ;
pci_read_config_byte ( fw_dev , 0x8D , & write_target ) ;
pci_write_config_byte ( fw_dev , 0x8D , 0xB7 ) ;
pci_read_config_byte ( fw_dev , 0xB7 , & disable ) ;
pci_write_config_byte ( fw_dev , 0xB7 , disable & ~ 0x02 ) ;
pci_write_config_byte ( fw_dev , 0x8E , write_enable ) ;
pci_write_config_byte ( fw_dev , 0x8D , write_target ) ;
} else {
/* via R5C832 */
pci_read_config_byte ( fw_dev , 0xCA , & write_enable ) ;
pci_read_config_byte ( fw_dev , 0xCB , & disable ) ;
pci_write_config_byte ( fw_dev , 0xCA , 0x57 ) ;
pci_write_config_byte ( fw_dev , 0xCB , disable & ~ 0x02 ) ;
pci_write_config_byte ( fw_dev , 0xCA , write_enable ) ;
}
2007-12-29 00:11:42 -08:00
printk ( KERN_INFO DRIVER_NAME
" : Controller is now re-enabled. \n " ) ;
return 0 ;
}
2007-09-15 12:54:08 -07:00
static int __devinit ricoh_mmc_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
u8 rev ;
2008-02-04 19:25:42 +01:00
u8 ctrlfound = 0 ;
2007-09-15 12:54:08 -07:00
struct pci_dev * fw_dev = NULL ;
BUG_ON ( pdev = = NULL ) ;
BUG_ON ( ent = = NULL ) ;
pci_read_config_byte ( pdev , PCI_CLASS_REVISION , & rev ) ;
printk ( KERN_INFO DRIVER_NAME
" : Ricoh MMC controller found at %s [%04x:%04x] (rev %x) \n " ,
pci_name ( pdev ) , ( int ) pdev - > vendor , ( int ) pdev - > device ,
( int ) rev ) ;
2008-02-04 19:25:42 +01:00
while ( ( fw_dev =
pci_get_device ( PCI_VENDOR_ID_RICOH ,
PCI_DEVICE_ID_RICOH_RL5C476 , fw_dev ) ) ) {
2007-09-15 12:54:08 -07:00
if ( PCI_SLOT ( pdev - > devfn ) = = PCI_SLOT ( fw_dev - > devfn ) & &
2008-11-30 20:27:50 -05:00
PCI_FUNC ( fw_dev - > devfn ) = = 0 & &
2007-09-15 12:54:08 -07:00
pdev - > bus = = fw_dev - > bus ) {
2008-02-04 19:25:42 +01:00
if ( ricoh_mmc_disable ( fw_dev ) ! = 0 )
2007-09-15 12:54:08 -07:00
return - ENODEV ;
pci_set_drvdata ( pdev , fw_dev ) ;
2008-02-04 19:25:42 +01:00
+ + ctrlfound ;
2007-09-15 12:54:08 -07:00
break ;
}
}
2008-02-04 19:25:42 +01:00
fw_dev = NULL ;
while ( ! ctrlfound & &
( fw_dev = pci_get_device ( PCI_VENDOR_ID_RICOH ,
PCI_DEVICE_ID_RICOH_R5C832 , fw_dev ) ) ) {
if ( PCI_SLOT ( pdev - > devfn ) = = PCI_SLOT ( fw_dev - > devfn ) & &
2008-11-30 20:27:50 -05:00
PCI_FUNC ( fw_dev - > devfn ) = = 0 & &
2008-02-04 19:25:42 +01:00
pdev - > bus = = fw_dev - > bus ) {
if ( ricoh_mmc_disable ( fw_dev ) ! = 0 )
return - ENODEV ;
pci_set_drvdata ( pdev , fw_dev ) ;
+ + ctrlfound ;
}
}
if ( ! ctrlfound ) {
2007-09-15 12:54:08 -07:00
printk ( KERN_WARNING DRIVER_NAME
2008-11-30 20:27:50 -05:00
" : Main Ricoh function not found. Cannot disable controller. \n " ) ;
2007-09-15 12:54:08 -07:00
return - ENODEV ;
}
return 0 ;
}
static void __devexit ricoh_mmc_remove ( struct pci_dev * pdev )
{
struct pci_dev * fw_dev = NULL ;
fw_dev = pci_get_drvdata ( pdev ) ;
BUG_ON ( fw_dev = = NULL ) ;
2007-12-29 00:11:42 -08:00
ricoh_mmc_enable ( fw_dev ) ;
2007-09-15 12:54:08 -07:00
pci_set_drvdata ( pdev , NULL ) ;
}
2009-01-18 14:11:20 -05:00
static int ricoh_mmc_suspend_late ( struct pci_dev * pdev , pm_message_t state )
2007-12-29 00:11:42 -08:00
{
struct pci_dev * fw_dev = NULL ;
fw_dev = pci_get_drvdata ( pdev ) ;
BUG_ON ( fw_dev = = NULL ) ;
printk ( KERN_INFO DRIVER_NAME " : Suspending. \n " ) ;
ricoh_mmc_enable ( fw_dev ) ;
return 0 ;
}
2009-01-18 14:11:20 -05:00
static int ricoh_mmc_resume_early ( struct pci_dev * pdev )
2007-12-29 00:11:42 -08:00
{
struct pci_dev * fw_dev = NULL ;
fw_dev = pci_get_drvdata ( pdev ) ;
BUG_ON ( fw_dev = = NULL ) ;
printk ( KERN_INFO DRIVER_NAME " : Resuming. \n " ) ;
ricoh_mmc_disable ( fw_dev ) ;
return 0 ;
}
2007-09-15 12:54:08 -07:00
static struct pci_driver ricoh_mmc_driver = {
. name = DRIVER_NAME ,
. id_table = pci_ids ,
. probe = ricoh_mmc_probe ,
. remove = __devexit_p ( ricoh_mmc_remove ) ,
2009-01-18 14:11:20 -05:00
. suspend_late = ricoh_mmc_suspend_late ,
. resume_early = ricoh_mmc_resume_early ,
2007-09-15 12:54:08 -07:00
} ;
/*****************************************************************************\
* *
* Driver init / exit *
* *
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __init ricoh_mmc_drv_init ( void )
{
printk ( KERN_INFO DRIVER_NAME
" : Ricoh MMC Controller disabling driver \n " ) ;
printk ( KERN_INFO DRIVER_NAME " : Copyright(c) Philip Langdale \n " ) ;
return pci_register_driver ( & ricoh_mmc_driver ) ;
}
static void __exit ricoh_mmc_drv_exit ( void )
{
pci_unregister_driver ( & ricoh_mmc_driver ) ;
}
module_init ( ricoh_mmc_drv_init ) ;
module_exit ( ricoh_mmc_drv_exit ) ;
MODULE_AUTHOR ( " Philip Langdale <philipl@alumni.utexas.net> " ) ;
MODULE_DESCRIPTION ( " Ricoh MMC Controller disabling driver " ) ;
MODULE_LICENSE ( " GPL " ) ;