1/* $NetBSD: sbsawdt_acpi.c,v 1.2 2018/10/24 11:04:54 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jared McNeill <jmcneill@invisible.ca>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * ARM Server Base System Architecture (SBSA)-compliant generic watchdog 34 * driver, as specified in SBSA v3.1: 35 * 36 * https://static.docs.arm.com/den0029/a/Server_Base_System_Architecture_v3_1_ARM_DEN_0029A.pdf 37 */ 38 39#include <sys/cdefs.h> 40__KERNEL_RCSID(0, "$NetBSD: sbsawdt_acpi.c,v 1.2 2018/10/24 11:04:54 jmcneill Exp $"); 41 42#include <sys/param.h> 43#include <sys/bus.h> 44#include <sys/cpu.h> 45#include <sys/device.h> 46#include <sys/wdog.h> 47 48#include <dev/sysmon/sysmonvar.h> 49 50#include <dev/acpi/acpireg.h> 51#include <dev/acpi/acpivar.h> 52 53extern struct bus_space arm_generic_bs_tag; 54 55#define SBSAWDT_REFRESH_SIZE 0x1000 56#define SBSAWDT_CONTROL_SIZE 0x1000 57 58/* Refresh frame registers */ 59#define R_WRR_REG 0x000 60#define R_W_IIDR_REG 0xfcc 61 62/* Control frame registers */ 63#define C_WCS_REG 0x000 64#define C_WCS_WS1 __BIT(2) 65#define C_WCS_WS0 __BIT(1) 66#define C_WCS_EN __BIT(0) 67#define C_WOR_REG 0x008 68#define C_WCV_LO_REG 0x010 69#define C_WCV_HI_REG 0x014 70#define C_W_IIDR_REG 0xfcc 71 72struct sbsawdt_acpi_softc { 73 device_t sc_dev; 74 bus_space_tag_t sc_bst; 75 bus_space_handle_t sc_bsh_c; /* control frame */ 76 bus_space_handle_t sc_bsh_r; /* refresh frame */ 77 78 uint32_t sc_cntfreq; 79 uint32_t sc_max_period; 80 struct sysmon_wdog sc_smw; 81}; 82 83#define REFRESH_RD4(sc, reg) \ 84 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_r, (reg)) 85#define REFRESH_WR4(sc, reg, val) \ 86 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_r, (reg), (val)) 87 88#define CONTROL_RD4(sc, reg) \ 89 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_c, (reg), (val)) 90#define CONTROL_WR4(sc, reg, val) \ 91 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_c, (reg), (val)) 92 93static int sbsawdt_acpi_match(device_t, cfdata_t, void *); 94static void sbsawdt_acpi_attach(device_t, device_t, void *); 95 96static int sbsawdt_acpi_tickle(struct sysmon_wdog *); 97static int sbsawdt_acpi_setmode(struct sysmon_wdog *); 98 99CFATTACH_DECL_NEW(sbsawdt_acpi, sizeof(struct sbsawdt_acpi_softc), sbsawdt_acpi_match, sbsawdt_acpi_attach, NULL, NULL); 100 101static int 102sbsawdt_acpi_match(device_t parent, cfdata_t cf, void *aux) 103{ 104 ACPI_GTDT_HEADER * const hdrp = aux; 105 106 if (hdrp->Type != ACPI_GTDT_TYPE_WATCHDOG) 107 return 0; 108 109 ACPI_GTDT_WATCHDOG * const wdog = aux; 110 111 if ((wdog->TimerFlags & ACPI_GTDT_WATCHDOG_SECURE) != 0) 112 return 0; /* We need a non-secure timer */ 113 114 return 1; 115} 116 117static void 118sbsawdt_acpi_attach(device_t parent, device_t self, void *aux) 119{ 120 struct sbsawdt_acpi_softc * const sc = device_private(self); 121 ACPI_GTDT_WATCHDOG * const wdog = aux; 122 123 aprint_naive("\n"); 124 aprint_normal(": mem %#" PRIx64 "-%#" PRIx64 ",%#" PRIx64 "-%#" PRIx64 "\n", 125 wdog->RefreshFrameAddress, wdog->RefreshFrameAddress + SBSAWDT_REFRESH_SIZE - 1, 126 wdog->ControlFrameAddress, wdog->ControlFrameAddress + SBSAWDT_CONTROL_SIZE - 1); 127 128 sc->sc_dev = self; 129 sc->sc_bst = &arm_generic_bs_tag; 130 sc->sc_cntfreq = gtmr_cntfrq_read(); 131 sc->sc_max_period = howmany((uint64_t)UINT32_MAX, sc->sc_cntfreq); 132 if (bus_space_map(sc->sc_bst, wdog->RefreshFrameAddress, SBSAWDT_REFRESH_SIZE, 0, &sc->sc_bsh_r) != 0) { 133 aprint_error_dev(self, "failed to map refresh frame\n"); 134 return; 135 } 136 if (bus_space_map(sc->sc_bst, wdog->ControlFrameAddress, SBSAWDT_CONTROL_SIZE, 0, &sc->sc_bsh_c) != 0) { 137 aprint_error_dev(self, "failed to map control frame\n"); 138 return; 139 } 140 141 sc->sc_smw.smw_name = device_xname(self); 142 sc->sc_smw.smw_cookie = sc; 143 sc->sc_smw.smw_period = sc->sc_max_period; 144 sc->sc_smw.smw_tickle = sbsawdt_acpi_tickle; 145 sc->sc_smw.smw_setmode = sbsawdt_acpi_setmode; 146 147 aprint_normal_dev(self, "default watchdog period is %u seconds\n", 148 sc->sc_smw.smw_period); 149 150 if (sysmon_wdog_register(&sc->sc_smw) != 0) 151 aprint_error_dev(self, "couldn't register with sysmon\n"); 152} 153 154static int 155sbsawdt_acpi_tickle(struct sysmon_wdog *smw) 156{ 157 struct sbsawdt_acpi_softc * const sc = smw->smw_cookie; 158 159 REFRESH_WR4(sc, R_WRR_REG, 0); 160 161 return 0; 162} 163 164static int 165sbsawdt_acpi_setmode(struct sysmon_wdog *smw) 166{ 167 struct sbsawdt_acpi_softc * const sc = smw->smw_cookie; 168 169 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 170 CONTROL_WR4(sc, C_WCS_REG, 0); 171 return 0; 172 } 173 174 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 175 smw->smw_period = sc->sc_max_period; 176 else if (smw->smw_period > sc->sc_max_period) 177 return EINVAL; 178 179 /* 180 * Divide the watchdog offset value by two. The first time that the 181 * offset is reached, the WD0 signal is raised with an interrupt. The 182 * second time that the offset is reached, the WD1 signal is raised 183 * which will either interrupt privileged software or cause a PE reset. 184 */ 185 const uint32_t wor = smw->smw_period * sc->sc_cntfreq / 2; 186 187 CONTROL_WR4(sc, C_WCS_REG, 0); 188 CONTROL_WR4(sc, C_WOR_REG, wor); 189 CONTROL_WR4(sc, C_WCS_REG, C_WCS_EN); 190 REFRESH_WR4(sc, R_WRR_REG, 0); 191 192 return 0; 193} 194