zy7_slcr.c revision 249997
1249997Swkoszek/*- 2249997Swkoszek * Copyright (C) 2013, Thomas Skibo. 3249997Swkoszek * All rights reserved. 4249997Swkoszek * 5249997Swkoszek * Redistribution and use in source and binary forms, with or without 6249997Swkoszek * modification, are permitted provided that the following conditions are met: 7249997Swkoszek * * Redistributions of source code must retain the above copyright 8249997Swkoszek * notice, this list of conditions and the following disclaimer. 9249997Swkoszek * * Redistributions in binary form must reproduce the above copyright 10249997Swkoszek * notice, this list of conditions and the following disclaimer in the 11249997Swkoszek * documentation and/or other materials provided with the distribution. 12249997Swkoszek * * The names of contributors may not be used to endorse or promote products 13249997Swkoszek * derived from this software without specific prior written permission. 14249997Swkoszek * 15249997Swkoszek * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16249997Swkoszek * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17249997Swkoszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18249997Swkoszek * ARE DISCLAIMED. IN NO EVENT SHALL AUTHORS OR CONTRIBUTORS BE LIABLE FOR 19249997Swkoszek * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20249997Swkoszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21249997Swkoszek * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22249997Swkoszek * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23249997Swkoszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24249997Swkoszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 25249997Swkoszek * DAMAGE. 26249997Swkoszek * 27249997Swkoszek */ 28249997Swkoszek 29249997Swkoszek/* Zynq-700 SLCR driver. Provides hooks for cpu_reset and PL control stuff. 30249997Swkoszek * In the future, maybe MIO control, clock control, etc. could go here. 31249997Swkoszek * 32249997Swkoszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 33249997Swkoszek * (v1.4) November 16, 2012. Xilinx doc UG585. 34249997Swkoszek */ 35249997Swkoszek 36249997Swkoszek#include <sys/cdefs.h> 37249997Swkoszek__FBSDID("$FreeBSD: head/sys/arm/xilinx/zy7_slcr.c 249997 2013-04-27 22:38:29Z wkoszek $"); 38249997Swkoszek 39249997Swkoszek#include <sys/param.h> 40249997Swkoszek#include <sys/systm.h> 41249997Swkoszek#include <sys/conf.h> 42249997Swkoszek#include <sys/kernel.h> 43249997Swkoszek#include <sys/module.h> 44249997Swkoszek#include <sys/lock.h> 45249997Swkoszek#include <sys/mutex.h> 46249997Swkoszek#include <sys/resource.h> 47249997Swkoszek#include <sys/sysctl.h> 48249997Swkoszek#include <sys/rman.h> 49249997Swkoszek 50249997Swkoszek#include <machine/bus.h> 51249997Swkoszek#include <machine/resource.h> 52249997Swkoszek#include <machine/stdarg.h> 53249997Swkoszek 54249997Swkoszek#include <dev/fdt/fdt_common.h> 55249997Swkoszek#include <dev/ofw/ofw_bus.h> 56249997Swkoszek#include <dev/ofw/ofw_bus_subr.h> 57249997Swkoszek 58249997Swkoszek#include <arm/xilinx/zy7_slcr.h> 59249997Swkoszek 60249997Swkoszekstruct zy7_slcr_softc { 61249997Swkoszek device_t dev; 62249997Swkoszek struct mtx sc_mtx; 63249997Swkoszek struct resource *mem_res; 64249997Swkoszek}; 65249997Swkoszek 66249997Swkoszekstatic struct zy7_slcr_softc *zy7_slcr_softc_p; 67249997Swkoszekextern void (*zynq7_cpu_reset); 68249997Swkoszek 69249997Swkoszek#define ZSLCR_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 70249997Swkoszek#define ZSLCR_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 71249997Swkoszek#define ZSLCR_LOCK_INIT(sc) \ 72249997Swkoszek mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ 73249997Swkoszek "zy7_slcr", MTX_SPIN) 74249997Swkoszek#define ZSLCR_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 75249997Swkoszek 76249997Swkoszek#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) 77249997Swkoszek#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) 78249997Swkoszek 79249997Swkoszek 80249997SwkoszekSYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000"); 81249997Swkoszek 82249997Swkoszekstatic char zynq_bootmode[64]; 83249997SwkoszekSYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0, 84249997Swkoszek "Zynq boot mode"); 85249997Swkoszek 86249997Swkoszekstatic char zynq_pssid[80]; 87249997SwkoszekSYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0, 88249997Swkoszek "Zynq PSS IDCODE"); 89249997Swkoszek 90249997Swkoszekstatic uint32_t zynq_reboot_status; 91249997SwkoszekSYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status, 92249997Swkoszek 0, "Zynq REBOOT_STATUS register"); 93249997Swkoszek 94249997Swkoszekstatic void 95249997Swkoszekzy7_slcr_unlock(struct zy7_slcr_softc *sc) 96249997Swkoszek{ 97249997Swkoszek 98249997Swkoszek /* Unlock SLCR with magic number. */ 99249997Swkoszek WR4(sc, ZY7_SLCR_UNLOCK, ZY7_SLCR_UNLOCK_MAGIC); 100249997Swkoszek} 101249997Swkoszek 102249997Swkoszekstatic void 103249997Swkoszekzy7_slcr_lock(struct zy7_slcr_softc *sc) 104249997Swkoszek{ 105249997Swkoszek 106249997Swkoszek /* Lock SLCR with magic number. */ 107249997Swkoszek WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC); 108249997Swkoszek} 109249997Swkoszek 110249997Swkoszek 111249997Swkoszekstatic void 112249997Swkoszekzy7_slcr_cpu_reset(void) 113249997Swkoszek{ 114249997Swkoszek struct zy7_slcr_softc *sc = zy7_slcr_softc_p; 115249997Swkoszek 116249997Swkoszek /* Unlock SLCR registers. */ 117249997Swkoszek zy7_slcr_unlock(sc); 118249997Swkoszek 119249997Swkoszek /* This has something to do with a work-around so the fsbl will load 120249997Swkoszek * the bitstream after soft-reboot. It's very important. 121249997Swkoszek */ 122249997Swkoszek WR4(sc, ZY7_SLCR_REBOOT_STAT, 123249997Swkoszek RD4(sc, ZY7_SLCR_REBOOT_STAT) & 0xf0ffffff); 124249997Swkoszek 125249997Swkoszek /* Soft reset */ 126249997Swkoszek WR4(sc, ZY7_SLCR_PSS_RST_CTRL, ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET); 127249997Swkoszek 128249997Swkoszek for (;;) 129249997Swkoszek ; 130249997Swkoszek} 131249997Swkoszek 132249997Swkoszek/* Assert PL resets and disable level shifters in preparation of programming 133249997Swkoszek * the PL (FPGA) section. Called from zy7_devcfg.c. 134249997Swkoszek */ 135249997Swkoszekvoid 136249997Swkoszekzy7_slcr_preload_pl(void) 137249997Swkoszek{ 138249997Swkoszek struct zy7_slcr_softc *sc = zy7_slcr_softc_p; 139249997Swkoszek 140249997Swkoszek if (!sc) 141249997Swkoszek return; 142249997Swkoszek 143249997Swkoszek ZSLCR_LOCK(sc); 144249997Swkoszek 145249997Swkoszek /* Unlock SLCR registers. */ 146249997Swkoszek zy7_slcr_unlock(sc); 147249997Swkoszek 148249997Swkoszek /* Assert top level output resets. */ 149249997Swkoszek WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, ZY7_SLCR_FPGA_RST_CTRL_RST_ALL); 150249997Swkoszek 151249997Swkoszek /* Disable all level shifters. */ 152249997Swkoszek WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0); 153249997Swkoszek 154249997Swkoszek /* Lock SLCR registers. */ 155249997Swkoszek zy7_slcr_lock(sc); 156249997Swkoszek 157249997Swkoszek ZSLCR_UNLOCK(sc); 158249997Swkoszek} 159249997Swkoszek 160249997Swkoszek/* After PL configuration, enable level shifters and deassert top-level 161249997Swkoszek * PL resets. Called from zy7_devcfg.c. Optionally, the level shifters 162249997Swkoszek * can be left disabled but that's rare of an FPGA application. That option 163249997Swkoszek * is controled by a sysctl in the devcfg driver. 164249997Swkoszek */ 165249997Swkoszekvoid 166249997Swkoszekzy7_slcr_postload_pl(int en_level_shifters) 167249997Swkoszek{ 168249997Swkoszek struct zy7_slcr_softc *sc = zy7_slcr_softc_p; 169249997Swkoszek 170249997Swkoszek if (!sc) 171249997Swkoszek return; 172249997Swkoszek 173249997Swkoszek ZSLCR_LOCK(sc); 174249997Swkoszek 175249997Swkoszek /* Unlock SLCR registers. */ 176249997Swkoszek zy7_slcr_unlock(sc); 177249997Swkoszek 178249997Swkoszek if (en_level_shifters) 179249997Swkoszek /* Enable level shifters. */ 180249997Swkoszek WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL); 181249997Swkoszek 182249997Swkoszek /* Deassert top level output resets. */ 183249997Swkoszek WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, 0); 184249997Swkoszek 185249997Swkoszek /* Lock SLCR registers. */ 186249997Swkoszek zy7_slcr_lock(sc); 187249997Swkoszek 188249997Swkoszek ZSLCR_UNLOCK(sc); 189249997Swkoszek} 190249997Swkoszek 191249997Swkoszekstatic int 192249997Swkoszekzy7_slcr_probe(device_t dev) 193249997Swkoszek{ 194249997Swkoszek if (!ofw_bus_is_compatible(dev, "xlnx,zy7_slcr")) 195249997Swkoszek return (ENXIO); 196249997Swkoszek 197249997Swkoszek device_set_desc(dev, "Zynq-7000 slcr block"); 198249997Swkoszek return (0); 199249997Swkoszek} 200249997Swkoszek 201249997Swkoszekstatic int 202249997Swkoszekzy7_slcr_attach(device_t dev) 203249997Swkoszek{ 204249997Swkoszek struct zy7_slcr_softc *sc = device_get_softc(dev); 205249997Swkoszek int rid; 206249997Swkoszek uint32_t bootmode; 207249997Swkoszek uint32_t pss_idcode; 208249997Swkoszek static char *bootdev_names[] = { 209249997Swkoszek "JTAG", "Quad-SPI", "NOR", "(3?)", 210249997Swkoszek "NAND", "SD Card", "(6?)", "(7?)" 211249997Swkoszek }; 212249997Swkoszek 213249997Swkoszek /* Allow only one attach. */ 214249997Swkoszek if (zy7_slcr_softc_p != NULL) 215249997Swkoszek return (ENXIO); 216249997Swkoszek 217249997Swkoszek sc->dev = dev; 218249997Swkoszek 219249997Swkoszek ZSLCR_LOCK_INIT(sc); 220249997Swkoszek 221249997Swkoszek /* Get memory resource. */ 222249997Swkoszek rid = 0; 223249997Swkoszek sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 224249997Swkoszek RF_ACTIVE); 225249997Swkoszek if (sc->mem_res == NULL) { 226249997Swkoszek device_printf(dev, "could not allocate memory resources.\n"); 227249997Swkoszek return (ENOMEM); 228249997Swkoszek } 229249997Swkoszek 230249997Swkoszek /* Hook up cpu_reset. */ 231249997Swkoszek zy7_slcr_softc_p = sc; 232249997Swkoszek zynq7_cpu_reset = zy7_slcr_cpu_reset; 233249997Swkoszek 234249997Swkoszek /* Read info and set sysctls. */ 235249997Swkoszek bootmode = RD4(sc, ZY7_SLCR_BOOT_MODE); 236249997Swkoszek snprintf(zynq_bootmode, sizeof(zynq_bootmode), 237249997Swkoszek "0x%x: boot device: %s", bootmode, 238249997Swkoszek bootdev_names[bootmode & ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK]); 239249997Swkoszek 240249997Swkoszek pss_idcode = RD4(sc, ZY7_SLCR_PSS_IDCODE); 241249997Swkoszek snprintf(zynq_pssid, sizeof(zynq_pssid), 242249997Swkoszek "0x%x: manufacturer: 0x%x device: 0x%x " 243249997Swkoszek "family: 0x%x sub-family: 0x%x rev: 0x%x", 244249997Swkoszek pss_idcode, 245249997Swkoszek (pss_idcode & ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK) >> 246249997Swkoszek ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT, 247249997Swkoszek (pss_idcode & ZY7_SLCR_PSS_IDCODE_DEVICE_MASK) >> 248249997Swkoszek ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT, 249249997Swkoszek (pss_idcode & ZY7_SLCR_PSS_IDCODE_FAMILY_MASK) >> 250249997Swkoszek ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT, 251249997Swkoszek (pss_idcode & ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK) >> 252249997Swkoszek ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT, 253249997Swkoszek (pss_idcode & ZY7_SLCR_PSS_IDCODE_REVISION_MASK) >> 254249997Swkoszek ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT); 255249997Swkoszek 256249997Swkoszek zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT); 257249997Swkoszek 258249997Swkoszek /* Lock SLCR registers. */ 259249997Swkoszek zy7_slcr_lock(sc); 260249997Swkoszek 261249997Swkoszek return (0); 262249997Swkoszek} 263249997Swkoszek 264249997Swkoszekstatic int 265249997Swkoszekzy7_slcr_detach(device_t dev) 266249997Swkoszek{ 267249997Swkoszek struct zy7_slcr_softc *sc = device_get_softc(dev); 268249997Swkoszek 269249997Swkoszek bus_generic_detach(dev); 270249997Swkoszek 271249997Swkoszek /* Release memory resource. */ 272249997Swkoszek if (sc->mem_res != NULL) 273249997Swkoszek bus_release_resource(dev, SYS_RES_MEMORY, 274249997Swkoszek rman_get_rid(sc->mem_res), sc->mem_res); 275249997Swkoszek 276249997Swkoszek zy7_slcr_softc_p = NULL; 277249997Swkoszek zynq7_cpu_reset = NULL; 278249997Swkoszek 279249997Swkoszek ZSLCR_LOCK_DESTROY(sc); 280249997Swkoszek 281249997Swkoszek return (0); 282249997Swkoszek} 283249997Swkoszek 284249997Swkoszekstatic device_method_t zy7_slcr_methods[] = { 285249997Swkoszek /* device_if */ 286249997Swkoszek DEVMETHOD(device_probe, zy7_slcr_probe), 287249997Swkoszek DEVMETHOD(device_attach, zy7_slcr_attach), 288249997Swkoszek DEVMETHOD(device_detach, zy7_slcr_detach), 289249997Swkoszek 290249997Swkoszek DEVMETHOD_END 291249997Swkoszek}; 292249997Swkoszek 293249997Swkoszekstatic driver_t zy7_slcr_driver = { 294249997Swkoszek "zy7_slcr", 295249997Swkoszek zy7_slcr_methods, 296249997Swkoszek sizeof(struct zy7_slcr_softc), 297249997Swkoszek}; 298249997Swkoszekstatic devclass_t zy7_slcr_devclass; 299249997Swkoszek 300249997SwkoszekDRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, zy7_slcr_devclass, 0, 0); 301249997SwkoszekMODULE_VERSION(zy7_slcr, 1); 302