2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-02-26 20:29:06 +04:00
/*
* MEN Chameleon Bus .
*
* Copyright ( C ) 2014 MEN Mikroelektronik GmbH ( www . men . de )
* Author : Johannes Thumshirn < johannes . thumshirn @ men . de >
*/
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/mcb.h>
# include "mcb-internal.h"
struct priv {
struct mcb_bus * bus ;
2014-12-16 12:09:20 +03:00
phys_addr_t mapbase ;
2014-02-26 20:29:06 +04:00
void __iomem * base ;
} ;
2014-04-24 16:35:25 +04:00
static int mcb_pci_get_irq ( struct mcb_device * mdev )
{
struct mcb_bus * mbus = mdev - > bus ;
struct device * dev = mbus - > carrier ;
struct pci_dev * pdev = to_pci_dev ( dev ) ;
return pdev - > irq ;
}
2014-02-26 20:29:06 +04:00
static int mcb_pci_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
2014-12-16 12:09:20 +03:00
struct resource * res ;
2014-02-26 20:29:06 +04:00
struct priv * priv ;
2023-04-11 11:33:28 +03:00
int ret , table_size ;
2014-02-26 20:29:06 +04:00
unsigned long flags ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
ret = pci_enable_device ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to enable PCI device \n " ) ;
return - ENODEV ;
}
2016-09-14 13:05:23 +03:00
pci_set_master ( pdev ) ;
2014-02-26 20:29:06 +04:00
2014-12-16 12:09:20 +03:00
priv - > mapbase = pci_resource_start ( pdev , 0 ) ;
if ( ! priv - > mapbase ) {
2014-02-26 20:29:06 +04:00
dev_err ( & pdev - > dev , " No PCI resource \n " ) ;
2015-10-28 10:22:15 +03:00
ret = - ENODEV ;
2015-01-12 18:26:32 +03:00
goto out_disable ;
2014-02-26 20:29:06 +04:00
}
2016-05-03 13:42:01 +03:00
res = devm_request_mem_region ( & pdev - > dev , priv - > mapbase ,
CHAM_HEADER_SIZE ,
KBUILD_MODNAME ) ;
2015-03-26 22:12:49 +03:00
if ( ! res ) {
2014-12-16 12:09:20 +03:00
dev_err ( & pdev - > dev , " Failed to request PCI memory \n " ) ;
2015-03-26 22:12:49 +03:00
ret = - EBUSY ;
2015-01-12 18:26:32 +03:00
goto out_disable ;
2014-02-26 20:29:06 +04:00
}
2016-05-03 13:42:01 +03:00
priv - > base = devm_ioremap ( & pdev - > dev , priv - > mapbase , CHAM_HEADER_SIZE ) ;
2014-02-26 20:29:06 +04:00
if ( ! priv - > base ) {
dev_err ( & pdev - > dev , " Cannot ioremap \n " ) ;
ret = - ENOMEM ;
2016-05-03 13:42:01 +03:00
goto out_disable ;
2014-02-26 20:29:06 +04:00
}
flags = pci_resource_flags ( pdev , 0 ) ;
if ( flags & IORESOURCE_IO ) {
ret = - ENOTSUPP ;
dev_err ( & pdev - > dev ,
" IO mapped PCI devices are not supported \n " ) ;
2016-05-03 13:42:01 +03:00
goto out_disable ;
2014-02-26 20:29:06 +04:00
}
pci_set_drvdata ( pdev , priv ) ;
2014-04-24 16:35:25 +04:00
priv - > bus = mcb_alloc_bus ( & pdev - > dev ) ;
if ( IS_ERR ( priv - > bus ) ) {
ret = PTR_ERR ( priv - > bus ) ;
2016-05-03 13:42:01 +03:00
goto out_disable ;
2014-04-24 16:35:25 +04:00
}
priv - > bus - > get_irq = mcb_pci_get_irq ;
2014-02-26 20:29:06 +04:00
2014-12-16 12:09:20 +03:00
ret = chameleon_parse_cells ( priv - > bus , priv - > mapbase , priv - > base ) ;
2014-02-26 20:29:06 +04:00
if ( ret < 0 )
2015-08-24 11:18:38 +03:00
goto out_mcb_bus ;
2014-02-26 20:29:06 +04:00
2023-04-11 11:33:28 +03:00
table_size = ret ;
if ( table_size < CHAM_HEADER_SIZE ) {
/* Release the previous resources */
devm_iounmap ( & pdev - > dev , priv - > base ) ;
devm_release_mem_region ( & pdev - > dev , priv - > mapbase , CHAM_HEADER_SIZE ) ;
/* Then, allocate it again with the actual chameleon table size */
res = devm_request_mem_region ( & pdev - > dev , priv - > mapbase ,
table_size ,
KBUILD_MODNAME ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " Failed to request PCI memory \n " ) ;
ret = - EBUSY ;
goto out_mcb_bus ;
}
priv - > base = devm_ioremap ( & pdev - > dev , priv - > mapbase , table_size ) ;
if ( ! priv - > base ) {
dev_err ( & pdev - > dev , " Cannot ioremap \n " ) ;
ret = - ENOMEM ;
goto out_mcb_bus ;
}
}
2014-02-26 20:29:06 +04:00
mcb_bus_add_devices ( priv - > bus ) ;
2014-12-16 12:09:20 +03:00
return 0 ;
2015-08-24 11:18:38 +03:00
out_mcb_bus :
mcb_release_bus ( priv - > bus ) ;
2015-01-12 18:26:32 +03:00
out_disable :
2014-02-26 20:29:06 +04:00
pci_disable_device ( pdev ) ;
return ret ;
}
static void mcb_pci_remove ( struct pci_dev * pdev )
{
struct priv * priv = pci_get_drvdata ( pdev ) ;
mcb_release_bus ( priv - > bus ) ;
2014-12-16 12:09:20 +03:00
pci_disable_device ( pdev ) ;
2014-02-26 20:29:06 +04:00
}
static const struct pci_device_id mcb_pci_tbl [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_MEN , PCI_DEVICE_ID_MEN_CHAMELEON ) } ,
2018-03-12 12:41:19 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_ALTERA , PCI_DEVICE_ID_MEN_CHAMELEON ) } ,
2014-02-26 20:29:06 +04:00
{ 0 } ,
} ;
MODULE_DEVICE_TABLE ( pci , mcb_pci_tbl ) ;
static struct pci_driver mcb_pci_driver = {
. name = " mcb-pci " ,
. id_table = mcb_pci_tbl ,
. probe = mcb_pci_probe ,
. remove = mcb_pci_remove ,
} ;
module_pci_driver ( mcb_pci_driver ) ;
MODULE_AUTHOR ( " Johannes Thumshirn <johannes.thumshirn@men.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " MCB over PCI support " ) ;
2019-10-16 13:01:58 +03:00
MODULE_IMPORT_NS ( MCB ) ;