2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-05-27 03:25:05 +04:00
/*
2016-05-03 12:54:34 +03:00
* Micro Crystal RV - 3029 / RV - 3049 rtc class driver
2011-05-27 03:25:05 +04:00
*
* Author : Gregory Hermant < gregory . hermant @ calao - systems . com >
2016-03-05 00:40:30 +03:00
* Michael Buesch < m @ bues . ch >
2011-05-27 03:25:05 +04:00
*
* based on previously existing rtc class drivers
*/
# include <linux/module.h>
# include <linux/i2c.h>
2016-05-03 12:54:34 +03:00
# include <linux/spi/spi.h>
2011-05-27 03:25:05 +04:00
# include <linux/bcd.h>
# include <linux/rtc.h>
2016-03-05 00:40:55 +03:00
# include <linux/delay.h>
# include <linux/of.h>
2016-03-10 20:34:46 +03:00
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2016-05-03 12:54:33 +03:00
# include <linux/regmap.h>
2011-05-27 03:25:05 +04:00
/* Register map */
/* control section */
2016-03-05 00:38:45 +03:00
# define RV3029_ONOFF_CTRL 0x00
2016-03-05 00:39:49 +03:00
# define RV3029_ONOFF_CTRL_WE BIT(0)
# define RV3029_ONOFF_CTRL_TE BIT(1)
# define RV3029_ONOFF_CTRL_TAR BIT(2)
# define RV3029_ONOFF_CTRL_EERE BIT(3)
# define RV3029_ONOFF_CTRL_SRON BIT(4)
# define RV3029_ONOFF_CTRL_TD0 BIT(5)
# define RV3029_ONOFF_CTRL_TD1 BIT(6)
# define RV3029_ONOFF_CTRL_CLKINT BIT(7)
2016-03-05 00:38:45 +03:00
# define RV3029_IRQ_CTRL 0x01
2016-03-05 00:39:49 +03:00
# define RV3029_IRQ_CTRL_AIE BIT(0)
# define RV3029_IRQ_CTRL_TIE BIT(1)
# define RV3029_IRQ_CTRL_V1IE BIT(2)
# define RV3029_IRQ_CTRL_V2IE BIT(3)
# define RV3029_IRQ_CTRL_SRIE BIT(4)
2016-03-05 00:38:45 +03:00
# define RV3029_IRQ_FLAGS 0x02
2016-03-05 00:39:49 +03:00
# define RV3029_IRQ_FLAGS_AF BIT(0)
# define RV3029_IRQ_FLAGS_TF BIT(1)
# define RV3029_IRQ_FLAGS_V1IF BIT(2)
# define RV3029_IRQ_FLAGS_V2IF BIT(3)
# define RV3029_IRQ_FLAGS_SRF BIT(4)
2016-03-05 00:38:45 +03:00
# define RV3029_STATUS 0x03
2016-03-05 00:39:49 +03:00
# define RV3029_STATUS_VLOW1 BIT(2)
# define RV3029_STATUS_VLOW2 BIT(3)
# define RV3029_STATUS_SR BIT(4)
# define RV3029_STATUS_PON BIT(5)
# define RV3029_STATUS_EEBUSY BIT(7)
2016-03-05 00:38:45 +03:00
# define RV3029_RST_CTRL 0x04
2016-03-05 00:39:49 +03:00
# define RV3029_RST_CTRL_SYSR BIT(4)
2016-03-05 00:38:45 +03:00
# define RV3029_CONTROL_SECTION_LEN 0x05
2011-05-27 03:25:05 +04:00
/* watch section */
2016-03-05 00:38:45 +03:00
# define RV3029_W_SEC 0x08
# define RV3029_W_MINUTES 0x09
# define RV3029_W_HOURS 0x0A
2016-03-05 00:39:49 +03:00
# define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */
# define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */
2016-03-05 00:38:45 +03:00
# define RV3029_W_DATE 0x0B
# define RV3029_W_DAYS 0x0C
# define RV3029_W_MONTHS 0x0D
# define RV3029_W_YEARS 0x0E
# define RV3029_WATCH_SECTION_LEN 0x07
2011-05-27 03:25:05 +04:00
/* alarm section */
2016-03-05 00:38:45 +03:00
# define RV3029_A_SC 0x10
# define RV3029_A_MN 0x11
# define RV3029_A_HR 0x12
# define RV3029_A_DT 0x13
# define RV3029_A_DW 0x14
# define RV3029_A_MO 0x15
# define RV3029_A_YR 0x16
2016-05-03 12:54:36 +03:00
# define RV3029_A_AE_X BIT(7)
2016-03-05 00:38:45 +03:00
# define RV3029_ALARM_SECTION_LEN 0x07
2011-05-27 03:25:05 +04:00
/* timer section */
2016-03-05 00:38:45 +03:00
# define RV3029_TIMER_LOW 0x18
# define RV3029_TIMER_HIGH 0x19
2011-05-27 03:25:05 +04:00
/* temperature section */
2016-03-05 00:38:45 +03:00
# define RV3029_TEMP_PAGE 0x20
2011-05-27 03:25:05 +04:00
/* eeprom data section */
2016-03-05 00:38:45 +03:00
# define RV3029_E2P_EEDATA1 0x28
# define RV3029_E2P_EEDATA2 0x29
2016-03-05 00:39:49 +03:00
# define RV3029_E2PDATA_SECTION_LEN 0x02
2011-05-27 03:25:05 +04:00
/* eeprom control section */
2016-03-05 00:38:45 +03:00
# define RV3029_CONTROL_E2P_EECTRL 0x30
2016-03-05 00:39:49 +03:00
# define RV3029_EECTRL_THP BIT(0) /* temp scan interval */
# define RV3029_EECTRL_THE BIT(1) /* thermometer enable */
# define RV3029_EECTRL_FD0 BIT(2) /* CLKOUT */
# define RV3029_EECTRL_FD1 BIT(3) /* CLKOUT */
# define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */
# define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */
# define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */
# define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */
# define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\
RV3029_TRICKLE_5K | \
RV3029_TRICKLE_20K | \
RV3029_TRICKLE_80K )
# define RV3029_TRICKLE_SHIFT 4
# define RV3029_CONTROL_E2P_XOFFS 0x31 /* XTAL offset */
# define RV3029_CONTROL_E2P_XOFFS_SIGN BIT(7) /* Sign: 1->pos, 0->neg */
# define RV3029_CONTROL_E2P_QCOEF 0x32 /* XTAL temp drift coef */
# define RV3029_CONTROL_E2P_TURNOVER 0x33 /* XTAL turnover temp (in *C) */
# define RV3029_CONTROL_E2P_TOV_MASK 0x3F /* XTAL turnover temp mask */
2011-05-27 03:25:05 +04:00
/* user ram section */
2019-12-15 01:10:22 +03:00
# define RV3029_RAM_PAGE 0x38
# define RV3029_RAM_SECTION_LEN 8
2011-05-27 03:25:05 +04:00
2016-05-03 12:54:33 +03:00
struct rv3029_data {
struct device * dev ;
struct rtc_device * rtc ;
struct regmap * regmap ;
int irq ;
} ;
2019-12-15 01:10:12 +03:00
static int rv3029_eeprom_busywait ( struct rv3029_data * rv3029 )
2016-03-05 00:40:55 +03:00
{
2019-12-15 01:10:12 +03:00
unsigned int sr ;
2016-03-05 00:40:55 +03:00
int i , ret ;
for ( i = 100 ; i > 0 ; i - - ) {
2019-12-15 01:10:12 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_STATUS , & sr ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
break ;
if ( ! ( sr & RV3029_STATUS_EEBUSY ) )
break ;
usleep_range ( 1000 , 10000 ) ;
}
if ( i < = 0 ) {
2019-12-15 01:10:12 +03:00
dev_err ( rv3029 - > dev , " EEPROM busy wait timeout. \n " ) ;
2016-03-05 00:40:55 +03:00
return - ETIMEDOUT ;
}
return ret ;
}
2019-12-15 01:10:15 +03:00
static int rv3029_eeprom_exit ( struct rv3029_data * rv3029 )
2016-03-05 00:40:55 +03:00
{
/* Re-enable eeprom refresh */
2019-12-15 01:10:09 +03:00
return regmap_update_bits ( rv3029 - > regmap , RV3029_ONOFF_CTRL ,
2016-05-03 12:54:32 +03:00
RV3029_ONOFF_CTRL_EERE ,
RV3029_ONOFF_CTRL_EERE ) ;
2016-03-05 00:40:55 +03:00
}
2019-12-15 01:10:15 +03:00
static int rv3029_eeprom_enter ( struct rv3029_data * rv3029 )
2016-03-05 00:40:55 +03:00
{
2019-12-15 01:10:12 +03:00
unsigned int sr ;
2016-03-05 00:40:55 +03:00
int ret ;
/* Check whether we are in the allowed voltage range. */
2019-12-15 01:10:12 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_STATUS , & sr ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
return ret ;
2019-12-15 01:10:17 +03:00
if ( sr & RV3029_STATUS_VLOW2 )
return - ENODEV ;
if ( sr & RV3029_STATUS_VLOW1 ) {
2016-03-05 00:40:55 +03:00
/* We clear the bits and retry once just in case
* we had a brown out in early startup .
*/
2019-12-15 01:10:10 +03:00
ret = regmap_update_bits ( rv3029 - > regmap , RV3029_STATUS ,
2019-12-15 01:10:17 +03:00
RV3029_STATUS_VLOW1 , 0 ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
return ret ;
usleep_range ( 1000 , 10000 ) ;
2019-12-15 01:10:12 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_STATUS , & sr ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
return ret ;
2019-12-15 01:10:17 +03:00
if ( sr & RV3029_STATUS_VLOW1 ) {
2019-12-15 01:10:15 +03:00
dev_err ( rv3029 - > dev ,
2016-03-05 00:40:55 +03:00
" Supply voltage is too low to safely access the EEPROM. \n " ) ;
return - ENODEV ;
}
}
/* Disable eeprom refresh. */
2019-12-15 01:10:09 +03:00
ret = regmap_update_bits ( rv3029 - > regmap , RV3029_ONOFF_CTRL ,
RV3029_ONOFF_CTRL_EERE , 0 ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
return ret ;
/* Wait for any previous eeprom accesses to finish. */
2019-12-15 01:10:12 +03:00
ret = rv3029_eeprom_busywait ( rv3029 ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
2019-12-15 01:10:15 +03:00
rv3029_eeprom_exit ( rv3029 ) ;
2016-03-05 00:40:55 +03:00
return ret ;
}
2019-12-15 01:10:15 +03:00
static int rv3029_eeprom_read ( struct rv3029_data * rv3029 , u8 reg ,
2016-03-05 00:40:55 +03:00
u8 buf [ ] , size_t len )
{
int ret , err ;
2019-12-15 01:10:15 +03:00
err = rv3029_eeprom_enter ( rv3029 ) ;
2016-03-05 00:40:55 +03:00
if ( err < 0 )
return err ;
2019-12-15 01:10:15 +03:00
ret = regmap_bulk_read ( rv3029 - > regmap , reg , buf , len ) ;
2016-03-05 00:40:55 +03:00
2019-12-15 01:10:15 +03:00
err = rv3029_eeprom_exit ( rv3029 ) ;
2016-03-05 00:40:55 +03:00
if ( err < 0 )
return err ;
return ret ;
}
2019-12-15 01:10:15 +03:00
static int rv3029_eeprom_write ( struct rv3029_data * rv3029 , u8 reg ,
2016-03-05 00:40:55 +03:00
u8 const buf [ ] , size_t len )
{
2019-12-15 01:10:15 +03:00
unsigned int tmp ;
2019-08-17 09:56:04 +03:00
int ret , err ;
2016-03-05 00:40:55 +03:00
size_t i ;
2019-12-15 01:10:15 +03:00
err = rv3029_eeprom_enter ( rv3029 ) ;
2019-08-17 09:56:04 +03:00
if ( err < 0 )
return err ;
2016-03-05 00:40:55 +03:00
for ( i = 0 ; i < len ; i + + , reg + + ) {
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , reg , & tmp ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
break ;
if ( tmp ! = buf [ i ] ) {
2019-12-15 01:10:15 +03:00
tmp = buf [ i ] ;
ret = regmap_write ( rv3029 - > regmap , reg , tmp ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
break ;
}
2019-12-15 01:10:12 +03:00
ret = rv3029_eeprom_busywait ( rv3029 ) ;
2016-03-05 00:40:55 +03:00
if ( ret < 0 )
break ;
}
2019-12-15 01:10:15 +03:00
err = rv3029_eeprom_exit ( rv3029 ) ;
2019-08-17 09:56:04 +03:00
if ( err < 0 )
return err ;
2016-03-05 00:40:55 +03:00
2019-08-17 09:56:04 +03:00
return ret ;
2016-03-05 00:40:55 +03:00
}
2019-12-15 01:10:15 +03:00
static int rv3029_eeprom_update_bits ( struct rv3029_data * rv3029 ,
2016-03-10 20:34:23 +03:00
u8 reg , u8 mask , u8 set )
{
u8 buf ;
int ret ;
2019-12-15 01:10:15 +03:00
ret = rv3029_eeprom_read ( rv3029 , reg , & buf , 1 ) ;
2016-03-10 20:34:23 +03:00
if ( ret < 0 )
return ret ;
buf & = ~ mask ;
buf | = set & mask ;
2019-12-15 01:10:15 +03:00
ret = rv3029_eeprom_write ( rv3029 , reg , & buf , 1 ) ;
2016-03-10 20:34:23 +03:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2016-05-03 12:54:38 +03:00
static irqreturn_t rv3029_handle_irq ( int irq , void * dev_id )
{
struct device * dev = dev_id ;
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2019-12-15 01:10:15 +03:00
unsigned int flags , controls ;
2016-05-03 12:54:38 +03:00
unsigned long events = 0 ;
int ret ;
2021-01-20 01:06:49 +03:00
rtc_lock ( rv3029 - > rtc ) ;
2016-05-03 12:54:38 +03:00
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_IRQ_CTRL , & controls ) ;
2016-05-03 12:54:38 +03:00
if ( ret ) {
dev_warn ( dev , " Read IRQ Control Register error %d \n " , ret ) ;
2021-01-20 01:06:49 +03:00
rtc_unlock ( rv3029 - > rtc ) ;
2016-05-03 12:54:38 +03:00
return IRQ_NONE ;
}
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_IRQ_FLAGS , & flags ) ;
2016-05-03 12:54:38 +03:00
if ( ret ) {
dev_warn ( dev , " Read IRQ Flags Register error %d \n " , ret ) ;
2021-01-20 01:06:49 +03:00
rtc_unlock ( rv3029 - > rtc ) ;
2016-05-03 12:54:38 +03:00
return IRQ_NONE ;
}
if ( flags & RV3029_IRQ_FLAGS_AF ) {
flags & = ~ RV3029_IRQ_FLAGS_AF ;
controls & = ~ RV3029_IRQ_CTRL_AIE ;
events | = RTC_AF ;
}
if ( events ) {
rtc_update_irq ( rv3029 - > rtc , 1 , events ) ;
2019-12-15 01:10:15 +03:00
regmap_write ( rv3029 - > regmap , RV3029_IRQ_FLAGS , flags ) ;
regmap_write ( rv3029 - > regmap , RV3029_IRQ_CTRL , controls ) ;
2016-05-03 12:54:38 +03:00
}
2021-01-20 01:06:49 +03:00
rtc_unlock ( rv3029 - > rtc ) ;
2016-05-03 12:54:38 +03:00
return IRQ_HANDLED ;
}
2016-05-03 12:54:33 +03:00
static int rv3029_read_time ( struct device * dev , struct rtc_time * tm )
2011-05-27 03:25:05 +04:00
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2019-12-15 01:10:17 +03:00
unsigned int sr ;
2011-05-27 03:25:05 +04:00
int ret ;
2016-03-05 00:38:45 +03:00
u8 regs [ RV3029_WATCH_SECTION_LEN ] = { 0 , } ;
2011-05-27 03:25:05 +04:00
2019-12-15 01:10:17 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_STATUS , & sr ) ;
if ( ret < 0 )
return ret ;
if ( sr & ( RV3029_STATUS_VLOW2 | RV3029_STATUS_PON ) )
return - EINVAL ;
2019-12-15 01:10:15 +03:00
ret = regmap_bulk_read ( rv3029 - > regmap , RV3029_W_SEC , regs ,
2016-05-03 12:54:32 +03:00
RV3029_WATCH_SECTION_LEN ) ;
2019-12-23 13:01:08 +03:00
if ( ret < 0 )
2011-05-27 03:25:05 +04:00
return ret ;
2016-05-03 12:54:35 +03:00
tm - > tm_sec = bcd2bin ( regs [ RV3029_W_SEC - RV3029_W_SEC ] ) ;
tm - > tm_min = bcd2bin ( regs [ RV3029_W_MINUTES - RV3029_W_SEC ] ) ;
2011-05-27 03:25:05 +04:00
/* HR field has a more complex interpretation */
{
2016-05-03 12:54:35 +03:00
const u8 _hr = regs [ RV3029_W_HOURS - RV3029_W_SEC ] ;
2016-03-05 00:38:45 +03:00
if ( _hr & RV3029_REG_HR_12_24 ) {
2011-05-27 03:25:05 +04:00
/* 12h format */
tm - > tm_hour = bcd2bin ( _hr & 0x1f ) ;
2016-03-05 00:38:45 +03:00
if ( _hr & RV3029_REG_HR_PM ) /* PM flag set */
2011-05-27 03:25:05 +04:00
tm - > tm_hour + = 12 ;
} else /* 24h format */
tm - > tm_hour = bcd2bin ( _hr & 0x3f ) ;
}
2016-05-03 12:54:35 +03:00
tm - > tm_mday = bcd2bin ( regs [ RV3029_W_DATE - RV3029_W_SEC ] ) ;
tm - > tm_mon = bcd2bin ( regs [ RV3029_W_MONTHS - RV3029_W_SEC ] ) - 1 ;
tm - > tm_year = bcd2bin ( regs [ RV3029_W_YEARS - RV3029_W_SEC ] ) + 100 ;
tm - > tm_wday = bcd2bin ( regs [ RV3029_W_DAYS - RV3029_W_SEC ] ) - 1 ;
2011-05-27 03:25:05 +04:00
return 0 ;
}
2016-05-03 12:54:33 +03:00
static int rv3029_read_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
2011-05-27 03:25:05 +04:00
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:05 +04:00
struct rtc_time * const tm = & alarm - > time ;
2019-12-15 01:10:15 +03:00
unsigned int controls , flags ;
2011-05-27 03:25:05 +04:00
int ret ;
2019-12-15 01:10:15 +03:00
u8 regs [ 8 ] ;
2011-05-27 03:25:05 +04:00
2019-12-15 01:10:15 +03:00
ret = regmap_bulk_read ( rv3029 - > regmap , RV3029_A_SC , regs ,
2016-05-03 12:54:32 +03:00
RV3029_ALARM_SECTION_LEN ) ;
2019-12-23 13:01:08 +03:00
if ( ret < 0 )
2011-05-27 03:25:05 +04:00
return ret ;
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_IRQ_CTRL , & controls ) ;
2019-12-23 13:01:08 +03:00
if ( ret )
2016-05-03 12:54:38 +03:00
return ret ;
2019-12-23 13:01:08 +03:00
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_IRQ_FLAGS , & flags ) ;
2019-12-23 13:01:08 +03:00
if ( ret < 0 )
2016-05-03 12:54:38 +03:00
return ret ;
2016-05-03 12:54:35 +03:00
tm - > tm_sec = bcd2bin ( regs [ RV3029_A_SC - RV3029_A_SC ] & 0x7f ) ;
tm - > tm_min = bcd2bin ( regs [ RV3029_A_MN - RV3029_A_SC ] & 0x7f ) ;
tm - > tm_hour = bcd2bin ( regs [ RV3029_A_HR - RV3029_A_SC ] & 0x3f ) ;
tm - > tm_mday = bcd2bin ( regs [ RV3029_A_DT - RV3029_A_SC ] & 0x3f ) ;
tm - > tm_mon = bcd2bin ( regs [ RV3029_A_MO - RV3029_A_SC ] & 0x1f ) - 1 ;
tm - > tm_year = bcd2bin ( regs [ RV3029_A_YR - RV3029_A_SC ] & 0x7f ) + 100 ;
tm - > tm_wday = bcd2bin ( regs [ RV3029_A_DW - RV3029_A_SC ] & 0x07 ) - 1 ;
2011-05-27 03:25:05 +04:00
2016-05-03 12:54:38 +03:00
alarm - > enabled = ! ! ( controls & RV3029_IRQ_CTRL_AIE ) ;
alarm - > pending = ( flags & RV3029_IRQ_FLAGS_AF ) & & alarm - > enabled ;
2011-05-27 03:25:05 +04:00
return 0 ;
}
2016-05-03 12:54:38 +03:00
static int rv3029_alarm_irq_enable ( struct device * dev , unsigned int enable )
2011-05-27 03:25:05 +04:00
{
2019-12-15 01:10:13 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:05 +04:00
2019-12-15 01:10:13 +03:00
return regmap_update_bits ( rv3029 - > regmap , RV3029_IRQ_CTRL ,
RV3029_IRQ_CTRL_AIE ,
enable ? RV3029_IRQ_CTRL_AIE : 0 ) ;
2011-05-27 03:25:05 +04:00
}
2016-05-03 12:54:33 +03:00
static int rv3029_set_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
2011-05-27 03:25:05 +04:00
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:05 +04:00
struct rtc_time * const tm = & alarm - > time ;
int ret ;
u8 regs [ 8 ] ;
2016-05-03 12:54:36 +03:00
/* Activate all the alarms with AE_x bit */
regs [ RV3029_A_SC - RV3029_A_SC ] = bin2bcd ( tm - > tm_sec ) | RV3029_A_AE_X ;
regs [ RV3029_A_MN - RV3029_A_SC ] = bin2bcd ( tm - > tm_min ) | RV3029_A_AE_X ;
regs [ RV3029_A_HR - RV3029_A_SC ] = ( bin2bcd ( tm - > tm_hour ) & 0x3f )
| RV3029_A_AE_X ;
regs [ RV3029_A_DT - RV3029_A_SC ] = ( bin2bcd ( tm - > tm_mday ) & 0x3f )
| RV3029_A_AE_X ;
regs [ RV3029_A_MO - RV3029_A_SC ] = ( bin2bcd ( tm - > tm_mon + 1 ) & 0x1f )
| RV3029_A_AE_X ;
regs [ RV3029_A_DW - RV3029_A_SC ] = ( bin2bcd ( tm - > tm_wday + 1 ) & 0x7 )
| RV3029_A_AE_X ;
regs [ RV3029_A_YR - RV3029_A_SC ] = ( bin2bcd ( tm - > tm_year - 100 ) )
| RV3029_A_AE_X ;
/* Write the alarm */
2019-12-15 01:10:15 +03:00
ret = regmap_bulk_write ( rv3029 - > regmap , RV3029_A_SC , regs ,
2016-05-03 12:54:32 +03:00
RV3029_ALARM_SECTION_LEN ) ;
2011-05-27 03:25:05 +04:00
if ( ret < 0 )
return ret ;
2019-12-15 01:10:14 +03:00
return rv3029_alarm_irq_enable ( dev , alarm - > enabled ) ;
2011-05-27 03:25:05 +04:00
}
2016-05-03 12:54:33 +03:00
static int rv3029_set_time ( struct device * dev , struct rtc_time * tm )
2011-05-27 03:25:05 +04:00
{
2019-12-15 01:10:10 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:05 +04:00
u8 regs [ 8 ] ;
int ret ;
2016-05-03 12:54:35 +03:00
regs [ RV3029_W_SEC - RV3029_W_SEC ] = bin2bcd ( tm - > tm_sec ) ;
regs [ RV3029_W_MINUTES - RV3029_W_SEC ] = bin2bcd ( tm - > tm_min ) ;
regs [ RV3029_W_HOURS - RV3029_W_SEC ] = bin2bcd ( tm - > tm_hour ) ;
regs [ RV3029_W_DATE - RV3029_W_SEC ] = bin2bcd ( tm - > tm_mday ) ;
regs [ RV3029_W_MONTHS - RV3029_W_SEC ] = bin2bcd ( tm - > tm_mon + 1 ) ;
2016-05-03 12:54:37 +03:00
regs [ RV3029_W_DAYS - RV3029_W_SEC ] = bin2bcd ( tm - > tm_wday + 1 ) & 0x7 ;
2016-05-03 12:54:35 +03:00
regs [ RV3029_W_YEARS - RV3029_W_SEC ] = bin2bcd ( tm - > tm_year - 100 ) ;
2011-05-27 03:25:05 +04:00
2019-12-15 01:10:15 +03:00
ret = regmap_bulk_write ( rv3029 - > regmap , RV3029_W_SEC , regs ,
2016-05-03 12:54:32 +03:00
RV3029_WATCH_SECTION_LEN ) ;
2011-05-27 03:25:05 +04:00
if ( ret < 0 )
return ret ;
2019-12-15 01:10:17 +03:00
/* clear PON and VLOW2 bits */
2019-12-15 01:10:10 +03:00
return regmap_update_bits ( rv3029 - > regmap , RV3029_STATUS ,
2019-12-15 01:10:17 +03:00
RV3029_STATUS_PON | RV3029_STATUS_VLOW2 , 0 ) ;
2011-05-27 03:25:05 +04:00
}
2016-05-03 12:54:35 +03:00
2019-12-15 01:10:16 +03:00
static int rv3029_ioctl ( struct device * dev , unsigned int cmd , unsigned long arg )
{
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
unsigned long vl = 0 ;
int sr , ret = 0 ;
switch ( cmd ) {
case RTC_VL_READ :
ret = regmap_read ( rv3029 - > regmap , RV3029_STATUS , & sr ) ;
if ( ret < 0 )
return ret ;
if ( sr & RV3029_STATUS_VLOW1 )
vl = RTC_VL_ACCURACY_LOW ;
if ( sr & ( RV3029_STATUS_VLOW2 | RV3029_STATUS_PON ) )
vl | = RTC_VL_DATA_INVALID ;
return put_user ( vl , ( unsigned int __user * ) arg ) ;
case RTC_VL_CLR :
return regmap_update_bits ( rv3029 - > regmap , RV3029_STATUS ,
RV3029_STATUS_VLOW1 , 0 ) ;
default :
return - ENOIOCTLCMD ;
}
}
2019-12-15 01:10:22 +03:00
static int rv3029_nvram_write ( void * priv , unsigned int offset , void * val ,
size_t bytes )
{
return regmap_bulk_write ( priv , RV3029_RAM_PAGE + offset , val , bytes ) ;
}
static int rv3029_nvram_read ( void * priv , unsigned int offset , void * val ,
size_t bytes )
{
return regmap_bulk_read ( priv , RV3029_RAM_PAGE + offset , val , bytes ) ;
}
2016-03-05 00:41:19 +03:00
static const struct rv3029_trickle_tab_elem {
u32 r ; /* resistance in ohms */
u8 conf ; /* trickle config bits */
} rv3029_trickle_tab [ ] = {
{
. r = 1076 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
RV3029_TRICKLE_20K | RV3029_TRICKLE_80K ,
} , {
. r = 1091 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
RV3029_TRICKLE_20K ,
} , {
. r = 1137 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
RV3029_TRICKLE_80K ,
} , {
. r = 1154 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K ,
} , {
. r = 1371 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K |
RV3029_TRICKLE_80K ,
} , {
. r = 1395 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K ,
} , {
. r = 1472 ,
. conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K ,
} , {
. r = 1500 ,
. conf = RV3029_TRICKLE_1K ,
} , {
. r = 3810 ,
. conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K |
RV3029_TRICKLE_80K ,
} , {
. r = 4000 ,
. conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K ,
} , {
. r = 4706 ,
. conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K ,
} , {
. r = 5000 ,
. conf = RV3029_TRICKLE_5K ,
} , {
. r = 16000 ,
. conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K ,
} , {
. r = 20000 ,
. conf = RV3029_TRICKLE_20K ,
} , {
. r = 80000 ,
. conf = RV3029_TRICKLE_80K ,
} ,
} ;
2016-05-03 12:54:33 +03:00
static void rv3029_trickle_config ( struct device * dev )
2016-03-05 00:41:19 +03:00
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2016-05-03 12:54:33 +03:00
struct device_node * of_node = dev - > of_node ;
2016-03-05 00:41:19 +03:00
const struct rv3029_trickle_tab_elem * elem ;
int i , err ;
u32 ohms ;
2016-03-10 20:34:23 +03:00
u8 trickle_set_bits ;
2016-03-05 00:41:19 +03:00
if ( ! of_node )
return ;
/* Configure the trickle charger. */
err = of_property_read_u32 ( of_node , " trickle-resistor-ohms " , & ohms ) ;
if ( err ) {
/* Disable trickle charger. */
2016-03-10 20:34:23 +03:00
trickle_set_bits = 0 ;
2016-03-05 00:41:19 +03:00
} else {
/* Enable trickle charger. */
for ( i = 0 ; i < ARRAY_SIZE ( rv3029_trickle_tab ) ; i + + ) {
elem = & rv3029_trickle_tab [ i ] ;
if ( elem - > r > = ohms )
break ;
}
2016-03-10 20:34:23 +03:00
trickle_set_bits = elem - > conf ;
2016-05-03 12:54:33 +03:00
dev_info ( dev ,
2016-03-05 00:41:19 +03:00
" Trickle charger enabled at %d ohms resistance. \n " ,
elem - > r ) ;
}
2019-12-15 01:10:15 +03:00
err = rv3029_eeprom_update_bits ( rv3029 , RV3029_CONTROL_E2P_EECTRL ,
2016-03-10 20:34:23 +03:00
RV3029_TRICKLE_MASK ,
trickle_set_bits ) ;
2016-05-03 12:54:35 +03:00
if ( err < 0 )
2016-05-03 12:54:33 +03:00
dev_err ( dev , " Failed to update trickle charger config \n " ) ;
2016-03-05 00:41:19 +03:00
}
2016-03-10 20:34:46 +03:00
# ifdef CONFIG_RTC_DRV_RV3029_HWMON
2019-12-15 01:10:15 +03:00
static int rv3029_read_temp ( struct rv3029_data * rv3029 , int * temp_mC )
2016-03-10 20:34:46 +03:00
{
2019-12-15 01:10:15 +03:00
unsigned int temp ;
2016-03-10 20:34:46 +03:00
int ret ;
2019-12-15 01:10:15 +03:00
ret = regmap_read ( rv3029 - > regmap , RV3029_TEMP_PAGE , & temp ) ;
2016-03-10 20:34:46 +03:00
if ( ret < 0 )
return ret ;
* temp_mC = ( ( int ) temp - 60 ) * 1000 ;
return 0 ;
}
static ssize_t rv3029_hwmon_show_temp ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2016-03-10 20:34:46 +03:00
int ret , temp_mC ;
2019-12-15 01:10:15 +03:00
ret = rv3029_read_temp ( rv3029 , & temp_mC ) ;
2016-03-10 20:34:46 +03:00
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %d \n " , temp_mC ) ;
}
static ssize_t rv3029_hwmon_set_update_interval ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
unsigned int th_set_bits = 0 ;
2016-03-10 20:34:46 +03:00
unsigned long interval_ms ;
int ret ;
ret = kstrtoul ( buf , 10 , & interval_ms ) ;
if ( ret < 0 )
return ret ;
if ( interval_ms ! = 0 ) {
th_set_bits | = RV3029_EECTRL_THE ;
if ( interval_ms > = 16000 )
th_set_bits | = RV3029_EECTRL_THP ;
}
2019-12-15 01:10:15 +03:00
ret = rv3029_eeprom_update_bits ( rv3029 , RV3029_CONTROL_E2P_EECTRL ,
2016-03-10 20:34:46 +03:00
RV3029_EECTRL_THE | RV3029_EECTRL_THP ,
th_set_bits ) ;
if ( ret < 0 )
return ret ;
return count ;
}
static ssize_t rv3029_hwmon_show_update_interval ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2019-12-15 01:10:15 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2016-03-10 20:34:46 +03:00
int ret , interval_ms ;
u8 eectrl ;
2019-12-15 01:10:15 +03:00
ret = rv3029_eeprom_read ( rv3029 , RV3029_CONTROL_E2P_EECTRL ,
2016-03-10 20:34:46 +03:00
& eectrl , 1 ) ;
if ( ret < 0 )
return ret ;
if ( eectrl & RV3029_EECTRL_THE ) {
if ( eectrl & RV3029_EECTRL_THP )
interval_ms = 16000 ;
else
interval_ms = 1000 ;
} else {
interval_ms = 0 ;
}
return sprintf ( buf , " %d \n " , interval_ms ) ;
}
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , rv3029_hwmon_show_temp ,
NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( update_interval , S_IWUSR | S_IRUGO ,
rv3029_hwmon_show_update_interval ,
rv3029_hwmon_set_update_interval , 0 ) ;
static struct attribute * rv3029_hwmon_attrs [ ] = {
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_update_interval . dev_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( rv3029_hwmon ) ;
2016-05-03 12:54:33 +03:00
static void rv3029_hwmon_register ( struct device * dev , const char * name )
2016-03-10 20:34:46 +03:00
{
2016-05-03 12:54:33 +03:00
struct rv3029_data * rv3029 = dev_get_drvdata ( dev ) ;
2016-03-10 20:34:46 +03:00
struct device * hwmon_dev ;
2016-05-03 12:54:33 +03:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , name , rv3029 ,
rv3029_hwmon_groups ) ;
2016-03-10 20:34:46 +03:00
if ( IS_ERR ( hwmon_dev ) ) {
2016-05-03 12:54:33 +03:00
dev_warn ( dev , " unable to register hwmon device %ld \n " ,
2016-05-03 12:54:32 +03:00
PTR_ERR ( hwmon_dev ) ) ;
2016-03-10 20:34:46 +03:00
}
}
# else /* CONFIG_RTC_DRV_RV3029_HWMON */
2016-05-03 12:54:33 +03:00
static void rv3029_hwmon_register ( struct device * dev , const char * name )
2016-03-10 20:34:46 +03:00
{
}
# endif /* CONFIG_RTC_DRV_RV3029_HWMON */
2021-01-11 02:17:49 +03:00
static const struct rtc_class_ops rv3029_rtc_ops = {
2016-05-03 12:54:33 +03:00
. read_time = rv3029_read_time ,
. set_time = rv3029_set_time ,
2019-12-15 01:10:16 +03:00
. ioctl = rv3029_ioctl ,
2021-01-11 02:17:49 +03:00
. read_alarm = rv3029_read_alarm ,
. set_alarm = rv3029_set_alarm ,
. alarm_irq_enable = rv3029_alarm_irq_enable ,
2011-05-27 03:25:05 +04:00
} ;
2016-05-03 12:54:33 +03:00
static int rv3029_probe ( struct device * dev , struct regmap * regmap , int irq ,
const char * name )
2011-05-27 03:25:05 +04:00
{
2016-05-03 12:54:33 +03:00
struct rv3029_data * rv3029 ;
2019-12-15 01:10:22 +03:00
struct nvmem_config nvmem_cfg = {
. name = " rv3029_nvram " ,
. word_size = 1 ,
. stride = 1 ,
. size = RV3029_RAM_SECTION_LEN ,
. type = NVMEM_TYPE_BATTERY_BACKED ,
. reg_read = rv3029_nvram_read ,
. reg_write = rv3029_nvram_write ,
} ;
2011-05-27 03:25:05 +04:00
int rc = 0 ;
2016-05-03 12:54:33 +03:00
rv3029 = devm_kzalloc ( dev , sizeof ( * rv3029 ) , GFP_KERNEL ) ;
if ( ! rv3029 )
return - ENOMEM ;
rv3029 - > regmap = regmap ;
rv3029 - > irq = irq ;
rv3029 - > dev = dev ;
dev_set_drvdata ( dev , rv3029 ) ;
2011-05-27 03:25:05 +04:00
2016-05-03 12:54:33 +03:00
rv3029_trickle_config ( dev ) ;
rv3029_hwmon_register ( dev , name ) ;
2019-12-15 01:10:18 +03:00
rv3029 - > rtc = devm_rtc_allocate_device ( dev ) ;
if ( IS_ERR ( rv3029 - > rtc ) )
2016-05-03 12:54:38 +03:00
return PTR_ERR ( rv3029 - > rtc ) ;
2016-03-05 00:41:19 +03:00
2016-05-03 12:54:38 +03:00
if ( rv3029 - > irq > 0 ) {
rc = devm_request_threaded_irq ( dev , rv3029 - > irq ,
NULL , rv3029_handle_irq ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
" rv3029 " , dev ) ;
if ( rc ) {
dev_warn ( dev , " unable to request IRQ, alarms disabled \n " ) ;
rv3029 - > irq = 0 ;
}
}
2021-01-11 02:17:49 +03:00
if ( ! rv3029 - > irq )
clear_bit ( RTC_FEATURE_ALARM , rv3029 - > rtc - > features ) ;
2016-05-03 12:54:38 +03:00
2019-12-15 01:10:18 +03:00
rv3029 - > rtc - > ops = & rv3029_rtc_ops ;
2019-12-15 01:10:19 +03:00
rv3029 - > rtc - > range_min = RTC_TIMESTAMP_BEGIN_2000 ;
rv3029 - > rtc - > range_max = RTC_TIMESTAMP_END_2079 ;
2019-12-15 01:10:18 +03:00
2020-11-09 19:34:08 +03:00
rc = devm_rtc_register_device ( rv3029 - > rtc ) ;
2019-12-15 01:10:22 +03:00
if ( rc )
return rc ;
nvmem_cfg . priv = rv3029 - > regmap ;
2020-11-09 19:34:06 +03:00
devm_rtc_nvmem_register ( rv3029 - > rtc , & nvmem_cfg ) ;
2019-12-15 01:10:22 +03:00
return 0 ;
2016-05-03 12:54:33 +03:00
}
2011-05-27 03:25:05 +04:00
2019-12-15 01:10:08 +03:00
static const struct regmap_range rv3029_holes_range [ ] = {
regmap_reg_range ( 0x05 , 0x07 ) ,
regmap_reg_range ( 0x0f , 0x0f ) ,
regmap_reg_range ( 0x17 , 0x17 ) ,
regmap_reg_range ( 0x1a , 0x1f ) ,
regmap_reg_range ( 0x21 , 0x27 ) ,
regmap_reg_range ( 0x34 , 0x37 ) ,
} ;
static const struct regmap_access_table rv3029_regs = {
. no_ranges = rv3029_holes_range ,
. n_no_ranges = ARRAY_SIZE ( rv3029_holes_range ) ,
} ;
static const struct regmap_config config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. rd_table = & rv3029_regs ,
. wr_table = & rv3029_regs ,
. max_register = 0x3f ,
} ;
2016-05-03 12:54:34 +03:00
# if IS_ENABLED(CONFIG_I2C)
2016-05-03 12:54:33 +03:00
static int rv3029_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct regmap * regmap ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_BYTE ) ) {
dev_err ( & client - > dev , " Adapter does not support SMBUS_I2C_BLOCK or SMBUS_I2C_BYTE \n " ) ;
return - ENODEV ;
}
2011-05-27 03:25:05 +04:00
2016-05-03 12:54:33 +03:00
regmap = devm_regmap_init_i2c ( client , & config ) ;
2019-12-23 13:01:08 +03:00
if ( IS_ERR ( regmap ) )
2016-05-03 12:54:33 +03:00
return PTR_ERR ( regmap ) ;
2011-05-27 03:25:05 +04:00
2016-05-03 12:54:33 +03:00
return rv3029_probe ( & client - > dev , regmap , client - > irq , client - > name ) ;
2011-05-27 03:25:05 +04:00
}
2017-08-19 22:07:55 +03:00
static const struct i2c_device_id rv3029_id [ ] = {
2016-05-04 12:50:02 +03:00
{ " rv3029 " , 0 } ,
{ " rv3029c2 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , rv3029_id ) ;
2021-02-02 14:22:12 +03:00
static const __maybe_unused struct of_device_id rv3029_of_match [ ] = {
2017-09-15 05:00:02 +03:00
{ . compatible = " microcrystal,rv3029 " } ,
2017-03-03 17:29:13 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , rv3029_of_match ) ;
2016-03-05 00:38:45 +03:00
static struct i2c_driver rv3029_driver = {
2011-05-27 03:25:05 +04:00
. driver = {
2019-12-15 01:10:07 +03:00
. name = " rv3029 " ,
2017-03-03 17:29:13 +03:00
. of_match_table = of_match_ptr ( rv3029_of_match ) ,
2011-05-27 03:25:05 +04:00
} ,
2016-05-03 12:54:33 +03:00
. probe = rv3029_i2c_probe ,
2016-03-05 00:38:45 +03:00
. id_table = rv3029_id ,
2011-05-27 03:25:05 +04:00
} ;
2019-12-23 13:14:30 +03:00
static int __init rv3029_register_driver ( void )
2016-05-03 12:54:34 +03:00
{
return i2c_add_driver ( & rv3029_driver ) ;
}
static void rv3029_unregister_driver ( void )
{
i2c_del_driver ( & rv3029_driver ) ;
}
# else
2019-12-23 13:14:30 +03:00
static int __init rv3029_register_driver ( void )
2016-05-03 12:54:34 +03:00
{
return 0 ;
}
static void rv3029_unregister_driver ( void )
{
}
# endif
# if IS_ENABLED(CONFIG_SPI_MASTER)
static int rv3049_probe ( struct spi_device * spi )
{
struct regmap * regmap ;
regmap = devm_regmap_init_spi ( spi , & config ) ;
2019-12-23 13:01:08 +03:00
if ( IS_ERR ( regmap ) )
2016-05-03 12:54:34 +03:00
return PTR_ERR ( regmap ) ;
return rv3029_probe ( & spi - > dev , regmap , spi - > irq , " rv3049 " ) ;
}
static struct spi_driver rv3049_driver = {
. driver = {
. name = " rv3049 " ,
} ,
. probe = rv3049_probe ,
} ;
2019-12-23 13:14:30 +03:00
static int __init rv3049_register_driver ( void )
2016-05-03 12:54:34 +03:00
{
return spi_register_driver ( & rv3049_driver ) ;
}
2019-12-23 13:14:30 +03:00
static void __exit rv3049_unregister_driver ( void )
2016-05-03 12:54:34 +03:00
{
spi_unregister_driver ( & rv3049_driver ) ;
}
# else
2019-12-23 13:14:30 +03:00
static int __init rv3049_register_driver ( void )
2016-05-03 12:54:34 +03:00
{
return 0 ;
}
2019-12-23 13:14:30 +03:00
static void __exit rv3049_unregister_driver ( void )
2016-05-03 12:54:34 +03:00
{
}
# endif
static int __init rv30x9_init ( void )
{
int ret ;
ret = rv3029_register_driver ( ) ;
2019-12-23 13:01:08 +03:00
if ( ret )
2016-05-03 12:54:34 +03:00
return ret ;
ret = rv3049_register_driver ( ) ;
2019-12-23 13:01:08 +03:00
if ( ret )
2016-05-03 12:54:34 +03:00
rv3029_unregister_driver ( ) ;
return ret ;
}
module_init ( rv30x9_init )
static void __exit rv30x9_exit ( void )
{
rv3049_unregister_driver ( ) ;
rv3029_unregister_driver ( ) ;
}
module_exit ( rv30x9_exit )
2011-05-27 03:25:05 +04:00
MODULE_AUTHOR ( " Gregory Hermant <gregory.hermant@calao-systems.com> " ) ;
2016-03-05 00:40:30 +03:00
MODULE_AUTHOR ( " Michael Buesch <m@bues.ch> " ) ;
2016-05-03 12:54:34 +03:00
MODULE_DESCRIPTION ( " Micro Crystal RV3029/RV3049 RTC driver " ) ;
2011-05-27 03:25:05 +04:00
MODULE_LICENSE ( " GPL " ) ;
2016-05-03 12:54:34 +03:00
MODULE_ALIAS ( " spi:rv3049 " ) ;