2010-02-19 13:26:25 +03:00
/*
* lpc_sch . c - LPC interface for Intel Poulsbo SCH
*
* LPC bridge function of the Intel SCH contains many other
* functional units , such as Interrupt controllers , Timers ,
* Power Management , System Management , GPIO , RTC , and LPC
* Configuration Registers .
*
* Copyright ( c ) 2010 CompuLab Ltd
2014-09-02 14:45:22 +04:00
* Copyright ( c ) 2014 Intel Corp .
2010-02-19 13:26:25 +03:00
* Author : Denis Turischev < denis @ compulab . co . il >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License 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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/acpi.h>
# include <linux/pci.h>
# include <linux/mfd/core.h>
# define SMBASE 0x40
# define SMBUS_IO_SIZE 64
# define GPIOBASE 0x44
# define GPIO_IO_SIZE 64
2012-04-18 01:09:22 +04:00
# define GPIO_IO_SIZE_CENTERTON 128
2010-02-19 13:26:25 +03:00
2014-09-02 14:45:21 +04:00
/* Intel Quark X1000 GPIO IRQ Number */
# define GPIO_IRQ_QUARK_X1000 9
2011-06-16 15:05:49 +04:00
# define WDTBASE 0x84
# define WDT_IO_SIZE 64
2014-09-02 14:45:19 +04:00
enum sch_chipsets {
LPC_SCH = 0 , /* Intel Poulsbo SCH */
LPC_ITC , /* Intel Tunnel Creek */
LPC_CENTERTON , /* Intel Centerton */
2014-09-02 14:45:21 +04:00
LPC_QUARK_X1000 , /* Intel Quark X1000 */
2010-02-19 13:26:25 +03:00
} ;
2014-09-02 14:45:19 +04:00
struct lpc_sch_info {
unsigned int io_size_smbus ;
unsigned int io_size_gpio ;
unsigned int io_size_wdt ;
2014-09-02 14:45:21 +04:00
int irq_gpio ;
2010-02-19 13:26:25 +03:00
} ;
2014-09-02 14:45:19 +04:00
static struct lpc_sch_info sch_chipset_info [ ] = {
[ LPC_SCH ] = {
. io_size_smbus = SMBUS_IO_SIZE ,
. io_size_gpio = GPIO_IO_SIZE ,
2014-09-02 14:45:21 +04:00
. irq_gpio = - 1 ,
2014-09-02 14:45:19 +04:00
} ,
[ LPC_ITC ] = {
. io_size_smbus = SMBUS_IO_SIZE ,
. io_size_gpio = GPIO_IO_SIZE ,
. io_size_wdt = WDT_IO_SIZE ,
2014-09-02 14:45:21 +04:00
. irq_gpio = - 1 ,
2014-09-02 14:45:19 +04:00
} ,
[ LPC_CENTERTON ] = {
. io_size_smbus = SMBUS_IO_SIZE ,
. io_size_gpio = GPIO_IO_SIZE_CENTERTON ,
. io_size_wdt = WDT_IO_SIZE ,
2014-09-02 14:45:21 +04:00
. irq_gpio = - 1 ,
} ,
[ LPC_QUARK_X1000 ] = {
. io_size_gpio = GPIO_IO_SIZE ,
. irq_gpio = GPIO_IRQ_QUARK_X1000 ,
2015-01-14 11:20:20 +03:00
. io_size_wdt = WDT_IO_SIZE ,
2014-09-02 14:45:19 +04:00
} ,
2011-06-16 15:05:49 +04:00
} ;
2013-12-03 03:15:39 +04:00
static const struct pci_device_id lpc_sch_ids [ ] = {
2014-09-02 14:45:19 +04:00
{ PCI_VDEVICE ( INTEL , PCI_DEVICE_ID_INTEL_SCH_LPC ) , LPC_SCH } ,
{ PCI_VDEVICE ( INTEL , PCI_DEVICE_ID_INTEL_ITC_LPC ) , LPC_ITC } ,
{ PCI_VDEVICE ( INTEL , PCI_DEVICE_ID_INTEL_CENTERTON_ILB ) , LPC_CENTERTON } ,
2014-09-02 14:45:21 +04:00
{ PCI_VDEVICE ( INTEL , PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB ) , LPC_QUARK_X1000 } ,
2010-02-19 13:26:25 +03:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , lpc_sch_ids ) ;
2014-09-02 14:45:19 +04:00
# define LPC_NO_RESOURCE 1
# define LPC_SKIP_RESOURCE 2
static int lpc_sch_get_io ( struct pci_dev * pdev , int where , const char * name ,
struct resource * res , int size )
2010-02-19 13:26:25 +03:00
{
unsigned int base_addr_cfg ;
unsigned short base_addr ;
2014-09-02 14:45:19 +04:00
if ( size = = 0 )
return LPC_NO_RESOURCE ;
pci_read_config_dword ( pdev , where , & base_addr_cfg ) ;
2013-02-09 03:20:36 +04:00
base_addr = 0 ;
if ( ! ( base_addr_cfg & ( 1 < < 31 ) ) )
2014-09-02 14:45:19 +04:00
dev_warn ( & pdev - > dev , " Decode of the %s I/O range disabled \n " ,
name ) ;
2013-02-09 03:20:36 +04:00
else
base_addr = ( unsigned short ) base_addr_cfg ;
2010-02-19 13:26:25 +03:00
if ( base_addr = = 0 ) {
2014-09-02 14:45:19 +04:00
dev_warn ( & pdev - > dev , " I/O space for %s uninitialized \n " , name ) ;
return LPC_SKIP_RESOURCE ;
2010-02-19 13:26:25 +03:00
}
2014-09-02 14:45:19 +04:00
res - > start = base_addr ;
res - > end = base_addr + size - 1 ;
res - > flags = IORESOURCE_IO ;
2011-03-13 18:28:59 +03:00
2014-09-02 14:45:19 +04:00
return 0 ;
}
2011-06-16 15:05:49 +04:00
2014-09-02 14:45:19 +04:00
static int lpc_sch_populate_cell ( struct pci_dev * pdev , int where ,
2014-09-02 14:45:21 +04:00
const char * name , int size , int irq ,
int id , struct mfd_cell * cell )
2014-09-02 14:45:19 +04:00
{
struct resource * res ;
int ret ;
2011-06-16 15:05:49 +04:00
2014-09-02 14:45:21 +04:00
res = devm_kcalloc ( & pdev - > dev , 2 , sizeof ( * res ) , GFP_KERNEL ) ;
2014-09-02 14:45:19 +04:00
if ( ! res )
return - ENOMEM ;
ret = lpc_sch_get_io ( pdev , where , name , res , size ) ;
if ( ret )
return ret ;
memset ( cell , 0 , sizeof ( * cell ) ) ;
cell - > name = name ;
cell - > resources = res ;
cell - > num_resources = 1 ;
cell - > ignore_resource_conflicts = true ;
cell - > id = id ;
2014-09-02 14:45:21 +04:00
/* Check if we need to add an IRQ resource */
if ( irq < 0 )
return 0 ;
res + + ;
res - > start = irq ;
res - > end = irq ;
res - > flags = IORESOURCE_IRQ ;
cell - > num_resources + + ;
2014-09-02 14:45:19 +04:00
return 0 ;
}
static int lpc_sch_probe ( struct pci_dev * dev , const struct pci_device_id * id )
{
struct mfd_cell lpc_sch_cells [ 3 ] ;
struct lpc_sch_info * info = & sch_chipset_info [ id - > driver_data ] ;
unsigned int cells = 0 ;
int ret ;
ret = lpc_sch_populate_cell ( dev , SMBASE , " isch_smbus " ,
2014-09-02 14:45:21 +04:00
info - > io_size_smbus , - 1 ,
2014-09-02 14:45:19 +04:00
id - > device , & lpc_sch_cells [ cells ] ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
cells + + ;
ret = lpc_sch_populate_cell ( dev , GPIOBASE , " sch_gpio " ,
2014-09-02 14:45:21 +04:00
info - > io_size_gpio , info - > irq_gpio ,
2014-09-02 14:45:19 +04:00
id - > device , & lpc_sch_cells [ cells ] ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
cells + + ;
ret = lpc_sch_populate_cell ( dev , WDTBASE , " ie6xx_wdt " ,
2014-09-02 14:45:21 +04:00
info - > io_size_wdt , - 1 ,
2014-09-02 14:45:19 +04:00
id - > device , & lpc_sch_cells [ cells ] ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
cells + + ;
2011-06-16 15:05:49 +04:00
2013-02-09 03:20:36 +04:00
if ( cells = = 0 ) {
dev_err ( & dev - > dev , " All decode registers disabled. \n " ) ;
return - ENODEV ;
2011-06-16 15:05:49 +04:00
}
2014-11-03 20:29:23 +03:00
return mfd_add_devices ( & dev - > dev , 0 , lpc_sch_cells , cells , NULL , 0 , NULL ) ;
2010-02-19 13:26:25 +03:00
}
2012-11-19 22:26:01 +04:00
static void lpc_sch_remove ( struct pci_dev * dev )
2010-02-19 13:26:25 +03:00
{
mfd_remove_devices ( & dev - > dev ) ;
}
static struct pci_driver lpc_sch_driver = {
. name = " lpc_sch " ,
. id_table = lpc_sch_ids ,
. probe = lpc_sch_probe ,
2012-11-19 22:20:24 +04:00
. remove = lpc_sch_remove ,
2010-02-19 13:26:25 +03:00
} ;
2012-04-03 05:09:19 +04:00
module_pci_driver ( lpc_sch_driver ) ;
2010-02-19 13:26:25 +03:00
MODULE_AUTHOR ( " Denis Turischev <denis@compulab.co.il> " ) ;
MODULE_DESCRIPTION ( " LPC interface for Intel Poulsbo SCH " ) ;
MODULE_LICENSE ( " GPL " ) ;