2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-02-02 14:26:25 +03:00
/*
* cros_ec_dev - expose the Chrome OS Embedded Controller to user - space
*
* Copyright ( C ) 2014 Google , Inc .
*/
2021-05-26 06:45:00 +03:00
# include <linux/dmi.h>
2020-01-15 02:22:20 +03:00
# include <linux/kconfig.h>
2016-08-01 12:54:38 +03:00
# include <linux/mfd/core.h>
2015-02-02 14:26:25 +03:00
# include <linux/module.h>
2018-06-20 08:47:28 +03:00
# include <linux/mod_devicetable.h>
2018-12-12 20:34:01 +03:00
# include <linux/of_platform.h>
2015-02-02 14:26:25 +03:00
# include <linux/platform_device.h>
2019-09-02 12:53:03 +03:00
# include <linux/platform_data/cros_ec_chardev.h>
2019-09-02 12:53:05 +03:00
# include <linux/platform_data/cros_ec_commands.h>
# include <linux/platform_data/cros_ec_proto.h>
2015-06-09 14:04:42 +03:00
# include <linux/slab.h>
2015-02-02 14:26:25 +03:00
2017-11-20 19:15:25 +03:00
# define DRV_NAME "cros-ec-dev"
2015-06-09 14:04:47 +03:00
static struct class cros_class = {
. name = " chromeos " ,
} ;
2019-09-02 12:53:07 +03:00
/**
2020-06-24 15:07:45 +03:00
* struct cros_feature_to_name - CrOS feature id to name / short description .
2019-09-02 12:53:07 +03:00
* @ id : The feature identifier .
* @ name : Device name associated with the feature id .
* @ desc : Short name that will be displayed .
*/
struct cros_feature_to_name {
unsigned int id ;
const char * name ;
const char * desc ;
} ;
2019-09-02 12:53:08 +03:00
/**
2020-06-24 15:07:45 +03:00
* struct cros_feature_to_cells - CrOS feature id to mfd cells association .
2019-09-02 12:53:08 +03:00
* @ id : The feature identifier .
* @ mfd_cells : Pointer to the array of mfd cells that needs to be added .
* @ num_cells : Number of mfd cells into the array .
*/
struct cros_feature_to_cells {
unsigned int id ;
const struct mfd_cell * mfd_cells ;
unsigned int num_cells ;
} ;
2019-09-02 12:53:07 +03:00
static const struct cros_feature_to_name cros_mcu_devices [ ] = {
{
. id = EC_FEATURE_FINGERPRINT ,
. name = CROS_EC_DEV_FP_NAME ,
. desc = " Fingerprint " ,
} ,
{
. id = EC_FEATURE_ISH ,
. name = CROS_EC_DEV_ISH_NAME ,
. desc = " Integrated Sensor Hub " ,
} ,
{
. id = EC_FEATURE_SCP ,
. name = CROS_EC_DEV_SCP_NAME ,
. desc = " System Control Processor " ,
} ,
{
. id = EC_FEATURE_TOUCHPAD ,
. name = CROS_EC_DEV_TP_NAME ,
. desc = " Touchpad " ,
} ,
} ;
2019-09-02 12:53:08 +03:00
static const struct mfd_cell cros_ec_cec_cells [ ] = {
{ . name = " cros-ec-cec " , } ,
} ;
static const struct mfd_cell cros_ec_rtc_cells [ ] = {
{ . name = " cros-ec-rtc " , } ,
} ;
2019-11-19 15:45:45 +03:00
static const struct mfd_cell cros_ec_sensorhub_cells [ ] = {
{ . name = " cros-ec-sensorhub " , } ,
} ;
2019-09-02 12:53:08 +03:00
static const struct mfd_cell cros_usbpd_charger_cells [ ] = {
{ . name = " cros-usbpd-charger " , } ,
{ . name = " cros-usbpd-logger " , } ,
} ;
2020-01-15 02:22:20 +03:00
static const struct mfd_cell cros_usbpd_notify_cells [ ] = {
{ . name = " cros-usbpd-notify " , } ,
} ;
2019-09-02 12:53:08 +03:00
static const struct cros_feature_to_cells cros_subdevices [ ] = {
{
. id = EC_FEATURE_CEC ,
. mfd_cells = cros_ec_cec_cells ,
. num_cells = ARRAY_SIZE ( cros_ec_cec_cells ) ,
} ,
{
. id = EC_FEATURE_RTC ,
. mfd_cells = cros_ec_rtc_cells ,
. num_cells = ARRAY_SIZE ( cros_ec_rtc_cells ) ,
} ,
{
. id = EC_FEATURE_USB_PD ,
. mfd_cells = cros_usbpd_charger_cells ,
. num_cells = ARRAY_SIZE ( cros_usbpd_charger_cells ) ,
} ,
} ;
static const struct mfd_cell cros_ec_platform_cells [ ] = {
{ . name = " cros-ec-chardev " , } ,
{ . name = " cros-ec-debugfs " , } ,
{ . name = " cros-ec-sysfs " , } ,
2022-04-19 03:04:08 +03:00
} ;
static const struct mfd_cell cros_ec_pchg_cells [ ] = {
2021-06-16 21:51:25 +03:00
{ . name = " cros-ec-pchg " , } ,
2019-09-02 12:53:08 +03:00
} ;
2021-05-26 06:45:00 +03:00
static const struct mfd_cell cros_ec_lightbar_cells [ ] = {
{ . name = " cros-ec-lightbar " , }
} ;
2019-09-02 12:53:08 +03:00
static const struct mfd_cell cros_ec_vbc_cells [ ] = {
{ . name = " cros-ec-vbc " , }
} ;
2018-12-04 18:58:43 +03:00
static void cros_ec_class_release ( struct device * dev )
{
kfree ( to_cros_ec_dev ( dev ) ) ;
}
2015-02-02 14:26:25 +03:00
static int ec_device_probe ( struct platform_device * pdev )
{
2015-06-09 14:04:47 +03:00
int retval = - ENOMEM ;
2018-12-12 20:34:01 +03:00
struct device_node * node ;
2015-06-09 14:04:47 +03:00
struct device * dev = & pdev - > dev ;
struct cros_ec_platform * ec_platform = dev_get_platdata ( dev ) ;
2018-12-04 18:58:43 +03:00
struct cros_ec_dev * ec = kzalloc ( sizeof ( * ec ) , GFP_KERNEL ) ;
2022-04-19 03:04:08 +03:00
struct ec_response_pchg_count pchg_count ;
2019-09-02 12:53:07 +03:00
int i ;
2015-06-09 14:04:47 +03:00
if ( ! ec )
return retval ;
2015-02-02 14:26:25 +03:00
2015-06-09 14:04:47 +03:00
dev_set_drvdata ( dev , ec ) ;
ec - > ec_dev = dev_get_drvdata ( dev - > parent ) ;
ec - > dev = dev ;
ec - > cmd_offset = ec_platform - > cmd_offset ;
2021-10-04 20:07:09 +03:00
ec - > features . flags [ 0 ] = - 1U ; /* Not cached yet */
ec - > features . flags [ 1 ] = - 1U ; /* Not cached yet */
2015-06-09 14:04:47 +03:00
device_initialize ( & ec - > class_dev ) ;
2015-02-02 14:26:25 +03:00
2019-09-02 12:53:07 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( cros_mcu_devices ) ; i + + ) {
2019-05-08 12:19:55 +03:00
/*
2019-09-02 12:53:07 +03:00
* Check whether this is actually a dedicated MCU rather
* than an standard EC .
2019-05-08 12:19:55 +03:00
*/
2019-09-02 12:53:07 +03:00
if ( cros_ec_check_features ( ec , cros_mcu_devices [ i ] . id ) ) {
dev_info ( dev , " CrOS %s MCU detected \n " ,
cros_mcu_devices [ i ] . desc ) ;
/*
* Help userspace differentiating ECs from other MCU ,
* regardless of the probing order .
*/
ec_platform - > ec_name = cros_mcu_devices [ i ] . name ;
break ;
}
2019-06-03 06:45:11 +03:00
}
2015-06-09 14:04:47 +03:00
/*
* Add the class device
*/
ec - > class_dev . class = & cros_class ;
ec - > class_dev . parent = dev ;
2018-12-04 18:58:43 +03:00
ec - > class_dev . release = cros_ec_class_release ;
2015-06-09 14:04:47 +03:00
retval = dev_set_name ( & ec - > class_dev , " %s " , ec_platform - > ec_name ) ;
if ( retval ) {
dev_err ( dev , " dev_set_name failed => %d \n " , retval ) ;
2017-03-17 21:48:14 +03:00
goto failed ;
2015-02-02 14:26:25 +03:00
}
2019-09-02 12:53:03 +03:00
retval = device_add ( & ec - > class_dev ) ;
if ( retval )
goto failed ;
2018-03-23 20:42:47 +03:00
/* check whether this EC is a sensor hub. */
2019-11-19 15:45:45 +03:00
if ( cros_ec_get_sensor_count ( ec ) > 0 ) {
retval = mfd_add_hotplug_devices ( ec - > dev ,
cros_ec_sensorhub_cells ,
ARRAY_SIZE ( cros_ec_sensorhub_cells ) ) ;
if ( retval )
dev_err ( ec - > dev , " failed to add %s subdevice: %d \n " ,
cros_ec_sensorhub_cells - > name , retval ) ;
}
2018-03-23 20:42:47 +03:00
2019-09-02 12:53:08 +03:00
/*
* The following subdevices can be detected by sending the
* EC_FEATURE_GET_CMD Embedded Controller device .
*/
for ( i = 0 ; i < ARRAY_SIZE ( cros_subdevices ) ; i + + ) {
if ( cros_ec_check_features ( ec , cros_subdevices [ i ] . id ) ) {
retval = mfd_add_hotplug_devices ( ec - > dev ,
cros_subdevices [ i ] . mfd_cells ,
cros_subdevices [ i ] . num_cells ) ;
if ( retval )
dev_err ( ec - > dev ,
" failed to add %s subdevice: %d \n " ,
cros_subdevices [ i ] . mfd_cells - > name ,
retval ) ;
}
2018-05-02 18:44:18 +03:00
}
2021-05-26 06:45:00 +03:00
/*
* Lightbar is a special case . Newer devices support autodetection ,
* but older ones do not .
*/
if ( cros_ec_check_features ( ec , EC_FEATURE_LIGHTBAR ) | |
dmi_match ( DMI_PRODUCT_NAME , " Link " ) ) {
retval = mfd_add_hotplug_devices ( ec - > dev ,
cros_ec_lightbar_cells ,
ARRAY_SIZE ( cros_ec_lightbar_cells ) ) ;
if ( retval )
dev_warn ( ec - > dev , " failed to add lightbar: %d \n " ,
retval ) ;
}
2020-01-15 02:22:20 +03:00
/*
* The PD notifier driver cell is separate since it only needs to be
* explicitly added on platforms that don ' t have the PD notifier ACPI
* device entry defined .
*/
2020-02-10 22:06:24 +03:00
if ( IS_ENABLED ( CONFIG_OF ) & & ec - > ec_dev - > dev - > of_node ) {
2020-01-15 02:22:20 +03:00
if ( cros_ec_check_features ( ec , EC_FEATURE_USB_PD ) ) {
retval = mfd_add_hotplug_devices ( ec - > dev ,
cros_usbpd_notify_cells ,
ARRAY_SIZE ( cros_usbpd_notify_cells ) ) ;
if ( retval )
dev_err ( ec - > dev ,
" failed to add PD notify devices: %d \n " ,
retval ) ;
}
}
2022-04-19 03:04:08 +03:00
/*
* The PCHG device cannot be detected by sending EC_FEATURE_GET_CMD , but
* it can be detected by querying the number of peripheral chargers .
*/
2022-06-06 23:18:03 +03:00
retval = cros_ec_cmd ( ec - > ec_dev , 0 , EC_CMD_PCHG_COUNT , NULL , 0 ,
& pchg_count , sizeof ( pchg_count ) ) ;
2022-04-19 03:04:08 +03:00
if ( retval > = 0 & & pchg_count . port_count ) {
retval = mfd_add_hotplug_devices ( ec - > dev ,
cros_ec_pchg_cells ,
ARRAY_SIZE ( cros_ec_pchg_cells ) ) ;
if ( retval )
dev_warn ( ec - > dev , " failed to add pchg: %d \n " ,
retval ) ;
}
2019-09-02 12:53:08 +03:00
/*
* The following subdevices cannot be detected by sending the
* EC_FEATURE_GET_CMD to the Embedded Controller device .
*/
2019-09-02 12:53:09 +03:00
retval = mfd_add_hotplug_devices ( ec - > dev , cros_ec_platform_cells ,
ARRAY_SIZE ( cros_ec_platform_cells ) ) ;
2018-12-12 20:33:57 +03:00
if ( retval )
dev_warn ( ec - > dev ,
" failed to add cros-ec platform devices: %d \n " ,
retval ) ;
2018-12-12 20:34:01 +03:00
/* Check whether this EC instance has a VBC NVRAM */
node = ec - > ec_dev - > dev - > of_node ;
if ( of_property_read_bool ( node , " google,has-vbc-nvram " ) ) {
2019-09-02 12:53:09 +03:00
retval = mfd_add_hotplug_devices ( ec - > dev , cros_ec_vbc_cells ,
ARRAY_SIZE ( cros_ec_vbc_cells ) ) ;
2018-12-12 20:34:01 +03:00
if ( retval )
dev_warn ( ec - > dev , " failed to add VBC devices: %d \n " ,
retval ) ;
}
2015-02-02 14:26:25 +03:00
return 0 ;
2015-06-09 14:04:47 +03:00
2017-03-17 21:48:14 +03:00
failed :
put_device ( & ec - > class_dev ) ;
2015-06-09 14:04:47 +03:00
return retval ;
2015-02-02 14:26:25 +03:00
}
static int ec_device_remove ( struct platform_device * pdev )
{
2015-06-09 14:04:47 +03:00
struct cros_ec_dev * ec = dev_get_drvdata ( & pdev - > dev ) ;
2017-05-16 18:46:48 +03:00
2018-12-10 21:00:02 +03:00
mfd_remove_devices ( ec - > dev ) ;
2015-06-09 14:04:47 +03:00
device_unregister ( & ec - > class_dev ) ;
2015-02-02 14:26:25 +03:00
return 0 ;
}
2015-06-22 09:27:20 +03:00
static const struct platform_device_id cros_ec_id [ ] = {
2017-11-20 19:15:25 +03:00
{ DRV_NAME , 0 } ,
2018-04-18 13:24:03 +03:00
{ /* sentinel */ }
2015-06-22 09:27:20 +03:00
} ;
MODULE_DEVICE_TABLE ( platform , cros_ec_id ) ;
2015-02-02 14:26:25 +03:00
static struct platform_driver cros_ec_dev_driver = {
. driver = {
2017-11-20 19:15:25 +03:00
. name = DRV_NAME ,
2015-02-02 14:26:25 +03:00
} ,
2018-09-27 06:33:17 +03:00
. id_table = cros_ec_id ,
2015-02-02 14:26:25 +03:00
. probe = ec_device_probe ,
. remove = ec_device_remove ,
} ;
static int __init cros_ec_dev_init ( void )
{
int ret ;
2015-06-09 14:04:47 +03:00
ret = class_register ( & cros_class ) ;
if ( ret ) {
2015-02-02 14:26:25 +03:00
pr_err ( CROS_EC_DEV_NAME " : failed to register device class \n " ) ;
2015-06-09 14:04:47 +03:00
return ret ;
2015-02-02 14:26:25 +03:00
}
/* Register the driver */
ret = platform_driver_register ( & cros_ec_dev_driver ) ;
if ( ret < 0 ) {
pr_warn ( CROS_EC_DEV_NAME " : can't register driver: %d \n " , ret ) ;
goto failed_devreg ;
}
return 0 ;
failed_devreg :
2015-06-09 14:04:47 +03:00
class_unregister ( & cros_class ) ;
2015-02-02 14:26:25 +03:00
return ret ;
}
static void __exit cros_ec_dev_exit ( void )
{
platform_driver_unregister ( & cros_ec_dev_driver ) ;
2015-06-09 14:04:47 +03:00
class_unregister ( & cros_class ) ;
2015-02-02 14:26:25 +03:00
}
module_init ( cros_ec_dev_init ) ;
module_exit ( cros_ec_dev_exit ) ;
MODULE_AUTHOR ( " Bill Richardson <wfrichar@chromium.org> " ) ;
MODULE_DESCRIPTION ( " Userspace interface to the Chrome OS Embedded Controller " ) ;
MODULE_VERSION ( " 1.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;