2016-09-15 15:44:03 +03:00
/* ADC MFD core driver for sunxi platforms
*
* Copyright ( c ) 2016 Quentin Schulz < quentin . schulz @ free - electrons . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*/
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/mfd/core.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/regmap.h>
# include <linux/mfd/sun4i-gpadc.h>
# define ARCH_SUN4I_A10 0
# define ARCH_SUN5I_A13 1
# define ARCH_SUN6I_A31 2
static struct resource adc_resources [ ] = {
DEFINE_RES_IRQ_NAMED ( SUN4I_GPADC_IRQ_FIFO_DATA , " FIFO_DATA_PENDING " ) ,
DEFINE_RES_IRQ_NAMED ( SUN4I_GPADC_IRQ_TEMP_DATA , " TEMP_DATA_PENDING " ) ,
} ;
static const struct regmap_irq sun4i_gpadc_regmap_irq [ ] = {
REGMAP_IRQ_REG ( SUN4I_GPADC_IRQ_FIFO_DATA , 0 ,
SUN4I_GPADC_INT_FIFOC_TP_DATA_IRQ_EN ) ,
REGMAP_IRQ_REG ( SUN4I_GPADC_IRQ_TEMP_DATA , 0 ,
SUN4I_GPADC_INT_FIFOC_TEMP_IRQ_EN ) ,
} ;
static const struct regmap_irq_chip sun4i_gpadc_regmap_irq_chip = {
. name = " sun4i_gpadc_irq_chip " ,
. status_base = SUN4I_GPADC_INT_FIFOS ,
. ack_base = SUN4I_GPADC_INT_FIFOS ,
. mask_base = SUN4I_GPADC_INT_FIFOC ,
. init_ack_masked = true ,
. mask_invert = true ,
. irqs = sun4i_gpadc_regmap_irq ,
. num_irqs = ARRAY_SIZE ( sun4i_gpadc_regmap_irq ) ,
. num_regs = 1 ,
} ;
static struct mfd_cell sun4i_gpadc_cells [ ] = {
{
. name = " sun4i-a10-gpadc-iio " ,
. resources = adc_resources ,
. num_resources = ARRAY_SIZE ( adc_resources ) ,
} ,
{ . name = " iio_hwmon " }
} ;
static struct mfd_cell sun5i_gpadc_cells [ ] = {
{
. name = " sun5i-a13-gpadc-iio " ,
. resources = adc_resources ,
. num_resources = ARRAY_SIZE ( adc_resources ) ,
} ,
{ . name = " iio_hwmon " } ,
} ;
static struct mfd_cell sun6i_gpadc_cells [ ] = {
{
. name = " sun6i-a31-gpadc-iio " ,
. resources = adc_resources ,
. num_resources = ARRAY_SIZE ( adc_resources ) ,
} ,
{ . name = " iio_hwmon " } ,
} ;
static const struct regmap_config sun4i_gpadc_regmap_config = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. fast_io = true ,
} ;
static const struct of_device_id sun4i_gpadc_of_match [ ] = {
{
. compatible = " allwinner,sun4i-a10-ts " ,
. data = ( void * ) ARCH_SUN4I_A10 ,
} , {
. compatible = " allwinner,sun5i-a13-ts " ,
. data = ( void * ) ARCH_SUN5I_A13 ,
} , {
. compatible = " allwinner,sun6i-a31-ts " ,
. data = ( void * ) ARCH_SUN6I_A31 ,
} , { /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , sun4i_gpadc_of_match ) ;
static int sun4i_gpadc_probe ( struct platform_device * pdev )
{
struct sun4i_gpadc_dev * dev ;
struct resource * mem ;
const struct of_device_id * of_id ;
const struct mfd_cell * cells ;
unsigned int irq , size ;
int ret ;
of_id = of_match_node ( sun4i_gpadc_of_match , pdev - > dev . of_node ) ;
if ( ! of_id )
return - EINVAL ;
2016-10-26 19:07:35 +03:00
switch ( ( long ) of_id - > data ) {
2016-09-15 15:44:03 +03:00
case ARCH_SUN4I_A10 :
cells = sun4i_gpadc_cells ;
size = ARRAY_SIZE ( sun4i_gpadc_cells ) ;
break ;
case ARCH_SUN5I_A13 :
cells = sun5i_gpadc_cells ;
size = ARRAY_SIZE ( sun5i_gpadc_cells ) ;
break ;
case ARCH_SUN6I_A31 :
cells = sun6i_gpadc_cells ;
size = ARRAY_SIZE ( sun6i_gpadc_cells ) ;
break ;
default :
return - EINVAL ;
}
dev = devm_kzalloc ( & pdev - > dev , sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
dev - > base = devm_ioremap_resource ( & pdev - > dev , mem ) ;
if ( IS_ERR ( dev - > base ) )
return PTR_ERR ( dev - > base ) ;
dev - > dev = & pdev - > dev ;
dev_set_drvdata ( dev - > dev , dev ) ;
dev - > regmap = devm_regmap_init_mmio ( dev - > dev , dev - > base ,
& sun4i_gpadc_regmap_config ) ;
if ( IS_ERR ( dev - > regmap ) ) {
ret = PTR_ERR ( dev - > regmap ) ;
dev_err ( & pdev - > dev , " failed to init regmap: %d \n " , ret ) ;
return ret ;
}
/* Disable all interrupts */
regmap_write ( dev - > regmap , SUN4I_GPADC_INT_FIFOC , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
ret = devm_regmap_add_irq_chip ( & pdev - > dev , dev - > regmap , irq ,
IRQF_ONESHOT , 0 ,
& sun4i_gpadc_regmap_irq_chip ,
& dev - > regmap_irqc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add irq chip: %d \n " , ret ) ;
return ret ;
}
ret = devm_mfd_add_devices ( dev - > dev , 0 , cells , size , NULL , 0 , NULL ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add MFD devices: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static struct platform_driver sun4i_gpadc_driver = {
. driver = {
. name = " sun4i-gpadc " ,
. of_match_table = of_match_ptr ( sun4i_gpadc_of_match ) ,
} ,
. probe = sun4i_gpadc_probe ,
} ;
module_platform_driver ( sun4i_gpadc_driver ) ;
MODULE_DESCRIPTION ( " Allwinner sunxi platforms' GPADC MFD core driver " ) ;
MODULE_AUTHOR ( " Quentin Schulz <quentin.schulz@free-electrons.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;