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