Skip to content

Commit bb9c715

Browse files
GuEe-GUIRbb666
authored andcommitted
[WDT] Intel 6300ESB Timer/Watchdog
Used for QEMU on PCI Signed-off-by: GuEe-GUI <[email protected]>
1 parent 2532450 commit bb9c715

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-0
lines changed

components/drivers/watchdog/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ config RT_WDT_DW
99
depends on RT_USING_RESET
1010
default n
1111

12+
config RT_WDT_I6300ESB
13+
bool "Intel 6300ESB Timer/Watchdog"
14+
depends on RT_USING_DM
15+
depends on RT_USING_WDT
16+
depends on RT_USING_PCI
17+
1218
if RT_USING_DM && RT_USING_WDT
1319
osource "$(SOC_DM_WDT_DIR)/Kconfig"
1420
endif

components/drivers/watchdog/SConscript

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ src = ['dev_watchdog.c']
1414
if GetDepend(['RT_WDT_DW']):
1515
src += ['watchdog-dw.c']
1616

17+
if GetDepend(['RT_WDT_I6300ESB']):
18+
src += ['watchdog-i6300esb.c']
19+
1720
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
1821

1922
Return('group')
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/*
2+
* Copyright (c) 2006-2022, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2022-11-26 GuEe-GUI first version
9+
*/
10+
11+
#include <rtthread.h>
12+
#include <rtdevice.h>
13+
14+
#define DBG_TAG "wdt.i6300esb"
15+
#define DBG_LVL DBG_INFO
16+
#include <rtdbg.h>
17+
18+
#define I6300ESB_REG_BAR 0
19+
20+
/* PCI configuration registers */
21+
#define ESB_CONFIG_PCI_REG 0x60 /* Config register */
22+
#define ESB_LOCK_PCI_REG 0x68 /* WDT lock register */
23+
24+
/* Memory mapped registers */
25+
#define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */
26+
#define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */
27+
#define ESB_GINTSR_REG 0x08 /* General Interrupt Status Reg */
28+
#define ESB_RELOAD_REG 0x0c /* Reload register */
29+
30+
/* Lock register bits */
31+
#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */
32+
#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */
33+
#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */
34+
35+
/* Config register bits */
36+
#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */
37+
#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */
38+
#define ESB_WDT_INTTYPE (0x03 << 0) /* Interrupt type on timer1 timeout */
39+
40+
/* Reload register bits */
41+
#define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */
42+
#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */
43+
44+
/* Magic constants */
45+
#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */
46+
#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */
47+
48+
/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
49+
#define ESB_HEARTBEAT_MIN 1
50+
#define ESB_HEARTBEAT_MAX 2046
51+
#define ESB_HEARTBEAT_DEFAULT 30
52+
53+
struct i6300esb_wdt
54+
{
55+
rt_watchdog_t parent;
56+
57+
void *regs;
58+
rt_uint32_t timeout;
59+
struct rt_pci_device *pdev;
60+
};
61+
62+
#define raw_to_i6300esb_wdt(raw) rt_container_of(raw, struct i6300esb_wdt, parent)
63+
64+
/*
65+
* Prepare for reloading the timer by unlocking the proper registers.
66+
* This is performed by first writing 0x80 followed by 0x86 to the
67+
* reload register. After this the appropriate registers can be written
68+
* to once before they need to be unlocked again.
69+
*/
70+
rt_inline void i6300esb_wdt_unlock_registers(struct i6300esb_wdt *esb)
71+
{
72+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_UNLOCK1;
73+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_UNLOCK2;
74+
}
75+
76+
static rt_uint32_t i6300esb_timer_start(struct i6300esb_wdt *esb)
77+
{
78+
i6300esb_wdt_unlock_registers(esb);
79+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD;
80+
81+
rt_pci_write_config_u8(esb->pdev, ESB_LOCK_PCI_REG, ESB_WDT_ENABLE);
82+
83+
return RT_EOK;
84+
}
85+
86+
static rt_uint32_t i6300esb_timer_stop(struct i6300esb_wdt *esb)
87+
{
88+
rt_uint8_t val;
89+
90+
/* First, reset timers as suggested by the docs */
91+
i6300esb_wdt_unlock_registers(esb);
92+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD;
93+
94+
/* Then disable the WDT */
95+
rt_pci_write_config_u8(esb->pdev, ESB_LOCK_PCI_REG, 0x0);
96+
rt_pci_read_config_u8(esb->pdev, ESB_LOCK_PCI_REG, &val);
97+
98+
/* Returns 0 if the timer was disabled, non-zero otherwise */
99+
return val & ESB_WDT_ENABLE;
100+
}
101+
102+
static rt_err_t esb_timer_keepalive(struct i6300esb_wdt *esb)
103+
{
104+
i6300esb_wdt_unlock_registers(esb);
105+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD;
106+
107+
return RT_EOK;
108+
}
109+
110+
static rt_err_t i6300esb_timer_set_heartbeat(struct i6300esb_wdt *esb, rt_uint32_t time)
111+
{
112+
rt_uint32_t val;
113+
114+
/*
115+
* We shift by 9, so if we are passed a value of 1 sec,
116+
* val will be 1 << 9 = 512, then write that to two
117+
* timers => 2 * 512 = 1024 (which is decremented at 1KHz)
118+
*/
119+
val = time << 9;
120+
121+
/* Write timer 1 */
122+
i6300esb_wdt_unlock_registers(esb);
123+
HWREG32(esb->regs + ESB_TIMER1_REG) = val;
124+
125+
/* Write timer 2 */
126+
i6300esb_wdt_unlock_registers(esb);
127+
HWREG32(esb->regs + ESB_TIMER2_REG) = val;
128+
129+
/* Reload */
130+
i6300esb_wdt_unlock_registers(esb);
131+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_RELOAD;
132+
133+
esb->timeout = time;
134+
135+
return RT_EOK;
136+
}
137+
138+
static rt_err_t i6300esb_wdt_init(rt_watchdog_t *wdt)
139+
{
140+
return RT_EOK;
141+
}
142+
143+
static rt_err_t i6300esb_wdt_control(rt_watchdog_t *wdt, int cmd, void *args)
144+
{
145+
rt_err_t err = RT_EOK;
146+
struct i6300esb_wdt *esb = raw_to_i6300esb_wdt(wdt);
147+
148+
switch (cmd)
149+
{
150+
case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
151+
*(rt_uint32_t *)args = esb->timeout;
152+
break;
153+
154+
case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
155+
err = i6300esb_timer_set_heartbeat(esb, *(rt_uint32_t *)args);
156+
break;
157+
158+
case RT_DEVICE_CTRL_WDT_KEEPALIVE:
159+
err = esb_timer_keepalive(esb);
160+
break;
161+
162+
case RT_DEVICE_CTRL_WDT_START:
163+
err = i6300esb_timer_start(esb);
164+
break;
165+
166+
case RT_DEVICE_CTRL_WDT_STOP:
167+
err = i6300esb_timer_stop(esb);
168+
break;
169+
170+
default:
171+
err = -RT_EINVAL;
172+
}
173+
174+
return err;
175+
}
176+
177+
static const struct rt_watchdog_ops i6300esb_wdt_ops =
178+
{
179+
.init = i6300esb_wdt_init,
180+
.control = i6300esb_wdt_control,
181+
};
182+
183+
static rt_err_t i6300esb_wdt_probe(struct rt_pci_device *pdev)
184+
{
185+
rt_err_t err;
186+
rt_uint8_t val1;
187+
rt_uint16_t val2;
188+
const char *dev_name;
189+
struct i6300esb_wdt *esb = rt_calloc(1, sizeof(*esb));
190+
191+
if (!esb)
192+
{
193+
return -RT_ENOMEM;
194+
}
195+
196+
esb->regs = rt_pci_iomap(pdev, I6300ESB_REG_BAR);
197+
198+
if (!esb->regs)
199+
{
200+
err = -RT_EIO;
201+
202+
goto _fail;
203+
}
204+
205+
/*
206+
* Config register:
207+
* Bit 5 : 0 = Enable WDT_OUTPUT
208+
* Bit 2 : 0 = set the timer frequency to the PCI clock
209+
* divided by 2^15 (approx 1KHz).
210+
* Bits 1:0 : 11 = WDT_INT_TYPE Disabled.
211+
* The watchdog has two timers, it can be setup so that the expiry of timer1
212+
* results in an interrupt and the expiry of timer2 results in a reboot.
213+
* We set it to not generate any interrupts as there is not much
214+
* we can do with it right now.
215+
*/
216+
rt_pci_write_config_u16(pdev, ESB_CONFIG_PCI_REG, 0x0003);
217+
218+
/* Check that the WDT isn't already locked */
219+
rt_pci_read_config_u8(pdev, ESB_LOCK_PCI_REG, &val1);
220+
if (val1 & ESB_WDT_LOCK)
221+
{
222+
LOG_W("Nowayout already set");
223+
}
224+
225+
/* Set the timer to watchdog mode and disable it for now */
226+
rt_pci_write_config_u8(pdev, ESB_LOCK_PCI_REG, 0x00);
227+
228+
/* Check if the watchdog was previously triggered */
229+
i6300esb_wdt_unlock_registers(esb);
230+
val2 = HWREG16(esb->regs + ESB_RELOAD_REG);
231+
if (val2 & ESB_WDT_TIMEOUT)
232+
{
233+
LOG_D("Card previously reset the CPU");
234+
}
235+
236+
/* Reset WDT_TIMEOUT flag and timers */
237+
i6300esb_wdt_unlock_registers(esb);
238+
HWREG16(esb->regs + ESB_RELOAD_REG) = ESB_WDT_TIMEOUT | ESB_WDT_RELOAD;
239+
240+
/* And set the correct timeout value */
241+
i6300esb_timer_set_heartbeat(esb, ESB_HEARTBEAT_DEFAULT);
242+
243+
pdev->parent.user_data = esb;
244+
245+
esb->pdev = pdev;
246+
esb->parent.ops = &i6300esb_wdt_ops;
247+
248+
rt_dm_dev_set_name_auto(&esb->parent.parent, "wdt");
249+
dev_name = rt_dm_dev_get_name(&esb->parent.parent);
250+
rt_hw_watchdog_register(&esb->parent, dev_name, 0, esb);
251+
252+
return RT_EOK;
253+
254+
_fail:
255+
if (esb->regs)
256+
{
257+
rt_iounmap(esb->regs);
258+
}
259+
260+
rt_free(esb);
261+
262+
return err;
263+
}
264+
265+
static rt_err_t i6300esb_wdt_remove(struct rt_pci_device *pdev)
266+
{
267+
struct i6300esb_wdt *esb = pdev->parent.user_data;
268+
269+
i6300esb_timer_stop(esb);
270+
271+
rt_device_unregister(&esb->parent.parent);
272+
273+
rt_iounmap(esb->regs);
274+
rt_free(esb);
275+
276+
return RT_EOK;
277+
}
278+
279+
static const struct rt_pci_device_id i6300esb_wdt_pci_ids[] =
280+
{
281+
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x25ab), },
282+
{ /* sentinel */ }
283+
};
284+
285+
static struct rt_pci_driver i6300esb_wdt_driver =
286+
{
287+
.name = "i6300esb-wdt",
288+
289+
.ids = i6300esb_wdt_pci_ids,
290+
.probe = i6300esb_wdt_probe,
291+
.remove = i6300esb_wdt_remove,
292+
};
293+
RT_PCI_DRIVER_EXPORT(i6300esb_wdt_driver);

0 commit comments

Comments
 (0)