| /* |
| * Copyright (C) 2013 Altera Corporation <www.altera.com> |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <common.h> |
| #include <asm/io.h> |
| #include <asm/arch/freeze_controller.h> |
| #include <asm/arch/scan_manager.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| static const struct socfpga_scan_manager *scan_manager_base = |
| (void *)(SOCFPGA_SCANMGR_ADDRESS); |
| static const struct socfpga_freeze_controller *freeze_controller_base = |
| (void *)(SOCFPGA_SYSMGR_ADDRESS + SYSMGR_FRZCTRL_ADDRESS); |
| |
| /* |
| * Function to check IO scan chain engine status and wait if the engine is |
| * is active. Poll the IO scan chain engine till maximum iteration reached. |
| */ |
| static inline uint32_t scan_chain_engine_is_idle(uint32_t max_iter) |
| { |
| uint32_t scanmgr_status; |
| |
| scanmgr_status = readl(&scan_manager_base->stat); |
| |
| /* Poll the engine until the scan engine is inactive */ |
| while (SCANMGR_STAT_ACTIVE_GET(scanmgr_status) || |
| (SCANMGR_STAT_WFIFOCNT_GET(scanmgr_status) > 0)) { |
| max_iter--; |
| if (max_iter > 0) |
| scanmgr_status = readl(&scan_manager_base->stat); |
| else |
| return 0; |
| } |
| return 1; |
| } |
| |
| /** |
| * scan_mgr_io_scan_chain_prg() - Program HPS IO Scan Chain |
| * @io_scan_chain_id: IO scan chain ID |
| */ |
| static int scan_mgr_io_scan_chain_prg(const unsigned int io_scan_chain_id) |
| { |
| uint16_t tdi_tdo_header; |
| uint32_t io_program_iter; |
| uint32_t io_scan_chain_data_residual; |
| uint32_t residual; |
| uint32_t i, ret; |
| uint32_t index = 0; |
| uint32_t io_scan_chain_len_in_bits; |
| const unsigned long *iocsr_scan_chain; |
| |
| ret = iocsr_get_config_table(io_scan_chain_id, &iocsr_scan_chain, |
| &io_scan_chain_len_in_bits); |
| if (ret) |
| return 1; |
| |
| /* |
| * De-assert reinit if the IO scan chain is intended for HIO. In |
| * this, its the chain 3. |
| */ |
| if (io_scan_chain_id == 3) |
| clrbits_le32(&freeze_controller_base->hioctrl, |
| SYSMGR_FRZCTRL_HIOCTRL_DLLRST_MASK); |
| |
| /* |
| * Check if the scan chain engine is inactive and the |
| * WFIFO is empty before enabling the IO scan chain |
| */ |
| if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) |
| return 1; |
| |
| /* |
| * Enable IO Scan chain based on scan chain id |
| * Note: only one chain can be enabled at a time |
| */ |
| setbits_le32(&scan_manager_base->en, 1 << io_scan_chain_id); |
| |
| /* |
| * Calculate number of iteration needed for full 128-bit (4 x32-bits) |
| * bits shifting. Each TDI_TDO packet can shift in maximum 128-bits |
| */ |
| io_program_iter = io_scan_chain_len_in_bits >> |
| IO_SCAN_CHAIN_128BIT_SHIFT; |
| io_scan_chain_data_residual = io_scan_chain_len_in_bits & |
| IO_SCAN_CHAIN_128BIT_MASK; |
| |
| /* Construct TDI_TDO packet for 128-bit IO scan chain (2 bytes) */ |
| tdi_tdo_header = TDI_TDO_HEADER_FIRST_BYTE | |
| (TDI_TDO_MAX_PAYLOAD << TDI_TDO_HEADER_SECOND_BYTE_SHIFT); |
| |
| /* Program IO scan chain in 128-bit iteration */ |
| for (i = 0; i < io_program_iter; i++) { |
| /* write TDI_TDO packet header to scan manager */ |
| writel(tdi_tdo_header, &scan_manager_base->fifo_double_byte); |
| |
| /* calculate array index. Multiply by 4 as write 4 x 32bits */ |
| index = i * 4; |
| |
| /* write 4 successive 32-bit IO scan chain data into WFIFO */ |
| writel(iocsr_scan_chain[index], |
| &scan_manager_base->fifo_quad_byte); |
| writel(iocsr_scan_chain[index + 1], |
| &scan_manager_base->fifo_quad_byte); |
| writel(iocsr_scan_chain[index + 2], |
| &scan_manager_base->fifo_quad_byte); |
| writel(iocsr_scan_chain[index + 3], |
| &scan_manager_base->fifo_quad_byte); |
| |
| /* |
| * Check if the scan chain engine has completed the |
| * IO scan chain data shifting |
| */ |
| if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) |
| goto error; |
| } |
| |
| /* Calculate array index for final TDI_TDO packet */ |
| index = io_program_iter * 4; |
| |
| /* Final TDI_TDO packet if any */ |
| if (io_scan_chain_data_residual) { |
| /* |
| * Calculate number of quad bytes FIFO write |
| * needed for the final TDI_TDO packet |
| */ |
| io_program_iter = io_scan_chain_data_residual >> |
| IO_SCAN_CHAIN_32BIT_SHIFT; |
| |
| /* |
| * Construct TDI_TDO packet for remaining IO |
| * scan chain (2 bytes) |
| */ |
| tdi_tdo_header = TDI_TDO_HEADER_FIRST_BYTE | |
| ((io_scan_chain_data_residual - 1) << |
| TDI_TDO_HEADER_SECOND_BYTE_SHIFT); |
| |
| /* |
| * Program the last part of IO scan chain write TDI_TDO packet |
| * header (2 bytes) to scan manager |
| */ |
| writel(tdi_tdo_header, &scan_manager_base->fifo_double_byte); |
| |
| for (i = 0; i < io_program_iter; i++) { |
| /* |
| * write remaining scan chain data into scan |
| * manager WFIFO with 4 bytes write |
| */ |
| writel(iocsr_scan_chain[index + i], |
| &scan_manager_base->fifo_quad_byte); |
| } |
| |
| index += io_program_iter; |
| residual = io_scan_chain_data_residual & |
| IO_SCAN_CHAIN_32BIT_MASK; |
| |
| if (IO_SCAN_CHAIN_PAYLOAD_24BIT < residual) { |
| /* |
| * write the last 4B scan chain data |
| * into scan manager WFIFO |
| */ |
| writel(iocsr_scan_chain[index], |
| &scan_manager_base->fifo_quad_byte); |
| } else { |
| /* |
| * write the remaining 1 - 3 bytes scan chain |
| * data into scan manager WFIFO byte by byte |
| * to prevent JTAG engine shifting unused data |
| * from the FIFO and mistaken the data as a |
| * valid command (even though unused bits are |
| * set to 0, but just to prevent hardware |
| * glitch) |
| */ |
| for (i = 0; i < residual; i += 8) { |
| writel(((iocsr_scan_chain[index] >> i) |
| & IO_SCAN_CHAIN_BYTE_MASK), |
| &scan_manager_base->fifo_single_byte); |
| } |
| } |
| |
| /* |
| * Check if the scan chain engine has completed the |
| * IO scan chain data shifting |
| */ |
| if (!scan_chain_engine_is_idle(SCAN_MAX_DELAY)) |
| goto error; |
| } |
| |
| /* Disable IO Scan chain when configuration done*/ |
| clrbits_le32(&scan_manager_base->en, 1 << io_scan_chain_id); |
| return 0; |
| |
| error: |
| /* Disable IO Scan chain when error detected */ |
| clrbits_le32(&scan_manager_base->en, 1 << io_scan_chain_id); |
| return 1; |
| } |
| |
| int scan_mgr_configure_iocsr(void) |
| { |
| int status = 0; |
| |
| /* configure the IOCSR through scan chain */ |
| status |= scan_mgr_io_scan_chain_prg(0); |
| status |= scan_mgr_io_scan_chain_prg(1); |
| status |= scan_mgr_io_scan_chain_prg(2); |
| status |= scan_mgr_io_scan_chain_prg(3); |
| return status; |
| } |