|
22 | 22 | #include <linux/hwmon-sysfs.h>
|
23 | 23 | #include <linux/clk-provider.h>
|
24 | 24 | #include <linux/regmap.h>
|
| 25 | +#include <linux/watchdog.h> |
25 | 26 |
|
26 | 27 | /*
|
27 | 28 | * We can't determine type by probing, but if we expect pre-Linux code
|
@@ -144,8 +145,15 @@ enum ds_type {
|
144 | 145 | # define M41TXX_BIT_CALIB_SIGN BIT(5)
|
145 | 146 | # define M41TXX_M_CALIBRATION GENMASK(4, 0)
|
146 | 147 |
|
| 148 | +#define DS1388_REG_WDOG_HUN_SECS 0x08 |
| 149 | +#define DS1388_REG_WDOG_SECS 0x09 |
147 | 150 | #define DS1388_REG_FLAG 0x0b
|
| 151 | +# define DS1388_BIT_WF BIT(6) |
148 | 152 | # define DS1388_BIT_OSF BIT(7)
|
| 153 | +#define DS1388_REG_CONTROL 0x0c |
| 154 | +# define DS1388_BIT_RST BIT(0) |
| 155 | +# define DS1388_BIT_WDE BIT(1) |
| 156 | + |
149 | 157 | /* negative offset step is -2.034ppm */
|
150 | 158 | #define M41TXX_NEG_OFFSET_STEP_PPB 2034
|
151 | 159 | /* positive offset step is +4.068ppm */
|
@@ -854,6 +862,72 @@ static int m41txx_rtc_set_offset(struct device *dev, long offset)
|
854 | 862 | ctrl_reg);
|
855 | 863 | }
|
856 | 864 |
|
| 865 | +#ifdef CONFIG_WATCHDOG_CORE |
| 866 | +static int ds1388_wdt_start(struct watchdog_device *wdt_dev) |
| 867 | +{ |
| 868 | + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); |
| 869 | + u8 regs[2]; |
| 870 | + int ret; |
| 871 | + |
| 872 | + ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, |
| 873 | + DS1388_BIT_WF, 0); |
| 874 | + if (ret) |
| 875 | + return ret; |
| 876 | + |
| 877 | + ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, |
| 878 | + DS1388_BIT_WDE | DS1388_BIT_RST, 0); |
| 879 | + if (ret) |
| 880 | + return ret; |
| 881 | + |
| 882 | + /* |
| 883 | + * watchdog timeouts are measured in seconds. So ignore hundredths of |
| 884 | + * seconds field. |
| 885 | + */ |
| 886 | + regs[0] = 0; |
| 887 | + regs[1] = bin2bcd(wdt_dev->timeout); |
| 888 | + |
| 889 | + ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, |
| 890 | + sizeof(regs)); |
| 891 | + if (ret) |
| 892 | + return ret; |
| 893 | + |
| 894 | + return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, |
| 895 | + DS1388_BIT_WDE | DS1388_BIT_RST, |
| 896 | + DS1388_BIT_WDE | DS1388_BIT_RST); |
| 897 | +} |
| 898 | + |
| 899 | +static int ds1388_wdt_stop(struct watchdog_device *wdt_dev) |
| 900 | +{ |
| 901 | + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); |
| 902 | + |
| 903 | + return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, |
| 904 | + DS1388_BIT_WDE | DS1388_BIT_RST, 0); |
| 905 | +} |
| 906 | + |
| 907 | +static int ds1388_wdt_ping(struct watchdog_device *wdt_dev) |
| 908 | +{ |
| 909 | + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); |
| 910 | + u8 regs[2]; |
| 911 | + |
| 912 | + return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, |
| 913 | + sizeof(regs)); |
| 914 | +} |
| 915 | + |
| 916 | +static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev, |
| 917 | + unsigned int val) |
| 918 | +{ |
| 919 | + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); |
| 920 | + u8 regs[2]; |
| 921 | + |
| 922 | + wdt_dev->timeout = val; |
| 923 | + regs[0] = 0; |
| 924 | + regs[1] = bin2bcd(wdt_dev->timeout); |
| 925 | + |
| 926 | + return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, |
| 927 | + sizeof(regs)); |
| 928 | +} |
| 929 | +#endif |
| 930 | + |
857 | 931 | static const struct rtc_class_ops rx8130_rtc_ops = {
|
858 | 932 | .read_time = ds1307_get_time,
|
859 | 933 | .set_time = ds1307_set_time,
|
@@ -1576,6 +1650,46 @@ static void ds1307_clks_register(struct ds1307 *ds1307)
|
1576 | 1650 |
|
1577 | 1651 | #endif /* CONFIG_COMMON_CLK */
|
1578 | 1652 |
|
| 1653 | +#ifdef CONFIG_WATCHDOG_CORE |
| 1654 | +static const struct watchdog_info ds1388_wdt_info = { |
| 1655 | + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
| 1656 | + .identity = "DS1388 watchdog", |
| 1657 | +}; |
| 1658 | + |
| 1659 | +static const struct watchdog_ops ds1388_wdt_ops = { |
| 1660 | + .owner = THIS_MODULE, |
| 1661 | + .start = ds1388_wdt_start, |
| 1662 | + .stop = ds1388_wdt_stop, |
| 1663 | + .ping = ds1388_wdt_ping, |
| 1664 | + .set_timeout = ds1388_wdt_set_timeout, |
| 1665 | + |
| 1666 | +}; |
| 1667 | + |
| 1668 | +static void ds1307_wdt_register(struct ds1307 *ds1307) |
| 1669 | +{ |
| 1670 | + struct watchdog_device *wdt; |
| 1671 | + |
| 1672 | + if (ds1307->type != ds_1388) |
| 1673 | + return; |
| 1674 | + |
| 1675 | + wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL); |
| 1676 | + |
| 1677 | + wdt->info = &ds1388_wdt_info; |
| 1678 | + wdt->ops = &ds1388_wdt_ops; |
| 1679 | + wdt->timeout = 99; |
| 1680 | + wdt->max_timeout = 99; |
| 1681 | + wdt->min_timeout = 1; |
| 1682 | + |
| 1683 | + watchdog_init_timeout(wdt, 0, ds1307->dev); |
| 1684 | + watchdog_set_drvdata(wdt, ds1307); |
| 1685 | + devm_watchdog_register_device(ds1307->dev, wdt); |
| 1686 | +} |
| 1687 | +#else |
| 1688 | +static void ds1307_wdt_register(struct ds1307 *ds1307) |
| 1689 | +{ |
| 1690 | +} |
| 1691 | +#endif /* CONFIG_WATCHDOG_CORE */ |
| 1692 | + |
1579 | 1693 | static const struct regmap_config regmap_config = {
|
1580 | 1694 | .reg_bits = 8,
|
1581 | 1695 | .val_bits = 8,
|
@@ -1865,6 +1979,7 @@ static int ds1307_probe(struct i2c_client *client,
|
1865 | 1979 |
|
1866 | 1980 | ds1307_hwmon_register(ds1307);
|
1867 | 1981 | ds1307_clks_register(ds1307);
|
| 1982 | + ds1307_wdt_register(ds1307); |
1868 | 1983 |
|
1869 | 1984 | return 0;
|
1870 | 1985 |
|
|
0 commit comments