Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/amdsbwd/amdsbwd.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2009 Andriy Gapon <[email protected]>
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
/*
30
* This is a driver for watchdog timer present in AMD SB600/SB7xx/SB8xx
31
* southbridges.
32
* Please see the following specifications for the descriptions of the
33
* registers and flags:
34
* - AMD SB600 Register Reference Guide, Public Version, Rev. 3.03 (SB600 RRG)
35
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/46155_sb600_rrg_pub_3.03.pdf
36
* - AMD SB700/710/750 Register Reference Guide (RRG)
37
* http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf
38
* - AMD SB700/710/750 Register Programming Requirements (RPR)
39
* http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf
40
* - AMD SB800-Series Southbridges Register Reference Guide (RRG)
41
* http://support.amd.com/us/Embedded_TechDocs/45482.pdf
42
* Please see the following for Watchdog Resource Table specification:
43
* - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT)
44
* http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx
45
* AMD SB600/SB7xx/SB8xx watchdog hardware seems to conform to the above
46
* specifications, but the table hasn't been spotted in the wild yet.
47
*/
48
49
#include <sys/cdefs.h>
50
#include "opt_amdsbwd.h"
51
52
#include <sys/param.h>
53
#include <sys/eventhandler.h>
54
#include <sys/kernel.h>
55
#include <sys/module.h>
56
#include <sys/systm.h>
57
#include <sys/sysctl.h>
58
#include <sys/bus.h>
59
#include <machine/bus.h>
60
#include <sys/rman.h>
61
#include <machine/cputypes.h>
62
#include <machine/md_var.h>
63
#include <machine/resource.h>
64
#include <sys/watchdog.h>
65
66
#include <dev/pci/pcivar.h>
67
#include <dev/amdsbwd/amd_chipset.h>
68
#include <isa/isavar.h>
69
70
/*
71
* Registers in the Watchdog IO space.
72
* See SB7xx RRG 2.3.4, WDRT.
73
*/
74
#define AMDSB_WD_CTRL 0x00
75
#define AMDSB_WD_RUN 0x01
76
#define AMDSB_WD_FIRED 0x02
77
#define AMDSB_WD_SHUTDOWN 0x04
78
#define AMDSB_WD_DISABLE 0x08
79
#define AMDSB_WD_RESERVED 0x70
80
#define AMDSB_WD_RELOAD 0x80
81
#define AMDSB_WD_COUNT 0x04
82
#define AMDSB_WD_COUNT_MASK 0xffff
83
#define AMDSB_WDIO_REG_WIDTH 4
84
85
#define amdsbwd_verbose_printf(dev, ...) \
86
do { \
87
if (bootverbose) \
88
device_printf(dev, __VA_ARGS__);\
89
} while (0)
90
91
struct amdsbwd_softc {
92
device_t dev;
93
eventhandler_tag ev_tag;
94
struct resource *res_ctrl;
95
struct resource *res_count;
96
int rid_ctrl;
97
int rid_count;
98
int ms_per_tick;
99
int max_ticks;
100
int active;
101
unsigned int timeout;
102
};
103
104
static void amdsbwd_identify(driver_t *driver, device_t parent);
105
static int amdsbwd_probe(device_t dev);
106
static int amdsbwd_attach(device_t dev);
107
static int amdsbwd_detach(device_t dev);
108
static int amdsbwd_suspend(device_t dev);
109
static int amdsbwd_resume(device_t dev);
110
111
static device_method_t amdsbwd_methods[] = {
112
DEVMETHOD(device_identify, amdsbwd_identify),
113
DEVMETHOD(device_probe, amdsbwd_probe),
114
DEVMETHOD(device_attach, amdsbwd_attach),
115
DEVMETHOD(device_detach, amdsbwd_detach),
116
DEVMETHOD(device_suspend, amdsbwd_suspend),
117
DEVMETHOD(device_resume, amdsbwd_resume),
118
#if 0
119
DEVMETHOD(device_shutdown, amdsbwd_detach),
120
#endif
121
DEVMETHOD_END
122
};
123
124
static driver_t amdsbwd_driver = {
125
"amdsbwd",
126
amdsbwd_methods,
127
sizeof(struct amdsbwd_softc)
128
};
129
130
DRIVER_MODULE(amdsbwd, isa, amdsbwd_driver, NULL, NULL);
131
132
static uint8_t
133
pmio_read(struct resource *res, uint8_t reg)
134
{
135
bus_write_1(res, 0, reg); /* Index */
136
return (bus_read_1(res, 1)); /* Data */
137
}
138
139
static void
140
pmio_write(struct resource *res, uint8_t reg, uint8_t val)
141
{
142
bus_write_1(res, 0, reg); /* Index */
143
bus_write_1(res, 1, val); /* Data */
144
}
145
146
static uint32_t
147
wdctrl_read(struct amdsbwd_softc *sc)
148
{
149
return (bus_read_4(sc->res_ctrl, 0));
150
}
151
152
static void
153
wdctrl_write(struct amdsbwd_softc *sc, uint32_t val)
154
{
155
bus_write_4(sc->res_ctrl, 0, val);
156
}
157
158
static __unused uint32_t
159
wdcount_read(struct amdsbwd_softc *sc)
160
{
161
return (bus_read_4(sc->res_count, 0));
162
}
163
164
static void
165
wdcount_write(struct amdsbwd_softc *sc, uint32_t val)
166
{
167
bus_write_4(sc->res_count, 0, val);
168
}
169
170
static void
171
amdsbwd_tmr_enable(struct amdsbwd_softc *sc)
172
{
173
uint32_t val;
174
175
val = wdctrl_read(sc);
176
val |= AMDSB_WD_RUN;
177
wdctrl_write(sc, val);
178
sc->active = 1;
179
amdsbwd_verbose_printf(sc->dev, "timer enabled\n");
180
}
181
182
static void
183
amdsbwd_tmr_disable(struct amdsbwd_softc *sc)
184
{
185
uint32_t val;
186
187
val = wdctrl_read(sc);
188
val &= ~AMDSB_WD_RUN;
189
wdctrl_write(sc, val);
190
sc->active = 0;
191
amdsbwd_verbose_printf(sc->dev, "timer disabled\n");
192
}
193
194
static void
195
amdsbwd_tmr_reload(struct amdsbwd_softc *sc)
196
{
197
uint32_t val;
198
199
val = wdctrl_read(sc);
200
val |= AMDSB_WD_RELOAD;
201
wdctrl_write(sc, val);
202
}
203
204
static void
205
amdsbwd_tmr_set(struct amdsbwd_softc *sc, uint16_t timeout)
206
{
207
208
timeout &= AMDSB_WD_COUNT_MASK;
209
wdcount_write(sc, timeout);
210
sc->timeout = timeout;
211
amdsbwd_verbose_printf(sc->dev, "timeout set to %u ticks\n", timeout);
212
}
213
214
static void
215
amdsbwd_event(void *arg, unsigned int cmd, int *error)
216
{
217
struct amdsbwd_softc *sc = arg;
218
uint64_t timeout;
219
220
if (cmd != 0) {
221
timeout = 0;
222
cmd &= WD_INTERVAL;
223
if (cmd >= WD_TO_1MS) {
224
timeout = (uint64_t)1 << (cmd - WD_TO_1MS);
225
timeout = timeout / sc->ms_per_tick;
226
}
227
/* For a too short timeout use 1 tick. */
228
if (timeout == 0)
229
timeout = 1;
230
/* For a too long timeout stop the timer. */
231
if (timeout > sc->max_ticks)
232
timeout = 0;
233
} else {
234
timeout = 0;
235
}
236
237
if (timeout != 0) {
238
if (timeout != sc->timeout)
239
amdsbwd_tmr_set(sc, timeout);
240
if (!sc->active)
241
amdsbwd_tmr_enable(sc);
242
amdsbwd_tmr_reload(sc);
243
*error = 0;
244
} else {
245
if (sc->active)
246
amdsbwd_tmr_disable(sc);
247
}
248
}
249
250
static void
251
amdsbwd_identify(driver_t *driver, device_t parent)
252
{
253
device_t child;
254
device_t smb_dev;
255
256
if (resource_disabled("amdsbwd", 0))
257
return;
258
if (device_find_child(parent, "amdsbwd", DEVICE_UNIT_ANY) != NULL)
259
return;
260
261
/*
262
* Try to identify SB600/SB7xx by PCI Device ID of SMBus device
263
* that should be present at bus 0, device 20, function 0.
264
*/
265
smb_dev = pci_find_bsf(0, 20, 0);
266
if (smb_dev == NULL)
267
return;
268
if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID &&
269
pci_get_devid(smb_dev) != AMDFCH_SMBUS_DEVID &&
270
pci_get_devid(smb_dev) != AMDCZ_SMBUS_DEVID &&
271
pci_get_devid(smb_dev) != HYGONCZ_SMBUS_DEVID)
272
return;
273
274
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd",
275
DEVICE_UNIT_ANY);
276
if (child == NULL)
277
device_printf(parent, "add amdsbwd child failed\n");
278
}
279
280
static void
281
amdsbwd_probe_sb7xx(device_t dev, struct resource *pmres, uint32_t *addr)
282
{
283
uint8_t val;
284
int i;
285
286
/* Report cause of previous reset for user's convenience. */
287
val = pmio_read(pmres, AMDSB_PM_RESET_STATUS0);
288
if (val != 0)
289
amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
290
val = pmio_read(pmres, AMDSB_PM_RESET_STATUS1);
291
if (val != 0)
292
amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val);
293
if ((val & AMDSB_WD_RST_STS) != 0)
294
device_printf(dev, "Previous Reset was caused by Watchdog\n");
295
296
/* Find base address of memory mapped WDT registers. */
297
for (*addr = 0, i = 0; i < 4; i++) {
298
*addr <<= 8;
299
*addr |= pmio_read(pmres, AMDSB_PM_WDT_BASE_MSB - i);
300
}
301
*addr &= ~0x07u;
302
303
/* Set watchdog timer tick to 1s. */
304
val = pmio_read(pmres, AMDSB_PM_WDT_CTRL);
305
val &= ~AMDSB_WDT_RES_MASK;
306
val |= AMDSB_WDT_RES_1S;
307
pmio_write(pmres, AMDSB_PM_WDT_CTRL, val);
308
309
/* Enable watchdog device (in stopped state). */
310
val = pmio_read(pmres, AMDSB_PM_WDT_CTRL);
311
val &= ~AMDSB_WDT_DISABLE;
312
pmio_write(pmres, AMDSB_PM_WDT_CTRL, val);
313
314
/*
315
* XXX TODO: Ensure that watchdog decode is enabled
316
* (register 0x41, bit 3).
317
*/
318
device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer");
319
}
320
321
static void
322
amdsbwd_probe_sb8xx(device_t dev, struct resource *pmres, uint32_t *addr)
323
{
324
uint32_t val;
325
int i;
326
327
/* Report cause of previous reset for user's convenience. */
328
329
val = pmio_read(pmres, AMDSB8_PM_RESET_CTRL);
330
if ((val & AMDSB8_RST_STS_DIS) != 0) {
331
val &= ~AMDSB8_RST_STS_DIS;
332
pmio_write(pmres, AMDSB8_PM_RESET_CTRL, val);
333
}
334
val = 0;
335
for (i = 3; i >= 0; i--) {
336
val <<= 8;
337
val |= pmio_read(pmres, AMDSB8_PM_RESET_STATUS + i);
338
}
339
if (val != 0)
340
amdsbwd_verbose_printf(dev, "ResetStatus = 0x%08x\n", val);
341
if ((val & AMDSB8_WD_RST_STS) != 0)
342
device_printf(dev, "Previous Reset was caused by Watchdog\n");
343
344
/* Find base address of memory mapped WDT registers. */
345
for (*addr = 0, i = 0; i < 4; i++) {
346
*addr <<= 8;
347
*addr |= pmio_read(pmres, AMDSB8_PM_WDT_EN + 3 - i);
348
}
349
*addr &= ~0x07u;
350
351
/* Set watchdog timer tick to 1s. */
352
val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL);
353
val &= ~AMDSB8_WDT_RES_MASK;
354
val |= AMDSB8_WDT_1HZ;
355
pmio_write(pmres, AMDSB8_PM_WDT_CTRL, val);
356
#ifdef AMDSBWD_DEBUG
357
val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL);
358
amdsbwd_verbose_printf(dev, "AMDSB8_PM_WDT_CTRL value = %#04x\n", val);
359
#endif
360
361
/*
362
* Enable watchdog device (in stopped state)
363
* and decoding of its address.
364
*/
365
val = pmio_read(pmres, AMDSB8_PM_WDT_EN);
366
val &= ~AMDSB8_WDT_DISABLE;
367
val |= AMDSB8_WDT_DEC_EN;
368
pmio_write(pmres, AMDSB8_PM_WDT_EN, val);
369
#ifdef AMDSBWD_DEBUG
370
val = pmio_read(pmres, AMDSB8_PM_WDT_EN);
371
device_printf(dev, "AMDSB8_PM_WDT_EN value = %#04x\n", val);
372
#endif
373
device_set_desc(dev, "AMD SB8xx/SB9xx/Axx Watchdog Timer");
374
}
375
376
static void
377
amdsbwd_probe_fch41(device_t dev, struct resource *pmres, uint32_t *addr)
378
{
379
uint8_t val;
380
381
/*
382
* Enable decoding of watchdog MMIO address.
383
*/
384
val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN0);
385
val |= AMDFCH41_WDT_EN;
386
pmio_write(pmres, AMDFCH41_PM_DECODE_EN0, val);
387
#ifdef AMDSBWD_DEBUG
388
val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN0);
389
device_printf(dev, "AMDFCH41_PM_DECODE_EN0 value = %#04x\n", val);
390
#endif
391
392
val = pmio_read(pmres, AMDFCH41_PM_ISA_CTRL);
393
if ((val & AMDFCH41_MMIO_EN) != 0) {
394
/* Fixed offset for the watchdog within ACPI MMIO range. */
395
amdsbwd_verbose_printf(dev, "ACPI MMIO range is enabled\n");
396
*addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_WDT_OFF;
397
} else {
398
/* Special fixed MMIO range for the watchdog. */
399
*addr = AMDFCH41_WDT_FIXED_ADDR;
400
}
401
402
/*
403
* Set watchdog timer tick to 1s and
404
* enable the watchdog device (in stopped state).
405
*/
406
val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN3);
407
val &= ~AMDFCH41_WDT_RES_MASK;
408
val |= AMDFCH41_WDT_RES_1S;
409
val &= ~AMDFCH41_WDT_EN_MASK;
410
val |= AMDFCH41_WDT_ENABLE;
411
pmio_write(pmres, AMDFCH41_PM_DECODE_EN3, val);
412
#ifdef AMDSBWD_DEBUG
413
val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN3);
414
amdsbwd_verbose_printf(dev, "AMDFCH41_PM_DECODE_EN3 value = %#04x\n",
415
val);
416
#endif
417
device_set_descf(dev, "%s FCH Rev 41h+ Watchdog Timer",
418
cpu_vendor_id == CPU_VENDOR_HYGON ? "Hygon" : "AMD");
419
}
420
421
static int
422
amdsbwd_probe(device_t dev)
423
{
424
struct resource *res;
425
device_t smb_dev;
426
uint32_t addr;
427
int rid;
428
int rc;
429
uint32_t devid;
430
uint8_t revid;
431
432
/* Do not claim some ISA PnP device by accident. */
433
if (isa_get_logicalid(dev) != 0)
434
return (ENXIO);
435
436
rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX,
437
AMDSB_PMIO_WIDTH);
438
if (rc != 0) {
439
device_printf(dev, "bus_set_resource for IO failed\n");
440
return (ENXIO);
441
}
442
rid = 0;
443
res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
444
RF_ACTIVE | RF_SHAREABLE);
445
if (res == NULL) {
446
device_printf(dev, "bus_alloc_resource for IO failed\n");
447
return (ENXIO);
448
}
449
450
smb_dev = pci_find_bsf(0, 20, 0);
451
KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n"));
452
devid = pci_get_devid(smb_dev);
453
revid = pci_get_revid(smb_dev);
454
if (devid == AMDSB_SMBUS_DEVID && revid < AMDSB8_SMBUS_REVID)
455
amdsbwd_probe_sb7xx(dev, res, &addr);
456
else if (devid == AMDSB_SMBUS_DEVID ||
457
(devid == AMDFCH_SMBUS_DEVID && revid < AMDFCH41_SMBUS_REVID) ||
458
(devid == AMDCZ_SMBUS_DEVID && revid < AMDCZ49_SMBUS_REVID))
459
amdsbwd_probe_sb8xx(dev, res, &addr);
460
else
461
amdsbwd_probe_fch41(dev, res, &addr);
462
463
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
464
bus_delete_resource(dev, SYS_RES_IOPORT, rid);
465
466
amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr);
467
rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL,
468
AMDSB_WDIO_REG_WIDTH);
469
if (rc != 0) {
470
device_printf(dev, "bus_set_resource for control failed\n");
471
return (ENXIO);
472
}
473
rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, addr + AMDSB_WD_COUNT,
474
AMDSB_WDIO_REG_WIDTH);
475
if (rc != 0) {
476
device_printf(dev, "bus_set_resource for count failed\n");
477
return (ENXIO);
478
}
479
480
return (0);
481
}
482
483
static int
484
amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc)
485
{
486
487
sc->max_ticks = UINT16_MAX;
488
sc->rid_ctrl = 0;
489
sc->rid_count = 1;
490
491
sc->ms_per_tick = 1000;
492
493
sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
494
&sc->rid_ctrl, RF_ACTIVE);
495
if (sc->res_ctrl == NULL) {
496
device_printf(dev, "bus_alloc_resource for ctrl failed\n");
497
return (ENXIO);
498
}
499
sc->res_count = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
500
&sc->rid_count, RF_ACTIVE);
501
if (sc->res_count == NULL) {
502
device_printf(dev, "bus_alloc_resource for count failed\n");
503
return (ENXIO);
504
}
505
return (0);
506
}
507
508
static int
509
amdsbwd_attach(device_t dev)
510
{
511
struct amdsbwd_softc *sc;
512
int rc;
513
514
sc = device_get_softc(dev);
515
sc->dev = dev;
516
517
rc = amdsbwd_attach_sb(dev, sc);
518
if (rc != 0)
519
goto fail;
520
521
#ifdef AMDSBWD_DEBUG
522
device_printf(dev, "wd ctrl = %#04x\n", wdctrl_read(sc));
523
device_printf(dev, "wd count = %#04x\n", wdcount_read(sc));
524
#endif
525
526
/* Setup initial state of Watchdog Control. */
527
wdctrl_write(sc, AMDSB_WD_FIRED);
528
529
if (wdctrl_read(sc) & AMDSB_WD_DISABLE) {
530
device_printf(dev, "watchdog hardware is disabled\n");
531
goto fail;
532
}
533
534
sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, amdsbwd_event, sc,
535
EVENTHANDLER_PRI_ANY);
536
537
return (0);
538
539
fail:
540
amdsbwd_detach(dev);
541
return (ENXIO);
542
}
543
544
static int
545
amdsbwd_detach(device_t dev)
546
{
547
struct amdsbwd_softc *sc;
548
549
sc = device_get_softc(dev);
550
if (sc->ev_tag != NULL)
551
EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
552
553
if (sc->active)
554
amdsbwd_tmr_disable(sc);
555
556
if (sc->res_ctrl != NULL)
557
bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ctrl,
558
sc->res_ctrl);
559
560
if (sc->res_count != NULL)
561
bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_count,
562
sc->res_count);
563
564
return (0);
565
}
566
567
static int
568
amdsbwd_suspend(device_t dev)
569
{
570
struct amdsbwd_softc *sc;
571
uint32_t val;
572
573
sc = device_get_softc(dev);
574
val = wdctrl_read(sc);
575
val &= ~AMDSB_WD_RUN;
576
wdctrl_write(sc, val);
577
return (0);
578
}
579
580
static int
581
amdsbwd_resume(device_t dev)
582
{
583
struct amdsbwd_softc *sc;
584
585
sc = device_get_softc(dev);
586
wdctrl_write(sc, AMDSB_WD_FIRED);
587
if (sc->active) {
588
amdsbwd_tmr_set(sc, sc->timeout);
589
amdsbwd_tmr_enable(sc);
590
amdsbwd_tmr_reload(sc);
591
}
592
return (0);
593
}
594
595