jz4740_wdt.c (ac94be498f84f7327533b62faca4c3da64434904) | jz4740_wdt.c (1d9c30745455c42bff07f500fc6ecaf4c10e942f) |
---|---|
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> 4 * JZ4740 Watchdog driver 5 */ 6 7#include <linux/mfd/ingenic-tcu.h> 8#include <linux/module.h> --- 4 unchanged lines hidden (view full) --- 13#include <linux/platform_device.h> 14#include <linux/io.h> 15#include <linux/device.h> 16#include <linux/clk.h> 17#include <linux/slab.h> 18#include <linux/err.h> 19#include <linux/of.h> 20 | 1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> 4 * JZ4740 Watchdog driver 5 */ 6 7#include <linux/mfd/ingenic-tcu.h> 8#include <linux/module.h> --- 4 unchanged lines hidden (view full) --- 13#include <linux/platform_device.h> 14#include <linux/io.h> 15#include <linux/device.h> 16#include <linux/clk.h> 17#include <linux/slab.h> 18#include <linux/err.h> 19#include <linux/of.h> 20 |
21#include <asm/mach-jz4740/timer.h> 22 23#define JZ_WDT_CLOCK_PCLK 0x1 24#define JZ_WDT_CLOCK_RTC 0x2 25#define JZ_WDT_CLOCK_EXT 0x4 26 27#define JZ_WDT_CLOCK_DIV_1 (0 << TCU_TCSR_PRESCALE_LSB) 28#define JZ_WDT_CLOCK_DIV_4 (1 << TCU_TCSR_PRESCALE_LSB) 29#define JZ_WDT_CLOCK_DIV_16 (2 << TCU_TCSR_PRESCALE_LSB) 30#define JZ_WDT_CLOCK_DIV_64 (3 << TCU_TCSR_PRESCALE_LSB) 31#define JZ_WDT_CLOCK_DIV_256 (4 << TCU_TCSR_PRESCALE_LSB) 32#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB) 33 | |
34#define DEFAULT_HEARTBEAT 5 35#define MAX_HEARTBEAT 2048 36 37static bool nowayout = WATCHDOG_NOWAYOUT; 38module_param(nowayout, bool, 0); 39MODULE_PARM_DESC(nowayout, 40 "Watchdog cannot be stopped once started (default=" 41 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 42 43static unsigned int heartbeat = DEFAULT_HEARTBEAT; 44module_param(heartbeat, uint, 0); 45MODULE_PARM_DESC(heartbeat, 46 "Watchdog heartbeat period in seconds from 1 to " 47 __MODULE_STRING(MAX_HEARTBEAT) ", default " 48 __MODULE_STRING(DEFAULT_HEARTBEAT)); 49 50struct jz4740_wdt_drvdata { 51 struct watchdog_device wdt; 52 void __iomem *base; | 21#define DEFAULT_HEARTBEAT 5 22#define MAX_HEARTBEAT 2048 23 24static bool nowayout = WATCHDOG_NOWAYOUT; 25module_param(nowayout, bool, 0); 26MODULE_PARM_DESC(nowayout, 27 "Watchdog cannot be stopped once started (default=" 28 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 29 30static unsigned int heartbeat = DEFAULT_HEARTBEAT; 31module_param(heartbeat, uint, 0); 32MODULE_PARM_DESC(heartbeat, 33 "Watchdog heartbeat period in seconds from 1 to " 34 __MODULE_STRING(MAX_HEARTBEAT) ", default " 35 __MODULE_STRING(DEFAULT_HEARTBEAT)); 36 37struct jz4740_wdt_drvdata { 38 struct watchdog_device wdt; 39 void __iomem *base; |
53 struct clk *rtc_clk; | 40 struct clk *clk; 41 unsigned long clk_rate; |
54}; 55 56static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) 57{ 58 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 59 60 writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); 61 return 0; 62} 63 64static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, 65 unsigned int new_timeout) 66{ 67 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | 42}; 43 44static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) 45{ 46 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 47 48 writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); 49 return 0; 50} 51 52static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, 53 unsigned int new_timeout) 54{ 55 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); |
68 unsigned int rtc_clk_rate; 69 unsigned int timeout_value; 70 unsigned short clock_div = JZ_WDT_CLOCK_DIV_1; | 56 u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout); |
71 u8 tcer; 72 | 57 u8 tcer; 58 |
73 rtc_clk_rate = clk_get_rate(drvdata->rtc_clk); 74 75 timeout_value = rtc_clk_rate * new_timeout; 76 while (timeout_value > 0xffff) { 77 if (clock_div == JZ_WDT_CLOCK_DIV_1024) { 78 /* Requested timeout too high; 79 * use highest possible value. */ 80 timeout_value = 0xffff; 81 break; 82 } 83 timeout_value >>= 2; 84 clock_div += (1 << TCU_TCSR_PRESCALE_LSB); 85 } 86 | |
87 tcer = readb(drvdata->base + TCU_REG_WDT_TCER); 88 writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); | 59 tcer = readb(drvdata->base + TCU_REG_WDT_TCER); 60 writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); |
89 writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR); | |
90 91 writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR); 92 writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); | 61 62 writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR); 63 writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); |
93 writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR); | |
94 95 if (tcer & TCU_WDT_TCER_TCEN) 96 writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); 97 98 wdt_dev->timeout = new_timeout; 99 return 0; 100} 101 102static int jz4740_wdt_start(struct watchdog_device *wdt_dev) 103{ 104 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | 64 65 if (tcer & TCU_WDT_TCER_TCEN) 66 writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); 67 68 wdt_dev->timeout = new_timeout; 69 return 0; 70} 71 72static int jz4740_wdt_start(struct watchdog_device *wdt_dev) 73{ 74 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); |
75 int ret; |
|
105 u8 tcer; 106 | 76 u8 tcer; 77 |
78 ret = clk_prepare_enable(drvdata->clk); 79 if (ret) 80 return ret; 81 |
|
107 tcer = readb(drvdata->base + TCU_REG_WDT_TCER); 108 | 82 tcer = readb(drvdata->base + TCU_REG_WDT_TCER); 83 |
109 jz4740_timer_enable_watchdog(); | |
110 jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 111 112 /* Start watchdog if it wasn't started already */ 113 if (!(tcer & TCU_WDT_TCER_TCEN)) 114 writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); 115 116 return 0; 117} 118 119static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) 120{ 121 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 122 123 writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); | 84 jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 85 86 /* Start watchdog if it wasn't started already */ 87 if (!(tcer & TCU_WDT_TCER_TCEN)) 88 writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); 89 90 return 0; 91} 92 93static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) 94{ 95 struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 96 97 writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); |
124 jz4740_timer_disable_watchdog(); | 98 clk_disable_unprepare(drvdata->clk); |
125 126 return 0; 127} 128 129static int jz4740_wdt_restart(struct watchdog_device *wdt_dev, 130 unsigned long action, void *data) 131{ 132 wdt_dev->timeout = 0; --- 24 unchanged lines hidden (view full) --- 157MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches); 158#endif 159 160static int jz4740_wdt_probe(struct platform_device *pdev) 161{ 162 struct device *dev = &pdev->dev; 163 struct jz4740_wdt_drvdata *drvdata; 164 struct watchdog_device *jz4740_wdt; | 99 100 return 0; 101} 102 103static int jz4740_wdt_restart(struct watchdog_device *wdt_dev, 104 unsigned long action, void *data) 105{ 106 wdt_dev->timeout = 0; --- 24 unchanged lines hidden (view full) --- 131MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches); 132#endif 133 134static int jz4740_wdt_probe(struct platform_device *pdev) 135{ 136 struct device *dev = &pdev->dev; 137 struct jz4740_wdt_drvdata *drvdata; 138 struct watchdog_device *jz4740_wdt; |
139 long rate; 140 int ret; |
|
165 166 drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata), 167 GFP_KERNEL); 168 if (!drvdata) 169 return -ENOMEM; 170 | 141 142 drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata), 143 GFP_KERNEL); 144 if (!drvdata) 145 return -ENOMEM; 146 |
171 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) 172 heartbeat = DEFAULT_HEARTBEAT; | 147 drvdata->clk = devm_clk_get(&pdev->dev, "wdt"); 148 if (IS_ERR(drvdata->clk)) { 149 dev_err(&pdev->dev, "cannot find WDT clock\n"); 150 return PTR_ERR(drvdata->clk); 151 } |
173 | 152 |
153 /* Set smallest clock possible */ 154 rate = clk_round_rate(drvdata->clk, 1); 155 if (rate < 0) 156 return rate; 157 158 ret = clk_set_rate(drvdata->clk, rate); 159 if (ret) 160 return ret; 161 162 drvdata->clk_rate = rate; |
|
174 jz4740_wdt = &drvdata->wdt; 175 jz4740_wdt->info = &jz4740_wdt_info; 176 jz4740_wdt->ops = &jz4740_wdt_ops; | 163 jz4740_wdt = &drvdata->wdt; 164 jz4740_wdt->info = &jz4740_wdt_info; 165 jz4740_wdt->ops = &jz4740_wdt_ops; |
177 jz4740_wdt->timeout = heartbeat; | |
178 jz4740_wdt->min_timeout = 1; | 166 jz4740_wdt->min_timeout = 1; |
179 jz4740_wdt->max_timeout = MAX_HEARTBEAT; | 167 jz4740_wdt->max_timeout = 0xffff / rate; 168 jz4740_wdt->timeout = clamp(heartbeat, 169 jz4740_wdt->min_timeout, 170 jz4740_wdt->max_timeout); |
180 jz4740_wdt->parent = dev; 181 watchdog_set_nowayout(jz4740_wdt, nowayout); 182 watchdog_set_drvdata(jz4740_wdt, drvdata); 183 184 drvdata->base = devm_platform_ioremap_resource(pdev, 0); 185 if (IS_ERR(drvdata->base)) 186 return PTR_ERR(drvdata->base); 187 | 171 jz4740_wdt->parent = dev; 172 watchdog_set_nowayout(jz4740_wdt, nowayout); 173 watchdog_set_drvdata(jz4740_wdt, drvdata); 174 175 drvdata->base = devm_platform_ioremap_resource(pdev, 0); 176 if (IS_ERR(drvdata->base)) 177 return PTR_ERR(drvdata->base); 178 |
188 drvdata->rtc_clk = devm_clk_get(dev, "rtc"); 189 if (IS_ERR(drvdata->rtc_clk)) { 190 dev_err(dev, "cannot find RTC clock\n"); 191 return PTR_ERR(drvdata->rtc_clk); 192 } 193 | |
194 return devm_watchdog_register_device(dev, &drvdata->wdt); 195} 196 197static struct platform_driver jz4740_wdt_driver = { 198 .probe = jz4740_wdt_probe, 199 .driver = { 200 .name = "jz4740-wdt", 201 .of_match_table = of_match_ptr(jz4740_wdt_of_matches), 202 }, 203}; 204 205module_platform_driver(jz4740_wdt_driver); 206 207MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 208MODULE_DESCRIPTION("jz4740 Watchdog Driver"); 209MODULE_LICENSE("GPL"); 210MODULE_ALIAS("platform:jz4740-wdt"); | 179 return devm_watchdog_register_device(dev, &drvdata->wdt); 180} 181 182static struct platform_driver jz4740_wdt_driver = { 183 .probe = jz4740_wdt_probe, 184 .driver = { 185 .name = "jz4740-wdt", 186 .of_match_table = of_match_ptr(jz4740_wdt_of_matches), 187 }, 188}; 189 190module_platform_driver(jz4740_wdt_driver); 191 192MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 193MODULE_DESCRIPTION("jz4740 Watchdog Driver"); 194MODULE_LICENSE("GPL"); 195MODULE_ALIAS("platform:jz4740-wdt"); |