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$
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$");
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),	\
74273645Sian	    "zy7_slcr", MTX_DEF)
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
80273645Sian#define ZYNQ_DEFAULT_PS_CLK_FREQUENCY	33333333	/* 33.3 Mhz */
81249997Swkoszek
82273645Sian
83249997SwkoszekSYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000");
84249997Swkoszek
85249997Swkoszekstatic char zynq_bootmode[64];
86249997SwkoszekSYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0,
87249997Swkoszek	      "Zynq boot mode");
88249997Swkoszek
89273645Sianstatic char zynq_pssid[100];
90249997SwkoszekSYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0,
91249997Swkoszek	   "Zynq PSS IDCODE");
92249997Swkoszek
93249997Swkoszekstatic uint32_t zynq_reboot_status;
94249997SwkoszekSYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status,
95249997Swkoszek	   0, "Zynq REBOOT_STATUS register");
96249997Swkoszek
97273645Sianstatic int ps_clk_frequency;
98273645SianSYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency,
99273645Sian	   0, "Zynq PS_CLK Frequency");
100273645Sian
101273645Sianstatic int io_pll_frequency;
102273645SianSYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency,
103273645Sian	   0, "Zynq IO PLL Frequency");
104273645Sian
105273645Sianstatic int arm_pll_frequency;
106273645SianSYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD,
107273645Sian	   &arm_pll_frequency, 0, "Zynq ARM PLL Frequency");
108273645Sian
109273645Sianstatic int ddr_pll_frequency;
110273645SianSYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD,
111273645Sian	   &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency");
112273645Sian
113249997Swkoszekstatic void
114249997Swkoszekzy7_slcr_unlock(struct zy7_slcr_softc *sc)
115249997Swkoszek{
116249997Swkoszek
117249997Swkoszek	/* Unlock SLCR with magic number. */
118249997Swkoszek	WR4(sc, ZY7_SLCR_UNLOCK, ZY7_SLCR_UNLOCK_MAGIC);
119249997Swkoszek}
120249997Swkoszek
121249997Swkoszekstatic void
122249997Swkoszekzy7_slcr_lock(struct zy7_slcr_softc *sc)
123249997Swkoszek{
124249997Swkoszek
125249997Swkoszek	/* Lock SLCR with magic number. */
126249997Swkoszek	WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC);
127249997Swkoszek}
128249997Swkoszek
129249997Swkoszek
130249997Swkoszekstatic void
131249997Swkoszekzy7_slcr_cpu_reset(void)
132249997Swkoszek{
133249997Swkoszek	struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
134249997Swkoszek
135249997Swkoszek	/* Unlock SLCR registers. */
136249997Swkoszek	zy7_slcr_unlock(sc);
137249997Swkoszek
138249997Swkoszek	/* This has something to do with a work-around so the fsbl will load
139249997Swkoszek	 * the bitstream after soft-reboot.  It's very important.
140249997Swkoszek	 */
141249997Swkoszek	WR4(sc, ZY7_SLCR_REBOOT_STAT,
142249997Swkoszek	    RD4(sc, ZY7_SLCR_REBOOT_STAT) & 0xf0ffffff);
143249997Swkoszek
144249997Swkoszek	/* Soft reset */
145249997Swkoszek	WR4(sc, ZY7_SLCR_PSS_RST_CTRL, ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET);
146249997Swkoszek
147249997Swkoszek	for (;;)
148249997Swkoszek		;
149249997Swkoszek}
150249997Swkoszek
151249997Swkoszek/* Assert PL resets and disable level shifters in preparation of programming
152249997Swkoszek * the PL (FPGA) section.  Called from zy7_devcfg.c.
153249997Swkoszek */
154249997Swkoszekvoid
155249997Swkoszekzy7_slcr_preload_pl(void)
156249997Swkoszek{
157249997Swkoszek	struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
158249997Swkoszek
159249997Swkoszek	if (!sc)
160249997Swkoszek		return;
161249997Swkoszek
162249997Swkoszek	ZSLCR_LOCK(sc);
163249997Swkoszek
164249997Swkoszek	/* Unlock SLCR registers. */
165249997Swkoszek	zy7_slcr_unlock(sc);
166249997Swkoszek
167249997Swkoszek	/* Assert top level output resets. */
168249997Swkoszek	WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, ZY7_SLCR_FPGA_RST_CTRL_RST_ALL);
169249997Swkoszek
170249997Swkoszek	/* Disable all level shifters. */
171249997Swkoszek	WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0);
172249997Swkoszek
173249997Swkoszek	/* Lock SLCR registers. */
174249997Swkoszek	zy7_slcr_lock(sc);
175249997Swkoszek
176249997Swkoszek	ZSLCR_UNLOCK(sc);
177249997Swkoszek}
178249997Swkoszek
179249997Swkoszek/* After PL configuration, enable level shifters and deassert top-level
180249997Swkoszek * PL resets.  Called from zy7_devcfg.c.  Optionally, the level shifters
181249997Swkoszek * can be left disabled but that's rare of an FPGA application. That option
182249997Swkoszek * is controled by a sysctl in the devcfg driver.
183249997Swkoszek */
184249997Swkoszekvoid
185249997Swkoszekzy7_slcr_postload_pl(int en_level_shifters)
186249997Swkoszek{
187249997Swkoszek	struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
188249997Swkoszek
189249997Swkoszek	if (!sc)
190249997Swkoszek		return;
191249997Swkoszek
192249997Swkoszek	ZSLCR_LOCK(sc);
193249997Swkoszek
194249997Swkoszek	/* Unlock SLCR registers. */
195249997Swkoszek	zy7_slcr_unlock(sc);
196249997Swkoszek
197249997Swkoszek	if (en_level_shifters)
198249997Swkoszek		/* Enable level shifters. */
199249997Swkoszek		WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL);
200249997Swkoszek
201249997Swkoszek	/* Deassert top level output resets. */
202249997Swkoszek	WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, 0);
203249997Swkoszek
204249997Swkoszek	/* Lock SLCR registers. */
205249997Swkoszek	zy7_slcr_lock(sc);
206249997Swkoszek
207249997Swkoszek	ZSLCR_UNLOCK(sc);
208249997Swkoszek}
209249997Swkoszek
210273645Sian/* Override cgem_set_refclk() in gigabit ethernet driver
211273645Sian * (sys/dev/cadence/if_cgem.c).  This function is called to
212273645Sian * request a change in the gem's reference clock speed.
213273645Sian */
214273645Sianint
215273645Siancgem_set_ref_clk(int unit, int frequency)
216273645Sian{
217273645Sian	struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
218273645Sian	int div0, div1;
219273645Sian
220273645Sian	if (!sc)
221273645Sian		return (-1);
222273645Sian
223273645Sian	/* Find suitable divisor pairs.  Round result to nearest khz
224273645Sian	 * to test for match.
225273645Sian	 */
226273645Sian	for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) {
227273645Sian		div0 = (io_pll_frequency + div1 * frequency / 2) /
228273645Sian			div1 / frequency;
229273645Sian		if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX &&
230273645Sian		    ((io_pll_frequency / div0 / div1) + 500) / 1000 ==
231273645Sian		    (frequency + 500) / 1000)
232273645Sian			break;
233273645Sian	}
234273645Sian
235273645Sian	if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX)
236273645Sian		return (-1);
237273645Sian
238273645Sian	ZSLCR_LOCK(sc);
239273645Sian
240273645Sian	/* Unlock SLCR registers. */
241273645Sian	zy7_slcr_unlock(sc);
242273645Sian
243273645Sian	/* Modify GEM reference clock. */
244273645Sian	WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL,
245273645Sian	    (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) |
246273645Sian	    (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) |
247273645Sian	    ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL |
248273645Sian	    ZY7_SLCR_GEM_CLK_CTRL_CLKACT);
249273645Sian
250273645Sian	/* Lock SLCR registers. */
251273645Sian	zy7_slcr_lock(sc);
252273645Sian
253273645Sian	ZSLCR_UNLOCK(sc);
254273645Sian
255273645Sian	return (0);
256273645Sian}
257273645Sian
258249997Swkoszekstatic int
259249997Swkoszekzy7_slcr_probe(device_t dev)
260249997Swkoszek{
261266152Sian
262266152Sian	if (!ofw_bus_status_okay(dev))
263266152Sian		return (ENXIO);
264266152Sian
265249997Swkoszek	if (!ofw_bus_is_compatible(dev, "xlnx,zy7_slcr"))
266249997Swkoszek		return (ENXIO);
267249997Swkoszek
268249997Swkoszek	device_set_desc(dev, "Zynq-7000 slcr block");
269249997Swkoszek	return (0);
270249997Swkoszek}
271249997Swkoszek
272249997Swkoszekstatic int
273249997Swkoszekzy7_slcr_attach(device_t dev)
274249997Swkoszek{
275249997Swkoszek	struct zy7_slcr_softc *sc = device_get_softc(dev);
276249997Swkoszek	int rid;
277273645Sian	phandle_t node;
278273645Sian	pcell_t cell;
279249997Swkoszek	uint32_t bootmode;
280249997Swkoszek	uint32_t pss_idcode;
281273645Sian	uint32_t arm_pll_ctrl;
282273645Sian	uint32_t ddr_pll_ctrl;
283273645Sian	uint32_t io_pll_ctrl;
284249997Swkoszek	static char *bootdev_names[] = {
285249997Swkoszek		"JTAG", "Quad-SPI", "NOR", "(3?)",
286249997Swkoszek		"NAND", "SD Card", "(6?)", "(7?)"
287249997Swkoszek	};
288249997Swkoszek
289249997Swkoszek	/* Allow only one attach. */
290249997Swkoszek	if (zy7_slcr_softc_p != NULL)
291249997Swkoszek		return (ENXIO);
292249997Swkoszek
293249997Swkoszek	sc->dev = dev;
294249997Swkoszek
295249997Swkoszek	ZSLCR_LOCK_INIT(sc);
296249997Swkoszek
297249997Swkoszek	/* Get memory resource. */
298249997Swkoszek	rid = 0;
299249997Swkoszek	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
300249997Swkoszek					     RF_ACTIVE);
301249997Swkoszek	if (sc->mem_res == NULL) {
302249997Swkoszek		device_printf(dev, "could not allocate memory resources.\n");
303249997Swkoszek		return (ENOMEM);
304249997Swkoszek	}
305249997Swkoszek
306249997Swkoszek	/* Hook up cpu_reset. */
307249997Swkoszek	zy7_slcr_softc_p = sc;
308249997Swkoszek	zynq7_cpu_reset = zy7_slcr_cpu_reset;
309249997Swkoszek
310249997Swkoszek	/* Read info and set sysctls. */
311249997Swkoszek	bootmode = RD4(sc, ZY7_SLCR_BOOT_MODE);
312249997Swkoszek	snprintf(zynq_bootmode, sizeof(zynq_bootmode),
313249997Swkoszek		 "0x%x: boot device: %s", bootmode,
314249997Swkoszek		 bootdev_names[bootmode & ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK]);
315249997Swkoszek
316249997Swkoszek	pss_idcode = RD4(sc, ZY7_SLCR_PSS_IDCODE);
317249997Swkoszek	snprintf(zynq_pssid, sizeof(zynq_pssid),
318249997Swkoszek		 "0x%x: manufacturer: 0x%x device: 0x%x "
319249997Swkoszek		 "family: 0x%x sub-family: 0x%x rev: 0x%x",
320249997Swkoszek		 pss_idcode,
321249997Swkoszek		 (pss_idcode & ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK) >>
322249997Swkoszek		 ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT,
323249997Swkoszek		 (pss_idcode & ZY7_SLCR_PSS_IDCODE_DEVICE_MASK) >>
324249997Swkoszek		 ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT,
325249997Swkoszek		 (pss_idcode & ZY7_SLCR_PSS_IDCODE_FAMILY_MASK) >>
326249997Swkoszek		 ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT,
327249997Swkoszek		 (pss_idcode & ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK) >>
328249997Swkoszek		 ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT,
329249997Swkoszek		 (pss_idcode & ZY7_SLCR_PSS_IDCODE_REVISION_MASK) >>
330249997Swkoszek		 ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT);
331249997Swkoszek
332249997Swkoszek	zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT);
333249997Swkoszek
334273645Sian	/* Derive PLL frequencies from PS_CLK. */
335273645Sian	node = ofw_bus_get_node(dev);
336273645Sian	if (OF_getprop(node, "clock-frequency", &cell, sizeof(cell)) > 0)
337273645Sian		ps_clk_frequency = fdt32_to_cpu(cell);
338273645Sian	else
339273645Sian		ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY;
340273645Sian
341273645Sian	arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL);
342273645Sian	ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL);
343273645Sian	io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL);
344273645Sian
345273645Sian	/* Determine ARM PLL frequency. */
346273645Sian	if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
347273645Sian	     (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
348273645Sian	    ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
349273645Sian	     (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
350273645Sian		/* PLL is bypassed. */
351273645Sian		arm_pll_frequency = ps_clk_frequency;
352273645Sian	else
353273645Sian		arm_pll_frequency = ps_clk_frequency *
354273645Sian			((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
355273645Sian			 ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
356273645Sian
357273645Sian	/* Determine DDR PLL frequency. */
358273645Sian	if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
359273645Sian	     (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
360273645Sian	    ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
361273645Sian	     (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
362273645Sian		/* PLL is bypassed. */
363273645Sian		ddr_pll_frequency = ps_clk_frequency;
364273645Sian	else
365273645Sian		ddr_pll_frequency = ps_clk_frequency *
366273645Sian			((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
367273645Sian			 ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
368273645Sian
369273645Sian	/* Determine IO PLL frequency. */
370273645Sian	if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
371273645Sian	     (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
372273645Sian	    ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
373273645Sian	     (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
374273645Sian		/* PLL is bypassed. */
375273645Sian		io_pll_frequency = ps_clk_frequency;
376273645Sian	else
377273645Sian		io_pll_frequency = ps_clk_frequency *
378273645Sian			((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
379273645Sian			 ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
380273645Sian
381249997Swkoszek	/* Lock SLCR registers. */
382249997Swkoszek	zy7_slcr_lock(sc);
383249997Swkoszek
384249997Swkoszek	return (0);
385249997Swkoszek}
386249997Swkoszek
387249997Swkoszekstatic int
388249997Swkoszekzy7_slcr_detach(device_t dev)
389249997Swkoszek{
390249997Swkoszek	struct zy7_slcr_softc *sc = device_get_softc(dev);
391249997Swkoszek
392249997Swkoszek	bus_generic_detach(dev);
393249997Swkoszek
394249997Swkoszek	/* Release memory resource. */
395249997Swkoszek	if (sc->mem_res != NULL)
396249997Swkoszek		bus_release_resource(dev, SYS_RES_MEMORY,
397249997Swkoszek			     rman_get_rid(sc->mem_res), sc->mem_res);
398249997Swkoszek
399249997Swkoszek	zy7_slcr_softc_p = NULL;
400249997Swkoszek	zynq7_cpu_reset = NULL;
401249997Swkoszek
402249997Swkoszek	ZSLCR_LOCK_DESTROY(sc);
403249997Swkoszek
404249997Swkoszek	return (0);
405249997Swkoszek}
406249997Swkoszek
407249997Swkoszekstatic device_method_t zy7_slcr_methods[] = {
408249997Swkoszek	/* device_if */
409249997Swkoszek	DEVMETHOD(device_probe, 	zy7_slcr_probe),
410249997Swkoszek	DEVMETHOD(device_attach, 	zy7_slcr_attach),
411249997Swkoszek	DEVMETHOD(device_detach, 	zy7_slcr_detach),
412249997Swkoszek
413249997Swkoszek	DEVMETHOD_END
414249997Swkoszek};
415249997Swkoszek
416249997Swkoszekstatic driver_t zy7_slcr_driver = {
417249997Swkoszek	"zy7_slcr",
418249997Swkoszek	zy7_slcr_methods,
419249997Swkoszek	sizeof(struct zy7_slcr_softc),
420249997Swkoszek};
421249997Swkoszekstatic devclass_t zy7_slcr_devclass;
422249997Swkoszek
423249997SwkoszekDRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, zy7_slcr_devclass, 0, 0);
424249997SwkoszekMODULE_VERSION(zy7_slcr, 1);
425