2005-04-17 02:20:36 +04:00
/*
* Linux driver attachment glue for PCI based controllers .
*
* Copyright ( c ) 2000 - 2001 Adaptec Inc .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the " NO WARRANTY " disclaimer below
* ( " Disclaimer " ) and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution .
* 3. Neither the names of the above - listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) version 2 as published by the Free
* Software Foundation .
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES .
*
* $ Id : //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#47 $
*/
# include "aic7xxx_osm.h"
# include "aic7xxx_pci.h"
static int ahc_linux_pci_dev_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent ) ;
static int ahc_linux_pci_reserve_io_region ( struct ahc_softc * ahc ,
u_long * base ) ;
static int ahc_linux_pci_reserve_mem_region ( struct ahc_softc * ahc ,
u_long * bus_addr ,
uint8_t __iomem * * maddr ) ;
static void ahc_linux_pci_dev_remove ( struct pci_dev * pdev ) ;
/* Define the macro locally since it's different for different class of chips.
*/
# define ID(x) ID_C(x, PCI_CLASS_STORAGE_SCSI)
static struct pci_device_id ahc_linux_pci_id_table [ ] = {
/* aic7850 based controllers */
ID ( ID_AHA_2902_04_10_15_20C_30C ) ,
/* aic7860 based controllers */
ID ( ID_AHA_2930CU ) ,
ID ( ID_AHA_1480A & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK ) ,
/* aic7870 based controllers */
ID ( ID_AHA_2940 ) ,
ID ( ID_AHA_3940 ) ,
ID ( ID_AHA_398X ) ,
ID ( ID_AHA_2944 ) ,
ID ( ID_AHA_3944 ) ,
ID ( ID_AHA_4944 ) ,
/* aic7880 based controllers */
ID ( ID_AHA_2940U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_3940U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2944U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_3944U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_398XU & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_4944U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2930U & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK ) ,
/* aic7890 based controllers */
ID ( ID_AHA_2930U2 ) ,
ID ( ID_AHA_2940U2B ) ,
ID ( ID_AHA_2940U2_OEM ) ,
ID ( ID_AHA_2940U2 ) ,
ID ( ID_AHA_2950U2B ) ,
ID16 ( ID_AIC7890_ARO & ID_AIC7895_ARO_MASK ) ,
ID ( ID_AAA_131U2 ) ,
/* aic7890 based controllers */
ID ( ID_AHA_29160 ) ,
ID ( ID_AHA_29160_CPQ ) ,
ID ( ID_AHA_29160N ) ,
ID ( ID_AHA_29160C ) ,
ID ( ID_AHA_29160B ) ,
ID ( ID_AHA_19160B ) ,
ID ( ID_AIC7892_ARO ) ,
/* aic7892 based controllers */
ID ( ID_AHA_2940U_DUAL ) ,
ID ( ID_AHA_3940AU ) ,
ID ( ID_AHA_3944AU ) ,
ID ( ID_AIC7895_ARO ) ,
ID ( ID_AHA_3950U2B_0 ) ,
ID ( ID_AHA_3950U2B_1 ) ,
ID ( ID_AHA_3950U2D_0 ) ,
ID ( ID_AHA_3950U2D_1 ) ,
ID ( ID_AIC7896_ARO ) ,
/* aic7899 based controllers */
ID ( ID_AHA_3960D ) ,
ID ( ID_AHA_3960D_CPQ ) ,
ID ( ID_AIC7899_ARO ) ,
/* Generic chip probes for devices we don't know exactly. */
ID ( ID_AIC7850 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7855 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7859 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7860 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7870 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7880 & ID_DEV_VENDOR_MASK ) ,
ID16 ( ID_AIC7890 & ID_9005_GENERIC_MASK ) ,
ID16 ( ID_AIC7892 & ID_9005_GENERIC_MASK ) ,
ID ( ID_AIC7895 & ID_DEV_VENDOR_MASK ) ,
ID16 ( ID_AIC7896 & ID_9005_GENERIC_MASK ) ,
ID16 ( ID_AIC7899 & ID_9005_GENERIC_MASK ) ,
ID ( ID_AIC7810 & ID_DEV_VENDOR_MASK ) ,
ID ( ID_AIC7815 & ID_DEV_VENDOR_MASK ) ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , ahc_linux_pci_id_table ) ;
2006-10-21 01:47:57 +04:00
static struct pci_driver aic7xxx_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " aic7xxx " ,
. probe = ahc_linux_pci_dev_probe ,
. remove = ahc_linux_pci_dev_remove ,
. id_table = ahc_linux_pci_id_table
} ;
static void
ahc_linux_pci_dev_remove ( struct pci_dev * pdev )
{
2005-06-11 02:14:30 +04:00
struct ahc_softc * ahc = pci_get_drvdata ( pdev ) ;
u_long s ;
2005-04-17 02:20:36 +04:00
2005-10-03 00:22:35 +04:00
if ( ahc - > platform_data & & ahc - > platform_data - > host )
scsi_remove_host ( ahc - > platform_data - > host ) ;
2005-06-11 02:14:30 +04:00
ahc_lock ( ahc , & s ) ;
ahc_intr_enable ( ahc , FALSE ) ;
ahc_unlock ( ahc , & s ) ;
ahc_free ( ahc ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-13 18:42:45 +04:00
static void
ahc_linux_pci_inherit_flags ( struct ahc_softc * ahc )
{
struct pci_dev * pdev = ahc - > dev_softc , * master_pdev ;
unsigned int master_devfn = PCI_DEVFN ( PCI_SLOT ( pdev - > devfn ) , 0 ) ;
master_pdev = pci_get_slot ( pdev - > bus , master_devfn ) ;
if ( master_pdev ) {
struct ahc_softc * master = pci_get_drvdata ( master_pdev ) ;
if ( master ) {
ahc - > flags & = ~ AHC_BIOS_ENABLED ;
ahc - > flags | = master - > flags & AHC_BIOS_ENABLED ;
ahc - > flags & = ~ AHC_PRIMARY_CHANNEL ;
ahc - > flags | = master - > flags & AHC_PRIMARY_CHANNEL ;
} else
printk ( KERN_ERR " aic7xxx: no multichannel peer found! \n " ) ;
pci_dev_put ( master_pdev ) ;
}
}
2005-04-17 02:20:36 +04:00
static int
ahc_linux_pci_dev_probe ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
char buf [ 80 ] ;
const uint64_t mask_39bit = 0x7FFFFFFFFFULL ;
struct ahc_softc * ahc ;
ahc_dev_softc_t pci ;
struct ahc_pci_identity * entry ;
char * name ;
int error ;
2005-08-30 20:48:07 +04:00
struct device * dev = & pdev - > dev ;
2005-04-17 02:20:36 +04:00
pci = pdev ;
entry = ahc_find_pci_device ( pci ) ;
if ( entry = = NULL )
return ( - ENODEV ) ;
/*
* Allocate a softc for this card and
* set it up for attachment by our
* common detect routine .
*/
sprintf ( buf , " ahc_pci:%d:%d:%d " ,
ahc_get_pci_bus ( pci ) ,
ahc_get_pci_slot ( pci ) ,
ahc_get_pci_function ( pci ) ) ;
name = malloc ( strlen ( buf ) + 1 , M_DEVBUF , M_NOWAIT ) ;
if ( name = = NULL )
return ( - ENOMEM ) ;
strcpy ( name , buf ) ;
ahc = ahc_alloc ( NULL , name ) ;
if ( ahc = = NULL )
return ( - ENOMEM ) ;
if ( pci_enable_device ( pdev ) ) {
ahc_free ( ahc ) ;
return ( - ENODEV ) ;
}
pci_set_master ( pdev ) ;
if ( sizeof ( dma_addr_t ) > 4
2005-08-30 20:48:07 +04:00
& & ahc - > features & AHC_LARGE_SCBS
& & dma_set_mask ( dev , mask_39bit ) = = 0
& & dma_get_required_mask ( dev ) > DMA_32BIT_MASK ) {
2005-04-17 02:20:36 +04:00
ahc - > flags | = AHC_39BIT_ADDRESSING ;
} else {
2005-08-30 20:48:07 +04:00
if ( dma_set_mask ( dev , DMA_32BIT_MASK ) ) {
2006-04-23 22:16:02 +04:00
ahc_free ( ahc ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " aic7xxx: No suitable DMA available. \n " ) ;
return ( - ENODEV ) ;
}
}
ahc - > dev_softc = pci ;
error = ahc_pci_config ( ahc , entry ) ;
if ( error ! = 0 ) {
ahc_free ( ahc ) ;
return ( - error ) ;
}
2005-08-13 18:42:45 +04:00
/*
* Second Function PCI devices need to inherit some
* settings from function 0.
*/
if ( ( ahc - > features & AHC_MULTI_FUNC ) & & PCI_FUNC ( pdev - > devfn ) ! = 0 )
ahc_linux_pci_inherit_flags ( ahc ) ;
2005-04-17 02:20:36 +04:00
pci_set_drvdata ( pdev , ahc ) ;
2005-06-28 18:49:44 +04:00
ahc_linux_register_host ( ahc , & aic7xxx_driver_template ) ;
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
}
int
ahc_linux_pci_init ( void )
{
2006-09-26 03:58:58 +04:00
return pci_register_driver ( & aic7xxx_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
void
ahc_linux_pci_exit ( void )
{
pci_unregister_driver ( & aic7xxx_pci_driver ) ;
}
static int
ahc_linux_pci_reserve_io_region ( struct ahc_softc * ahc , u_long * base )
{
if ( aic7xxx_allow_memio = = 0 )
return ( ENOMEM ) ;
* base = pci_resource_start ( ahc - > dev_softc , 0 ) ;
if ( * base = = 0 )
return ( ENOMEM ) ;
if ( request_region ( * base , 256 , " aic7xxx " ) = = 0 )
return ( ENOMEM ) ;
return ( 0 ) ;
}
static int
ahc_linux_pci_reserve_mem_region ( struct ahc_softc * ahc ,
u_long * bus_addr ,
uint8_t __iomem * * maddr )
{
u_long start ;
int error ;
error = 0 ;
start = pci_resource_start ( ahc - > dev_softc , 1 ) ;
if ( start ! = 0 ) {
* bus_addr = start ;
if ( request_mem_region ( start , 0x1000 , " aic7xxx " ) = = 0 )
error = ENOMEM ;
if ( error = = 0 ) {
* maddr = ioremap_nocache ( start , 256 ) ;
if ( * maddr = = NULL ) {
error = ENOMEM ;
release_mem_region ( start , 0x1000 ) ;
}
}
} else
error = ENOMEM ;
return ( error ) ;
}
int
ahc_pci_map_registers ( struct ahc_softc * ahc )
{
uint32_t command ;
u_long base ;
uint8_t __iomem * maddr ;
int error ;
/*
* If its allowed , we prefer memory mapped access .
*/
command = ahc_pci_read_config ( ahc - > dev_softc , PCIR_COMMAND , 4 ) ;
command & = ~ ( PCIM_CMD_PORTEN | PCIM_CMD_MEMEN ) ;
base = 0 ;
maddr = NULL ;
error = ahc_linux_pci_reserve_mem_region ( ahc , & base , & maddr ) ;
if ( error = = 0 ) {
ahc - > platform_data - > mem_busaddr = base ;
ahc - > tag = BUS_SPACE_MEMIO ;
ahc - > bsh . maddr = maddr ;
ahc_pci_write_config ( ahc - > dev_softc , PCIR_COMMAND ,
command | PCIM_CMD_MEMEN , 4 ) ;
/*
* Do a quick test to see if memory mapped
* I / O is functioning correctly .
*/
if ( ahc_pci_test_register_access ( ahc ) ! = 0 ) {
printf ( " aic7xxx: PCI Device %d:%d:%d "
" failed memory mapped test. Using PIO. \n " ,
ahc_get_pci_bus ( ahc - > dev_softc ) ,
ahc_get_pci_slot ( ahc - > dev_softc ) ,
ahc_get_pci_function ( ahc - > dev_softc ) ) ;
iounmap ( maddr ) ;
release_mem_region ( ahc - > platform_data - > mem_busaddr ,
0x1000 ) ;
ahc - > bsh . maddr = NULL ;
maddr = NULL ;
} else
command | = PCIM_CMD_MEMEN ;
} else {
printf ( " aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
" unavailable. Cannot memory map device. \n " ,
ahc_get_pci_bus ( ahc - > dev_softc ) ,
ahc_get_pci_slot ( ahc - > dev_softc ) ,
ahc_get_pci_function ( ahc - > dev_softc ) ,
base ) ;
}
/*
* We always prefer memory mapped access .
*/
if ( maddr = = NULL ) {
error = ahc_linux_pci_reserve_io_region ( ahc , & base ) ;
if ( error = = 0 ) {
ahc - > tag = BUS_SPACE_PIO ;
ahc - > bsh . ioport = base ;
command | = PCIM_CMD_PORTEN ;
} else {
printf ( " aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
" unavailable. Cannot map device. \n " ,
ahc_get_pci_bus ( ahc - > dev_softc ) ,
ahc_get_pci_slot ( ahc - > dev_softc ) ,
ahc_get_pci_function ( ahc - > dev_softc ) ,
base ) ;
}
}
ahc_pci_write_config ( ahc - > dev_softc , PCIR_COMMAND , command , 4 ) ;
return ( error ) ;
}
int
ahc_pci_map_int ( struct ahc_softc * ahc )
{
int error ;
error = request_irq ( ahc - > dev_softc - > irq , ahc_linux_isr ,
2006-07-02 06:29:42 +04:00
IRQF_SHARED , " aic7xxx " , ahc ) ;
2005-04-17 02:20:36 +04:00
if ( error = = 0 )
ahc - > platform_data - > irq = ahc - > dev_softc - > irq ;
return ( - error ) ;
}