2016-11-22 17:44:12 -08:00
/*
* Copyright ( c ) 2007 - 2016 , Synaptics Incorporated
* Copyright ( C ) 2016 Zodiac Inflight Innovations
*
* 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/kernel.h>
# include <linux/rmi.h>
# include <linux/firmware.h>
# include <asm/unaligned.h>
# include <asm/unaligned.h>
2016-12-10 23:27:32 -08:00
# include <linux/bitops.h>
2016-11-22 17:44:12 -08:00
# include "rmi_driver.h"
# include "rmi_f34.h"
static int rmi_f34_write_bootloader_id ( struct f34_data * f34 )
{
struct rmi_function * fn = f34 - > fn ;
struct rmi_device * rmi_dev = fn - > rmi_dev ;
u8 bootloader_id [ F34_BOOTLOADER_ID_LEN ] ;
int ret ;
ret = rmi_read_block ( rmi_dev , fn - > fd . query_base_addr ,
bootloader_id , sizeof ( bootloader_id ) ) ;
if ( ret ) {
dev_err ( & fn - > dev , " %s: Reading bootloader ID failed: %d \n " ,
__func__ , ret ) ;
return ret ;
}
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " %s: writing bootloader id '%c%c' \n " ,
__func__ , bootloader_id [ 0 ] , bootloader_id [ 1 ] ) ;
ret = rmi_write_block ( rmi_dev ,
fn - > fd . data_base_addr + F34_BLOCK_DATA_OFFSET ,
bootloader_id , sizeof ( bootloader_id ) ) ;
if ( ret ) {
dev_err ( & fn - > dev , " Failed to write bootloader ID: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int rmi_f34_command ( struct f34_data * f34 , u8 command ,
unsigned int timeout , bool write_bl_id )
{
struct rmi_function * fn = f34 - > fn ;
struct rmi_device * rmi_dev = fn - > rmi_dev ;
int ret ;
if ( write_bl_id ) {
ret = rmi_f34_write_bootloader_id ( f34 ) ;
if ( ret )
return ret ;
}
init_completion ( & f34 - > v5 . cmd_done ) ;
ret = rmi_read ( rmi_dev , f34 - > v5 . ctrl_address , & f34 - > v5 . status ) ;
if ( ret ) {
dev_err ( & f34 - > fn - > dev ,
" %s: Failed to read cmd register: %d (command %#02x) \n " ,
__func__ , ret , command ) ;
return ret ;
}
f34 - > v5 . status | = command & 0x0f ;
ret = rmi_write ( rmi_dev , f34 - > v5 . ctrl_address , f34 - > v5 . status ) ;
if ( ret < 0 ) {
dev_err ( & f34 - > fn - > dev ,
" Failed to write F34 command %#02x: %d \n " ,
command , ret ) ;
return ret ;
}
if ( ! wait_for_completion_timeout ( & f34 - > v5 . cmd_done ,
msecs_to_jiffies ( timeout ) ) ) {
ret = rmi_read ( rmi_dev , f34 - > v5 . ctrl_address , & f34 - > v5 . status ) ;
if ( ret ) {
dev_err ( & f34 - > fn - > dev ,
" %s: cmd %#02x timed out: %d \n " ,
__func__ , command , ret ) ;
return ret ;
}
if ( f34 - > v5 . status & 0x7f ) {
dev_err ( & f34 - > fn - > dev ,
" %s: cmd %#02x timed out, status: %#02x \n " ,
__func__ , command , f34 - > v5 . status ) ;
return - ETIMEDOUT ;
}
}
return 0 ;
}
static int rmi_f34_attention ( struct rmi_function * fn , unsigned long * irq_bits )
{
struct f34_data * f34 = dev_get_drvdata ( & fn - > dev ) ;
int ret ;
2016-12-10 23:27:32 -08:00
if ( f34 - > bl_version ! = 5 )
return 0 ;
2016-11-22 17:44:12 -08:00
ret = rmi_read ( f34 - > fn - > rmi_dev , f34 - > v5 . ctrl_address , & f34 - > v5 . status ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " %s: status: %#02x, ret: %d \n " ,
__func__ , f34 - > v5 . status , ret ) ;
if ( ! ret & & ! ( f34 - > v5 . status & 0x7f ) )
complete ( & f34 - > v5 . cmd_done ) ;
return 0 ;
}
static int rmi_f34_write_blocks ( struct f34_data * f34 , const void * data ,
int block_count , u8 command )
{
struct rmi_function * fn = f34 - > fn ;
struct rmi_device * rmi_dev = fn - > rmi_dev ;
u16 address = fn - > fd . data_base_addr + F34_BLOCK_DATA_OFFSET ;
u8 start_address [ ] = { 0 , 0 } ;
int i ;
int ret ;
ret = rmi_write_block ( rmi_dev , fn - > fd . data_base_addr ,
start_address , sizeof ( start_address ) ) ;
if ( ret ) {
dev_err ( & fn - > dev , " Failed to write initial zeros: %d \n " , ret ) ;
return ret ;
}
for ( i = 0 ; i < block_count ; i + + ) {
ret = rmi_write_block ( rmi_dev , address ,
data , f34 - > v5 . block_size ) ;
if ( ret ) {
dev_err ( & fn - > dev ,
" failed to write block #%d: %d \n " , i , ret ) ;
return ret ;
}
ret = rmi_f34_command ( f34 , command , F34_IDLE_WAIT_MS , false ) ;
if ( ret ) {
dev_err ( & fn - > dev ,
" Failed to write command for block #%d: %d \n " ,
i , ret ) ;
return ret ;
}
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " wrote block %d of %d \n " ,
i + 1 , block_count ) ;
data + = f34 - > v5 . block_size ;
}
return 0 ;
}
static int rmi_f34_write_firmware ( struct f34_data * f34 , const void * data )
{
return rmi_f34_write_blocks ( f34 , data , f34 - > v5 . fw_blocks ,
F34_WRITE_FW_BLOCK ) ;
}
static int rmi_f34_write_config ( struct f34_data * f34 , const void * data )
{
return rmi_f34_write_blocks ( f34 , data , f34 - > v5 . config_blocks ,
F34_WRITE_CONFIG_BLOCK ) ;
}
int rmi_f34_enable_flash ( struct f34_data * f34 )
{
return rmi_f34_command ( f34 , F34_ENABLE_FLASH_PROG ,
F34_ENABLE_WAIT_MS , true ) ;
}
static int rmi_f34_flash_firmware ( struct f34_data * f34 ,
const struct rmi_f34_firmware * syn_fw )
{
struct rmi_function * fn = f34 - > fn ;
int ret ;
if ( syn_fw - > image_size ) {
dev_info ( & fn - > dev , " Erasing firmware... \n " ) ;
ret = rmi_f34_command ( f34 , F34_ERASE_ALL ,
F34_ERASE_WAIT_MS , true ) ;
if ( ret )
return ret ;
dev_info ( & fn - > dev , " Writing firmware (%d bytes)... \n " ,
syn_fw - > image_size ) ;
ret = rmi_f34_write_firmware ( f34 , syn_fw - > data ) ;
if ( ret )
return ret ;
}
if ( syn_fw - > config_size ) {
/*
* We only need to erase config if we haven ' t updated
* firmware .
*/
if ( ! syn_fw - > image_size ) {
dev_info ( & fn - > dev , " Erasing config... \n " ) ;
ret = rmi_f34_command ( f34 , F34_ERASE_CONFIG ,
F34_ERASE_WAIT_MS , true ) ;
if ( ret )
return ret ;
}
dev_info ( & fn - > dev , " Writing config (%d bytes)... \n " ,
syn_fw - > config_size ) ;
ret = rmi_f34_write_config ( f34 ,
& syn_fw - > data [ syn_fw - > image_size ] ) ;
if ( ret )
return ret ;
}
return 0 ;
}
int rmi_f34_update_firmware ( struct f34_data * f34 , const struct firmware * fw )
{
const struct rmi_f34_firmware * syn_fw ;
int ret ;
syn_fw = ( const struct rmi_f34_firmware * ) fw - > data ;
BUILD_BUG_ON ( offsetof ( struct rmi_f34_firmware , data ) ! =
F34_FW_IMAGE_OFFSET ) ;
rmi_dbg ( RMI_DEBUG_FN , & f34 - > fn - > dev ,
" FW size:%d, checksum:%08x, image_size:%d, config_size:%d \n " ,
( int ) fw - > size ,
le32_to_cpu ( syn_fw - > checksum ) ,
le32_to_cpu ( syn_fw - > image_size ) ,
le32_to_cpu ( syn_fw - > config_size ) ) ;
rmi_dbg ( RMI_DEBUG_FN , & f34 - > fn - > dev ,
" FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x \n " ,
syn_fw - > bootloader_version ,
( int ) sizeof ( syn_fw - > product_id ) , syn_fw - > product_id ,
syn_fw - > product_info [ 0 ] , syn_fw - > product_info [ 1 ] ) ;
if ( syn_fw - > image_size & &
syn_fw - > image_size ! = f34 - > v5 . fw_blocks * f34 - > v5 . block_size ) {
dev_err ( & f34 - > fn - > dev ,
" Bad firmware image: fw size %d, expected %d \n " ,
syn_fw - > image_size ,
f34 - > v5 . fw_blocks * f34 - > v5 . block_size ) ;
ret = - EILSEQ ;
goto out ;
}
if ( syn_fw - > config_size & &
syn_fw - > config_size ! = f34 - > v5 . config_blocks * f34 - > v5 . block_size ) {
dev_err ( & f34 - > fn - > dev ,
" Bad firmware image: config size %d, expected %d \n " ,
syn_fw - > config_size ,
f34 - > v5 . config_blocks * f34 - > v5 . block_size ) ;
ret = - EILSEQ ;
goto out ;
}
if ( syn_fw - > image_size & & ! syn_fw - > config_size ) {
dev_err ( & f34 - > fn - > dev , " Bad firmware image: no config data \n " ) ;
ret = - EILSEQ ;
goto out ;
}
dev_info ( & f34 - > fn - > dev , " Firmware image OK \n " ) ;
mutex_lock ( & f34 - > v5 . flash_mutex ) ;
ret = rmi_f34_flash_firmware ( f34 , syn_fw ) ;
mutex_unlock ( & f34 - > v5 . flash_mutex ) ;
out :
return ret ;
}
static int rmi_firmware_update ( struct rmi_driver_data * data ,
const struct firmware * fw )
{
2016-11-30 16:59:30 -08:00
struct rmi_device * rmi_dev = data - > rmi_dev ;
struct device * dev = & rmi_dev - > dev ;
2016-11-22 17:44:12 -08:00
struct f34_data * f34 ;
int ret ;
if ( ! data - > f34_container ) {
dev_warn ( dev , " %s: No F34 present! \n " , __func__ ) ;
return - EINVAL ;
}
2016-12-10 23:27:32 -08:00
f34 = dev_get_drvdata ( & data - > f34_container - > dev ) ;
if ( f34 - > bl_version = = 7 ) {
if ( data - > pdt_props & HAS_BSR ) {
dev_err ( dev , " %s: LTS not supported \n " , __func__ ) ;
return - ENODEV ;
}
} else if ( f34 - > bl_version ! = 5 ) {
2016-11-22 17:44:12 -08:00
dev_warn ( dev , " F34 V%d not supported! \n " ,
data - > f34_container - > fd . function_version ) ;
return - ENODEV ;
}
/* Enter flash mode */
2016-12-10 23:27:32 -08:00
if ( f34 - > bl_version = = 7 )
ret = rmi_f34v7_start_reflash ( f34 , fw ) ;
else
ret = rmi_f34_enable_flash ( f34 ) ;
2016-11-22 17:44:12 -08:00
if ( ret )
return ret ;
2016-11-30 16:59:30 -08:00
rmi_disable_irq ( rmi_dev , false ) ;
2016-11-22 17:44:12 -08:00
/* Tear down functions and re-probe */
2016-11-30 16:59:30 -08:00
rmi_free_function_list ( rmi_dev ) ;
2016-11-22 17:44:12 -08:00
ret = rmi_probe_interrupts ( data ) ;
if ( ret )
return ret ;
ret = rmi_init_functions ( data ) ;
if ( ret )
return ret ;
2016-12-10 23:27:32 -08:00
if ( ! data - > bootloader_mode | | ! data - > f34_container ) {
2016-11-22 17:44:12 -08:00
dev_warn ( dev , " %s: No F34 present or not in bootloader! \n " ,
__func__ ) ;
return - EINVAL ;
}
2016-11-30 16:59:30 -08:00
rmi_enable_irq ( rmi_dev , false ) ;
2016-11-22 17:44:12 -08:00
f34 = dev_get_drvdata ( & data - > f34_container - > dev ) ;
/* Perform firmware update */
2016-12-10 23:27:32 -08:00
if ( f34 - > bl_version = = 7 )
ret = rmi_f34v7_do_reflash ( f34 , fw ) ;
else
ret = rmi_f34_update_firmware ( f34 , fw ) ;
2016-11-22 17:44:12 -08:00
dev_info ( & f34 - > fn - > dev , " Firmware update complete, status:%d \n " , ret ) ;
2016-11-30 16:59:30 -08:00
rmi_disable_irq ( rmi_dev , false ) ;
2016-11-22 17:44:12 -08:00
/* Re-probe */
rmi_dbg ( RMI_DEBUG_FN , dev , " Re-probing device \n " ) ;
2016-11-30 16:59:30 -08:00
rmi_free_function_list ( rmi_dev ) ;
2016-11-22 17:44:12 -08:00
2016-11-30 16:59:30 -08:00
ret = rmi_scan_pdt ( rmi_dev , NULL , rmi_initial_reset ) ;
2016-11-22 17:44:12 -08:00
if ( ret < 0 )
dev_warn ( dev , " RMI reset failed! \n " ) ;
ret = rmi_probe_interrupts ( data ) ;
if ( ret )
return ret ;
ret = rmi_init_functions ( data ) ;
if ( ret )
return ret ;
2016-11-30 16:59:30 -08:00
rmi_enable_irq ( rmi_dev , false ) ;
2016-11-22 17:44:12 -08:00
if ( data - > f01_container - > dev . driver )
/* Driver already bound, so enable ATTN now. */
2016-11-30 16:59:30 -08:00
return rmi_enable_sensor ( rmi_dev ) ;
2016-11-22 17:44:12 -08:00
rmi_dbg ( RMI_DEBUG_FN , dev , " %s complete \n " , __func__ ) ;
return ret ;
}
2016-12-10 23:27:32 -08:00
static int rmi_firmware_update ( struct rmi_driver_data * data ,
const struct firmware * fw ) ;
2016-11-22 17:44:12 -08:00
static ssize_t rmi_driver_update_fw_store ( struct device * dev ,
struct device_attribute * dattr ,
const char * buf , size_t count )
{
struct rmi_driver_data * data = dev_get_drvdata ( dev ) ;
char fw_name [ NAME_MAX ] ;
const struct firmware * fw ;
size_t copy_count = count ;
int ret ;
if ( count = = 0 | | count > = NAME_MAX )
return - EINVAL ;
if ( buf [ count - 1 ] = = ' \0 ' | | buf [ count - 1 ] = = ' \n ' )
copy_count - = 1 ;
strncpy ( fw_name , buf , copy_count ) ;
fw_name [ copy_count ] = ' \0 ' ;
ret = request_firmware ( & fw , fw_name , dev ) ;
if ( ret )
return ret ;
dev_info ( dev , " Flashing %s \n " , fw_name ) ;
ret = rmi_firmware_update ( data , fw ) ;
release_firmware ( fw ) ;
return ret ? : count ;
}
static DEVICE_ATTR ( update_fw , 0200 , NULL , rmi_driver_update_fw_store ) ;
static struct attribute * rmi_firmware_attrs [ ] = {
& dev_attr_update_fw . attr ,
NULL
} ;
static struct attribute_group rmi_firmware_attr_group = {
. attrs = rmi_firmware_attrs ,
} ;
static int rmi_f34_probe ( struct rmi_function * fn )
{
struct f34_data * f34 ;
unsigned char f34_queries [ 9 ] ;
bool has_config_id ;
2016-12-10 23:27:32 -08:00
u8 version = fn - > fd . function_version ;
2016-11-22 17:44:12 -08:00
int ret ;
f34 = devm_kzalloc ( & fn - > dev , sizeof ( struct f34_data ) , GFP_KERNEL ) ;
if ( ! f34 )
return - ENOMEM ;
f34 - > fn = fn ;
dev_set_drvdata ( & fn - > dev , f34 ) ;
2016-12-10 23:27:32 -08:00
/* v5 code only supported version 0, try V7 probe */
if ( version > 0 )
return rmi_f34v7_probe ( f34 ) ;
else if ( version ! = 0 )
return - ENODEV ;
f34 - > bl_version = 5 ;
2016-11-22 17:44:12 -08:00
ret = rmi_read_block ( fn - > rmi_dev , fn - > fd . query_base_addr ,
f34_queries , sizeof ( f34_queries ) ) ;
if ( ret ) {
dev_err ( & fn - > dev , " %s: Failed to query properties \n " ,
__func__ ) ;
return ret ;
}
snprintf ( f34 - > bootloader_id , sizeof ( f34 - > bootloader_id ) ,
" %c%c " , f34_queries [ 0 ] , f34_queries [ 1 ] ) ;
mutex_init ( & f34 - > v5 . flash_mutex ) ;
init_completion ( & f34 - > v5 . cmd_done ) ;
f34 - > v5 . block_size = get_unaligned_le16 ( & f34_queries [ 3 ] ) ;
f34 - > v5 . fw_blocks = get_unaligned_le16 ( & f34_queries [ 5 ] ) ;
f34 - > v5 . config_blocks = get_unaligned_le16 ( & f34_queries [ 7 ] ) ;
f34 - > v5 . ctrl_address = fn - > fd . data_base_addr + F34_BLOCK_DATA_OFFSET +
f34 - > v5 . block_size ;
has_config_id = f34_queries [ 2 ] & ( 1 < < 2 ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " Bootloader ID: %s \n " ,
f34 - > bootloader_id ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " Block size: %d \n " ,
f34 - > v5 . block_size ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " FW blocks: %d \n " ,
f34 - > v5 . fw_blocks ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " CFG blocks: %d \n " ,
f34 - > v5 . config_blocks ) ;
if ( has_config_id ) {
ret = rmi_read_block ( fn - > rmi_dev , fn - > fd . control_base_addr ,
f34_queries , sizeof ( f34_queries ) ) ;
if ( ret ) {
dev_err ( & fn - > dev , " Failed to read F34 config ID \n " ) ;
return ret ;
}
snprintf ( f34 - > configuration_id , sizeof ( f34 - > configuration_id ) ,
" %02x%02x%02x%02x " ,
f34_queries [ 0 ] , f34_queries [ 1 ] ,
f34_queries [ 2 ] , f34_queries [ 3 ] ) ;
rmi_dbg ( RMI_DEBUG_FN , & fn - > dev , " Configuration ID: %s \n " ,
f34 - > configuration_id ) ;
}
return 0 ;
}
int rmi_f34_create_sysfs ( struct rmi_device * rmi_dev )
{
return sysfs_create_group ( & rmi_dev - > dev . kobj , & rmi_firmware_attr_group ) ;
}
void rmi_f34_remove_sysfs ( struct rmi_device * rmi_dev )
{
sysfs_remove_group ( & rmi_dev - > dev . kobj , & rmi_firmware_attr_group ) ;
}
struct rmi_function_handler rmi_f34_handler = {
. driver = {
. name = " rmi4_f34 " ,
} ,
. func = 0x34 ,
. probe = rmi_f34_probe ,
. attention = rmi_f34_attention ,
} ;