2012-06-12 06:32:14 +04:00
/*
* Copyright 2011 - 2012 Calxeda , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/ctype.h>
# include <linux/edac.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/of_platform.h>
# include "edac_module.h"
# define SR_CLR_SB_ECC_INTR 0x0
# define SR_CLR_DB_ECC_INTR 0x4
struct hb_l2_drvdata {
void __iomem * base ;
int sb_irq ;
int db_irq ;
} ;
static irqreturn_t highbank_l2_err_handler ( int irq , void * dev_id )
{
struct edac_device_ctl_info * dci = dev_id ;
struct hb_l2_drvdata * drvdata = dci - > pvt_info ;
if ( irq = = drvdata - > sb_irq ) {
writel ( 1 , drvdata - > base + SR_CLR_SB_ECC_INTR ) ;
edac_device_handle_ce ( dci , 0 , 0 , dci - > ctl_name ) ;
}
if ( irq = = drvdata - > db_irq ) {
writel ( 1 , drvdata - > base + SR_CLR_DB_ECC_INTR ) ;
edac_device_handle_ue ( dci , 0 , 0 , dci - > ctl_name ) ;
}
return IRQ_HANDLED ;
}
2013-10-10 19:36:19 +04:00
static const struct of_device_id hb_l2_err_of_match [ ] = {
{ . compatible = " calxeda,hb-sregs-l2-ecc " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , hb_l2_err_of_match ) ;
2012-12-22 01:23:51 +04:00
static int highbank_l2_err_probe ( struct platform_device * pdev )
2012-06-12 06:32:14 +04:00
{
2013-10-10 19:36:19 +04:00
const struct of_device_id * id ;
2012-06-12 06:32:14 +04:00
struct edac_device_ctl_info * dci ;
struct hb_l2_drvdata * drvdata ;
struct resource * r ;
int res = 0 ;
dci = edac_device_alloc_ctl_info ( sizeof ( * drvdata ) , " cpu " ,
1 , " L " , 1 , 2 , NULL , 0 , 0 ) ;
if ( ! dci )
return - ENOMEM ;
drvdata = dci - > pvt_info ;
dci - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , dci ) ;
if ( ! devres_open_group ( & pdev - > dev , NULL , GFP_KERNEL ) )
return - ENOMEM ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! r ) {
dev_err ( & pdev - > dev , " Unable to get mem resource \n " ) ;
res = - ENODEV ;
goto err ;
}
if ( ! devm_request_mem_region ( & pdev - > dev , r - > start ,
resource_size ( r ) , dev_name ( & pdev - > dev ) ) ) {
dev_err ( & pdev - > dev , " Error while requesting mem region \n " ) ;
res = - EBUSY ;
goto err ;
}
drvdata - > base = devm_ioremap ( & pdev - > dev , r - > start , resource_size ( r ) ) ;
if ( ! drvdata - > base ) {
dev_err ( & pdev - > dev , " Unable to map regs \n " ) ;
res = - ENOMEM ;
goto err ;
}
2013-10-10 19:36:19 +04:00
id = of_match_device ( hb_l2_err_of_match , & pdev - > dev ) ;
dci - > mod_name = pdev - > dev . driver - > name ;
dci - > ctl_name = id ? id - > compatible : " unknown " ;
2013-10-10 20:23:38 +04:00
dci - > dev_name = dev_name ( & pdev - > dev ) ;
if ( edac_device_add_device ( dci ) )
goto err ;
2012-06-12 06:32:14 +04:00
drvdata - > db_irq = platform_get_irq ( pdev , 0 ) ;
res = devm_request_irq ( & pdev - > dev , drvdata - > db_irq ,
highbank_l2_err_handler ,
0 , dev_name ( & pdev - > dev ) , dci ) ;
if ( res < 0 )
2013-10-10 20:23:38 +04:00
goto err2 ;
2012-06-12 06:32:14 +04:00
drvdata - > sb_irq = platform_get_irq ( pdev , 1 ) ;
res = devm_request_irq ( & pdev - > dev , drvdata - > sb_irq ,
highbank_l2_err_handler ,
0 , dev_name ( & pdev - > dev ) , dci ) ;
if ( res < 0 )
2013-10-10 20:23:38 +04:00
goto err2 ;
2012-06-12 06:32:14 +04:00
devres_close_group ( & pdev - > dev , NULL ) ;
return 0 ;
2013-10-10 20:23:38 +04:00
err2 :
edac_device_del_device ( & pdev - > dev ) ;
2012-06-12 06:32:14 +04:00
err :
devres_release_group ( & pdev - > dev , NULL ) ;
edac_device_free_ctl_info ( dci ) ;
return res ;
}
static int highbank_l2_err_remove ( struct platform_device * pdev )
{
struct edac_device_ctl_info * dci = platform_get_drvdata ( pdev ) ;
edac_device_del_device ( & pdev - > dev ) ;
edac_device_free_ctl_info ( dci ) ;
return 0 ;
}
static struct platform_driver highbank_l2_edac_driver = {
. probe = highbank_l2_err_probe ,
. remove = highbank_l2_err_remove ,
. driver = {
. name = " hb_l2_edac " ,
. of_match_table = hb_l2_err_of_match ,
} ,
} ;
module_platform_driver ( highbank_l2_edac_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Calxeda, Inc. " ) ;
MODULE_DESCRIPTION ( " EDAC Driver for Calxeda Highbank L2 Cache " ) ;