2020-06-08 12:17:36 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Khadas System control Microcontroller
*
* Copyright ( C ) 2020 BayLibre SAS
*
* Author ( s ) : Neil Armstrong < narmstrong @ baylibre . com >
*/
# include <linux/bitfield.h>
# include <linux/i2c.h>
# include <linux/mfd/core.h>
# include <linux/mfd/khadas-mcu.h>
# include <linux/module.h>
# include <linux/regmap.h>
static bool khadas_mcu_reg_volatile ( struct device * dev , unsigned int reg )
{
if ( reg > = KHADAS_MCU_USER_DATA_0_REG & &
reg < KHADAS_MCU_PWR_OFF_CMD_REG )
return true ;
switch ( reg ) {
case KHADAS_MCU_PWR_OFF_CMD_REG :
case KHADAS_MCU_PASSWD_START_REG :
case KHADAS_MCU_CHECK_VEN_PASSWD_REG :
case KHADAS_MCU_CHECK_USER_PASSWD_REG :
case KHADAS_MCU_WOL_INIT_START_REG :
case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG :
return true ;
default :
return false ;
}
}
static bool khadas_mcu_reg_writeable ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case KHADAS_MCU_PASSWD_VEN_0_REG :
case KHADAS_MCU_PASSWD_VEN_1_REG :
case KHADAS_MCU_PASSWD_VEN_2_REG :
case KHADAS_MCU_PASSWD_VEN_3_REG :
case KHADAS_MCU_PASSWD_VEN_4_REG :
case KHADAS_MCU_PASSWD_VEN_5_REG :
case KHADAS_MCU_MAC_0_REG :
case KHADAS_MCU_MAC_1_REG :
case KHADAS_MCU_MAC_2_REG :
case KHADAS_MCU_MAC_3_REG :
case KHADAS_MCU_MAC_4_REG :
case KHADAS_MCU_MAC_5_REG :
case KHADAS_MCU_USID_0_REG :
case KHADAS_MCU_USID_1_REG :
case KHADAS_MCU_USID_2_REG :
case KHADAS_MCU_USID_3_REG :
case KHADAS_MCU_USID_4_REG :
case KHADAS_MCU_USID_5_REG :
case KHADAS_MCU_VERSION_0_REG :
case KHADAS_MCU_VERSION_1_REG :
case KHADAS_MCU_DEVICE_NO_0_REG :
case KHADAS_MCU_DEVICE_NO_1_REG :
case KHADAS_MCU_FACTORY_TEST_REG :
case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG :
return false ;
default :
return true ;
}
}
static const struct regmap_config khadas_mcu_regmap_config = {
. reg_bits = 8 ,
. reg_stride = 1 ,
. val_bits = 8 ,
. max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG ,
. volatile_reg = khadas_mcu_reg_volatile ,
. writeable_reg = khadas_mcu_reg_writeable ,
. cache_type = REGCACHE_RBTREE ,
} ;
static struct mfd_cell khadas_mcu_fan_cells [ ] = {
/* VIM1/2 Rev13+ and VIM3 only */
{ . name = " khadas-mcu-fan-ctrl " , } ,
} ;
static struct mfd_cell khadas_mcu_cells [ ] = {
{ . name = " khadas-mcu-user-mem " , } ,
} ;
static int khadas_mcu_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct khadas_mcu * ddata ;
int ret ;
ddata = devm_kzalloc ( dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
if ( ! ddata )
return - ENOMEM ;
i2c_set_clientdata ( client , ddata ) ;
ddata - > dev = dev ;
ddata - > regmap = devm_regmap_init_i2c ( client , & khadas_mcu_regmap_config ) ;
if ( IS_ERR ( ddata - > regmap ) ) {
ret = PTR_ERR ( ddata - > regmap ) ;
dev_err ( dev , " Failed to allocate register map: %d \n " , ret ) ;
return ret ;
}
ret = devm_mfd_add_devices ( dev , PLATFORM_DEVID_NONE ,
khadas_mcu_cells ,
ARRAY_SIZE ( khadas_mcu_cells ) ,
NULL , 0 , NULL ) ;
if ( ret )
return ret ;
if ( of_find_property ( dev - > of_node , " #cooling-cells " , NULL ) )
return devm_mfd_add_devices ( dev , PLATFORM_DEVID_NONE ,
khadas_mcu_fan_cells ,
ARRAY_SIZE ( khadas_mcu_fan_cells ) ,
NULL , 0 , NULL ) ;
return 0 ;
}
2020-08-28 11:37:04 +03:00
# ifdef CONFIG_OF
2020-06-08 12:17:36 +03:00
static const struct of_device_id khadas_mcu_of_match [ ] = {
{ . compatible = " khadas,mcu " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , khadas_mcu_of_match ) ;
2020-08-28 11:37:04 +03:00
# endif
2020-06-08 12:17:36 +03:00
static struct i2c_driver khadas_mcu_driver = {
. driver = {
. name = " khadas-mcu-core " ,
. of_match_table = of_match_ptr ( khadas_mcu_of_match ) ,
} ,
. probe = khadas_mcu_probe ,
} ;
module_i2c_driver ( khadas_mcu_driver ) ;
MODULE_DESCRIPTION ( " Khadas MCU core driver " ) ;
MODULE_AUTHOR ( " Neil Armstrong <narmstrong@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;