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