1a9caca6aSWojciech A. Koszek /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3af3dc4a7SPedro F. Giffuni * 440713190SWojciech A. Koszek * Copyright (c) 2013 Thomas Skibo 5a9caca6aSWojciech A. Koszek * All rights reserved. 6a9caca6aSWojciech A. Koszek * 7a9caca6aSWojciech A. Koszek * Redistribution and use in source and binary forms, with or without 840713190SWojciech A. Koszek * modification, are permitted provided that the following conditions 940713190SWojciech A. Koszek * are met: 1040713190SWojciech A. Koszek * 1. Redistributions of source code must retain the above copyright 11a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer. 1240713190SWojciech A. Koszek * 2. Redistributions in binary form must reproduce the above copyright 13a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer in the 14a9caca6aSWojciech A. Koszek * documentation and/or other materials provided with the distribution. 15a9caca6aSWojciech A. Koszek * 1640713190SWojciech A. Koszek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1740713190SWojciech A. Koszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a9caca6aSWojciech A. Koszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1940713190SWojciech A. Koszek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2040713190SWojciech A. Koszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2140713190SWojciech A. Koszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2240713190SWojciech A. Koszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2340713190SWojciech A. Koszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a9caca6aSWojciech A. Koszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2540713190SWojciech A. Koszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2640713190SWojciech A. Koszek * SUCH DAMAGE. 27a9caca6aSWojciech A. Koszek */ 28a9caca6aSWojciech A. Koszek 2940713190SWojciech A. Koszek /* 3040713190SWojciech A. Koszek * Zynq-7000 Devcfg driver. This allows programming the PL (FPGA) section 31a9caca6aSWojciech A. Koszek * of Zynq. 32a9caca6aSWojciech A. Koszek * 33a9caca6aSWojciech A. Koszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 34a9caca6aSWojciech A. Koszek * (v1.4) November 16, 2012. Xilinx doc UG585. PL Configuration is 35a9caca6aSWojciech A. Koszek * covered in section 6.4.5. 36a9caca6aSWojciech A. Koszek */ 37a9caca6aSWojciech A. Koszek 38a9caca6aSWojciech A. Koszek #include <sys/param.h> 39a9caca6aSWojciech A. Koszek #include <sys/systm.h> 40a9caca6aSWojciech A. Koszek #include <sys/conf.h> 41a9caca6aSWojciech A. Koszek #include <sys/kernel.h> 42a9caca6aSWojciech A. Koszek #include <sys/module.h> 43a9caca6aSWojciech A. Koszek #include <sys/sysctl.h> 44a9caca6aSWojciech A. Koszek #include <sys/lock.h> 45a9caca6aSWojciech A. Koszek #include <sys/mutex.h> 46a9caca6aSWojciech A. Koszek #include <sys/resource.h> 47a9caca6aSWojciech A. Koszek #include <sys/rman.h> 48a9caca6aSWojciech A. Koszek #include <sys/uio.h> 49a9caca6aSWojciech A. Koszek 50a9caca6aSWojciech A. Koszek #include <machine/bus.h> 51a9caca6aSWojciech A. Koszek #include <machine/resource.h> 52a9caca6aSWojciech A. Koszek #include <machine/stdarg.h> 53a9caca6aSWojciech A. Koszek 54a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h> 55a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h> 56a9caca6aSWojciech A. Koszek 57a9caca6aSWojciech A. Koszek #include <arm/xilinx/zy7_slcr.h> 58a9caca6aSWojciech A. Koszek 59a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc { 60a9caca6aSWojciech A. Koszek device_t dev; 61a9caca6aSWojciech A. Koszek struct mtx sc_mtx; 62a9caca6aSWojciech A. Koszek struct resource *mem_res; 63a9caca6aSWojciech A. Koszek struct resource *irq_res; 64a9caca6aSWojciech A. Koszek struct cdev *sc_ctl_dev; 65a9caca6aSWojciech A. Koszek void *intrhandle; 66a9caca6aSWojciech A. Koszek 67a9caca6aSWojciech A. Koszek bus_dma_tag_t dma_tag; 68a9caca6aSWojciech A. Koszek bus_dmamap_t dma_map; 69a9caca6aSWojciech A. Koszek 70a9caca6aSWojciech A. Koszek int is_open; 718e01fdeaSOleksandr Tymoshenko 728e01fdeaSOleksandr Tymoshenko struct sysctl_ctx_list sysctl_tree; 738e01fdeaSOleksandr Tymoshenko struct sysctl_oid *sysctl_tree_top; 74a9caca6aSWojciech A. Koszek }; 75a9caca6aSWojciech A. Koszek 76a9caca6aSWojciech A. Koszek static struct zy7_devcfg_softc *zy7_devcfg_softc_p; 77a9caca6aSWojciech A. Koszek 788e01fdeaSOleksandr Tymoshenko #define FCLK_NUM 4 798e01fdeaSOleksandr Tymoshenko 808e01fdeaSOleksandr Tymoshenko struct zy7_fclk_config { 818e01fdeaSOleksandr Tymoshenko int source; 828e01fdeaSOleksandr Tymoshenko int frequency; 838e01fdeaSOleksandr Tymoshenko int actual_frequency; 848e01fdeaSOleksandr Tymoshenko }; 858e01fdeaSOleksandr Tymoshenko 868e01fdeaSOleksandr Tymoshenko static struct zy7_fclk_config fclk_configs[FCLK_NUM]; 878e01fdeaSOleksandr Tymoshenko 88a9caca6aSWojciech A. Koszek #define DEVCFG_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 89a9caca6aSWojciech A. Koszek #define DEVCFG_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 90a9caca6aSWojciech A. Koszek #define DEVCFG_SC_LOCK_INIT(sc) \ 91a9caca6aSWojciech A. Koszek mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ 92a9caca6aSWojciech A. Koszek "zy7_devcfg", MTX_DEF) 93a9caca6aSWojciech A. Koszek #define DEVCFG_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx); 94a9caca6aSWojciech A. Koszek #define DEVCFG_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED); 95a9caca6aSWojciech A. Koszek 96a9caca6aSWojciech A. Koszek #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) 97a9caca6aSWojciech A. Koszek #define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) 98a9caca6aSWojciech A. Koszek 997029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, fpga, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 100a9caca6aSWojciech A. Koszek "Xilinx Zynq-7000 PL (FPGA) section"); 101a9caca6aSWojciech A. Koszek 102a9caca6aSWojciech A. Koszek static int zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS); 1037029da5cSPawel Biernacki SYSCTL_PROC(_hw_fpga, OID_AUTO, pl_done, 1047029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, NULL, 0, 1057029da5cSPawel Biernacki zy7_devcfg_sysctl_pl_done, "I", 1067029da5cSPawel Biernacki "PL section config DONE signal"); 107a9caca6aSWojciech A. Koszek 108a9caca6aSWojciech A. Koszek static int zy7_en_level_shifters = 1; 109a9caca6aSWojciech A. Koszek SYSCTL_INT(_hw_fpga, OID_AUTO, en_level_shifters, CTLFLAG_RW, 110a9caca6aSWojciech A. Koszek &zy7_en_level_shifters, 0, 111a9caca6aSWojciech A. Koszek "Enable PS-PL level shifters after device config"); 112a9caca6aSWojciech A. Koszek 113a9caca6aSWojciech A. Koszek static int zy7_ps_vers = 0; 114a9caca6aSWojciech A. Koszek SYSCTL_INT(_hw, OID_AUTO, ps_vers, CTLFLAG_RD, &zy7_ps_vers, 0, 115a9caca6aSWojciech A. Koszek "Zynq-7000 PS version"); 116a9caca6aSWojciech A. Koszek 1178e01fdeaSOleksandr Tymoshenko static int zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS); 1188e01fdeaSOleksandr Tymoshenko SYSCTL_PROC(_hw_fpga, OID_AUTO, level_shifters, 1197029da5cSPawel Biernacki CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, NULL, 0, 1207029da5cSPawel Biernacki zy7_devcfg_fclk_sysctl_level_shifters, "I", 1217029da5cSPawel Biernacki "Enable/disable level shifters"); 122a9caca6aSWojciech A. Koszek 123a9caca6aSWojciech A. Koszek /* cdev entry points. */ 124a9caca6aSWojciech A. Koszek static int zy7_devcfg_open(struct cdev *, int, int, struct thread *); 125a9caca6aSWojciech A. Koszek static int zy7_devcfg_write(struct cdev *, struct uio *, int); 126a9caca6aSWojciech A. Koszek static int zy7_devcfg_close(struct cdev *, int, int, struct thread *); 127a9caca6aSWojciech A. Koszek 128a9caca6aSWojciech A. Koszek struct cdevsw zy7_devcfg_cdevsw = { 129a9caca6aSWojciech A. Koszek .d_version = D_VERSION, 130a9caca6aSWojciech A. Koszek .d_open = zy7_devcfg_open, 131a9caca6aSWojciech A. Koszek .d_write = zy7_devcfg_write, 132a9caca6aSWojciech A. Koszek .d_close = zy7_devcfg_close, 133a9caca6aSWojciech A. Koszek .d_name = "devcfg", 134a9caca6aSWojciech A. Koszek }; 135a9caca6aSWojciech A. Koszek 136a9caca6aSWojciech A. Koszek /* Devcfg block registers. */ 137a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL 0x0000 138a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_FORCE_RST (1<<31) 139a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCFG_PROG_B (1<<30) 140a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCFG_POR_CNT_4K (1<<29) 141a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCAP_PR (1<<27) 142a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCAP_MODE (1<<26) 143a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_QTR_PCAP_RATE_EN (1<<25) 144a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_MULTIBOOT_EN (1<<24) 145a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_JTAG_CHAIN_DIS (1<<23) 146a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_USER_MODE (1<<15) 147a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_RESVD_WR11 (3<<13) /* always write 11 */ 148a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCFG_AES_FUSE (1<<12) 149a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_PCFG_AES_EN_MASK (7<<9) /* all 1's or 0's */ 150a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_SEU_EN (1<<8) 151a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_SEC_EN (1<<7) 152a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_SPNIDEN (1<<6) 153a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_SPIDEN (1<<5) 154a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_NIDEN (1<<4) 155a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_DBGEN (1<<3) 156a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CTRL_DAP_EN_MASK (7<<0) /* all 1's to enable */ 157a9caca6aSWojciech A. Koszek 158a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK 0x004 159a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK_AES_FUSE_LOCK (1<<4) 160a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK_AES_EN (1<<3) 161a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK_SEU_LOCK (1<<2) 162a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK_SEC_LOCK (1<<1) 163a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_LOCK_DBG_LOCK (1<<0) 164a9caca6aSWojciech A. Koszek 165a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG 0x008 166a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_RFIFO_TH_MASK (3<<10) 167a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_WFIFO_TH_MASK (3<<8) 168a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_RCLK_EDGE (1<<7) 169a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_WCLK_EDGE (1<<6) 170a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_DIS_SRC_INC (1<<5) 171a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_CFG_DIS_DST_INC (1<<4) 172a9caca6aSWojciech A. Koszek 173a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_STATUS 0x00C 174a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_MASK 0x010 175a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PSS_GTS_USR_B (1<<31) 176a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PSS_FST_CFG_B (1<<30) 177a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PSS_GPWRDWN_B (1<<29) 178a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PSS_GTS_CFG_B (1<<28) 179a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_CFG_RESET_B (1<<27) 180a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_AXI_WTO (1<<23) /* axi write timeout */ 181a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_AXI_WERR (1<<22) /* axi write err */ 182a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_AXI_RTO (1<<21) /* axi read timeout */ 183a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_AXI_RERR (1<<20) /* axi read err */ 184a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_RX_FIFO_OV (1<<18) /* rx fifo overflow */ 185a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_WR_FIFO_LVL (1<<17) /* wr fifo < level */ 186a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_RD_FIFO_LVL (1<<16) /* rd fifo >= level */ 187a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_DMA_CMD_ERR (1<<15) 188a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_DMA_Q_OV (1<<14) 189a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_DMA_DONE (1<<13) 190a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_DMA_PCAP_DONE (1<<12) 191a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_P2D_LEN_ERR (1<<11) 192a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_HMAC_ERR (1<<6) 193a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_SEU_ERR (1<<5) 194a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_POR_B (1<<4) 195a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_CFG_RST (1<<3) 196a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_DONE (1<<2) 197a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_INIT_PE (1<<1) 198a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_PCFG_INIT_NE (1<<0) 199a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_ERRORS 0x00f0f860 200a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_INT_ALL 0xf8f7f87f 201a9caca6aSWojciech A. Koszek 202a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS 0x014 203a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_DMA_CMD_Q_F (1<<31) /* cmd queue full */ 204a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_DMA_CMD_Q_E (1<<30) /* cmd queue empty */ 205a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_DONE_COUNT_MASK (3<<28) 206a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_DONE_COUNT_SHIFT 28 207a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_MASK (0x1f<<20) 208a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_SHIFT 20 209a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_MASK (0x7f<<12) 210a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_SHIFT 12 211a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PSS_GTS_USR_B (1<<11) 212a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PSS_FST_CFG_B (1<<10) 213a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PSS_GPWRDWN_B (1<<9) 214a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PSS_GTS_CFG_B (1<<8) 215a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_ILL_APB_ACCE (1<<6) 216a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PSS_CFG_RESET_B (1<<5) 217a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_PCFG_INIT (1<<4) 218a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_EFUSE_BBRAM_KEY_DIS (1<<3) 219a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_EFUSE_SEC_EN (1<<2) 220a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_STATUS_EFUSE_JTAG_DIS (1<<1) 221a9caca6aSWojciech A. Koszek 222a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_SRC_ADDR 0x018 223a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_DST_ADDR 0x01c 224a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP 1 225a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_ADDR_ILLEGAL 0xffffffff 226a9caca6aSWojciech A. Koszek 227a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_SRC_LEN 0x020 /* in 4-byte words. */ 228a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_SRC_LEN_MAX 0x7ffffff 229a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_DMA_DST_LEN 0x024 230a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_ROM_SHADOW 0x028 231a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MULTIBOOT_ADDR 0x02c 232a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_SW_ID 0x030 233a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_UNLOCK 0x034 234a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_UNLOCK_MAGIC 0x757bdf0d 235a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MCTRL 0x080 236a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MCTRL_PS_VERS_MASK (0xf<<28) 237a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT 28 238a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MCTRL_PCFG_POR_B (1<<8) 239a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK (1<<4) 240a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_CFG 0x100 241a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_INT_STAT 0x104 242a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_INT_MASK 0x108 243a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_MSTS 0x10c 244a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_CMD_FIFO 0x110 245a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_RD_FIFO 0x114 246a9caca6aSWojciech A. Koszek #define ZY7_DEVCFG_XADCIF_MCTL 0x118 247a9caca6aSWojciech A. Koszek 2488e01fdeaSOleksandr Tymoshenko static int 2498e01fdeaSOleksandr Tymoshenko zy7_devcfg_fclk_sysctl_source(SYSCTL_HANDLER_ARGS) 2508e01fdeaSOleksandr Tymoshenko { 2518e01fdeaSOleksandr Tymoshenko char buf[4]; 2528e01fdeaSOleksandr Tymoshenko struct zy7_fclk_config *cfg; 2538e01fdeaSOleksandr Tymoshenko int unit; 2548e01fdeaSOleksandr Tymoshenko int error; 2558e01fdeaSOleksandr Tymoshenko 2568e01fdeaSOleksandr Tymoshenko cfg = arg1; 2578e01fdeaSOleksandr Tymoshenko unit = arg2; 2588e01fdeaSOleksandr Tymoshenko 2598e01fdeaSOleksandr Tymoshenko switch (cfg->source) { 2608e01fdeaSOleksandr Tymoshenko case ZY7_PL_FCLK_SRC_IO: 2618e01fdeaSOleksandr Tymoshenko case ZY7_PL_FCLK_SRC_IO_ALT: 2628e01fdeaSOleksandr Tymoshenko strncpy(buf, "IO", sizeof(buf)); 2638e01fdeaSOleksandr Tymoshenko break; 2648e01fdeaSOleksandr Tymoshenko case ZY7_PL_FCLK_SRC_DDR: 2658e01fdeaSOleksandr Tymoshenko strncpy(buf, "DDR", sizeof(buf)); 2668e01fdeaSOleksandr Tymoshenko break; 2678e01fdeaSOleksandr Tymoshenko case ZY7_PL_FCLK_SRC_ARM: 2688e01fdeaSOleksandr Tymoshenko strncpy(buf, "ARM", sizeof(buf)); 2698e01fdeaSOleksandr Tymoshenko break; 2708e01fdeaSOleksandr Tymoshenko default: 2718e01fdeaSOleksandr Tymoshenko strncpy(buf, "???", sizeof(buf)); 2728e01fdeaSOleksandr Tymoshenko break; 2738e01fdeaSOleksandr Tymoshenko } 2748e01fdeaSOleksandr Tymoshenko 2758e01fdeaSOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 2768e01fdeaSOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 2778e01fdeaSOleksandr Tymoshenko return (error); 2788e01fdeaSOleksandr Tymoshenko 2798e01fdeaSOleksandr Tymoshenko if (strcasecmp(buf, "io") == 0) 2808e01fdeaSOleksandr Tymoshenko cfg->source = ZY7_PL_FCLK_SRC_IO; 2818e01fdeaSOleksandr Tymoshenko else if (strcasecmp(buf, "ddr") == 0) 2828e01fdeaSOleksandr Tymoshenko cfg->source = ZY7_PL_FCLK_SRC_DDR; 2838e01fdeaSOleksandr Tymoshenko else if (strcasecmp(buf, "arm") == 0) 2848e01fdeaSOleksandr Tymoshenko cfg->source = ZY7_PL_FCLK_SRC_ARM; 2858e01fdeaSOleksandr Tymoshenko else 2868e01fdeaSOleksandr Tymoshenko return (EINVAL); 2878e01fdeaSOleksandr Tymoshenko 2888e01fdeaSOleksandr Tymoshenko zy7_pl_fclk_set_source(unit, cfg->source); 2898e01fdeaSOleksandr Tymoshenko if (cfg->frequency > 0) 2908e01fdeaSOleksandr Tymoshenko cfg->actual_frequency = zy7_pl_fclk_get_freq(unit); 2918e01fdeaSOleksandr Tymoshenko 2928e01fdeaSOleksandr Tymoshenko return (0); 2938e01fdeaSOleksandr Tymoshenko } 2948e01fdeaSOleksandr Tymoshenko 2958e01fdeaSOleksandr Tymoshenko static int 2968e01fdeaSOleksandr Tymoshenko zy7_devcfg_fclk_sysctl_freq(SYSCTL_HANDLER_ARGS) 2978e01fdeaSOleksandr Tymoshenko { 2988e01fdeaSOleksandr Tymoshenko struct zy7_fclk_config *cfg; 2998e01fdeaSOleksandr Tymoshenko int unit; 3008e01fdeaSOleksandr Tymoshenko int error; 3018e01fdeaSOleksandr Tymoshenko int freq; 3028e01fdeaSOleksandr Tymoshenko int new_actual_freq; 3038e01fdeaSOleksandr Tymoshenko 3048e01fdeaSOleksandr Tymoshenko cfg = arg1; 3058e01fdeaSOleksandr Tymoshenko unit = arg2; 3068e01fdeaSOleksandr Tymoshenko 3078e01fdeaSOleksandr Tymoshenko freq = cfg->frequency; 3088e01fdeaSOleksandr Tymoshenko 3098e01fdeaSOleksandr Tymoshenko error = sysctl_handle_int(oidp, &freq, 0, req); 3108e01fdeaSOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 3118e01fdeaSOleksandr Tymoshenko return (error); 3128e01fdeaSOleksandr Tymoshenko 3138e01fdeaSOleksandr Tymoshenko if (freq > 0) { 3148e01fdeaSOleksandr Tymoshenko new_actual_freq = zy7_pl_fclk_set_freq(unit, freq); 3158e01fdeaSOleksandr Tymoshenko if (new_actual_freq < 0) 3168e01fdeaSOleksandr Tymoshenko return (EINVAL); 3178e01fdeaSOleksandr Tymoshenko if (!zy7_pl_fclk_enabled(unit)) 3188e01fdeaSOleksandr Tymoshenko zy7_pl_fclk_enable(unit); 3198e01fdeaSOleksandr Tymoshenko } 3208e01fdeaSOleksandr Tymoshenko else { 3218e01fdeaSOleksandr Tymoshenko zy7_pl_fclk_disable(unit); 3228e01fdeaSOleksandr Tymoshenko new_actual_freq = 0; 3238e01fdeaSOleksandr Tymoshenko } 3248e01fdeaSOleksandr Tymoshenko 3258e01fdeaSOleksandr Tymoshenko cfg->frequency = freq; 3268e01fdeaSOleksandr Tymoshenko cfg->actual_frequency = new_actual_freq; 3278e01fdeaSOleksandr Tymoshenko 3288e01fdeaSOleksandr Tymoshenko return (0); 3298e01fdeaSOleksandr Tymoshenko } 3308e01fdeaSOleksandr Tymoshenko 3318e01fdeaSOleksandr Tymoshenko static int 3328e01fdeaSOleksandr Tymoshenko zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS) 3338e01fdeaSOleksandr Tymoshenko { 3348e01fdeaSOleksandr Tymoshenko int error, enabled; 3358e01fdeaSOleksandr Tymoshenko 3368e01fdeaSOleksandr Tymoshenko enabled = zy7_pl_level_shifters_enabled(); 3378e01fdeaSOleksandr Tymoshenko 3388e01fdeaSOleksandr Tymoshenko error = sysctl_handle_int(oidp, &enabled, 0, req); 3398e01fdeaSOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 3408e01fdeaSOleksandr Tymoshenko return (error); 3418e01fdeaSOleksandr Tymoshenko 3428e01fdeaSOleksandr Tymoshenko if (enabled) 3438e01fdeaSOleksandr Tymoshenko zy7_pl_level_shifters_enable(); 3448e01fdeaSOleksandr Tymoshenko else 3458e01fdeaSOleksandr Tymoshenko zy7_pl_level_shifters_disable(); 3468e01fdeaSOleksandr Tymoshenko 3478e01fdeaSOleksandr Tymoshenko return (0); 3488e01fdeaSOleksandr Tymoshenko } 3498e01fdeaSOleksandr Tymoshenko 3508e01fdeaSOleksandr Tymoshenko static int 3518e01fdeaSOleksandr Tymoshenko zy7_devcfg_init_fclk_sysctl(struct zy7_devcfg_softc *sc) 3528e01fdeaSOleksandr Tymoshenko { 3538e01fdeaSOleksandr Tymoshenko struct sysctl_oid *fclk_node; 3548e01fdeaSOleksandr Tymoshenko char fclk_num[4]; 3558e01fdeaSOleksandr Tymoshenko int i; 3568e01fdeaSOleksandr Tymoshenko 3578e01fdeaSOleksandr Tymoshenko sysctl_ctx_init(&sc->sysctl_tree); 3588e01fdeaSOleksandr Tymoshenko sc->sysctl_tree_top = SYSCTL_ADD_NODE(&sc->sysctl_tree, 3598e01fdeaSOleksandr Tymoshenko SYSCTL_STATIC_CHILDREN(_hw_fpga), OID_AUTO, "fclk", 3607029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 3618e01fdeaSOleksandr Tymoshenko if (sc->sysctl_tree_top == NULL) { 3628e01fdeaSOleksandr Tymoshenko sysctl_ctx_free(&sc->sysctl_tree); 3638e01fdeaSOleksandr Tymoshenko return (-1); 3648e01fdeaSOleksandr Tymoshenko } 3658e01fdeaSOleksandr Tymoshenko 3668e01fdeaSOleksandr Tymoshenko for (i = 0; i < FCLK_NUM; i++) { 3678e01fdeaSOleksandr Tymoshenko snprintf(fclk_num, sizeof(fclk_num), "%d", i); 3688e01fdeaSOleksandr Tymoshenko fclk_node = SYSCTL_ADD_NODE(&sc->sysctl_tree, 3698e01fdeaSOleksandr Tymoshenko SYSCTL_CHILDREN(sc->sysctl_tree_top), OID_AUTO, fclk_num, 3707029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 3718e01fdeaSOleksandr Tymoshenko 3728e01fdeaSOleksandr Tymoshenko SYSCTL_ADD_INT(&sc->sysctl_tree, 3738e01fdeaSOleksandr Tymoshenko SYSCTL_CHILDREN(fclk_node), OID_AUTO, 3748e01fdeaSOleksandr Tymoshenko "actual_freq", CTLFLAG_RD, 3758e01fdeaSOleksandr Tymoshenko &fclk_configs[i].actual_frequency, i, 3768e01fdeaSOleksandr Tymoshenko "Actual frequency"); 3778e01fdeaSOleksandr Tymoshenko SYSCTL_ADD_PROC(&sc->sysctl_tree, 3788e01fdeaSOleksandr Tymoshenko SYSCTL_CHILDREN(fclk_node), OID_AUTO, 3797029da5cSPawel Biernacki "freq", CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, 3808e01fdeaSOleksandr Tymoshenko &fclk_configs[i], i, 3818e01fdeaSOleksandr Tymoshenko zy7_devcfg_fclk_sysctl_freq, 3828e01fdeaSOleksandr Tymoshenko "I", "Configured frequency"); 3838e01fdeaSOleksandr Tymoshenko SYSCTL_ADD_PROC(&sc->sysctl_tree, 3848e01fdeaSOleksandr Tymoshenko SYSCTL_CHILDREN(fclk_node), OID_AUTO, 3857029da5cSPawel Biernacki "source", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 3868e01fdeaSOleksandr Tymoshenko &fclk_configs[i], i, 3878e01fdeaSOleksandr Tymoshenko zy7_devcfg_fclk_sysctl_source, 3888e01fdeaSOleksandr Tymoshenko "A", "Clock source"); 3898e01fdeaSOleksandr Tymoshenko } 3908e01fdeaSOleksandr Tymoshenko 3918e01fdeaSOleksandr Tymoshenko return (0); 3928e01fdeaSOleksandr Tymoshenko } 393a9caca6aSWojciech A. Koszek 394a9caca6aSWojciech A. Koszek /* Enable programming the PL through PCAP. */ 395a9caca6aSWojciech A. Koszek static void 396a9caca6aSWojciech A. Koszek zy7_devcfg_init_hw(struct zy7_devcfg_softc *sc) 397a9caca6aSWojciech A. Koszek { 398a9caca6aSWojciech A. Koszek 399a9caca6aSWojciech A. Koszek DEVCFG_SC_ASSERT_LOCKED(sc); 400a9caca6aSWojciech A. Koszek 401a9caca6aSWojciech A. Koszek /* Set devcfg control register. */ 402a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_CTRL, 403a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_PCFG_PROG_B | 404a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_PCAP_PR | 405a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_PCAP_MODE | 406a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_USER_MODE | 407a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_RESVD_WR11 | 408a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_SPNIDEN | 409a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_SPIDEN | 410a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_NIDEN | 411a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_DBGEN | 412a9caca6aSWojciech A. Koszek ZY7_DEVCFG_CTRL_DAP_EN_MASK); 413a9caca6aSWojciech A. Koszek 414a9caca6aSWojciech A. Koszek /* Turn off internal PCAP loopback. */ 415a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_MCTRL, RD4(sc, ZY7_DEVCFG_MCTRL) & 416a9caca6aSWojciech A. Koszek ~ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK); 417a9caca6aSWojciech A. Koszek } 418a9caca6aSWojciech A. Koszek 419a9caca6aSWojciech A. Koszek /* Clear previous configuration of the PL by asserting PROG_B. */ 420a9caca6aSWojciech A. Koszek static int 421a9caca6aSWojciech A. Koszek zy7_devcfg_reset_pl(struct zy7_devcfg_softc *sc) 422a9caca6aSWojciech A. Koszek { 423a9caca6aSWojciech A. Koszek uint32_t devcfg_ctl; 424a9caca6aSWojciech A. Koszek int tries, err; 425a9caca6aSWojciech A. Koszek 426a9caca6aSWojciech A. Koszek DEVCFG_SC_ASSERT_LOCKED(sc); 427a9caca6aSWojciech A. Koszek 428a9caca6aSWojciech A. Koszek devcfg_ctl = RD4(sc, ZY7_DEVCFG_CTRL); 429a9caca6aSWojciech A. Koszek 4300bd55d38SIan Lepore /* Clear sticky bits and set up INIT signal positive edge interrupt. */ 4310bd55d38SIan Lepore WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); 4320bd55d38SIan Lepore WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_PCFG_INIT_PE); 4330bd55d38SIan Lepore 434a9caca6aSWojciech A. Koszek /* Deassert PROG_B (active low). */ 435a9caca6aSWojciech A. Koszek devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B; 436a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); 437a9caca6aSWojciech A. Koszek 4380bd55d38SIan Lepore /* 4390bd55d38SIan Lepore * Wait for INIT to assert. If it is already asserted, we may not get 4400bd55d38SIan Lepore * an edge interrupt so cancel it and continue. 4410bd55d38SIan Lepore */ 4420bd55d38SIan Lepore if ((RD4(sc, ZY7_DEVCFG_STATUS) & 4430bd55d38SIan Lepore ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) { 4440bd55d38SIan Lepore /* Already asserted. Cancel interrupt. */ 4450bd55d38SIan Lepore WR4(sc, ZY7_DEVCFG_INT_MASK, ~0); 4460bd55d38SIan Lepore } 4470bd55d38SIan Lepore else { 4480bd55d38SIan Lepore /* Wait for positive edge interrupt. */ 4490bd55d38SIan Lepore err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i1", hz); 4500bd55d38SIan Lepore if (err != 0) 4510bd55d38SIan Lepore return (err); 452a9caca6aSWojciech A. Koszek } 453a9caca6aSWojciech A. Koszek 4540bd55d38SIan Lepore /* Reassert PROG_B (active low). */ 455a9caca6aSWojciech A. Koszek devcfg_ctl &= ~ZY7_DEVCFG_CTRL_PCFG_PROG_B; 456a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); 457a9caca6aSWojciech A. Koszek 4580bd55d38SIan Lepore /* Wait for INIT deasserted. This happens almost instantly. */ 459a9caca6aSWojciech A. Koszek tries = 0; 460a9caca6aSWojciech A. Koszek while ((RD4(sc, ZY7_DEVCFG_STATUS) & 461a9caca6aSWojciech A. Koszek ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) { 462a9caca6aSWojciech A. Koszek if (++tries >= 100) 463a9caca6aSWojciech A. Koszek return (EIO); 464a9caca6aSWojciech A. Koszek DELAY(5); 465a9caca6aSWojciech A. Koszek } 466a9caca6aSWojciech A. Koszek 4670bd55d38SIan Lepore /* Clear sticky bits and set up INIT positive edge interrupt. */ 468a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); 469a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_PCFG_INIT_PE); 470a9caca6aSWojciech A. Koszek 471a9caca6aSWojciech A. Koszek /* Deassert PROG_B again. */ 472a9caca6aSWojciech A. Koszek devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B; 473a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); 474a9caca6aSWojciech A. Koszek 4750bd55d38SIan Lepore /* 4760bd55d38SIan Lepore * Wait for INIT asserted indicating FPGA internal initialization 4770bd55d38SIan Lepore * is complete. 478a9caca6aSWojciech A. Koszek */ 4790bd55d38SIan Lepore err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i2", hz); 480a9caca6aSWojciech A. Koszek if (err != 0) 481a9caca6aSWojciech A. Koszek return (err); 482a9caca6aSWojciech A. Koszek 483a9caca6aSWojciech A. Koszek /* Clear sticky DONE bit in interrupt status. */ 484a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); 485a9caca6aSWojciech A. Koszek 486a9caca6aSWojciech A. Koszek return (0); 487a9caca6aSWojciech A. Koszek } 488a9caca6aSWojciech A. Koszek 489a9caca6aSWojciech A. Koszek /* Callback function for bus_dmamap_load(). */ 490a9caca6aSWojciech A. Koszek static void 491a9caca6aSWojciech A. Koszek zy7_dma_cb2(void *arg, bus_dma_segment_t *seg, int nsegs, int error) 492a9caca6aSWojciech A. Koszek { 493a9caca6aSWojciech A. Koszek if (!error && nsegs == 1) 494a9caca6aSWojciech A. Koszek *(bus_addr_t *)arg = seg[0].ds_addr; 495a9caca6aSWojciech A. Koszek } 496a9caca6aSWojciech A. Koszek 497a9caca6aSWojciech A. Koszek static int 498a9caca6aSWojciech A. Koszek zy7_devcfg_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 499a9caca6aSWojciech A. Koszek { 500a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = dev->si_drv1; 501a9caca6aSWojciech A. Koszek int err; 502a9caca6aSWojciech A. Koszek 503a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK(sc); 504a9caca6aSWojciech A. Koszek if (sc->is_open) { 505a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 506a9caca6aSWojciech A. Koszek return (EBUSY); 507a9caca6aSWojciech A. Koszek } 508a9caca6aSWojciech A. Koszek 509a9caca6aSWojciech A. Koszek sc->dma_map = NULL; 510a9caca6aSWojciech A. Koszek err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 4, 0, 511a9caca6aSWojciech A. Koszek BUS_SPACE_MAXADDR_32BIT, 512a9caca6aSWojciech A. Koszek BUS_SPACE_MAXADDR, 513a9caca6aSWojciech A. Koszek NULL, NULL, 514a9caca6aSWojciech A. Koszek PAGE_SIZE, 515a9caca6aSWojciech A. Koszek 1, 516a9caca6aSWojciech A. Koszek PAGE_SIZE, 517a9caca6aSWojciech A. Koszek 0, 518a9caca6aSWojciech A. Koszek busdma_lock_mutex, 519a9caca6aSWojciech A. Koszek &sc->sc_mtx, 520a9caca6aSWojciech A. Koszek &sc->dma_tag); 521a9caca6aSWojciech A. Koszek if (err) { 522a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 523a9caca6aSWojciech A. Koszek return (err); 524a9caca6aSWojciech A. Koszek } 525a9caca6aSWojciech A. Koszek 526a9caca6aSWojciech A. Koszek sc->is_open = 1; 527a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 528a9caca6aSWojciech A. Koszek return (0); 529a9caca6aSWojciech A. Koszek } 530a9caca6aSWojciech A. Koszek 531a9caca6aSWojciech A. Koszek static int 532a9caca6aSWojciech A. Koszek zy7_devcfg_write(struct cdev *dev, struct uio *uio, int ioflag) 533a9caca6aSWojciech A. Koszek { 534a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = dev->si_drv1; 535a9caca6aSWojciech A. Koszek void *dma_mem; 536a9caca6aSWojciech A. Koszek bus_addr_t dma_physaddr; 537a9caca6aSWojciech A. Koszek int segsz, err; 538a9caca6aSWojciech A. Koszek 539a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK(sc); 540a9caca6aSWojciech A. Koszek 541a9caca6aSWojciech A. Koszek /* First write? Reset PL. */ 542a9caca6aSWojciech A. Koszek if (uio->uio_offset == 0 && uio->uio_resid > 0) { 543a9caca6aSWojciech A. Koszek zy7_devcfg_init_hw(sc); 544a9caca6aSWojciech A. Koszek zy7_slcr_preload_pl(); 545a9caca6aSWojciech A. Koszek err = zy7_devcfg_reset_pl(sc); 546a9caca6aSWojciech A. Koszek if (err != 0) { 547a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 548a9caca6aSWojciech A. Koszek return (err); 549a9caca6aSWojciech A. Koszek } 550a9caca6aSWojciech A. Koszek } 551a9caca6aSWojciech A. Koszek 552a9caca6aSWojciech A. Koszek /* Allocate dma memory and load. */ 553a9caca6aSWojciech A. Koszek err = bus_dmamem_alloc(sc->dma_tag, &dma_mem, BUS_DMA_NOWAIT, 554a9caca6aSWojciech A. Koszek &sc->dma_map); 555a9caca6aSWojciech A. Koszek if (err != 0) { 556a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 557a9caca6aSWojciech A. Koszek return (err); 558a9caca6aSWojciech A. Koszek } 559a9caca6aSWojciech A. Koszek err = bus_dmamap_load(sc->dma_tag, sc->dma_map, dma_mem, PAGE_SIZE, 560a9caca6aSWojciech A. Koszek zy7_dma_cb2, &dma_physaddr, 0); 561a9caca6aSWojciech A. Koszek if (err != 0) { 562a9caca6aSWojciech A. Koszek bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map); 563a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 564a9caca6aSWojciech A. Koszek return (err); 565a9caca6aSWojciech A. Koszek } 566a9caca6aSWojciech A. Koszek 567a9caca6aSWojciech A. Koszek while (uio->uio_resid > 0) { 568a9caca6aSWojciech A. Koszek /* If DONE signal has been set, we shouldn't write anymore. */ 569a9caca6aSWojciech A. Koszek if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & 570a9caca6aSWojciech A. Koszek ZY7_DEVCFG_INT_PCFG_DONE) != 0) { 571a9caca6aSWojciech A. Koszek err = EIO; 572a9caca6aSWojciech A. Koszek break; 573a9caca6aSWojciech A. Koszek } 574a9caca6aSWojciech A. Koszek 575a9caca6aSWojciech A. Koszek /* uiomove the data from user buffer to our dma map. */ 576a9caca6aSWojciech A. Koszek segsz = MIN(PAGE_SIZE, uio->uio_resid); 5770bd55d38SIan Lepore DEVCFG_SC_UNLOCK(sc); 578a9caca6aSWojciech A. Koszek err = uiomove(dma_mem, segsz, uio); 5790bd55d38SIan Lepore DEVCFG_SC_LOCK(sc); 580a9caca6aSWojciech A. Koszek if (err != 0) 581a9caca6aSWojciech A. Koszek break; 582a9caca6aSWojciech A. Koszek 583a9caca6aSWojciech A. Koszek /* Flush the cache to memory. */ 584a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->dma_tag, sc->dma_map, 585a9caca6aSWojciech A. Koszek BUS_DMASYNC_PREWRITE); 586a9caca6aSWojciech A. Koszek 587a9caca6aSWojciech A. Koszek /* Program devcfg's DMA engine. The ordering of these 588a9caca6aSWojciech A. Koszek * register writes is critical. 589a9caca6aSWojciech A. Koszek */ 590a9caca6aSWojciech A. Koszek if (uio->uio_resid > segsz) 591a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR, 592a9caca6aSWojciech A. Koszek (uint32_t) dma_physaddr); 593a9caca6aSWojciech A. Koszek else 594a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR, 595a9caca6aSWojciech A. Koszek (uint32_t) dma_physaddr | 596a9caca6aSWojciech A. Koszek ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP); 597a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_DMA_DST_ADDR, ZY7_DEVCFG_DMA_ADDR_ILLEGAL); 598a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_DMA_SRC_LEN, (segsz+3)/4); 599a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_DMA_DST_LEN, 0); 600a9caca6aSWojciech A. Koszek 601a9caca6aSWojciech A. Koszek /* Now clear done bit and set up DMA done interrupt. */ 602a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); 603a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_DMA_DONE); 604a9caca6aSWojciech A. Koszek 605a9caca6aSWojciech A. Koszek /* Wait for DMA done interrupt. */ 606a9caca6aSWojciech A. Koszek err = mtx_sleep(sc->dma_map, &sc->sc_mtx, PCATCH, 607a9caca6aSWojciech A. Koszek "zy7dma", hz); 608a9caca6aSWojciech A. Koszek if (err != 0) 609a9caca6aSWojciech A. Koszek break; 610a9caca6aSWojciech A. Koszek 611a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->dma_tag, sc->dma_map, 612a9caca6aSWojciech A. Koszek BUS_DMASYNC_POSTWRITE); 613a9caca6aSWojciech A. Koszek 614a9caca6aSWojciech A. Koszek /* Check DONE signal. */ 615a9caca6aSWojciech A. Koszek if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & 616a9caca6aSWojciech A. Koszek ZY7_DEVCFG_INT_PCFG_DONE) != 0) 617a9caca6aSWojciech A. Koszek zy7_slcr_postload_pl(zy7_en_level_shifters); 618a9caca6aSWojciech A. Koszek } 619a9caca6aSWojciech A. Koszek 620a9caca6aSWojciech A. Koszek bus_dmamap_unload(sc->dma_tag, sc->dma_map); 621a9caca6aSWojciech A. Koszek bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map); 622a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 623a9caca6aSWojciech A. Koszek return (err); 624a9caca6aSWojciech A. Koszek } 625a9caca6aSWojciech A. Koszek 626a9caca6aSWojciech A. Koszek static int 627a9caca6aSWojciech A. Koszek zy7_devcfg_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 628a9caca6aSWojciech A. Koszek { 629a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = dev->si_drv1; 630a9caca6aSWojciech A. Koszek 631a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK(sc); 632a9caca6aSWojciech A. Koszek sc->is_open = 0; 633a9caca6aSWojciech A. Koszek bus_dma_tag_destroy(sc->dma_tag); 634a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 635a9caca6aSWojciech A. Koszek 6368e01fdeaSOleksandr Tymoshenko zy7_slcr_postload_pl(zy7_en_level_shifters); 6378e01fdeaSOleksandr Tymoshenko 638a9caca6aSWojciech A. Koszek return (0); 639a9caca6aSWojciech A. Koszek } 640a9caca6aSWojciech A. Koszek 641a9caca6aSWojciech A. Koszek static void 642a9caca6aSWojciech A. Koszek zy7_devcfg_intr(void *arg) 643a9caca6aSWojciech A. Koszek { 644a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = (struct zy7_devcfg_softc *)arg; 645a9caca6aSWojciech A. Koszek uint32_t istatus, imask; 646a9caca6aSWojciech A. Koszek 647a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK(sc); 648a9caca6aSWojciech A. Koszek 649a9caca6aSWojciech A. Koszek istatus = RD4(sc, ZY7_DEVCFG_INT_STATUS); 650a9caca6aSWojciech A. Koszek imask = ~RD4(sc, ZY7_DEVCFG_INT_MASK); 651a9caca6aSWojciech A. Koszek 652a9caca6aSWojciech A. Koszek /* Turn interrupt off. */ 653a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_MASK, ~0); 654a9caca6aSWojciech A. Koszek 655a9caca6aSWojciech A. Koszek if ((istatus & imask) == 0) { 656a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 657a9caca6aSWojciech A. Koszek return; 658a9caca6aSWojciech A. Koszek } 659a9caca6aSWojciech A. Koszek 660a9caca6aSWojciech A. Koszek /* DMA done? */ 661a9caca6aSWojciech A. Koszek if ((istatus & ZY7_DEVCFG_INT_DMA_DONE) != 0) 662a9caca6aSWojciech A. Koszek wakeup(sc->dma_map); 663a9caca6aSWojciech A. Koszek 664a9caca6aSWojciech A. Koszek /* INIT_B positive edge? */ 665a9caca6aSWojciech A. Koszek if ((istatus & ZY7_DEVCFG_INT_PCFG_INIT_PE) != 0) 666a9caca6aSWojciech A. Koszek wakeup(sc); 667a9caca6aSWojciech A. Koszek 668a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 669a9caca6aSWojciech A. Koszek } 670a9caca6aSWojciech A. Koszek 671a9caca6aSWojciech A. Koszek /* zy7_devcfg_sysctl_pl_done() returns status of the PL_DONE signal. 672a9caca6aSWojciech A. Koszek */ 673a9caca6aSWojciech A. Koszek static int 674a9caca6aSWojciech A. Koszek zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS) 675a9caca6aSWojciech A. Koszek { 676a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = zy7_devcfg_softc_p; 677a9caca6aSWojciech A. Koszek int pl_done = 0; 678a9caca6aSWojciech A. Koszek 679a9caca6aSWojciech A. Koszek if (sc) { 680a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK(sc); 681a9caca6aSWojciech A. Koszek 682a9caca6aSWojciech A. Koszek /* PCFG_DONE bit is sticky. Clear it before checking it. */ 683a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_PCFG_DONE); 684a9caca6aSWojciech A. Koszek pl_done = ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & 685a9caca6aSWojciech A. Koszek ZY7_DEVCFG_INT_PCFG_DONE) != 0); 686a9caca6aSWojciech A. Koszek 687a9caca6aSWojciech A. Koszek DEVCFG_SC_UNLOCK(sc); 688a9caca6aSWojciech A. Koszek } 689a9caca6aSWojciech A. Koszek return (sysctl_handle_int(oidp, &pl_done, 0, req)); 690a9caca6aSWojciech A. Koszek } 691a9caca6aSWojciech A. Koszek 692a9caca6aSWojciech A. Koszek static int 693a9caca6aSWojciech A. Koszek zy7_devcfg_probe(device_t dev) 694a9caca6aSWojciech A. Koszek { 695add35ed5SIan Lepore 696add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 697add35ed5SIan Lepore return (ENXIO); 698add35ed5SIan Lepore 699a9caca6aSWojciech A. Koszek if (!ofw_bus_is_compatible(dev, "xlnx,zy7_devcfg")) 700a9caca6aSWojciech A. Koszek return (ENXIO); 701a9caca6aSWojciech A. Koszek 702a9caca6aSWojciech A. Koszek device_set_desc(dev, "Zynq devcfg block"); 703a9caca6aSWojciech A. Koszek return (0); 704a9caca6aSWojciech A. Koszek } 705a9caca6aSWojciech A. Koszek 706a9caca6aSWojciech A. Koszek static int zy7_devcfg_detach(device_t dev); 707a9caca6aSWojciech A. Koszek 708a9caca6aSWojciech A. Koszek static int 709a9caca6aSWojciech A. Koszek zy7_devcfg_attach(device_t dev) 710a9caca6aSWojciech A. Koszek { 711a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = device_get_softc(dev); 7128e01fdeaSOleksandr Tymoshenko int i; 713a9caca6aSWojciech A. Koszek int rid, err; 714a9caca6aSWojciech A. Koszek 715a9caca6aSWojciech A. Koszek /* Allow only one attach. */ 716a9caca6aSWojciech A. Koszek if (zy7_devcfg_softc_p != NULL) 717a9caca6aSWojciech A. Koszek return (ENXIO); 718a9caca6aSWojciech A. Koszek 719a9caca6aSWojciech A. Koszek sc->dev = dev; 720a9caca6aSWojciech A. Koszek 721a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK_INIT(sc); 722a9caca6aSWojciech A. Koszek 723a9caca6aSWojciech A. Koszek /* Get memory resource. */ 724a9caca6aSWojciech A. Koszek rid = 0; 725a9caca6aSWojciech A. Koszek sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 726a9caca6aSWojciech A. Koszek RF_ACTIVE); 727a9caca6aSWojciech A. Koszek if (sc->mem_res == NULL) { 728a9caca6aSWojciech A. Koszek device_printf(dev, "could not allocate memory resources.\n"); 729a9caca6aSWojciech A. Koszek zy7_devcfg_detach(dev); 730a9caca6aSWojciech A. Koszek return (ENOMEM); 731a9caca6aSWojciech A. Koszek } 732a9caca6aSWojciech A. Koszek 733a9caca6aSWojciech A. Koszek /* Allocate IRQ. */ 734a9caca6aSWojciech A. Koszek rid = 0; 735a9caca6aSWojciech A. Koszek sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 736a9caca6aSWojciech A. Koszek RF_ACTIVE); 737a9caca6aSWojciech A. Koszek if (sc->irq_res == NULL) { 738a9caca6aSWojciech A. Koszek device_printf(dev, "cannot allocate IRQ\n"); 739a9caca6aSWojciech A. Koszek zy7_devcfg_detach(dev); 740a9caca6aSWojciech A. Koszek return (ENOMEM); 741a9caca6aSWojciech A. Koszek } 742a9caca6aSWojciech A. Koszek 743a9caca6aSWojciech A. Koszek /* Activate the interrupt. */ 744a9caca6aSWojciech A. Koszek err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 745a9caca6aSWojciech A. Koszek NULL, zy7_devcfg_intr, sc, &sc->intrhandle); 746a9caca6aSWojciech A. Koszek if (err) { 747a9caca6aSWojciech A. Koszek device_printf(dev, "cannot setup IRQ\n"); 748a9caca6aSWojciech A. Koszek zy7_devcfg_detach(dev); 749a9caca6aSWojciech A. Koszek return (err); 750a9caca6aSWojciech A. Koszek } 751a9caca6aSWojciech A. Koszek 752a9caca6aSWojciech A. Koszek /* Create /dev/devcfg */ 753a9caca6aSWojciech A. Koszek sc->sc_ctl_dev = make_dev(&zy7_devcfg_cdevsw, 0, 754a9caca6aSWojciech A. Koszek UID_ROOT, GID_WHEEL, 0600, "devcfg"); 755a9caca6aSWojciech A. Koszek if (sc->sc_ctl_dev == NULL) { 756a9caca6aSWojciech A. Koszek device_printf(dev, "failed to create /dev/devcfg"); 757a9caca6aSWojciech A. Koszek zy7_devcfg_detach(dev); 758a9caca6aSWojciech A. Koszek return (ENXIO); 759a9caca6aSWojciech A. Koszek } 760a9caca6aSWojciech A. Koszek sc->sc_ctl_dev->si_drv1 = sc; 761a9caca6aSWojciech A. Koszek 762a9caca6aSWojciech A. Koszek zy7_devcfg_softc_p = sc; 763a9caca6aSWojciech A. Koszek 764a9caca6aSWojciech A. Koszek /* Unlock devcfg registers. */ 765a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_UNLOCK, ZY7_DEVCFG_UNLOCK_MAGIC); 766a9caca6aSWojciech A. Koszek 767a9caca6aSWojciech A. Koszek /* Make sure interrupts are completely disabled. */ 768a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); 769a9caca6aSWojciech A. Koszek WR4(sc, ZY7_DEVCFG_INT_MASK, 0xffffffff); 770a9caca6aSWojciech A. Koszek 771a9caca6aSWojciech A. Koszek /* Get PS_VERS for SYSCTL. */ 772a9caca6aSWojciech A. Koszek zy7_ps_vers = (RD4(sc, ZY7_DEVCFG_MCTRL) & 773a9caca6aSWojciech A. Koszek ZY7_DEVCFG_MCTRL_PS_VERS_MASK) >> 774a9caca6aSWojciech A. Koszek ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT; 775a9caca6aSWojciech A. Koszek 7768e01fdeaSOleksandr Tymoshenko for (i = 0; i < FCLK_NUM; i++) { 7778e01fdeaSOleksandr Tymoshenko fclk_configs[i].source = zy7_pl_fclk_get_source(i); 7788e01fdeaSOleksandr Tymoshenko fclk_configs[i].actual_frequency = 7798e01fdeaSOleksandr Tymoshenko zy7_pl_fclk_enabled(i) ? zy7_pl_fclk_get_freq(i) : 0; 7808e01fdeaSOleksandr Tymoshenko /* Initially assume actual frequency is the configure one */ 7818e01fdeaSOleksandr Tymoshenko fclk_configs[i].frequency = fclk_configs[i].actual_frequency; 7828e01fdeaSOleksandr Tymoshenko } 7838e01fdeaSOleksandr Tymoshenko 7848e01fdeaSOleksandr Tymoshenko if (zy7_devcfg_init_fclk_sysctl(sc) < 0) 7858e01fdeaSOleksandr Tymoshenko device_printf(dev, "failed to initialized sysctl tree\n"); 7868e01fdeaSOleksandr Tymoshenko 787a9caca6aSWojciech A. Koszek return (0); 788a9caca6aSWojciech A. Koszek } 789a9caca6aSWojciech A. Koszek 790a9caca6aSWojciech A. Koszek static int 791a9caca6aSWojciech A. Koszek zy7_devcfg_detach(device_t dev) 792a9caca6aSWojciech A. Koszek { 793a9caca6aSWojciech A. Koszek struct zy7_devcfg_softc *sc = device_get_softc(dev); 794*64d1a02eSJohn Baldwin int error; 795*64d1a02eSJohn Baldwin 796*64d1a02eSJohn Baldwin error = bus_generic_detach(dev); 797*64d1a02eSJohn Baldwin if (error != 0) 798*64d1a02eSJohn Baldwin return (error); 799a9caca6aSWojciech A. Koszek 8008e01fdeaSOleksandr Tymoshenko if (sc->sysctl_tree_top != NULL) { 8018e01fdeaSOleksandr Tymoshenko sysctl_ctx_free(&sc->sysctl_tree); 8028e01fdeaSOleksandr Tymoshenko sc->sysctl_tree_top = NULL; 8038e01fdeaSOleksandr Tymoshenko } 8048e01fdeaSOleksandr Tymoshenko 805a9caca6aSWojciech A. Koszek /* Get rid of /dev/devcfg0. */ 806a9caca6aSWojciech A. Koszek if (sc->sc_ctl_dev != NULL) 807a9caca6aSWojciech A. Koszek destroy_dev(sc->sc_ctl_dev); 808a9caca6aSWojciech A. Koszek 809a9caca6aSWojciech A. Koszek /* Teardown and release interrupt. */ 810a9caca6aSWojciech A. Koszek if (sc->irq_res != NULL) { 811a9caca6aSWojciech A. Koszek if (sc->intrhandle) 812a9caca6aSWojciech A. Koszek bus_teardown_intr(dev, sc->irq_res, sc->intrhandle); 813a9caca6aSWojciech A. Koszek bus_release_resource(dev, SYS_RES_IRQ, 814a9caca6aSWojciech A. Koszek rman_get_rid(sc->irq_res), sc->irq_res); 815a9caca6aSWojciech A. Koszek } 816a9caca6aSWojciech A. Koszek 817a9caca6aSWojciech A. Koszek /* Release memory resource. */ 818a9caca6aSWojciech A. Koszek if (sc->mem_res != NULL) 819a9caca6aSWojciech A. Koszek bus_release_resource(dev, SYS_RES_MEMORY, 820a9caca6aSWojciech A. Koszek rman_get_rid(sc->mem_res), sc->mem_res); 821a9caca6aSWojciech A. Koszek 822a9caca6aSWojciech A. Koszek zy7_devcfg_softc_p = NULL; 823a9caca6aSWojciech A. Koszek 824a9caca6aSWojciech A. Koszek DEVCFG_SC_LOCK_DESTROY(sc); 825a9caca6aSWojciech A. Koszek 826a9caca6aSWojciech A. Koszek return (0); 827a9caca6aSWojciech A. Koszek } 828a9caca6aSWojciech A. Koszek 829a9caca6aSWojciech A. Koszek static device_method_t zy7_devcfg_methods[] = { 830a9caca6aSWojciech A. Koszek /* device_if */ 831a9caca6aSWojciech A. Koszek DEVMETHOD(device_probe, zy7_devcfg_probe), 832a9caca6aSWojciech A. Koszek DEVMETHOD(device_attach, zy7_devcfg_attach), 833a9caca6aSWojciech A. Koszek DEVMETHOD(device_detach, zy7_devcfg_detach), 834a9caca6aSWojciech A. Koszek 835a9caca6aSWojciech A. Koszek DEVMETHOD_END 836a9caca6aSWojciech A. Koszek }; 837a9caca6aSWojciech A. Koszek 838a9caca6aSWojciech A. Koszek static driver_t zy7_devcfg_driver = { 839a9caca6aSWojciech A. Koszek "zy7_devcfg", 840a9caca6aSWojciech A. Koszek zy7_devcfg_methods, 841a9caca6aSWojciech A. Koszek sizeof(struct zy7_devcfg_softc), 842a9caca6aSWojciech A. Koszek }; 843a9caca6aSWojciech A. Koszek 844680ccae3SJohn Baldwin DRIVER_MODULE(zy7_devcfg, simplebus, zy7_devcfg_driver, 0, 0); 845a9caca6aSWojciech A. Koszek MODULE_DEPEND(zy7_devcfg, zy7_slcr, 1, 1, 1); 846