2013-02-26 02:08:37 +04:00
/*
* ChromeOS EC multi - function device
*
* Copyright ( C ) 2012 Google , Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that 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 .
*
* The ChromeOS EC multi function device is used to mux all the requests
* to the EC device for its multiple features : keyboard controller ,
* battery charging and regulator control , firmware update .
*/
# include <linux/interrupt.h>
# include <linux/slab.h>
2013-03-20 12:46:15 +04:00
# include <linux/module.h>
2013-02-26 02:08:37 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/cros_ec.h>
# include <linux/mfd/cros_ec_commands.h>
2014-09-18 19:18:58 +04:00
# include <linux/delay.h>
# define EC_COMMAND_RETRIES 50
2013-02-26 02:08:37 +04:00
int cros_ec_prepare_tx ( struct cros_ec_device * ec_dev ,
2014-06-18 22:14:02 +04:00
struct cros_ec_command * msg )
2013-02-26 02:08:37 +04:00
{
uint8_t * out ;
int csum , i ;
2014-06-18 22:14:02 +04:00
BUG_ON ( msg - > outsize > EC_PROTO2_MAX_PARAM_SIZE ) ;
2013-02-26 02:08:37 +04:00
out = ec_dev - > dout ;
out [ 0 ] = EC_CMD_VERSION0 + msg - > version ;
2014-06-18 22:14:02 +04:00
out [ 1 ] = msg - > command ;
out [ 2 ] = msg - > outsize ;
2013-02-26 02:08:37 +04:00
csum = out [ 0 ] + out [ 1 ] + out [ 2 ] ;
2014-06-18 22:14:02 +04:00
for ( i = 0 ; i < msg - > outsize ; i + + )
csum + = out [ EC_MSG_TX_HEADER_BYTES + i ] = msg - > outdata [ i ] ;
out [ EC_MSG_TX_HEADER_BYTES + msg - > outsize ] = ( uint8_t ) ( csum & 0xff ) ;
2013-02-26 02:08:37 +04:00
2014-06-18 22:14:02 +04:00
return EC_MSG_TX_PROTO_BYTES + msg - > outsize ;
2013-02-26 02:08:37 +04:00
}
2013-03-20 12:46:15 +04:00
EXPORT_SYMBOL ( cros_ec_prepare_tx ) ;
2013-02-26 02:08:37 +04:00
2014-06-18 22:14:05 +04:00
int cros_ec_check_result ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
switch ( msg - > result ) {
case EC_RES_SUCCESS :
return 0 ;
case EC_RES_IN_PROGRESS :
dev_dbg ( ec_dev - > dev , " command 0x%02x in progress \n " ,
msg - > command ) ;
return - EAGAIN ;
default :
dev_dbg ( ec_dev - > dev , " command 0x%02x returned %d \n " ,
msg - > command , msg - > result ) ;
return 0 ;
}
}
EXPORT_SYMBOL ( cros_ec_check_result ) ;
2014-09-18 19:18:56 +04:00
int cros_ec_cmd_xfer ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
2014-09-18 19:18:57 +04:00
int ret ;
mutex_lock ( & ec_dev - > lock ) ;
ret = ec_dev - > cmd_xfer ( ec_dev , msg ) ;
2014-09-18 19:18:58 +04:00
if ( msg - > result = = EC_RES_IN_PROGRESS ) {
int i ;
struct cros_ec_command status_msg ;
struct ec_response_get_comms_status status ;
status_msg . version = 0 ;
status_msg . command = EC_CMD_GET_COMMS_STATUS ;
status_msg . outdata = NULL ;
status_msg . outsize = 0 ;
status_msg . indata = ( uint8_t * ) & status ;
status_msg . insize = sizeof ( status ) ;
/*
* Query the EC ' s status until it ' s no longer busy or
* we encounter an error .
*/
for ( i = 0 ; i < EC_COMMAND_RETRIES ; i + + ) {
usleep_range ( 10000 , 11000 ) ;
ret = ec_dev - > cmd_xfer ( ec_dev , & status_msg ) ;
if ( ret < 0 )
break ;
msg - > result = status_msg . result ;
if ( status_msg . result ! = EC_RES_SUCCESS )
break ;
if ( ! ( status . flags & EC_COMMS_STATUS_PROCESSING ) )
break ;
}
}
2014-09-18 19:18:57 +04:00
mutex_unlock ( & ec_dev - > lock ) ;
return ret ;
2014-09-18 19:18:56 +04:00
}
EXPORT_SYMBOL ( cros_ec_cmd_xfer ) ;
2013-11-18 17:33:06 +04:00
static const struct mfd_cell cros_devs [ ] = {
2013-02-26 02:08:37 +04:00
{
. name = " cros-ec-keyb " ,
. id = 1 ,
. of_compatible = " google,cros-ec-keyb " ,
} ,
2014-04-30 21:44:09 +04:00
{
. name = " cros-ec-i2c-tunnel " ,
. id = 2 ,
. of_compatible = " google,cros-ec-i2c-tunnel " ,
} ,
2013-02-26 02:08:37 +04:00
} ;
int cros_ec_register ( struct cros_ec_device * ec_dev )
{
struct device * dev = ec_dev - > dev ;
int err = 0 ;
if ( ec_dev - > din_size ) {
2013-05-23 19:25:10 +04:00
ec_dev - > din = devm_kzalloc ( dev , ec_dev - > din_size , GFP_KERNEL ) ;
if ( ! ec_dev - > din )
return - ENOMEM ;
2013-02-26 02:08:37 +04:00
}
if ( ec_dev - > dout_size ) {
2013-05-23 19:25:10 +04:00
ec_dev - > dout = devm_kzalloc ( dev , ec_dev - > dout_size , GFP_KERNEL ) ;
if ( ! ec_dev - > dout )
return - ENOMEM ;
2013-02-26 02:08:37 +04:00
}
2014-09-18 19:18:57 +04:00
mutex_init ( & ec_dev - > lock ) ;
2013-02-26 02:08:37 +04:00
err = mfd_add_devices ( dev , 0 , cros_devs ,
ARRAY_SIZE ( cros_devs ) ,
NULL , ec_dev - > irq , NULL ) ;
if ( err ) {
dev_err ( dev , " failed to add mfd devices \n " ) ;
2014-06-18 22:14:07 +04:00
return err ;
2013-02-26 02:08:37 +04:00
}
2014-06-18 22:14:03 +04:00
dev_info ( dev , " Chrome EC device registered \n " ) ;
2013-02-26 02:08:37 +04:00
return 0 ;
}
2013-03-20 12:46:15 +04:00
EXPORT_SYMBOL ( cros_ec_register ) ;
2013-02-26 02:08:37 +04:00
int cros_ec_remove ( struct cros_ec_device * ec_dev )
{
mfd_remove_devices ( ec_dev - > dev ) ;
return 0 ;
}
2013-03-20 12:46:15 +04:00
EXPORT_SYMBOL ( cros_ec_remove ) ;
2013-02-26 02:08:37 +04:00
# ifdef CONFIG_PM_SLEEP
int cros_ec_suspend ( struct cros_ec_device * ec_dev )
{
struct device * dev = ec_dev - > dev ;
if ( device_may_wakeup ( dev ) )
ec_dev - > wake_enabled = ! enable_irq_wake ( ec_dev - > irq ) ;
disable_irq ( ec_dev - > irq ) ;
ec_dev - > was_wake_device = ec_dev - > wake_enabled ;
return 0 ;
}
2013-03-20 12:46:15 +04:00
EXPORT_SYMBOL ( cros_ec_suspend ) ;
2013-02-26 02:08:37 +04:00
int cros_ec_resume ( struct cros_ec_device * ec_dev )
{
enable_irq ( ec_dev - > irq ) ;
if ( ec_dev - > wake_enabled ) {
disable_irq_wake ( ec_dev - > irq ) ;
ec_dev - > wake_enabled = 0 ;
}
return 0 ;
}
2013-03-20 12:46:15 +04:00
EXPORT_SYMBOL ( cros_ec_resume ) ;
2013-02-26 02:08:37 +04:00
# endif
2014-04-17 23:32:17 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " ChromeOS EC core driver " ) ;