2012-04-12 12:48:44 +04:00
/*
* Copyright ( c ) 2009 - 2011 Wind River Systems , Inc .
2012-11-09 18:20:01 +04:00
* Copyright ( c ) 2011 ST Microelectronics ( Alessandro Rubini , Davide Ciminaghi )
2012-04-12 12:48:44 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/errno.h>
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/pci.h>
# include <linux/seq_file.h>
# include <linux/platform_device.h>
# include <linux/mfd/core.h>
# include <linux/mfd/sta2x11-mfd.h>
2012-11-09 18:19:53 +04:00
# include <linux/regmap.h>
2012-04-12 12:48:44 +04:00
# include <asm/sta2x11.h>
2012-11-09 18:19:53 +04:00
static inline int __reg_within_range ( unsigned int r ,
unsigned int start ,
unsigned int end )
{
return ( ( r > = start ) & & ( r < = end ) ) ;
}
2012-04-12 12:48:44 +04:00
/* This describes STA2X11 MFD chip for us, we may have several */
struct sta2x11_mfd {
struct sta2x11_instance * instance ;
2012-11-09 18:19:53 +04:00
struct regmap * regmap [ sta2x11_n_mfd_plat_devs ] ;
2012-11-09 18:19:58 +04:00
spinlock_t lock [ sta2x11_n_mfd_plat_devs ] ;
2012-04-12 12:48:44 +04:00
struct list_head list ;
2012-11-09 18:19:52 +04:00
void __iomem * regs [ sta2x11_n_mfd_plat_devs ] ;
2012-04-12 12:48:44 +04:00
} ;
static LIST_HEAD ( sta2x11_mfd_list ) ;
/* Three functions to act on the list */
static struct sta2x11_mfd * sta2x11_mfd_find ( struct pci_dev * pdev )
{
struct sta2x11_instance * instance ;
struct sta2x11_mfd * mfd ;
if ( ! pdev & & ! list_empty ( & sta2x11_mfd_list ) ) {
pr_warning ( " %s: Unspecified device, "
" using first instance \n " , __func__ ) ;
return list_entry ( sta2x11_mfd_list . next ,
struct sta2x11_mfd , list ) ;
}
instance = sta2x11_get_instance ( pdev ) ;
if ( ! instance )
return NULL ;
list_for_each_entry ( mfd , & sta2x11_mfd_list , list ) {
if ( mfd - > instance = = instance )
return mfd ;
}
return NULL ;
}
static int __devinit sta2x11_mfd_add ( struct pci_dev * pdev , gfp_t flags )
{
2012-11-09 18:19:58 +04:00
int i ;
2012-04-12 12:48:44 +04:00
struct sta2x11_mfd * mfd = sta2x11_mfd_find ( pdev ) ;
struct sta2x11_instance * instance ;
if ( mfd )
return - EBUSY ;
instance = sta2x11_get_instance ( pdev ) ;
if ( ! instance )
return - EINVAL ;
mfd = kzalloc ( sizeof ( * mfd ) , flags ) ;
if ( ! mfd )
return - ENOMEM ;
INIT_LIST_HEAD ( & mfd - > list ) ;
2012-11-09 18:19:58 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( mfd - > lock ) ; i + + )
spin_lock_init ( & mfd - > lock [ i ] ) ;
2012-04-12 12:48:44 +04:00
mfd - > instance = instance ;
list_add ( & mfd - > list , & sta2x11_mfd_list ) ;
return 0 ;
}
static int __devexit mfd_remove ( struct pci_dev * pdev )
{
struct sta2x11_mfd * mfd = sta2x11_mfd_find ( pdev ) ;
if ( ! mfd )
return - ENODEV ;
list_del ( & mfd - > list ) ;
kfree ( mfd ) ;
return 0 ;
}
2012-11-09 18:19:52 +04:00
/* This function is exported and is not expected to fail */
u32 __sta2x11_mfd_mask ( struct pci_dev * pdev , u32 reg , u32 mask , u32 val ,
enum sta2x11_mfd_plat_dev index )
2012-04-12 12:48:44 +04:00
{
struct sta2x11_mfd * mfd = sta2x11_mfd_find ( pdev ) ;
u32 r ;
unsigned long flags ;
2012-11-09 18:19:52 +04:00
void __iomem * regs = mfd - > regs [ index ] ;
2012-04-12 12:48:44 +04:00
if ( ! mfd ) {
dev_warn ( & pdev - > dev , " : can't access sctl regs \n " ) ;
return 0 ;
}
2012-11-09 18:19:52 +04:00
if ( ! regs ) {
2012-04-12 12:48:44 +04:00
dev_warn ( & pdev - > dev , " : system ctl not initialized \n " ) ;
return 0 ;
}
2012-11-09 18:19:58 +04:00
spin_lock_irqsave ( & mfd - > lock [ index ] , flags ) ;
2012-11-09 18:19:52 +04:00
r = readl ( regs + reg ) ;
2012-04-12 12:48:44 +04:00
r & = ~ mask ;
r | = val ;
if ( mask )
2012-11-09 18:19:52 +04:00
writel ( r , regs + reg ) ;
2012-11-09 18:19:58 +04:00
spin_unlock_irqrestore ( & mfd - > lock [ index ] , flags ) ;
2012-04-12 12:48:44 +04:00
return r ;
}
2012-11-09 18:19:52 +04:00
EXPORT_SYMBOL ( __sta2x11_mfd_mask ) ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:54 +04:00
int sta2x11_mfd_get_regs_data ( struct platform_device * dev ,
enum sta2x11_mfd_plat_dev index ,
void __iomem * * regs ,
spinlock_t * * lock )
{
struct pci_dev * pdev = * ( struct pci_dev * * ) ( dev - > dev . platform_data ) ;
struct sta2x11_mfd * mfd ;
if ( ! pdev )
return - ENODEV ;
mfd = sta2x11_mfd_find ( pdev ) ;
if ( ! mfd )
return - ENODEV ;
if ( index > = sta2x11_n_mfd_plat_devs )
return - ENODEV ;
* regs = mfd - > regs [ index ] ;
* lock = & mfd - > lock [ index ] ;
pr_debug ( " %s %d *regs = %p \n " , __func__ , __LINE__ , * regs ) ;
return * regs ? 0 : - ENODEV ;
}
EXPORT_SYMBOL ( sta2x11_mfd_get_regs_data ) ;
2012-11-09 18:19:53 +04:00
/*
* Special sta2x11 - mfd regmap lock / unlock functions
*/
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static void sta2x11_regmap_lock ( void * __lock )
{
spinlock_t * lock = __lock ;
spin_lock ( lock ) ;
}
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static void sta2x11_regmap_unlock ( void * __lock )
{
spinlock_t * lock = __lock ;
spin_unlock ( lock ) ;
}
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:59 +04:00
/* OTP (one time programmable registers do not require locking */
static void sta2x11_regmap_nolock ( void * __lock )
{
}
2012-11-09 18:19:53 +04:00
static const char * sta2x11_mfd_names [ sta2x11_n_mfd_plat_devs ] = {
2012-11-09 18:19:55 +04:00
[ sta2x11_sctl ] = STA2X11_MFD_SCTL_NAME ,
[ sta2x11_apbreg ] = STA2X11_MFD_APBREG_NAME ,
[ sta2x11_apb_soc_regs ] = STA2X11_MFD_APB_SOC_REGS_NAME ,
2012-11-09 18:19:59 +04:00
[ sta2x11_scr ] = STA2X11_MFD_SCR_NAME ,
2012-04-12 12:48:44 +04:00
} ;
2012-11-09 18:19:53 +04:00
static bool sta2x11_sctl_writeable_reg ( struct device * dev , unsigned int reg )
{
return ! __reg_within_range ( reg , SCTL_SCPCIECSBRST , SCTL_SCRSTSTA ) ;
}
static struct regmap_config sta2x11_sctl_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. lock = sta2x11_regmap_lock ,
. unlock = sta2x11_regmap_unlock ,
. max_register = SCTL_SCRSTSTA ,
. writeable_reg = sta2x11_sctl_writeable_reg ,
2012-11-09 18:19:52 +04:00
} ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:59 +04:00
static bool sta2x11_scr_readable_reg ( struct device * dev , unsigned int reg )
{
return ( reg = = STA2X11_SECR_CR ) | |
__reg_within_range ( reg , STA2X11_SECR_FVR0 , STA2X11_SECR_FVR1 ) ;
}
static bool sta2x11_scr_writeable_reg ( struct device * dev , unsigned int reg )
{
return false ;
}
static struct regmap_config sta2x11_scr_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. lock = sta2x11_regmap_nolock ,
. unlock = sta2x11_regmap_nolock ,
. max_register = STA2X11_SECR_FVR1 ,
. readable_reg = sta2x11_scr_readable_reg ,
. writeable_reg = sta2x11_scr_writeable_reg ,
} ;
2012-11-09 18:19:53 +04:00
static bool sta2x11_apbreg_readable_reg ( struct device * dev , unsigned int reg )
{
/* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */
if ( reg > = APBREG_BSR_SARAC )
reg - = APBREG_BSR_SARAC ;
switch ( reg ) {
case APBREG_BSR :
case APBREG_PAER :
case APBREG_PWAC :
case APBREG_PRAC :
case APBREG_PCG :
case APBREG_PUR :
case APBREG_EMU_PCG :
return true ;
default :
return false ;
}
}
static bool sta2x11_apbreg_writeable_reg ( struct device * dev , unsigned int reg )
{
if ( reg > = APBREG_BSR_SARAC )
reg - = APBREG_BSR_SARAC ;
if ( ! sta2x11_apbreg_readable_reg ( dev , reg ) )
return false ;
return reg ! = APBREG_PAER ;
}
static struct regmap_config sta2x11_apbreg_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. lock = sta2x11_regmap_lock ,
. unlock = sta2x11_regmap_unlock ,
. max_register = APBREG_EMU_PCG_SARAC ,
. readable_reg = sta2x11_apbreg_readable_reg ,
. writeable_reg = sta2x11_apbreg_writeable_reg ,
2012-11-09 18:19:52 +04:00
} ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static bool sta2x11_apb_soc_regs_readable_reg ( struct device * dev ,
unsigned int reg )
{
return reg < = PCIE_SoC_INT_ROUTER_STATUS3_REG | |
__reg_within_range ( reg , DMA_IP_CTRL_REG , SPARE3_RESERVED ) | |
__reg_within_range ( reg , MASTER_LOCK_REG ,
SYSTEM_CONFIG_STATUS_REG ) | |
reg = = MSP_CLK_CTRL_REG | |
__reg_within_range ( reg , COMPENSATION_REG1 , TEST_CTL_REG ) ;
}
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static bool sta2x11_apb_soc_regs_writeable_reg ( struct device * dev ,
unsigned int reg )
{
if ( ! sta2x11_apb_soc_regs_readable_reg ( dev , reg ) )
return false ;
switch ( reg ) {
case PCIE_COMMON_CLOCK_CONFIG_0_4_0 :
case SYSTEM_CONFIG_STATUS_REG :
case COMPENSATION_REG1 :
case PCIE_SoC_INT_ROUTER_STATUS0_REG . . . PCIE_SoC_INT_ROUTER_STATUS3_REG :
case PCIE_PM_STATUS_0_PORT_0_4 . . . PCIE_PM_STATUS_7_0_EP4 :
return false ;
default :
return true ;
}
}
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static struct regmap_config sta2x11_apb_soc_regs_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. lock = sta2x11_regmap_lock ,
. unlock = sta2x11_regmap_unlock ,
. max_register = TEST_CTL_REG ,
. readable_reg = sta2x11_apb_soc_regs_readable_reg ,
. writeable_reg = sta2x11_apb_soc_regs_writeable_reg ,
2012-11-09 18:19:52 +04:00
} ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:53 +04:00
static struct regmap_config *
sta2x11_mfd_regmap_configs [ sta2x11_n_mfd_plat_devs ] = {
[ sta2x11_sctl ] = & sta2x11_sctl_regmap_config ,
[ sta2x11_apbreg ] = & sta2x11_apbreg_regmap_config ,
[ sta2x11_apb_soc_regs ] = & sta2x11_apb_soc_regs_regmap_config ,
2012-11-09 18:19:59 +04:00
[ sta2x11_scr ] = & sta2x11_scr_regmap_config ,
2012-11-09 18:19:52 +04:00
} ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:59 +04:00
/* Probe for the four platform devices */
2012-11-09 18:19:52 +04:00
static int sta2x11_mfd_platform_probe ( struct platform_device * dev ,
enum sta2x11_mfd_plat_dev index )
2012-04-12 12:48:44 +04:00
{
struct pci_dev * * pdev ;
struct sta2x11_mfd * mfd ;
struct resource * res ;
2012-11-09 18:19:52 +04:00
const char * name = sta2x11_mfd_names [ index ] ;
2012-11-09 18:19:53 +04:00
struct regmap_config * regmap_config = sta2x11_mfd_regmap_configs [ index ] ;
2012-04-12 12:48:44 +04:00
pdev = dev - > dev . platform_data ;
mfd = sta2x11_mfd_find ( * pdev ) ;
if ( ! mfd )
return - ENODEV ;
2012-11-09 18:19:53 +04:00
if ( ! regmap_config )
return - ENODEV ;
2012-04-12 12:48:44 +04:00
res = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENOMEM ;
2012-11-09 18:19:52 +04:00
if ( ! request_mem_region ( res - > start , resource_size ( res ) , name ) )
2012-04-12 12:48:44 +04:00
return - EBUSY ;
2012-11-09 18:19:52 +04:00
mfd - > regs [ index ] = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! mfd - > regs [ index ] ) {
2012-04-12 12:48:44 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
return - ENOMEM ;
}
2012-11-09 18:19:53 +04:00
regmap_config - > lock_arg = & mfd - > lock ;
/*
No caching , registers could be reached both via regmap and via
void __iomem *
*/
regmap_config - > cache_type = REGCACHE_NONE ;
mfd - > regmap [ index ] = devm_regmap_init_mmio ( & dev - > dev , mfd - > regs [ index ] ,
regmap_config ) ;
WARN_ON ( ! mfd - > regmap [ index ] ) ;
2012-04-12 12:48:44 +04:00
return 0 ;
}
2012-11-09 18:19:52 +04:00
static int sta2x11_sctl_probe ( struct platform_device * dev )
{
return sta2x11_mfd_platform_probe ( dev , sta2x11_sctl ) ;
}
static int sta2x11_apbreg_probe ( struct platform_device * dev )
{
return sta2x11_mfd_platform_probe ( dev , sta2x11_apbreg ) ;
}
static int sta2x11_apb_soc_regs_probe ( struct platform_device * dev )
{
return sta2x11_mfd_platform_probe ( dev , sta2x11_apb_soc_regs ) ;
}
2012-11-09 18:19:59 +04:00
static int sta2x11_scr_probe ( struct platform_device * dev )
{
return sta2x11_mfd_platform_probe ( dev , sta2x11_scr ) ;
}
2012-11-09 18:19:52 +04:00
/* The three platform drivers */
2012-04-12 12:48:44 +04:00
static struct platform_driver sta2x11_sctl_platform_driver = {
. driver = {
2012-11-09 18:19:55 +04:00
. name = STA2X11_MFD_SCTL_NAME ,
2012-04-12 12:48:44 +04:00
. owner = THIS_MODULE ,
} ,
. probe = sta2x11_sctl_probe ,
} ;
static int __init sta2x11_sctl_init ( void )
{
pr_info ( " %s \n " , __func__ ) ;
return platform_driver_register ( & sta2x11_sctl_platform_driver ) ;
}
static struct platform_driver sta2x11_platform_driver = {
. driver = {
2012-11-09 18:19:55 +04:00
. name = STA2X11_MFD_APBREG_NAME ,
2012-04-12 12:48:44 +04:00
. owner = THIS_MODULE ,
} ,
. probe = sta2x11_apbreg_probe ,
} ;
static int __init sta2x11_apbreg_init ( void )
{
pr_info ( " %s \n " , __func__ ) ;
return platform_driver_register ( & sta2x11_platform_driver ) ;
}
2012-11-09 18:19:52 +04:00
static struct platform_driver sta2x11_apb_soc_regs_platform_driver = {
. driver = {
2012-11-09 18:19:55 +04:00
. name = STA2X11_MFD_APB_SOC_REGS_NAME ,
2012-11-09 18:19:52 +04:00
. owner = THIS_MODULE ,
} ,
. probe = sta2x11_apb_soc_regs_probe ,
} ;
static int __init sta2x11_apb_soc_regs_init ( void )
{
pr_info ( " %s \n " , __func__ ) ;
return platform_driver_register ( & sta2x11_apb_soc_regs_platform_driver ) ;
}
2012-11-09 18:19:59 +04:00
static struct platform_driver sta2x11_scr_platform_driver = {
. driver = {
. name = STA2X11_MFD_SCR_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = sta2x11_scr_probe ,
} ;
static int __init sta2x11_scr_init ( void )
{
pr_info ( " %s \n " , __func__ ) ;
return platform_driver_register ( & sta2x11_scr_platform_driver ) ;
}
2012-04-12 12:48:44 +04:00
/*
2012-11-09 18:19:52 +04:00
* What follows are the PCI devices that host the above pdevs .
2012-04-12 12:48:44 +04:00
* Each logic block is 4 kB and they are all consecutive : we use this info .
*/
2012-11-09 18:19:52 +04:00
/* Mfd 0 device */
/* Mfd 0, Bar 0 */
enum mfd0_bar0_cells {
2012-04-12 12:48:44 +04:00
STA2X11_GPIO_0 = 0 ,
STA2X11_GPIO_1 ,
STA2X11_GPIO_2 ,
STA2X11_GPIO_3 ,
STA2X11_SCTL ,
STA2X11_SCR ,
STA2X11_TIME ,
} ;
2012-11-09 18:19:52 +04:00
/* Mfd 0 , Bar 1 */
enum mfd0_bar1_cells {
2012-04-12 12:48:44 +04:00
STA2X11_APBREG = 0 ,
} ;
# define CELL_4K(_name, _cell) { \
. name = _name , \
. start = _cell * 4096 , . end = _cell * 4096 + 4095 , \
. flags = IORESOURCE_MEM , \
}
static const __devinitconst struct resource gpio_resources [ ] = {
{
2012-11-09 18:19:55 +04:00
/* 4 consecutive cells, 1 driver */
. name = STA2X11_MFD_GPIO_NAME ,
2012-04-12 12:48:44 +04:00
. start = 0 ,
. end = ( 4 * 4096 ) - 1 ,
. flags = IORESOURCE_MEM ,
}
} ;
static const __devinitconst struct resource sctl_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_SCTL_NAME , STA2X11_SCTL ) ,
2012-04-12 12:48:44 +04:00
} ;
static const __devinitconst struct resource scr_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_SCR_NAME , STA2X11_SCR ) ,
2012-04-12 12:48:44 +04:00
} ;
static const __devinitconst struct resource time_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_TIME_NAME , STA2X11_TIME ) ,
2012-04-12 12:48:44 +04:00
} ;
static const __devinitconst struct resource apbreg_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_APBREG_NAME , STA2X11_APBREG ) ,
2012-04-12 12:48:44 +04:00
} ;
# define DEV(_name, _r) \
{ . name = _name , . num_resources = ARRAY_SIZE ( _r ) , . resources = _r , }
2012-11-09 18:19:52 +04:00
static __devinitdata struct mfd_cell sta2x11_mfd0_bar0 [ ] = {
2012-11-09 18:19:55 +04:00
/* offset 0: we add pdata later */
DEV ( STA2X11_MFD_GPIO_NAME , gpio_resources ) ,
DEV ( STA2X11_MFD_SCTL_NAME , sctl_resources ) ,
DEV ( STA2X11_MFD_SCR_NAME , scr_resources ) ,
DEV ( STA2X11_MFD_TIME_NAME , time_resources ) ,
2012-04-12 12:48:44 +04:00
} ;
2012-11-09 18:19:52 +04:00
static __devinitdata struct mfd_cell sta2x11_mfd0_bar1 [ ] = {
2012-11-09 18:19:55 +04:00
DEV ( STA2X11_MFD_APBREG_NAME , apbreg_resources ) ,
2012-04-12 12:48:44 +04:00
} ;
2012-11-09 18:19:52 +04:00
/* Mfd 1 devices */
/* Mfd 1, Bar 0 */
enum mfd1_bar0_cells {
STA2X11_VIC = 0 ,
} ;
/* Mfd 1, Bar 1 */
enum mfd1_bar1_cells {
STA2X11_APB_SOC_REGS = 0 ,
} ;
static const __devinitconst struct resource vic_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_VIC_NAME , STA2X11_VIC ) ,
2012-11-09 18:19:52 +04:00
} ;
static const __devinitconst struct resource apb_soc_regs_resources [ ] = {
2012-11-09 18:19:55 +04:00
CELL_4K ( STA2X11_MFD_APB_SOC_REGS_NAME , STA2X11_APB_SOC_REGS ) ,
2012-11-09 18:19:52 +04:00
} ;
static __devinitdata struct mfd_cell sta2x11_mfd1_bar0 [ ] = {
2012-11-09 18:19:55 +04:00
DEV ( STA2X11_MFD_VIC_NAME , vic_resources ) ,
2012-11-09 18:19:52 +04:00
} ;
static __devinitdata struct mfd_cell sta2x11_mfd1_bar1 [ ] = {
2012-11-09 18:19:55 +04:00
DEV ( STA2X11_MFD_APB_SOC_REGS_NAME , apb_soc_regs_resources ) ,
2012-11-09 18:19:52 +04:00
} ;
2012-04-12 12:48:44 +04:00
static int sta2x11_mfd_suspend ( struct pci_dev * pdev , pm_message_t state )
{
pci_save_state ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_set_power_state ( pdev , pci_choose_state ( pdev , state ) ) ;
return 0 ;
}
static int sta2x11_mfd_resume ( struct pci_dev * pdev )
{
int err ;
pci_set_power_state ( pdev , 0 ) ;
err = pci_enable_device ( pdev ) ;
if ( err )
return err ;
pci_restore_state ( pdev ) ;
return 0 ;
}
2012-11-09 18:19:52 +04:00
struct sta2x11_mfd_bar_setup_data {
struct mfd_cell * cells ;
int ncells ;
} ;
struct sta2x11_mfd_setup_data {
struct sta2x11_mfd_bar_setup_data bars [ 2 ] ;
} ;
# define STA2X11_MFD0 0
# define STA2X11_MFD1 1
static struct sta2x11_mfd_setup_data mfd_setup_data [ ] = {
/* Mfd 0: gpio, sctl, scr, timers / apbregs */
[ STA2X11_MFD0 ] = {
. bars = {
[ 0 ] = {
. cells = sta2x11_mfd0_bar0 ,
. ncells = ARRAY_SIZE ( sta2x11_mfd0_bar0 ) ,
} ,
[ 1 ] = {
. cells = sta2x11_mfd0_bar1 ,
. ncells = ARRAY_SIZE ( sta2x11_mfd0_bar1 ) ,
} ,
} ,
} ,
/* Mfd 1: vic / apb-soc-regs */
[ STA2X11_MFD1 ] = {
. bars = {
[ 0 ] = {
. cells = sta2x11_mfd1_bar0 ,
. ncells = ARRAY_SIZE ( sta2x11_mfd1_bar0 ) ,
} ,
[ 1 ] = {
. cells = sta2x11_mfd1_bar1 ,
. ncells = ARRAY_SIZE ( sta2x11_mfd1_bar1 ) ,
} ,
} ,
} ,
} ;
static void __devinit sta2x11_mfd_setup ( struct pci_dev * pdev ,
struct sta2x11_mfd_setup_data * sd )
{
int i , j ;
for ( i = 0 ; i < ARRAY_SIZE ( sd - > bars ) ; i + + )
for ( j = 0 ; j < sd - > bars [ i ] . ncells ; j + + ) {
sd - > bars [ i ] . cells [ j ] . pdata_size = sizeof ( pdev ) ;
sd - > bars [ i ] . cells [ j ] . platform_data = & pdev ;
}
}
2012-04-12 12:48:44 +04:00
static int __devinit sta2x11_mfd_probe ( struct pci_dev * pdev ,
const struct pci_device_id * pci_id )
{
int err , i ;
2012-11-09 18:19:52 +04:00
struct sta2x11_mfd_setup_data * setup_data ;
2012-04-12 12:48:44 +04:00
dev_info ( & pdev - > dev , " %s \n " , __func__ ) ;
err = pci_enable_device ( pdev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Can't enable device. \n " ) ;
return err ;
}
err = pci_enable_msi ( pdev ) ;
if ( err )
dev_info ( & pdev - > dev , " Enable msi failed \n " ) ;
2012-11-09 18:19:52 +04:00
setup_data = pci_id - > device = = PCI_DEVICE_ID_STMICRO_GPIO ?
& mfd_setup_data [ STA2X11_MFD0 ] :
& mfd_setup_data [ STA2X11_MFD1 ] ;
2012-04-12 12:48:44 +04:00
/* platform data is the pci device for all of them */
2012-11-09 18:19:52 +04:00
sta2x11_mfd_setup ( pdev , setup_data ) ;
2012-04-12 12:48:44 +04:00
/* Record this pdev before mfd_add_devices: their probe looks for it */
2012-11-09 18:19:56 +04:00
if ( ! sta2x11_mfd_find ( pdev ) )
sta2x11_mfd_add ( pdev , GFP_ATOMIC ) ;
2012-04-12 12:48:44 +04:00
2012-11-09 18:19:52 +04:00
/* Just 2 bars for all mfd's at present */
for ( i = 0 ; i < 2 ; i + + ) {
err = mfd_add_devices ( & pdev - > dev , - 1 ,
setup_data - > bars [ i ] . cells ,
setup_data - > bars [ i ] . ncells ,
& pdev - > resource [ i ] ,
0 , NULL ) ;
if ( err ) {
dev_err ( & pdev - > dev ,
" mfd_add_devices[%d] failed: %d \n " , i , err ) ;
goto err_disable ;
}
2012-04-12 12:48:44 +04:00
}
return 0 ;
err_disable :
mfd_remove_devices ( & pdev - > dev ) ;
pci_disable_device ( pdev ) ;
pci_disable_msi ( pdev ) ;
return err ;
}
static DEFINE_PCI_DEVICE_TABLE ( sta2x11_mfd_tbl ) = {
{ PCI_DEVICE ( PCI_VENDOR_ID_STMICRO , PCI_DEVICE_ID_STMICRO_GPIO ) } ,
2012-11-09 18:19:52 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_STMICRO , PCI_DEVICE_ID_STMICRO_VIC ) } ,
2012-04-12 12:48:44 +04:00
{ 0 , } ,
} ;
static struct pci_driver sta2x11_mfd_driver = {
. name = " sta2x11-mfd " ,
. id_table = sta2x11_mfd_tbl ,
. probe = sta2x11_mfd_probe ,
. suspend = sta2x11_mfd_suspend ,
. resume = sta2x11_mfd_resume ,
} ;
static int __init sta2x11_mfd_init ( void )
{
pr_info ( " %s \n " , __func__ ) ;
return pci_register_driver ( & sta2x11_mfd_driver ) ;
}
/*
* All of this must be ready before " normal " devices like MMCI appear .
* But MFD ( the pci device ) can ' t be too early . The following choice
* prepares platform drivers very early and probe the PCI device later ,
* but before other PCI devices .
*/
subsys_initcall ( sta2x11_apbreg_init ) ;
subsys_initcall ( sta2x11_sctl_init ) ;
2012-11-09 18:19:52 +04:00
subsys_initcall ( sta2x11_apb_soc_regs_init ) ;
2012-11-09 18:19:59 +04:00
subsys_initcall ( sta2x11_scr_init ) ;
2012-04-12 12:48:44 +04:00
rootfs_initcall ( sta2x11_mfd_init ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Wind River " ) ;
MODULE_DESCRIPTION ( " STA2x11 mfd for GPIO, SCTL and APBREG " ) ;
MODULE_DEVICE_TABLE ( pci , sta2x11_mfd_tbl ) ;