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