2005-04-16 15:20:36 -07:00
/*
* cpcihp_zt5550 . c
*
* Intel / Ziatech ZT5550 CompactPCI Host Controller driver
*
* Copyright 2002 SOMA Networks , Inc .
* Copyright 2001 Intel San Luis Obispo
* Copyright 2000 , 2001 MontaVista Software Inc .
*
* 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 SOFTWARE IS PROVIDED " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Send feedback to < scottm @ somanetworks . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/pci.h>
2006-07-01 19:29:41 -07:00
# include <linux/interrupt.h>
# include <linux/signal.h> /* IRQF_SHARED */
2005-04-16 15:20:36 -07:00
# include "cpci_hotplug.h"
# include "cpcihp_zt5550.h"
# define DRIVER_VERSION "0.2"
# define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
# define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver"
# define MY_NAME "cpcihp_zt5550"
# define dbg(format, arg...) \
do { \
if ( debug ) \
printk ( KERN_DEBUG " %s: " format " \n " , \
MY_NAME , # # arg ) ; \
} while ( 0 )
# define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
# define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
# define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
2012-01-13 09:32:20 +10:30
static bool debug ;
static bool poll ;
2005-04-16 15:20:36 -07:00
static struct cpci_hp_controller_ops zt5550_hpc_ops ;
static struct cpci_hp_controller zt5550_hpc ;
/* Primary cPCI bus bridge device */
static struct pci_dev * bus0_dev ;
static struct pci_bus * bus0 ;
/* Host controller device */
static struct pci_dev * hc_dev ;
/* Host controller register addresses */
static void __iomem * hc_registers ;
static void __iomem * csr_hc_index ;
static void __iomem * csr_hc_data ;
static void __iomem * csr_int_status ;
static void __iomem * csr_int_mask ;
static int zt5550_hc_config ( struct pci_dev * pdev )
{
2005-09-28 15:15:16 -06:00
int ret ;
2005-04-16 15:20:36 -07:00
/* Since we know that no boards exist with two HC chips, treat it as an error */
if ( hc_dev ) {
err ( " too many host controller devices? " ) ;
return - EBUSY ;
}
2005-09-28 15:15:16 -06:00
ret = pci_enable_device ( pdev ) ;
if ( ret ) {
err ( " cannot enable %s \n " , pci_name ( pdev ) ) ;
return ret ;
}
2005-04-16 15:20:36 -07:00
hc_dev = pdev ;
dbg ( " hc_dev = %p " , hc_dev ) ;
2006-06-12 15:14:29 -07:00
dbg ( " pci resource start %llx " , ( unsigned long long ) pci_resource_start ( hc_dev , 1 ) ) ;
dbg ( " pci resource len %llx " , ( unsigned long long ) pci_resource_len ( hc_dev , 1 ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! request_mem_region ( pci_resource_start ( hc_dev , 1 ) ,
pci_resource_len ( hc_dev , 1 ) , MY_NAME ) ) {
err ( " cannot reserve MMIO region " ) ;
2005-09-28 15:15:16 -06:00
ret = - ENOMEM ;
goto exit_disable_device ;
2005-04-16 15:20:36 -07:00
}
hc_registers =
ioremap ( pci_resource_start ( hc_dev , 1 ) , pci_resource_len ( hc_dev , 1 ) ) ;
if ( ! hc_registers ) {
2006-06-12 15:14:29 -07:00
err ( " cannot remap MMIO region %llx @ %llx " ,
( unsigned long long ) pci_resource_len ( hc_dev , 1 ) ,
( unsigned long long ) pci_resource_start ( hc_dev , 1 ) ) ;
2005-09-28 15:15:16 -06:00
ret = - ENODEV ;
goto exit_release_region ;
2005-04-16 15:20:36 -07:00
}
csr_hc_index = hc_registers + CSR_HCINDEX ;
csr_hc_data = hc_registers + CSR_HCDATA ;
csr_int_status = hc_registers + CSR_INTSTAT ;
csr_int_mask = hc_registers + CSR_INTMASK ;
/*
* Disable host control , fault and serial interrupts
*/
dbg ( " disabling host control, fault and serial interrupts " ) ;
writeb ( ( u8 ) HC_INT_MASK_REG , csr_hc_index ) ;
writeb ( ( u8 ) ALL_INDEXED_INTS_MASK , csr_hc_data ) ;
dbg ( " disabled host control, fault and serial interrupts " ) ;
/*
* Disable timer0 , timer1 and ENUM interrupts
*/
dbg ( " disabling timer0, timer1 and ENUM interrupts " ) ;
writeb ( ( u8 ) ALL_DIRECT_INTS_MASK , csr_int_mask ) ;
dbg ( " disabled timer0, timer1 and ENUM interrupts " ) ;
return 0 ;
2005-09-28 15:15:16 -06:00
exit_release_region :
release_mem_region ( pci_resource_start ( hc_dev , 1 ) ,
pci_resource_len ( hc_dev , 1 ) ) ;
exit_disable_device :
pci_disable_device ( hc_dev ) ;
return ret ;
2005-04-16 15:20:36 -07:00
}
static int zt5550_hc_cleanup ( void )
{
if ( ! hc_dev )
return - ENODEV ;
iounmap ( hc_registers ) ;
release_mem_region ( pci_resource_start ( hc_dev , 1 ) ,
pci_resource_len ( hc_dev , 1 ) ) ;
2005-09-28 15:15:16 -06:00
pci_disable_device ( hc_dev ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int zt5550_hc_query_enum ( void )
{
u8 value ;
value = inb_p ( ENUM_PORT ) ;
return ( ( value & ENUM_MASK ) = = ENUM_MASK ) ;
}
static int zt5550_hc_check_irq ( void * dev_id )
{
int ret ;
u8 reg ;
ret = 0 ;
if ( dev_id = = zt5550_hpc . dev_id ) {
reg = readb ( csr_int_status ) ;
if ( reg )
ret = 1 ;
}
return ret ;
}
static int zt5550_hc_enable_irq ( void )
{
u8 reg ;
if ( hc_dev = = NULL ) {
return - ENODEV ;
}
reg = readb ( csr_int_mask ) ;
reg = reg & ~ ENUM_INT_MASK ;
writeb ( reg , csr_int_mask ) ;
return 0 ;
}
static int zt5550_hc_disable_irq ( void )
{
u8 reg ;
if ( hc_dev = = NULL ) {
return - ENODEV ;
}
reg = readb ( csr_int_mask ) ;
reg = reg | ENUM_INT_MASK ;
writeb ( reg , csr_int_mask ) ;
return 0 ;
}
static int zt5550_hc_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
int status ;
status = zt5550_hc_config ( pdev ) ;
if ( status ! = 0 ) {
return status ;
}
dbg ( " returned from zt5550_hc_config " ) ;
memset ( & zt5550_hpc , 0 , sizeof ( struct cpci_hp_controller ) ) ;
zt5550_hpc_ops . query_enum = zt5550_hc_query_enum ;
zt5550_hpc . ops = & zt5550_hpc_ops ;
if ( ! poll ) {
zt5550_hpc . irq = hc_dev - > irq ;
2006-07-01 19:29:41 -07:00
zt5550_hpc . irq_flags = IRQF_SHARED ;
2005-04-16 15:20:36 -07:00
zt5550_hpc . dev_id = hc_dev ;
zt5550_hpc_ops . enable_irq = zt5550_hc_enable_irq ;
zt5550_hpc_ops . disable_irq = zt5550_hc_disable_irq ;
zt5550_hpc_ops . check_irq = zt5550_hc_check_irq ;
} else {
info ( " using ENUM# polling mode " ) ;
}
status = cpci_hp_register_controller ( & zt5550_hpc ) ;
if ( status ! = 0 ) {
err ( " could not register cPCI hotplug controller " ) ;
goto init_hc_error ;
}
dbg ( " registered controller " ) ;
/* Look for first device matching cPCI bus's bridge vendor and device IDs */
if ( ! ( bus0_dev = pci_get_device ( PCI_VENDOR_ID_DEC ,
PCI_DEVICE_ID_DEC_21154 , NULL ) ) ) {
status = - ENODEV ;
goto init_register_error ;
}
bus0 = bus0_dev - > subordinate ;
pci_dev_put ( bus0_dev ) ;
status = cpci_hp_register_bus ( bus0 , 0x0a , 0x0f ) ;
if ( status ! = 0 ) {
err ( " could not register cPCI hotplug bus " ) ;
goto init_register_error ;
}
dbg ( " registered bus " ) ;
status = cpci_hp_start ( ) ;
if ( status ! = 0 ) {
err ( " could not started cPCI hotplug system " ) ;
cpci_hp_unregister_bus ( bus0 ) ;
goto init_register_error ;
}
dbg ( " started cpci hp system " ) ;
return 0 ;
init_register_error :
cpci_hp_unregister_controller ( & zt5550_hpc ) ;
init_hc_error :
err ( " status = %d " , status ) ;
zt5550_hc_cleanup ( ) ;
return status ;
}
2012-11-21 15:35:00 -05:00
static void zt5550_hc_remove_one ( struct pci_dev * pdev )
2005-04-16 15:20:36 -07:00
{
cpci_hp_stop ( ) ;
cpci_hp_unregister_bus ( bus0 ) ;
cpci_hp_unregister_controller ( & zt5550_hpc ) ;
zt5550_hc_cleanup ( ) ;
}
static struct pci_device_id zt5550_hc_pci_tbl [ ] = {
{ PCI_VENDOR_ID_ZIATECH , PCI_DEVICE_ID_ZIATECH_5550_HC , PCI_ANY_ID , PCI_ANY_ID , } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , zt5550_hc_pci_tbl ) ;
static struct pci_driver zt5550_hc_driver = {
. name = " zt5550_hc " ,
. id_table = zt5550_hc_pci_tbl ,
. probe = zt5550_hc_init_one ,
2012-11-21 15:35:00 -05:00
. remove = zt5550_hc_remove_one ,
2005-04-16 15:20:36 -07:00
} ;
static int __init zt5550_init ( void )
{
struct resource * r ;
2007-04-13 15:34:26 -07:00
int rc ;
2005-04-16 15:20:36 -07:00
info ( DRIVER_DESC " version: " DRIVER_VERSION ) ;
r = request_region ( ENUM_PORT , 1 , " #ENUM hotswap signal register " ) ;
if ( ! r )
return - EBUSY ;
2007-04-13 15:34:26 -07:00
rc = pci_register_driver ( & zt5550_hc_driver ) ;
if ( rc < 0 )
release_region ( ENUM_PORT , 1 ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
static void __exit
zt5550_exit ( void )
{
pci_unregister_driver ( & zt5550_hc_driver ) ;
release_region ( ENUM_PORT , 1 ) ;
}
module_init ( zt5550_init ) ;
module_exit ( zt5550_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , 0644 ) ;
MODULE_PARM_DESC ( debug , " Debugging mode enabled or not " ) ;
module_param ( poll , bool , 0644 ) ;
MODULE_PARM_DESC ( poll , " #ENUM polling mode enabled or not " ) ;