2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-12-01 18:32:47 +03:00
/*
* Copyright ( C ) 2015 Zodiac Inflight Innovations
*
* Author : Martyn Welch < martyn . welch @ collabora . co . uk >
*
* Based on twl4030_wdt . c by Timo Kokkonen < timo . t . kokkonen at nokia . com > :
*
* Copyright ( C ) Nokia Corporation
*/
2016-08-10 19:18:03 +03:00
# include <linux/delay.h>
2015-12-01 18:32:47 +03:00
# include <linux/i2c.h>
2016-08-10 19:18:03 +03:00
# include <linux/ihex.h>
# include <linux/firmware.h>
2015-12-01 18:32:47 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include <linux/types.h>
# include <linux/version.h>
# include <linux/watchdog.h>
2019-08-12 23:08:55 +03:00
# include <asm/unaligned.h>
2015-12-01 18:32:47 +03:00
# define ZIIRAVE_TIMEOUT_MIN 3
# define ZIIRAVE_TIMEOUT_MAX 255
2019-08-12 23:08:48 +03:00
# define ZIIRAVE_TIMEOUT_DEFAULT 30
2015-12-01 18:32:47 +03:00
# define ZIIRAVE_PING_VALUE 0x0
# define ZIIRAVE_STATE_INITIAL 0x0
# define ZIIRAVE_STATE_OFF 0x1
# define ZIIRAVE_STATE_ON 0x2
2016-08-10 19:18:03 +03:00
# define ZIIRAVE_FW_NAME "ziirave_wdt.fw"
2016-02-26 19:05:12 +03:00
static char * ziirave_reasons [ ] = { " power cycle " , " hw watchdog " , NULL , NULL ,
2015-12-01 18:32:47 +03:00
" host request " , NULL , " illegal configuration " ,
" illegal instruction " , " illegal trap " ,
" unknown " } ;
# define ZIIRAVE_WDT_FIRM_VER_MAJOR 0x1
# define ZIIRAVE_WDT_BOOT_VER_MAJOR 0x3
# define ZIIRAVE_WDT_RESET_REASON 0x5
# define ZIIRAVE_WDT_STATE 0x6
# define ZIIRAVE_WDT_TIMEOUT 0x7
# define ZIIRAVE_WDT_TIME_LEFT 0x8
# define ZIIRAVE_WDT_PING 0x9
# define ZIIRAVE_WDT_RESET_DURATION 0xa
2016-08-10 19:18:03 +03:00
# define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20
# define ZIIRAVE_FIRM_PKT_DATA_SIZE 16
2019-08-12 23:08:58 +03:00
# define ZIIRAVE_FIRM_FLASH_MEMORY_START (2 * 0x1600)
# define ZIIRAVE_FIRM_FLASH_MEMORY_END (2 * 0x2bbf)
2019-08-12 23:08:50 +03:00
# define ZIIRAVE_FIRM_PAGE_SIZE 128
2016-08-10 19:18:03 +03:00
/* Received and ready for next Download packet. */
# define ZIIRAVE_FIRM_DOWNLOAD_ACK 1
/* Firmware commands */
# define ZIIRAVE_CMD_DOWNLOAD_START 0x10
# define ZIIRAVE_CMD_DOWNLOAD_END 0x11
# define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR 0x12
# define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE 0x13
# define ZIIRAVE_CMD_RESET_PROCESSOR 0x0b
# define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c
# define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e
2019-08-12 23:09:00 +03:00
# define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC 1
2019-08-12 23:09:02 +03:00
# define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC 1
2019-08-12 23:09:00 +03:00
2019-08-12 23:08:49 +03:00
# define ZIIRAVE_FW_VERSION_FMT "02.%02u.%02u"
# define ZIIRAVE_BL_VERSION_FMT "01.%02u.%02u"
2015-12-01 18:32:47 +03:00
struct ziirave_wdt_rev {
unsigned char major ;
unsigned char minor ;
} ;
struct ziirave_wdt_data {
2016-08-10 19:18:03 +03:00
struct mutex sysfs_mutex ;
2015-12-01 18:32:47 +03:00
struct watchdog_device wdd ;
struct ziirave_wdt_rev bootloader_rev ;
struct ziirave_wdt_rev firmware_rev ;
int reset_reason ;
} ;
static int wdt_timeout ;
module_param ( wdt_timeout , int , 0 ) ;
MODULE_PARM_DESC ( wdt_timeout , " Watchdog timeout in seconds " ) ;
static int reset_duration ;
module_param ( reset_duration , int , 0 ) ;
MODULE_PARM_DESC ( reset_duration ,
" Watchdog reset pulse duration in milliseconds " ) ;
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
static int ziirave_wdt_revision ( struct i2c_client * client ,
struct ziirave_wdt_rev * rev , u8 command )
{
int ret ;
ret = i2c_smbus_read_byte_data ( client , command ) ;
if ( ret < 0 )
return ret ;
rev - > major = ret ;
ret = i2c_smbus_read_byte_data ( client , command + 1 ) ;
if ( ret < 0 )
return ret ;
rev - > minor = ret ;
return 0 ;
}
static int ziirave_wdt_set_state ( struct watchdog_device * wdd , int state )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
return i2c_smbus_write_byte_data ( client , ZIIRAVE_WDT_STATE , state ) ;
}
static int ziirave_wdt_start ( struct watchdog_device * wdd )
{
return ziirave_wdt_set_state ( wdd , ZIIRAVE_STATE_ON ) ;
}
static int ziirave_wdt_stop ( struct watchdog_device * wdd )
{
return ziirave_wdt_set_state ( wdd , ZIIRAVE_STATE_OFF ) ;
}
static int ziirave_wdt_ping ( struct watchdog_device * wdd )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
return i2c_smbus_write_byte_data ( client , ZIIRAVE_WDT_PING ,
ZIIRAVE_PING_VALUE ) ;
}
static int ziirave_wdt_set_timeout ( struct watchdog_device * wdd ,
unsigned int timeout )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
int ret ;
ret = i2c_smbus_write_byte_data ( client , ZIIRAVE_WDT_TIMEOUT , timeout ) ;
if ( ! ret )
wdd - > timeout = timeout ;
return ret ;
}
static unsigned int ziirave_wdt_get_timeleft ( struct watchdog_device * wdd )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
int ret ;
ret = i2c_smbus_read_byte_data ( client , ZIIRAVE_WDT_TIME_LEFT ) ;
if ( ret < 0 )
ret = 0 ;
return ret ;
}
2019-08-12 23:09:03 +03:00
static int ziirave_firm_read_ack ( struct watchdog_device * wdd )
2016-08-10 19:18:03 +03:00
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
int ret ;
2019-08-12 23:09:03 +03:00
ret = i2c_smbus_read_byte ( client ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " Failed to read status byte \n " ) ;
return ret ;
}
2016-08-10 19:18:03 +03:00
return ret = = ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : - EIO ;
}
2019-08-12 23:08:58 +03:00
static int ziirave_firm_set_read_addr ( struct watchdog_device * wdd , u32 addr )
2016-08-10 19:18:03 +03:00
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
2019-08-12 23:08:58 +03:00
const u16 addr16 = ( u16 ) addr / 2 ;
2016-08-10 19:18:03 +03:00
u8 address [ 2 ] ;
2019-08-12 23:08:58 +03:00
put_unaligned_le16 ( addr16 , address ) ;
2016-08-10 19:18:03 +03:00
return i2c_smbus_write_block_data ( client ,
ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR ,
2019-08-12 23:08:53 +03:00
sizeof ( address ) , address ) ;
2016-08-10 19:18:03 +03:00
}
2019-08-12 23:08:58 +03:00
static bool ziirave_firm_addr_readonly ( u32 addr )
{
return addr < ZIIRAVE_FIRM_FLASH_MEMORY_START | |
addr > ZIIRAVE_FIRM_FLASH_MEMORY_END ;
}
2016-08-10 19:18:03 +03:00
/*
* ziirave_firm_write_pkt ( ) - Build and write a firmware packet
*
* A packet to send to the firmware is composed by following bytes :
* Length | Addr0 | Addr1 | Data0 . . Data15 | Checksum |
* Where ,
* Length : A data byte containing the length of the data .
* Addr0 : Low byte of the address .
* Addr1 : High byte of the address .
* Data0 . . Data15 : Array of 16 bytes of data .
* Checksum : Checksum byte to verify data integrity .
*/
2019-08-12 23:08:50 +03:00
static int __ziirave_firm_write_pkt ( struct watchdog_device * wdd ,
u32 addr , const u8 * data , u8 len )
2016-08-10 19:18:03 +03:00
{
2019-08-12 23:08:50 +03:00
const u16 addr16 = ( u16 ) addr / 2 ;
2016-08-10 19:18:03 +03:00
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
u8 i , checksum = 0 , packet [ ZIIRAVE_FIRM_PKT_TOTAL_SIZE ] ;
int ret ;
2019-08-12 23:08:51 +03:00
/* Check max data size */
if ( len > ZIIRAVE_FIRM_PKT_DATA_SIZE ) {
dev_err ( & client - > dev , " Firmware packet too long (%d) \n " ,
len ) ;
return - EMSGSIZE ;
}
2019-08-12 23:08:58 +03:00
/*
* Ignore packets that are targeting program memory outisde of
* app partition , since they will be ignored by the
* bootloader . At the same time , we need to make sure we ' ll
* allow zero length packet that will be sent as the last step
* of firmware update
*/
if ( len & & ziirave_firm_addr_readonly ( addr ) )
return 0 ;
2016-08-10 19:18:03 +03:00
/* Packet length */
2019-08-12 23:08:50 +03:00
packet [ 0 ] = len ;
2016-08-10 19:18:03 +03:00
/* Packet address */
2019-08-12 23:08:55 +03:00
put_unaligned_le16 ( addr16 , packet + 1 ) ;
2016-08-10 19:18:03 +03:00
2019-08-12 23:08:50 +03:00
memcpy ( packet + 3 , data , len ) ;
2019-08-12 23:08:54 +03:00
memset ( packet + 3 + len , 0 , ZIIRAVE_FIRM_PKT_DATA_SIZE - len ) ;
2016-08-10 19:18:03 +03:00
/* Packet checksum */
2019-08-12 23:08:52 +03:00
for ( i = 0 ; i < len + 3 ; i + + )
2016-08-10 19:18:03 +03:00
checksum + = packet [ i ] ;
packet [ ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1 ] = checksum ;
2019-08-12 23:09:05 +03:00
ret = i2c_smbus_write_block_data ( client , ZIIRAVE_CMD_DOWNLOAD_PACKET ,
sizeof ( packet ) , packet ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" Failed to send DOWNLOAD_PACKET: %d \n " , ret ) ;
return ret ;
}
ret = ziirave_firm_read_ack ( wdd ) ;
2016-08-10 19:18:03 +03:00
if ( ret )
dev_err ( & client - > dev ,
" Failed to write firmware packet at address 0x%04x: %d \n " ,
2019-08-12 23:08:58 +03:00
addr , ret ) ;
2016-08-10 19:18:03 +03:00
return ret ;
}
2019-08-12 23:08:50 +03:00
static int ziirave_firm_write_pkt ( struct watchdog_device * wdd ,
u32 addr , const u8 * data , u8 len )
{
const u8 max_write_len = ZIIRAVE_FIRM_PAGE_SIZE -
( addr - ALIGN_DOWN ( addr , ZIIRAVE_FIRM_PAGE_SIZE ) ) ;
int ret ;
if ( len > max_write_len ) {
/*
* If data crossed page boundary we need to split this
* write in two
*/
ret = __ziirave_firm_write_pkt ( wdd , addr , data , max_write_len ) ;
if ( ret )
return ret ;
addr + = max_write_len ;
data + = max_write_len ;
len - = max_write_len ;
}
return __ziirave_firm_write_pkt ( wdd , addr , data , len ) ;
}
2016-08-10 19:18:03 +03:00
static int ziirave_firm_verify ( struct watchdog_device * wdd ,
const struct firmware * fw )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
const struct ihex_binrec * rec ;
int i , ret ;
u8 data [ ZIIRAVE_FIRM_PKT_DATA_SIZE ] ;
for ( rec = ( void * ) fw - > data ; rec ; rec = ihex_next_binrec ( rec ) ) {
2019-08-12 23:08:57 +03:00
const u16 len = be16_to_cpu ( rec - > len ) ;
2019-08-12 23:08:58 +03:00
const u32 addr = be32_to_cpu ( rec - > addr ) ;
2019-08-12 23:08:57 +03:00
2019-08-12 23:08:58 +03:00
if ( ziirave_firm_addr_readonly ( addr ) )
2016-08-10 19:18:03 +03:00
continue ;
ret = ziirave_firm_set_read_addr ( wdd , addr ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" Failed to send SET_READ_ADDR command: %d \n " ,
ret ) ;
return ret ;
}
2019-08-12 23:08:57 +03:00
for ( i = 0 ; i < len ; i + + ) {
2016-08-10 19:18:03 +03:00
ret = i2c_smbus_read_byte_data ( client ,
ZIIRAVE_CMD_DOWNLOAD_READ_BYTE ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev ,
" Failed to READ DATA: %d \n " , ret ) ;
return ret ;
}
data [ i ] = ret ;
}
2019-08-12 23:08:57 +03:00
if ( memcmp ( data , rec - > data , len ) ) {
2016-08-10 19:18:03 +03:00
dev_err ( & client - > dev ,
" Firmware mismatch at address 0x%04x \n " , addr ) ;
return - EINVAL ;
}
}
return 0 ;
}
static int ziirave_firm_upload ( struct watchdog_device * wdd ,
const struct firmware * fw )
{
struct i2c_client * client = to_i2c_client ( wdd - > parent ) ;
const struct ihex_binrec * rec ;
2019-08-12 23:08:50 +03:00
int ret ;
2016-08-10 19:18:03 +03:00
2019-08-12 23:09:00 +03:00
ret = i2c_smbus_write_byte_data ( client ,
ZIIRAVE_CMD_JUMP_TO_BOOTLOADER ,
ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC ) ;
2019-08-12 23:08:47 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to jump to bootloader \n " ) ;
2016-08-10 19:18:03 +03:00
return ret ;
2019-08-12 23:08:47 +03:00
}
2016-08-10 19:18:03 +03:00
msleep ( 500 ) ;
2019-08-12 23:09:04 +03:00
ret = i2c_smbus_write_byte ( client , ZIIRAVE_CMD_DOWNLOAD_START ) ;
2019-08-12 23:08:47 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to start download \n " ) ;
2016-08-10 19:18:03 +03:00
return ret ;
2019-08-12 23:08:47 +03:00
}
2016-08-10 19:18:03 +03:00
2019-08-12 23:09:04 +03:00
ret = ziirave_firm_read_ack ( wdd ) ;
if ( ret ) {
dev_err ( & client - > dev , " No ACK for start download \n " ) ;
return ret ;
}
2016-08-10 19:18:03 +03:00
msleep ( 500 ) ;
for ( rec = ( void * ) fw - > data ; rec ; rec = ihex_next_binrec ( rec ) ) {
2019-08-12 23:08:50 +03:00
ret = ziirave_firm_write_pkt ( wdd , be32_to_cpu ( rec - > addr ) ,
rec - > data , be16_to_cpu ( rec - > len ) ) ;
if ( ret )
return ret ;
2016-08-10 19:18:03 +03:00
}
2019-08-12 23:08:50 +03:00
/*
* Finish firmware download process by sending a zero length
* payload
*/
ret = ziirave_firm_write_pkt ( wdd , 0 , NULL , 0 ) ;
2016-08-10 19:18:03 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to send EMPTY packet: %d \n " , ret ) ;
return ret ;
}
/* This sleep seems to be required */
msleep ( 20 ) ;
/* Start firmware verification */
ret = ziirave_firm_verify ( wdd , fw ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" Failed to verify firmware: %d \n " , ret ) ;
return ret ;
}
/* End download operation */
2019-08-12 23:09:01 +03:00
ret = i2c_smbus_write_byte ( client , ZIIRAVE_CMD_DOWNLOAD_END ) ;
2019-08-12 23:08:47 +03:00
if ( ret ) {
dev_err ( & client - > dev ,
" Failed to end firmware download: %d \n " , ret ) ;
2016-08-10 19:18:03 +03:00
return ret ;
2019-08-12 23:08:47 +03:00
}
2016-08-10 19:18:03 +03:00
/* Reset the processor */
2019-08-12 23:09:02 +03:00
ret = i2c_smbus_write_byte_data ( client ,
ZIIRAVE_CMD_RESET_PROCESSOR ,
ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC ) ;
2019-08-12 23:08:47 +03:00
if ( ret ) {
dev_err ( & client - > dev ,
" Failed to reset the watchdog: %d \n " , ret ) ;
2016-08-10 19:18:03 +03:00
return ret ;
2019-08-12 23:08:47 +03:00
}
2016-08-10 19:18:03 +03:00
msleep ( 500 ) ;
return 0 ;
}
2015-12-01 18:32:47 +03:00
static const struct watchdog_info ziirave_wdt_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING ,
. identity = " Zodiac RAVE Watchdog " ,
} ;
static const struct watchdog_ops ziirave_wdt_ops = {
. owner = THIS_MODULE ,
. start = ziirave_wdt_start ,
. stop = ziirave_wdt_stop ,
. ping = ziirave_wdt_ping ,
. set_timeout = ziirave_wdt_set_timeout ,
. get_timeleft = ziirave_wdt_get_timeleft ,
} ;
static ssize_t ziirave_wdt_sysfs_show_firm ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
struct ziirave_wdt_data * w_priv = i2c_get_clientdata ( client ) ;
2016-08-10 19:18:03 +03:00
int ret ;
ret = mutex_lock_interruptible ( & w_priv - > sysfs_mutex ) ;
if ( ret )
return ret ;
2019-08-12 23:08:49 +03:00
ret = sprintf ( buf , ZIIRAVE_FW_VERSION_FMT , w_priv - > firmware_rev . major ,
2016-08-10 19:18:03 +03:00
w_priv - > firmware_rev . minor ) ;
mutex_unlock ( & w_priv - > sysfs_mutex ) ;
2015-12-01 18:32:47 +03:00
2016-08-10 19:18:03 +03:00
return ret ;
2015-12-01 18:32:47 +03:00
}
static DEVICE_ATTR ( firmware_version , S_IRUGO , ziirave_wdt_sysfs_show_firm ,
NULL ) ;
static ssize_t ziirave_wdt_sysfs_show_boot ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
struct ziirave_wdt_data * w_priv = i2c_get_clientdata ( client ) ;
2016-08-10 19:18:03 +03:00
int ret ;
ret = mutex_lock_interruptible ( & w_priv - > sysfs_mutex ) ;
if ( ret )
return ret ;
2015-12-01 18:32:47 +03:00
2019-08-12 23:08:49 +03:00
ret = sprintf ( buf , ZIIRAVE_BL_VERSION_FMT , w_priv - > bootloader_rev . major ,
2016-08-10 19:18:03 +03:00
w_priv - > bootloader_rev . minor ) ;
mutex_unlock ( & w_priv - > sysfs_mutex ) ;
return ret ;
2015-12-01 18:32:47 +03:00
}
static DEVICE_ATTR ( bootloader_version , S_IRUGO , ziirave_wdt_sysfs_show_boot ,
NULL ) ;
static ssize_t ziirave_wdt_sysfs_show_reason ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
struct ziirave_wdt_data * w_priv = i2c_get_clientdata ( client ) ;
2016-08-10 19:18:03 +03:00
int ret ;
2015-12-01 18:32:47 +03:00
2016-08-10 19:18:03 +03:00
ret = mutex_lock_interruptible ( & w_priv - > sysfs_mutex ) ;
if ( ret )
return ret ;
ret = sprintf ( buf , " %s " , ziirave_reasons [ w_priv - > reset_reason ] ) ;
mutex_unlock ( & w_priv - > sysfs_mutex ) ;
return ret ;
2015-12-01 18:32:47 +03:00
}
static DEVICE_ATTR ( reset_reason , S_IRUGO , ziirave_wdt_sysfs_show_reason ,
NULL ) ;
2016-08-10 19:18:03 +03:00
static ssize_t ziirave_wdt_sysfs_store_firm ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
struct ziirave_wdt_data * w_priv = i2c_get_clientdata ( client ) ;
const struct firmware * fw ;
int err ;
err = request_ihex_firmware ( & fw , ZIIRAVE_FW_NAME , dev ) ;
if ( err ) {
dev_err ( & client - > dev , " Failed to request ihex firmware \n " ) ;
return err ;
}
err = mutex_lock_interruptible ( & w_priv - > sysfs_mutex ) ;
if ( err )
goto release_firmware ;
err = ziirave_firm_upload ( & w_priv - > wdd , fw ) ;
if ( err ) {
dev_err ( & client - > dev , " The firmware update failed: %d \n " , err ) ;
goto unlock_mutex ;
}
/* Update firmware version */
err = ziirave_wdt_revision ( client , & w_priv - > firmware_rev ,
ZIIRAVE_WDT_FIRM_VER_MAJOR ) ;
if ( err ) {
dev_err ( & client - > dev , " Failed to read firmware version: %d \n " ,
err ) ;
goto unlock_mutex ;
}
2019-08-12 23:08:49 +03:00
dev_info ( & client - > dev ,
" Firmware updated to version " ZIIRAVE_FW_VERSION_FMT " \n " ,
2016-08-10 19:18:03 +03:00
w_priv - > firmware_rev . major , w_priv - > firmware_rev . minor ) ;
/* Restore the watchdog timeout */
err = ziirave_wdt_set_timeout ( & w_priv - > wdd , w_priv - > wdd . timeout ) ;
if ( err )
dev_err ( & client - > dev , " Failed to set timeout: %d \n " , err ) ;
unlock_mutex :
mutex_unlock ( & w_priv - > sysfs_mutex ) ;
release_firmware :
release_firmware ( fw ) ;
return err ? err : count ;
}
static DEVICE_ATTR ( update_firmware , S_IWUSR , NULL ,
ziirave_wdt_sysfs_store_firm ) ;
2015-12-01 18:32:47 +03:00
static struct attribute * ziirave_wdt_attrs [ ] = {
& dev_attr_firmware_version . attr ,
& dev_attr_bootloader_version . attr ,
& dev_attr_reset_reason . attr ,
2016-08-10 19:18:03 +03:00
& dev_attr_update_firmware . attr ,
2015-12-01 18:32:47 +03:00
NULL
} ;
2016-01-04 02:11:57 +03:00
ATTRIBUTE_GROUPS ( ziirave_wdt ) ;
2015-12-01 18:32:47 +03:00
static int ziirave_wdt_init_duration ( struct i2c_client * client )
{
int ret ;
if ( ! reset_duration ) {
/* See if the reset pulse duration is provided in an of_node */
if ( ! client - > dev . of_node )
ret = - ENODEV ;
else
ret = of_property_read_u32 ( client - > dev . of_node ,
" reset-duration-ms " ,
& reset_duration ) ;
if ( ret ) {
dev_info ( & client - > dev ,
2019-08-12 23:08:59 +03:00
" No reset pulse duration specified, using default \n " ) ;
2015-12-01 18:32:47 +03:00
return 0 ;
}
}
if ( reset_duration < 1 | | reset_duration > 255 )
return - EINVAL ;
dev_info ( & client - > dev , " Setting reset duration to %dms " ,
reset_duration ) ;
return i2c_smbus_write_byte_data ( client , ZIIRAVE_WDT_RESET_DURATION ,
reset_duration ) ;
}
static int ziirave_wdt_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int ret ;
struct ziirave_wdt_data * w_priv ;
int val ;
2019-08-12 23:09:06 +03:00
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA ) )
2015-12-01 18:32:47 +03:00
return - ENODEV ;
w_priv = devm_kzalloc ( & client - > dev , sizeof ( * w_priv ) , GFP_KERNEL ) ;
if ( ! w_priv )
return - ENOMEM ;
2016-08-10 19:18:03 +03:00
mutex_init ( & w_priv - > sysfs_mutex ) ;
2015-12-01 18:32:47 +03:00
w_priv - > wdd . info = & ziirave_wdt_info ;
w_priv - > wdd . ops = & ziirave_wdt_ops ;
w_priv - > wdd . min_timeout = ZIIRAVE_TIMEOUT_MIN ;
w_priv - > wdd . max_timeout = ZIIRAVE_TIMEOUT_MAX ;
w_priv - > wdd . parent = & client - > dev ;
2016-01-04 02:11:57 +03:00
w_priv - > wdd . groups = ziirave_wdt_groups ;
2015-12-01 18:32:47 +03:00
2019-04-19 21:16:01 +03:00
watchdog_init_timeout ( & w_priv - > wdd , wdt_timeout , & client - > dev ) ;
2015-12-01 18:32:47 +03:00
/*
* The default value set in the watchdog should be perfectly valid , so
* pass that in if we haven ' t provided one via the module parameter or
* of property .
*/
if ( w_priv - > wdd . timeout = = 0 ) {
val = i2c_smbus_read_byte_data ( client , ZIIRAVE_WDT_TIMEOUT ) ;
2019-08-12 23:08:46 +03:00
if ( val < 0 ) {
dev_err ( & client - > dev , " Failed to read timeout \n " ) ;
2015-12-01 18:32:47 +03:00
return val ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
2019-08-12 23:08:48 +03:00
if ( val > ZIIRAVE_TIMEOUT_MAX | |
val < ZIIRAVE_TIMEOUT_MIN )
val = ZIIRAVE_TIMEOUT_DEFAULT ;
2015-12-01 18:32:47 +03:00
w_priv - > wdd . timeout = val ;
2019-08-12 23:08:48 +03:00
}
2015-12-01 18:32:47 +03:00
2019-08-12 23:08:48 +03:00
ret = ziirave_wdt_set_timeout ( & w_priv - > wdd , w_priv - > wdd . timeout ) ;
if ( ret ) {
dev_err ( & client - > dev , " Failed to set timeout \n " ) ;
return ret ;
2015-12-01 18:32:47 +03:00
}
2019-08-12 23:08:48 +03:00
dev_info ( & client - > dev , " Timeout set to %ds \n " , w_priv - > wdd . timeout ) ;
2015-12-01 18:32:47 +03:00
watchdog_set_nowayout ( & w_priv - > wdd , nowayout ) ;
i2c_set_clientdata ( client , w_priv ) ;
/* If in unconfigured state, set to stopped */
val = i2c_smbus_read_byte_data ( client , ZIIRAVE_WDT_STATE ) ;
2019-08-12 23:08:46 +03:00
if ( val < 0 ) {
dev_err ( & client - > dev , " Failed to read state \n " ) ;
2015-12-01 18:32:47 +03:00
return val ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
if ( val = = ZIIRAVE_STATE_INITIAL )
ziirave_wdt_stop ( & w_priv - > wdd ) ;
ret = ziirave_wdt_init_duration ( client ) ;
2019-08-12 23:08:46 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to init duration \n " ) ;
2015-12-01 18:32:47 +03:00
return ret ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
ret = ziirave_wdt_revision ( client , & w_priv - > firmware_rev ,
ZIIRAVE_WDT_FIRM_VER_MAJOR ) ;
2019-08-12 23:08:46 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to read firmware version \n " ) ;
2015-12-01 18:32:47 +03:00
return ret ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
2019-08-12 23:08:49 +03:00
dev_info ( & client - > dev ,
" Firmware version: " ZIIRAVE_FW_VERSION_FMT " \n " ,
w_priv - > firmware_rev . major , w_priv - > firmware_rev . minor ) ;
2015-12-01 18:32:47 +03:00
ret = ziirave_wdt_revision ( client , & w_priv - > bootloader_rev ,
ZIIRAVE_WDT_BOOT_VER_MAJOR ) ;
2019-08-12 23:08:46 +03:00
if ( ret ) {
dev_err ( & client - > dev , " Failed to read bootloader version \n " ) ;
2015-12-01 18:32:47 +03:00
return ret ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
2019-08-12 23:08:49 +03:00
dev_info ( & client - > dev ,
" Bootloader version: " ZIIRAVE_BL_VERSION_FMT " \n " ,
w_priv - > bootloader_rev . major , w_priv - > bootloader_rev . minor ) ;
2015-12-01 18:32:47 +03:00
w_priv - > reset_reason = i2c_smbus_read_byte_data ( client ,
ZIIRAVE_WDT_RESET_REASON ) ;
2019-08-12 23:08:46 +03:00
if ( w_priv - > reset_reason < 0 ) {
dev_err ( & client - > dev , " Failed to read reset reason \n " ) ;
2015-12-01 18:32:47 +03:00
return w_priv - > reset_reason ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
if ( w_priv - > reset_reason > = ARRAY_SIZE ( ziirave_reasons ) | |
2019-08-12 23:08:46 +03:00
! ziirave_reasons [ w_priv - > reset_reason ] ) {
dev_err ( & client - > dev , " Invalid reset reason \n " ) ;
2015-12-01 18:32:47 +03:00
return - ENODEV ;
2019-08-12 23:08:46 +03:00
}
2015-12-01 18:32:47 +03:00
ret = watchdog_register_device ( & w_priv - > wdd ) ;
2016-01-04 02:11:57 +03:00
return ret ;
2015-12-01 18:32:47 +03:00
}
static int ziirave_wdt_remove ( struct i2c_client * client )
{
struct ziirave_wdt_data * w_priv = i2c_get_clientdata ( client ) ;
watchdog_unregister_device ( & w_priv - > wdd ) ;
return 0 ;
}
2017-08-21 19:48:38 +03:00
static const struct i2c_device_id ziirave_wdt_id [ ] = {
2016-07-09 12:43:19 +03:00
{ " rave-wdt " , 0 } ,
2015-12-01 18:32:47 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ziirave_wdt_id ) ;
static const struct of_device_id zrv_wdt_of_match [ ] = {
{ . compatible = " zii,rave-wdt " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , zrv_wdt_of_match ) ;
static struct i2c_driver ziirave_wdt_driver = {
. driver = {
. name = " ziirave_wdt " ,
. of_match_table = zrv_wdt_of_match ,
} ,
. probe = ziirave_wdt_probe ,
. remove = ziirave_wdt_remove ,
. id_table = ziirave_wdt_id ,
} ;
module_i2c_driver ( ziirave_wdt_driver ) ;
MODULE_AUTHOR ( " Martyn Welch <martyn.welch@collabora.co.uk " ) ;
MODULE_DESCRIPTION ( " Zodiac Aerospace RAVE Switch Watchdog Processor Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;