18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * FPGA Manager Driver for Altera Arria/Cyclone/Stratix CvP 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 DENX Software Engineering 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Anatolij Gustschin <agust@denx.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Manage Altera FPGA firmware using PCIe CvP. 108c2ecf20Sopenharmony_ci * Firmware must be in binary "rbf" format. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-mgr.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/sizes.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define CVP_BAR 0 /* BAR used for data transfer in memory mode */ 218c2ecf20Sopenharmony_ci#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ 228c2ecf20Sopenharmony_ci#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Vendor Specific Extended Capability Registers */ 258c2ecf20Sopenharmony_ci#define VSE_PCIE_EXT_CAP_ID 0x0 268c2ecf20Sopenharmony_ci#define VSE_PCIE_EXT_CAP_ID_VAL 0x000b /* 16bit */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS 0x1c /* 32bit */ 298c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */ 308c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */ 318c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_CVP_EN BIT(20) /* ctrl block is enabling CVP */ 328c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_USERMODE BIT(21) /* USERMODE */ 338c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_CFG_DONE BIT(23) /* CVP_CONFIG_DONE */ 348c2ecf20Sopenharmony_ci#define VSE_CVP_STATUS_PLD_CLK_IN_USE BIT(24) /* PLD_CLK_IN_USE */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VSE_CVP_MODE_CTRL 0x20 /* 32bit */ 378c2ecf20Sopenharmony_ci#define VSE_CVP_MODE_CTRL_CVP_MODE BIT(0) /* CVP (1) or normal mode (0) */ 388c2ecf20Sopenharmony_ci#define VSE_CVP_MODE_CTRL_HIP_CLK_SEL BIT(1) /* PMA (1) or fabric clock (0) */ 398c2ecf20Sopenharmony_ci#define VSE_CVP_MODE_CTRL_NUMCLKS_OFF 8 /* NUMCLKS bits offset */ 408c2ecf20Sopenharmony_ci#define VSE_CVP_MODE_CTRL_NUMCLKS_MASK GENMASK(15, 8) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define VSE_CVP_DATA 0x28 /* 32bit */ 438c2ecf20Sopenharmony_ci#define VSE_CVP_PROG_CTRL 0x2c /* 32bit */ 448c2ecf20Sopenharmony_ci#define VSE_CVP_PROG_CTRL_CONFIG BIT(0) 458c2ecf20Sopenharmony_ci#define VSE_CVP_PROG_CTRL_START_XFER BIT(1) 468c2ecf20Sopenharmony_ci#define VSE_CVP_PROG_CTRL_MASK GENMASK(1, 0) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define VSE_UNCOR_ERR_STATUS 0x34 /* 32bit */ 498c2ecf20Sopenharmony_ci#define VSE_UNCOR_ERR_CVP_CFG_ERR BIT(5) /* CVP_CONFIG_ERROR_LATCHED */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define V1_VSEC_OFFSET 0x200 /* Vendor Specific Offset V1 */ 528c2ecf20Sopenharmony_ci/* V2 Defines */ 538c2ecf20Sopenharmony_ci#define VSE_CVP_TX_CREDITS 0x49 /* 8bit */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define V2_CREDIT_TIMEOUT_US 20000 568c2ecf20Sopenharmony_ci#define V2_CHECK_CREDIT_US 10 578c2ecf20Sopenharmony_ci#define V2_POLL_TIMEOUT_US 1000000 588c2ecf20Sopenharmony_ci#define V2_USER_TIMEOUT_US 500000 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define V1_POLL_TIMEOUT_US 10 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define DRV_NAME "altera-cvp" 638c2ecf20Sopenharmony_ci#define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Write block sizes */ 668c2ecf20Sopenharmony_ci#define ALTERA_CVP_V1_SIZE 4 678c2ecf20Sopenharmony_ci#define ALTERA_CVP_V2_SIZE 4096 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Optional CvP config error status check for debugging */ 708c2ecf20Sopenharmony_cistatic bool altera_cvp_chkcfg; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct cvp_priv; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct altera_cvp_conf { 758c2ecf20Sopenharmony_ci struct fpga_manager *mgr; 768c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 778c2ecf20Sopenharmony_ci void __iomem *map; 788c2ecf20Sopenharmony_ci void (*write_data)(struct altera_cvp_conf *conf, 798c2ecf20Sopenharmony_ci u32 data); 808c2ecf20Sopenharmony_ci char mgr_name[64]; 818c2ecf20Sopenharmony_ci u8 numclks; 828c2ecf20Sopenharmony_ci u32 sent_packets; 838c2ecf20Sopenharmony_ci u32 vsec_offset; 848c2ecf20Sopenharmony_ci const struct cvp_priv *priv; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct cvp_priv { 888c2ecf20Sopenharmony_ci void (*switch_clk)(struct altera_cvp_conf *conf); 898c2ecf20Sopenharmony_ci int (*clear_state)(struct altera_cvp_conf *conf); 908c2ecf20Sopenharmony_ci int (*wait_credit)(struct fpga_manager *mgr, u32 blocks); 918c2ecf20Sopenharmony_ci size_t block_size; 928c2ecf20Sopenharmony_ci int poll_time_us; 938c2ecf20Sopenharmony_ci int user_time_us; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int altera_read_config_byte(struct altera_cvp_conf *conf, 978c2ecf20Sopenharmony_ci int where, u8 *val) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return pci_read_config_byte(conf->pci_dev, conf->vsec_offset + where, 1008c2ecf20Sopenharmony_ci val); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int altera_read_config_dword(struct altera_cvp_conf *conf, 1048c2ecf20Sopenharmony_ci int where, u32 *val) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci return pci_read_config_dword(conf->pci_dev, conf->vsec_offset + where, 1078c2ecf20Sopenharmony_ci val); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int altera_write_config_dword(struct altera_cvp_conf *conf, 1118c2ecf20Sopenharmony_ci int where, u32 val) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci return pci_write_config_dword(conf->pci_dev, conf->vsec_offset + where, 1148c2ecf20Sopenharmony_ci val); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic enum fpga_mgr_states altera_cvp_state(struct fpga_manager *mgr) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 1208c2ecf20Sopenharmony_ci u32 status; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_STATUS, &status); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (status & VSE_CVP_STATUS_CFG_DONE) 1258c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_OPERATING; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (status & VSE_CVP_STATUS_CVP_EN) 1288c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_POWER_UP; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_UNKNOWN; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void altera_cvp_write_data_iomem(struct altera_cvp_conf *conf, u32 val) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci writel(val, conf->map); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void altera_cvp_write_data_config(struct altera_cvp_conf *conf, u32 val) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci pci_write_config_dword(conf->pci_dev, conf->vsec_offset + VSE_CVP_DATA, 1418c2ecf20Sopenharmony_ci val); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* switches between CvP clock and internal clock */ 1458c2ecf20Sopenharmony_cistatic void altera_cvp_dummy_write(struct altera_cvp_conf *conf) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned int i; 1488c2ecf20Sopenharmony_ci u32 val; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* set 1 CVP clock cycle for every CVP Data Register Write */ 1518c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val); 1528c2ecf20Sopenharmony_ci val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; 1538c2ecf20Sopenharmony_ci val |= 1 << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; 1548c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < CVP_DUMMY_WR; i++) 1578c2ecf20Sopenharmony_ci conf->write_data(conf, 0); /* dummy data, could be any value */ 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask, 1618c2ecf20Sopenharmony_ci u32 status_val, int timeout_us) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci unsigned int retries; 1648c2ecf20Sopenharmony_ci u32 val; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci retries = timeout_us / 10; 1678c2ecf20Sopenharmony_ci if (timeout_us % 10) 1688c2ecf20Sopenharmony_ci retries++; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci do { 1718c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_STATUS, &val); 1728c2ecf20Sopenharmony_ci if ((val & status_mask) == status_val) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* use small usleep value to re-check and break early */ 1768c2ecf20Sopenharmony_ci usleep_range(10, 11); 1778c2ecf20Sopenharmony_ci } while (--retries); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int altera_cvp_chk_error(struct fpga_manager *mgr, size_t bytes) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 1858c2ecf20Sopenharmony_ci u32 val; 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* STEP 10 (optional) - check CVP_CONFIG_ERROR flag */ 1898c2ecf20Sopenharmony_ci ret = altera_read_config_dword(conf, VSE_CVP_STATUS, &val); 1908c2ecf20Sopenharmony_ci if (ret || (val & VSE_CVP_STATUS_CFG_ERR)) { 1918c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "CVP_CONFIG_ERROR after %zu bytes!\n", 1928c2ecf20Sopenharmony_ci bytes); 1938c2ecf20Sopenharmony_ci return -EPROTO; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * CvP Version2 Functions 2008c2ecf20Sopenharmony_ci * Recent Intel FPGAs use a credit mechanism to throttle incoming 2018c2ecf20Sopenharmony_ci * bitstreams and a different method of clearing the state. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int altera_cvp_v2_clear_state(struct altera_cvp_conf *conf) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci u32 val; 2078c2ecf20Sopenharmony_ci int ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Clear the START_XFER and CVP_CONFIG bits */ 2108c2ecf20Sopenharmony_ci ret = altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val); 2118c2ecf20Sopenharmony_ci if (ret) { 2128c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, 2138c2ecf20Sopenharmony_ci "Error reading CVP Program Control Register\n"); 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci val &= ~VSE_CVP_PROG_CTRL_MASK; 2188c2ecf20Sopenharmony_ci ret = altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, 2218c2ecf20Sopenharmony_ci "Error writing CVP Program Control Register\n"); 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0, 2268c2ecf20Sopenharmony_ci conf->priv->poll_time_us); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr, 2308c2ecf20Sopenharmony_ci u32 blocks) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci u32 timeout = V2_CREDIT_TIMEOUT_US / V2_CHECK_CREDIT_US; 2338c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 2348c2ecf20Sopenharmony_ci int ret; 2358c2ecf20Sopenharmony_ci u8 val; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci do { 2388c2ecf20Sopenharmony_ci ret = altera_read_config_byte(conf, VSE_CVP_TX_CREDITS, &val); 2398c2ecf20Sopenharmony_ci if (ret) { 2408c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, 2418c2ecf20Sopenharmony_ci "Error reading CVP Credit Register\n"); 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Return if there is space in FIFO */ 2468c2ecf20Sopenharmony_ci if (val - (u8)conf->sent_packets) 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = altera_cvp_chk_error(mgr, blocks * ALTERA_CVP_V2_SIZE); 2508c2ecf20Sopenharmony_ci if (ret) { 2518c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, 2528c2ecf20Sopenharmony_ci "CE Bit error credit reg[0x%x]:sent[0x%x]\n", 2538c2ecf20Sopenharmony_ci val, conf->sent_packets); 2548c2ecf20Sopenharmony_ci return -EAGAIN; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Limit the check credit byte traffic */ 2588c2ecf20Sopenharmony_ci usleep_range(V2_CHECK_CREDIT_US, V2_CHECK_CREDIT_US + 1); 2598c2ecf20Sopenharmony_ci } while (timeout--); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, "Timeout waiting for credit\n"); 2628c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int altera_cvp_send_block(struct altera_cvp_conf *conf, 2668c2ecf20Sopenharmony_ci const u32 *data, size_t len) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci u32 mask, words = len / sizeof(u32); 2698c2ecf20Sopenharmony_ci int i, remainder; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < words; i++) 2728c2ecf20Sopenharmony_ci conf->write_data(conf, *data++); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* write up to 3 trailing bytes, if any */ 2758c2ecf20Sopenharmony_ci remainder = len % sizeof(u32); 2768c2ecf20Sopenharmony_ci if (remainder) { 2778c2ecf20Sopenharmony_ci mask = BIT(remainder * 8) - 1; 2788c2ecf20Sopenharmony_ci if (mask) 2798c2ecf20Sopenharmony_ci conf->write_data(conf, *data & mask); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int altera_cvp_teardown(struct fpga_manager *mgr, 2868c2ecf20Sopenharmony_ci struct fpga_image_info *info) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 2898c2ecf20Sopenharmony_ci int ret; 2908c2ecf20Sopenharmony_ci u32 val; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* STEP 12 - reset START_XFER bit */ 2938c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val); 2948c2ecf20Sopenharmony_ci val &= ~VSE_CVP_PROG_CTRL_START_XFER; 2958c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* STEP 13 - reset CVP_CONFIG bit */ 2988c2ecf20Sopenharmony_ci val &= ~VSE_CVP_PROG_CTRL_CONFIG; 2998c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * STEP 14 3038c2ecf20Sopenharmony_ci * - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy 3048c2ecf20Sopenharmony_ci * writes to the HIP 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci if (conf->priv->switch_clk) 3078c2ecf20Sopenharmony_ci conf->priv->switch_clk(conf); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */ 3108c2ecf20Sopenharmony_ci ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0, 3118c2ecf20Sopenharmony_ci conf->priv->poll_time_us); 3128c2ecf20Sopenharmony_ci if (ret) 3138c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n"); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int altera_cvp_write_init(struct fpga_manager *mgr, 3198c2ecf20Sopenharmony_ci struct fpga_image_info *info, 3208c2ecf20Sopenharmony_ci const char *buf, size_t count) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 3238c2ecf20Sopenharmony_ci u32 iflags, val; 3248c2ecf20Sopenharmony_ci int ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci iflags = info ? info->flags : 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (iflags & FPGA_MGR_PARTIAL_RECONFIG) { 3298c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Determine allowed clock to data ratio */ 3348c2ecf20Sopenharmony_ci if (iflags & FPGA_MGR_COMPRESSED_BITSTREAM) 3358c2ecf20Sopenharmony_ci conf->numclks = 8; /* ratio for all compressed images */ 3368c2ecf20Sopenharmony_ci else if (iflags & FPGA_MGR_ENCRYPTED_BITSTREAM) 3378c2ecf20Sopenharmony_ci conf->numclks = 4; /* for uncompressed and encrypted images */ 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci conf->numclks = 1; /* for uncompressed and unencrypted images */ 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* STEP 1 - read CVP status and check CVP_EN flag */ 3428c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_STATUS, &val); 3438c2ecf20Sopenharmony_ci if (!(val & VSE_CVP_STATUS_CVP_EN)) { 3448c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val); 3458c2ecf20Sopenharmony_ci return -ENODEV; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (val & VSE_CVP_STATUS_CFG_RDY) { 3498c2ecf20Sopenharmony_ci dev_warn(&mgr->dev, "CvP already started, teardown first\n"); 3508c2ecf20Sopenharmony_ci ret = altera_cvp_teardown(mgr, info); 3518c2ecf20Sopenharmony_ci if (ret) 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * STEP 2 3578c2ecf20Sopenharmony_ci * - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned) 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci /* switch from fabric to PMA clock */ 3608c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val); 3618c2ecf20Sopenharmony_ci val |= VSE_CVP_MODE_CTRL_HIP_CLK_SEL; 3628c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* set CVP mode */ 3658c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val); 3668c2ecf20Sopenharmony_ci val |= VSE_CVP_MODE_CTRL_CVP_MODE; 3678c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * STEP 3 3718c2ecf20Sopenharmony_ci * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci if (conf->priv->switch_clk) 3748c2ecf20Sopenharmony_ci conf->priv->switch_clk(conf); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (conf->priv->clear_state) { 3778c2ecf20Sopenharmony_ci ret = conf->priv->clear_state(conf); 3788c2ecf20Sopenharmony_ci if (ret) { 3798c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Problem clearing out state\n"); 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci conf->sent_packets = 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* STEP 4 - set CVP_CONFIG bit */ 3878c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val); 3888c2ecf20Sopenharmony_ci /* request control block to begin transfer using CVP */ 3898c2ecf20Sopenharmony_ci val |= VSE_CVP_PROG_CTRL_CONFIG; 3908c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* STEP 5 - poll CVP_CONFIG READY for 1 with timeout */ 3938c2ecf20Sopenharmony_ci ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 3948c2ecf20Sopenharmony_ci VSE_CVP_STATUS_CFG_RDY, 3958c2ecf20Sopenharmony_ci conf->priv->poll_time_us); 3968c2ecf20Sopenharmony_ci if (ret) { 3978c2ecf20Sopenharmony_ci dev_warn(&mgr->dev, "CFG_RDY == 1 timeout\n"); 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * STEP 6 4038c2ecf20Sopenharmony_ci * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci if (conf->priv->switch_clk) 4068c2ecf20Sopenharmony_ci conf->priv->switch_clk(conf); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (altera_cvp_chkcfg) { 4098c2ecf20Sopenharmony_ci ret = altera_cvp_chk_error(mgr, 0); 4108c2ecf20Sopenharmony_ci if (ret) { 4118c2ecf20Sopenharmony_ci dev_warn(&mgr->dev, "CFG_RDY == 1 timeout\n"); 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* STEP 7 - set START_XFER */ 4178c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_PROG_CTRL, &val); 4188c2ecf20Sopenharmony_ci val |= VSE_CVP_PROG_CTRL_START_XFER; 4198c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_PROG_CTRL, val); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */ 4228c2ecf20Sopenharmony_ci if (conf->priv->switch_clk) { 4238c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val); 4248c2ecf20Sopenharmony_ci val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; 4258c2ecf20Sopenharmony_ci val |= conf->numclks << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; 4268c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int altera_cvp_write(struct fpga_manager *mgr, const char *buf, 4328c2ecf20Sopenharmony_ci size_t count) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 4358c2ecf20Sopenharmony_ci size_t done, remaining, len; 4368c2ecf20Sopenharmony_ci const u32 *data; 4378c2ecf20Sopenharmony_ci int status = 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* STEP 9 - write 32-bit data from RBF file to CVP data register */ 4408c2ecf20Sopenharmony_ci data = (u32 *)buf; 4418c2ecf20Sopenharmony_ci remaining = count; 4428c2ecf20Sopenharmony_ci done = 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci while (remaining) { 4458c2ecf20Sopenharmony_ci /* Use credit throttling if available */ 4468c2ecf20Sopenharmony_ci if (conf->priv->wait_credit) { 4478c2ecf20Sopenharmony_ci status = conf->priv->wait_credit(mgr, done); 4488c2ecf20Sopenharmony_ci if (status) { 4498c2ecf20Sopenharmony_ci dev_err(&conf->pci_dev->dev, 4508c2ecf20Sopenharmony_ci "Wait Credit ERR: 0x%x\n", status); 4518c2ecf20Sopenharmony_ci return status; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci len = min(conf->priv->block_size, remaining); 4568c2ecf20Sopenharmony_ci altera_cvp_send_block(conf, data, len); 4578c2ecf20Sopenharmony_ci data += len / sizeof(u32); 4588c2ecf20Sopenharmony_ci done += len; 4598c2ecf20Sopenharmony_ci remaining -= len; 4608c2ecf20Sopenharmony_ci conf->sent_packets++; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * STEP 10 (optional) and STEP 11 4648c2ecf20Sopenharmony_ci * - check error flag 4658c2ecf20Sopenharmony_ci * - loop until data transfer completed 4668c2ecf20Sopenharmony_ci * Config images can be huge (more than 40 MiB), so 4678c2ecf20Sopenharmony_ci * only check after a new 4k data block has been written. 4688c2ecf20Sopenharmony_ci * This reduces the number of checks and speeds up the 4698c2ecf20Sopenharmony_ci * configuration process. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci if (altera_cvp_chkcfg && !(done % SZ_4K)) { 4728c2ecf20Sopenharmony_ci status = altera_cvp_chk_error(mgr, done); 4738c2ecf20Sopenharmony_ci if (status < 0) 4748c2ecf20Sopenharmony_ci return status; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (altera_cvp_chkcfg) 4798c2ecf20Sopenharmony_ci status = altera_cvp_chk_error(mgr, count); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return status; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int altera_cvp_write_complete(struct fpga_manager *mgr, 4858c2ecf20Sopenharmony_ci struct fpga_image_info *info) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 4888c2ecf20Sopenharmony_ci u32 mask, val; 4898c2ecf20Sopenharmony_ci int ret; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret = altera_cvp_teardown(mgr, info); 4928c2ecf20Sopenharmony_ci if (ret) 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */ 4968c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_UNCOR_ERR_STATUS, &val); 4978c2ecf20Sopenharmony_ci if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) { 4988c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n"); 4998c2ecf20Sopenharmony_ci return -EPROTO; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */ 5038c2ecf20Sopenharmony_ci altera_read_config_dword(conf, VSE_CVP_MODE_CTRL, &val); 5048c2ecf20Sopenharmony_ci val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL; 5058c2ecf20Sopenharmony_ci val &= ~VSE_CVP_MODE_CTRL_CVP_MODE; 5068c2ecf20Sopenharmony_ci altera_write_config_dword(conf, VSE_CVP_MODE_CTRL, val); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */ 5098c2ecf20Sopenharmony_ci mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE; 5108c2ecf20Sopenharmony_ci ret = altera_cvp_wait_status(conf, mask, mask, 5118c2ecf20Sopenharmony_ci conf->priv->user_time_us); 5128c2ecf20Sopenharmony_ci if (ret) 5138c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n"); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic const struct fpga_manager_ops altera_cvp_ops = { 5198c2ecf20Sopenharmony_ci .state = altera_cvp_state, 5208c2ecf20Sopenharmony_ci .write_init = altera_cvp_write_init, 5218c2ecf20Sopenharmony_ci .write = altera_cvp_write, 5228c2ecf20Sopenharmony_ci .write_complete = altera_cvp_write_complete, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic const struct cvp_priv cvp_priv_v1 = { 5268c2ecf20Sopenharmony_ci .switch_clk = altera_cvp_dummy_write, 5278c2ecf20Sopenharmony_ci .block_size = ALTERA_CVP_V1_SIZE, 5288c2ecf20Sopenharmony_ci .poll_time_us = V1_POLL_TIMEOUT_US, 5298c2ecf20Sopenharmony_ci .user_time_us = TIMEOUT_US, 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const struct cvp_priv cvp_priv_v2 = { 5338c2ecf20Sopenharmony_ci .clear_state = altera_cvp_v2_clear_state, 5348c2ecf20Sopenharmony_ci .wait_credit = altera_cvp_v2_wait_for_credit, 5358c2ecf20Sopenharmony_ci .block_size = ALTERA_CVP_V2_SIZE, 5368c2ecf20Sopenharmony_ci .poll_time_us = V2_POLL_TIMEOUT_US, 5378c2ecf20Sopenharmony_ci .user_time_us = V2_USER_TIMEOUT_US, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic ssize_t chkcfg_show(struct device_driver *dev, char *buf) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci return snprintf(buf, 3, "%d\n", altera_cvp_chkcfg); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic ssize_t chkcfg_store(struct device_driver *drv, const char *buf, 5468c2ecf20Sopenharmony_ci size_t count) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci int ret; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci ret = kstrtobool(buf, &altera_cvp_chkcfg); 5518c2ecf20Sopenharmony_ci if (ret) 5528c2ecf20Sopenharmony_ci return ret; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return count; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(chkcfg); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int altera_cvp_probe(struct pci_dev *pdev, 5608c2ecf20Sopenharmony_ci const struct pci_device_id *dev_id); 5618c2ecf20Sopenharmony_cistatic void altera_cvp_remove(struct pci_dev *pdev); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic struct pci_device_id altera_cvp_id_tbl[] = { 5648c2ecf20Sopenharmony_ci { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, 5658c2ecf20Sopenharmony_ci { } 5668c2ecf20Sopenharmony_ci}; 5678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, altera_cvp_id_tbl); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic struct pci_driver altera_cvp_driver = { 5708c2ecf20Sopenharmony_ci .name = DRV_NAME, 5718c2ecf20Sopenharmony_ci .id_table = altera_cvp_id_tbl, 5728c2ecf20Sopenharmony_ci .probe = altera_cvp_probe, 5738c2ecf20Sopenharmony_ci .remove = altera_cvp_remove, 5748c2ecf20Sopenharmony_ci}; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int altera_cvp_probe(struct pci_dev *pdev, 5778c2ecf20Sopenharmony_ci const struct pci_device_id *dev_id) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf; 5808c2ecf20Sopenharmony_ci struct fpga_manager *mgr; 5818c2ecf20Sopenharmony_ci int ret, offset; 5828c2ecf20Sopenharmony_ci u16 cmd, val; 5838c2ecf20Sopenharmony_ci u32 regval; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Discover the Vendor Specific Offset for this device */ 5868c2ecf20Sopenharmony_ci offset = pci_find_next_ext_capability(pdev, 0, PCI_EXT_CAP_ID_VNDR); 5878c2ecf20Sopenharmony_ci if (!offset) { 5888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No Vendor Specific Offset.\n"); 5898c2ecf20Sopenharmony_ci return -ENODEV; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * First check if this is the expected FPGA device. PCI config 5948c2ecf20Sopenharmony_ci * space access works without enabling the PCI device, memory 5958c2ecf20Sopenharmony_ci * space access is enabled further down. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci pci_read_config_word(pdev, offset + VSE_PCIE_EXT_CAP_ID, &val); 5988c2ecf20Sopenharmony_ci if (val != VSE_PCIE_EXT_CAP_ID_VAL) { 5998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Wrong EXT_CAP_ID value 0x%x\n", val); 6008c2ecf20Sopenharmony_ci return -ENODEV; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, offset + VSE_CVP_STATUS, ®val); 6048c2ecf20Sopenharmony_ci if (!(regval & VSE_CVP_STATUS_CVP_EN)) { 6058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 6068c2ecf20Sopenharmony_ci "CVP is disabled for this device: CVP_STATUS Reg 0x%x\n", 6078c2ecf20Sopenharmony_ci regval); 6088c2ecf20Sopenharmony_ci return -ENODEV; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL); 6128c2ecf20Sopenharmony_ci if (!conf) 6138c2ecf20Sopenharmony_ci return -ENOMEM; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci conf->vsec_offset = offset; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* 6188c2ecf20Sopenharmony_ci * Enable memory BAR access. We cannot use pci_enable_device() here 6198c2ecf20Sopenharmony_ci * because it will make the driver unusable with FPGA devices that 6208c2ecf20Sopenharmony_ci * have additional big IOMEM resources (e.g. 4GiB BARs) on 32-bit 6218c2ecf20Sopenharmony_ci * platform. Such BARs will not have an assigned address range and 6228c2ecf20Sopenharmony_ci * pci_enable_device() will fail, complaining about not claimed BAR, 6238c2ecf20Sopenharmony_ci * even if the concerned BAR is not needed for FPGA configuration 6248c2ecf20Sopenharmony_ci * at all. Thus, enable the device via PCI config space command. 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &cmd); 6278c2ecf20Sopenharmony_ci if (!(cmd & PCI_COMMAND_MEMORY)) { 6288c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MEMORY; 6298c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ret = pci_request_region(pdev, CVP_BAR, "CVP"); 6338c2ecf20Sopenharmony_ci if (ret) { 6348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Requesting CVP BAR region failed\n"); 6358c2ecf20Sopenharmony_ci goto err_disable; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci conf->pci_dev = pdev; 6398c2ecf20Sopenharmony_ci conf->write_data = altera_cvp_write_data_iomem; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (conf->vsec_offset == V1_VSEC_OFFSET) 6428c2ecf20Sopenharmony_ci conf->priv = &cvp_priv_v1; 6438c2ecf20Sopenharmony_ci else 6448c2ecf20Sopenharmony_ci conf->priv = &cvp_priv_v2; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci conf->map = pci_iomap(pdev, CVP_BAR, 0); 6478c2ecf20Sopenharmony_ci if (!conf->map) { 6488c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Mapping CVP BAR failed\n"); 6498c2ecf20Sopenharmony_ci conf->write_data = altera_cvp_write_data_config; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", 6538c2ecf20Sopenharmony_ci ALTERA_CVP_MGR_NAME, pci_name(pdev)); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name, 6568c2ecf20Sopenharmony_ci &altera_cvp_ops, conf); 6578c2ecf20Sopenharmony_ci if (!mgr) { 6588c2ecf20Sopenharmony_ci ret = -ENOMEM; 6598c2ecf20Sopenharmony_ci goto err_unmap; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, mgr); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = fpga_mgr_register(mgr); 6658c2ecf20Sopenharmony_ci if (ret) 6668c2ecf20Sopenharmony_ci goto err_unmap; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cierr_unmap: 6718c2ecf20Sopenharmony_ci if (conf->map) 6728c2ecf20Sopenharmony_ci pci_iounmap(pdev, conf->map); 6738c2ecf20Sopenharmony_ci pci_release_region(pdev, CVP_BAR); 6748c2ecf20Sopenharmony_cierr_disable: 6758c2ecf20Sopenharmony_ci cmd &= ~PCI_COMMAND_MEMORY; 6768c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 6778c2ecf20Sopenharmony_ci return ret; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic void altera_cvp_remove(struct pci_dev *pdev) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct fpga_manager *mgr = pci_get_drvdata(pdev); 6838c2ecf20Sopenharmony_ci struct altera_cvp_conf *conf = mgr->priv; 6848c2ecf20Sopenharmony_ci u16 cmd; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci fpga_mgr_unregister(mgr); 6878c2ecf20Sopenharmony_ci if (conf->map) 6888c2ecf20Sopenharmony_ci pci_iounmap(pdev, conf->map); 6898c2ecf20Sopenharmony_ci pci_release_region(pdev, CVP_BAR); 6908c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &cmd); 6918c2ecf20Sopenharmony_ci cmd &= ~PCI_COMMAND_MEMORY; 6928c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int __init altera_cvp_init(void) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci int ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = pci_register_driver(&altera_cvp_driver); 7008c2ecf20Sopenharmony_ci if (ret) 7018c2ecf20Sopenharmony_ci return ret; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ret = driver_create_file(&altera_cvp_driver.driver, 7048c2ecf20Sopenharmony_ci &driver_attr_chkcfg); 7058c2ecf20Sopenharmony_ci if (ret) 7068c2ecf20Sopenharmony_ci pr_warn("Can't create sysfs chkcfg file\n"); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void __exit altera_cvp_exit(void) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); 7148c2ecf20Sopenharmony_ci pci_unregister_driver(&altera_cvp_driver); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cimodule_init(altera_cvp_init); 7188c2ecf20Sopenharmony_cimodule_exit(altera_cvp_exit); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); 7228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Module to load Altera FPGA over CvP"); 723