1213496Scognet/*- 2213496Scognet * Copyright (c) 2010 Greg Ansley. All rights reserved. 3213496Scognet * 4213496Scognet * Redistribution and use in source and binary forms, with or without 5213496Scognet * modification, are permitted provided that the following conditions 6213496Scognet * are met: 7213496Scognet * 1. Redistributions of source code must retain the above copyright 8213496Scognet * notice, this list of conditions and the following disclaimer. 9213496Scognet * 2. Redistributions in binary form must reproduce the above copyright 10213496Scognet * notice, this list of conditions and the following disclaimer in the 11213496Scognet * documentation and/or other materials provided with the distribution. 12213496Scognet * 13213496Scognet * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14213496Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15213496Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16213496Scognet * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17213496Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18213496Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19213496Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20213496Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21213496Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22213496Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23213496Scognet * SUCH DAMAGE. 24213496Scognet */ 25213496Scognet 26266196Sian#include "opt_platform.h" 27266196Sian 28213496Scognet#include <sys/cdefs.h> 29213496Scognet__FBSDID("$FreeBSD$"); 30213496Scognet 31213496Scognet#include <sys/param.h> 32213496Scognet#include <sys/bus.h> 33213496Scognet#include <sys/kernel.h> 34213496Scognet#include <sys/module.h> 35213496Scognet#include <sys/rman.h> 36213496Scognet#include <sys/systm.h> 37213496Scognet 38213496Scognet#include <machine/bus.h> 39213496Scognet 40213496Scognet#include <arm/at91/at91var.h> 41213496Scognet#include <arm/at91/at91_rstreg.h> 42213496Scognet#include <arm/at91/at91board.h> 43213496Scognet 44266196Sian#ifdef FDT 45266196Sian#include <dev/fdt/fdt_common.h> 46266196Sian#include <dev/ofw/ofw_bus.h> 47266196Sian#include <dev/ofw/ofw_bus_subr.h> 48266196Sian#define FDT_HACKS 1 49266196Sian#endif 50266196Sian 51213496Scognet#define RST_TIMEOUT (5) /* Seconds to hold NRST for hard reset */ 52234281Smarius#define RST_TICK (20) /* sample NRST at hz/RST_TICK intervals */ 53213496Scognet 54266196Sian#ifndef FDT 55238369Simpstatic int at91_rst_intr(void *arg); 56266196Sian#endif 57213496Scognet 58238369Simpstatic struct at91_rst_softc { 59213496Scognet struct resource *mem_res; /* Memory resource */ 60213496Scognet struct resource *irq_res; /* IRQ resource */ 61213496Scognet void *intrhand; /* Interrupt handle */ 62213496Scognet struct callout tick_ch; /* Tick callout */ 63213496Scognet device_t sc_dev; 64213496Scognet u_int shutdown; /* Shutdown in progress */ 65238369Simp} *at91_rst_sc; 66213496Scognet 67213496Scognetstatic inline uint32_t 68238369SimpRD4(struct at91_rst_softc *sc, bus_size_t off) 69213496Scognet{ 70213496Scognet 71213496Scognet return (bus_read_4(sc->mem_res, off)); 72213496Scognet} 73213496Scognet 74213496Scognetstatic inline void 75238369SimpWR4(struct at91_rst_softc *sc, bus_size_t off, uint32_t val) 76213496Scognet{ 77213496Scognet 78213496Scognet bus_write_4(sc->mem_res, off, val); 79213496Scognet} 80213496Scognet 81237130Simpvoid cpu_reset_sam9g20(void) __attribute__((weak)); 82237130Simpvoid cpu_reset_sam9g20(void) {} 83237130Simp 84238369Simpvoid 85238369Simpat91_rst_cpu_reset(void) 86213496Scognet{ 87213496Scognet 88238369Simp if (at91_rst_sc) { 89237130Simp cpu_reset_sam9g20(); /* May be null */ 90237130Simp 91238369Simp WR4(at91_rst_sc, RST_MR, 92237130Simp RST_MR_ERSTL(0xd) | RST_MR_URSTEN | RST_MR_KEY); 93237130Simp 94238369Simp WR4(at91_rst_sc, RST_CR, 95237130Simp RST_CR_PROCRST | 96237130Simp RST_CR_PERRST | 97237130Simp RST_CR_EXTRST | 98237130Simp RST_CR_KEY); 99213496Scognet } 100237130Simp while(1) 101237130Simp continue; 102213496Scognet} 103213496Scognet 104213496Scognetstatic int 105238369Simpat91_rst_probe(device_t dev) 106213496Scognet{ 107266196Sian#ifdef FDT 108266196Sian if (!ofw_bus_is_compatible(dev, "atmel,at91sam9260-rstc")) 109266196Sian return (ENXIO); 110266196Sian#endif 111237130Simp 112237130Simp device_set_desc(dev, "AT91SAM9 Reset Controller"); 113237130Simp return (0); 114237130Simp} 115237130Simp 116237130Simpstatic int 117238369Simpat91_rst_attach(device_t dev) 118237130Simp{ 119238369Simp struct at91_rst_softc *sc; 120213496Scognet const char *cause; 121266196Sian int rid, err = 0; 122213496Scognet 123238369Simp at91_rst_sc = sc = device_get_softc(dev); 124213496Scognet sc->sc_dev = dev; 125213496Scognet 126213496Scognet callout_init(&sc->tick_ch, 0); 127213496Scognet 128213496Scognet rid = 0; 129213496Scognet sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 130213496Scognet RF_ACTIVE); 131213496Scognet if (sc->mem_res == NULL) { 132213496Scognet device_printf(dev, "could not allocate memory resources.\n"); 133213496Scognet err = ENOMEM; 134213496Scognet goto out; 135213496Scognet } 136266196Sian 137266196Sian#ifndef FDT_HACKS 138213496Scognet rid = 0; 139213496Scognet sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 140213496Scognet RF_ACTIVE | RF_SHAREABLE); 141213496Scognet if (sc->irq_res == NULL) { 142213496Scognet device_printf(dev, "could not allocate interrupt resources.\n"); 143213496Scognet err = ENOMEM; 144213496Scognet goto out; 145213496Scognet } 146213496Scognet 147213496Scognet /* Activate the interrupt. */ 148213496Scognet err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 149238369Simp at91_rst_intr, NULL, sc, &sc->intrhand); 150213496Scognet if (err) 151213496Scognet device_printf(dev, "could not establish interrupt handler.\n"); 152266196Sian#endif 153213496Scognet 154238369Simp WR4(at91_rst_sc, RST_MR, RST_MR_ERSTL(0xd) | RST_MR_URSIEN | RST_MR_KEY); 155213496Scognet 156213496Scognet switch (RD4(sc, RST_SR) & RST_SR_RST_MASK) { 157213496Scognet case RST_SR_RST_POW: 158213496Scognet cause = "Power On"; 159213496Scognet break; 160213496Scognet case RST_SR_RST_WAKE: 161213496Scognet cause = "Wake Up"; 162213496Scognet break; 163234281Smarius case RST_SR_RST_WDT: 164213496Scognet cause = "Watchdog"; 165213496Scognet break; 166213496Scognet case RST_SR_RST_SOFT: 167213496Scognet cause = "Software Request"; 168213496Scognet break; 169213496Scognet case RST_SR_RST_USR: 170213496Scognet cause = "External (User)"; 171213496Scognet break; 172213496Scognet default: 173213496Scognet cause = "Unknown"; 174213496Scognet break; 175213496Scognet } 176213496Scognet 177213496Scognet device_printf(dev, "Reset cause: %s.\n", cause); 178213496Scognetout: 179213496Scognet return (err); 180213496Scognet} 181213496Scognet 182266196Sian#ifndef FDT_HACKS 183213496Scognetstatic void 184238369Simpat91_rst_tick(void *argp) 185213496Scognet{ 186238369Simp struct at91_rst_softc *sc = argp; 187213496Scognet 188213496Scognet if (sc->shutdown++ >= RST_TIMEOUT * RST_TICK) { 189213496Scognet /* User released the button in morre than RST_TIMEOUT */ 190213496Scognet cpu_reset(); 191213496Scognet } else if ((RD4(sc, RST_SR) & RST_SR_NRSTL)) { 192213496Scognet /* User released the button in less than RST_TIMEOUT */ 193234281Smarius sc->shutdown = 0; 194213496Scognet device_printf(sc->sc_dev, "shutting down...\n"); 195213496Scognet shutdown_nice(0); 196213496Scognet } else { 197238369Simp callout_reset(&sc->tick_ch, hz/RST_TICK, at91_rst_tick, sc); 198213496Scognet } 199213496Scognet} 200213496Scognet 201213496Scognetstatic int 202238369Simpat91_rst_intr(void *argp) 203213496Scognet{ 204238369Simp struct at91_rst_softc *sc = argp; 205213496Scognet 206213496Scognet if (RD4(sc, RST_SR) & RST_SR_URSTS) { 207234281Smarius if (sc->shutdown == 0) 208238369Simp callout_reset(&sc->tick_ch, hz/RST_TICK, at91_rst_tick, sc); 209213496Scognet return (FILTER_HANDLED); 210213496Scognet } 211213496Scognet return (FILTER_STRAY); 212213496Scognet} 213266196Sian#endif 214213496Scognet 215238369Simpstatic device_method_t at91_rst_methods[] = { 216238369Simp DEVMETHOD(device_probe, at91_rst_probe), 217238369Simp DEVMETHOD(device_attach, at91_rst_attach), 218234281Smarius DEVMETHOD_END 219213496Scognet}; 220213496Scognet 221238369Simpstatic driver_t at91_rst_driver = { 222213496Scognet "at91_rst", 223238369Simp at91_rst_methods, 224238369Simp sizeof(struct at91_rst_softc), 225213496Scognet}; 226213496Scognet 227238369Simpstatic devclass_t at91_rst_devclass; 228213496Scognet 229266196Sian#ifdef FDT 230266196SianDRIVER_MODULE(at91_rst, simplebus, at91_rst_driver, at91_rst_devclass, NULL, 231266196Sian NULL); 232266196Sian#else 233238369SimpDRIVER_MODULE(at91_rst, atmelarm, at91_rst_driver, at91_rst_devclass, NULL, 234234281Smarius NULL); 235266196Sian#endif 236