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