diff options
Diffstat (limited to 'drivers/rtc/rtc-ds1307.c')
| -rw-r--r-- | drivers/rtc/rtc-ds1307.c | 126 | 
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 1f7e8aefc1eb..49702942bb08 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -22,6 +22,7 @@  #include <linux/hwmon-sysfs.h>  #include <linux/clk-provider.h>  #include <linux/regmap.h> +#include <linux/watchdog.h>  /*   * We can't determine type by probing, but if we expect pre-Linux code @@ -144,6 +145,15 @@ enum ds_type {  #	define M41TXX_BIT_CALIB_SIGN	BIT(5)  #	define M41TXX_M_CALIBRATION	GENMASK(4, 0) +#define DS1388_REG_WDOG_HUN_SECS	0x08 +#define DS1388_REG_WDOG_SECS		0x09 +#define DS1388_REG_FLAG			0x0b +#	define DS1388_BIT_WF		BIT(6) +#	define DS1388_BIT_OSF		BIT(7) +#define DS1388_REG_CONTROL		0x0c +#	define DS1388_BIT_RST		BIT(0) +#	define DS1388_BIT_WDE		BIT(1) +  /* negative offset step is -2.034ppm */  #define M41TXX_NEG_OFFSET_STEP_PPB	2034  /* positive offset step is +4.068ppm */ @@ -252,6 +262,13 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)  		if (tmp & DS1340_BIT_OSF)  			return -EINVAL;  		break; +	case ds_1388: +		ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); +		if (ret) +			return ret; +		if (tmp & DS1388_BIT_OSF) +			return -EINVAL; +		break;  	case mcp794xx:  		if (!(tmp & MCP794XX_BIT_ST))  			return -EINVAL; @@ -845,6 +862,72 @@ static int m41txx_rtc_set_offset(struct device *dev, long offset)  				  ctrl_reg);  } +#ifdef CONFIG_WATCHDOG_CORE +static int ds1388_wdt_start(struct watchdog_device *wdt_dev) +{ +	struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); +	u8 regs[2]; +	int ret; + +	ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, +				 DS1388_BIT_WF, 0); +	if (ret) +		return ret; + +	ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, +				 DS1388_BIT_WDE | DS1388_BIT_RST, 0); +	if (ret) +		return ret; + +	/* +	 * watchdog timeouts are measured in seconds. So ignore hundredths of +	 * seconds field. +	 */ +	regs[0] = 0; +	regs[1] = bin2bcd(wdt_dev->timeout); + +	ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, +				sizeof(regs)); +	if (ret) +		return ret; + +	return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, +				  DS1388_BIT_WDE | DS1388_BIT_RST, +				  DS1388_BIT_WDE | DS1388_BIT_RST); +} + +static int ds1388_wdt_stop(struct watchdog_device *wdt_dev) +{ +	struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); + +	return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, +				  DS1388_BIT_WDE | DS1388_BIT_RST, 0); +} + +static int ds1388_wdt_ping(struct watchdog_device *wdt_dev) +{ +	struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); +	u8 regs[2]; + +	return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, +				sizeof(regs)); +} + +static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev, +				  unsigned int val) +{ +	struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); +	u8 regs[2]; + +	wdt_dev->timeout = val; +	regs[0] = 0; +	regs[1] = bin2bcd(wdt_dev->timeout); + +	return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, +				 sizeof(regs)); +} +#endif +  static const struct rtc_class_ops rx8130_rtc_ops = {  	.read_time      = ds1307_get_time,  	.set_time       = ds1307_set_time, @@ -1567,6 +1650,48 @@ static void ds1307_clks_register(struct ds1307 *ds1307)  #endif /* CONFIG_COMMON_CLK */ +#ifdef CONFIG_WATCHDOG_CORE +static const struct watchdog_info ds1388_wdt_info = { +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +	.identity = "DS1388 watchdog", +}; + +static const struct watchdog_ops ds1388_wdt_ops = { +	.owner = THIS_MODULE, +	.start = ds1388_wdt_start, +	.stop = ds1388_wdt_stop, +	.ping = ds1388_wdt_ping, +	.set_timeout = ds1388_wdt_set_timeout, + +}; + +static void ds1307_wdt_register(struct ds1307 *ds1307) +{ +	struct watchdog_device	*wdt; + +	if (ds1307->type != ds_1388) +		return; + +	wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL); +	if (!wdt) +		return; + +	wdt->info = &ds1388_wdt_info; +	wdt->ops = &ds1388_wdt_ops; +	wdt->timeout = 99; +	wdt->max_timeout = 99; +	wdt->min_timeout = 1; + +	watchdog_init_timeout(wdt, 0, ds1307->dev); +	watchdog_set_drvdata(wdt, ds1307); +	devm_watchdog_register_device(ds1307->dev, wdt); +} +#else +static void ds1307_wdt_register(struct ds1307 *ds1307) +{ +} +#endif /* CONFIG_WATCHDOG_CORE */ +  static const struct regmap_config regmap_config = {  	.reg_bits = 8,  	.val_bits = 8, @@ -1856,6 +1981,7 @@ static int ds1307_probe(struct i2c_client *client,  	ds1307_hwmon_register(ds1307);  	ds1307_clks_register(ds1307); +	ds1307_wdt_register(ds1307);  	return 0;  |