162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Endpoint Function Driver to implement Non-Transparent Bridge functionality 462306a36Sopenharmony_ci * Between PCI RC and EP 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2020 Texas Instruments 762306a36Sopenharmony_ci * Copyright (C) 2022 NXP 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on pci-epf-ntb.c 1062306a36Sopenharmony_ci * Author: Frank Li <Frank.Li@nxp.com> 1162306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * +------------+ +---------------------------------------+ 1662306a36Sopenharmony_ci * | | | | 1762306a36Sopenharmony_ci * +------------+ | +--------------+ 1862306a36Sopenharmony_ci * | NTB | | | NTB | 1962306a36Sopenharmony_ci * | NetDev | | | NetDev | 2062306a36Sopenharmony_ci * +------------+ | +--------------+ 2162306a36Sopenharmony_ci * | NTB | | | NTB | 2262306a36Sopenharmony_ci * | Transfer | | | Transfer | 2362306a36Sopenharmony_ci * +------------+ | +--------------+ 2462306a36Sopenharmony_ci * | | | | | 2562306a36Sopenharmony_ci * | PCI NTB | | | | 2662306a36Sopenharmony_ci * | EPF | | | | 2762306a36Sopenharmony_ci * | Driver | | | PCI Virtual | 2862306a36Sopenharmony_ci * | | +---------------+ | NTB Driver | 2962306a36Sopenharmony_ci * | | | PCI EP NTB |<------>| | 3062306a36Sopenharmony_ci * | | | FN Driver | | | 3162306a36Sopenharmony_ci * +------------+ +---------------+ +--------------+ 3262306a36Sopenharmony_ci * | | | | | | 3362306a36Sopenharmony_ci * | PCI Bus | <-----> | PCI EP Bus | | Virtual PCI | 3462306a36Sopenharmony_ci * | | PCI | | | Bus | 3562306a36Sopenharmony_ci * +------------+ +---------------+--------+--------------+ 3662306a36Sopenharmony_ci * PCIe Root Port PCI EP 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <linux/io.h> 4162306a36Sopenharmony_ci#include <linux/module.h> 4262306a36Sopenharmony_ci#include <linux/slab.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/pci-epc.h> 4562306a36Sopenharmony_ci#include <linux/pci-epf.h> 4662306a36Sopenharmony_ci#include <linux/ntb.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct workqueue_struct *kpcintb_workqueue; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define COMMAND_CONFIGURE_DOORBELL 1 5162306a36Sopenharmony_ci#define COMMAND_TEARDOWN_DOORBELL 2 5262306a36Sopenharmony_ci#define COMMAND_CONFIGURE_MW 3 5362306a36Sopenharmony_ci#define COMMAND_TEARDOWN_MW 4 5462306a36Sopenharmony_ci#define COMMAND_LINK_UP 5 5562306a36Sopenharmony_ci#define COMMAND_LINK_DOWN 6 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define COMMAND_STATUS_OK 1 5862306a36Sopenharmony_ci#define COMMAND_STATUS_ERROR 2 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define LINK_STATUS_UP BIT(0) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define SPAD_COUNT 64 6362306a36Sopenharmony_ci#define DB_COUNT 4 6462306a36Sopenharmony_ci#define NTB_MW_OFFSET 2 6562306a36Sopenharmony_ci#define DB_COUNT_MASK GENMASK(15, 0) 6662306a36Sopenharmony_ci#define MSIX_ENABLE BIT(16) 6762306a36Sopenharmony_ci#define MAX_DB_COUNT 32 6862306a36Sopenharmony_ci#define MAX_MW 4 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum epf_ntb_bar { 7162306a36Sopenharmony_ci BAR_CONFIG, 7262306a36Sopenharmony_ci BAR_DB, 7362306a36Sopenharmony_ci BAR_MW0, 7462306a36Sopenharmony_ci BAR_MW1, 7562306a36Sopenharmony_ci BAR_MW2, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * +--------------------------------------------------+ Base 8062306a36Sopenharmony_ci * | | 8162306a36Sopenharmony_ci * | | 8262306a36Sopenharmony_ci * | | 8362306a36Sopenharmony_ci * | Common Control Register | 8462306a36Sopenharmony_ci * | | 8562306a36Sopenharmony_ci * | | 8662306a36Sopenharmony_ci * | | 8762306a36Sopenharmony_ci * +-----------------------+--------------------------+ Base+spad_offset 8862306a36Sopenharmony_ci * | | | 8962306a36Sopenharmony_ci * | Peer Spad Space | Spad Space | 9062306a36Sopenharmony_ci * | | | 9162306a36Sopenharmony_ci * | | | 9262306a36Sopenharmony_ci * +-----------------------+--------------------------+ Base+spad_offset 9362306a36Sopenharmony_ci * | | | +spad_count * 4 9462306a36Sopenharmony_ci * | | | 9562306a36Sopenharmony_ci * | Spad Space | Peer Spad Space | 9662306a36Sopenharmony_ci * | | | 9762306a36Sopenharmony_ci * +-----------------------+--------------------------+ 9862306a36Sopenharmony_ci * Virtual PCI PCIe Endpoint 9962306a36Sopenharmony_ci * NTB Driver NTB Driver 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistruct epf_ntb_ctrl { 10262306a36Sopenharmony_ci u32 command; 10362306a36Sopenharmony_ci u32 argument; 10462306a36Sopenharmony_ci u16 command_status; 10562306a36Sopenharmony_ci u16 link_status; 10662306a36Sopenharmony_ci u32 topology; 10762306a36Sopenharmony_ci u64 addr; 10862306a36Sopenharmony_ci u64 size; 10962306a36Sopenharmony_ci u32 num_mws; 11062306a36Sopenharmony_ci u32 reserved; 11162306a36Sopenharmony_ci u32 spad_offset; 11262306a36Sopenharmony_ci u32 spad_count; 11362306a36Sopenharmony_ci u32 db_entry_size; 11462306a36Sopenharmony_ci u32 db_data[MAX_DB_COUNT]; 11562306a36Sopenharmony_ci u32 db_offset[MAX_DB_COUNT]; 11662306a36Sopenharmony_ci} __packed; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct epf_ntb { 11962306a36Sopenharmony_ci struct ntb_dev ntb; 12062306a36Sopenharmony_ci struct pci_epf *epf; 12162306a36Sopenharmony_ci struct config_group group; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci u32 num_mws; 12462306a36Sopenharmony_ci u32 db_count; 12562306a36Sopenharmony_ci u32 spad_count; 12662306a36Sopenharmony_ci u64 mws_size[MAX_MW]; 12762306a36Sopenharmony_ci u64 db; 12862306a36Sopenharmony_ci u32 vbus_number; 12962306a36Sopenharmony_ci u16 vntb_pid; 13062306a36Sopenharmony_ci u16 vntb_vid; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci bool linkup; 13362306a36Sopenharmony_ci u32 spad_size; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci enum pci_barno epf_ntb_bar[6]; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci struct epf_ntb_ctrl *reg; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci u32 *epf_db; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci phys_addr_t vpci_mw_phy[MAX_MW]; 14262306a36Sopenharmony_ci void __iomem *vpci_mw_addr[MAX_MW]; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci struct delayed_work cmd_handler; 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group) 14862306a36Sopenharmony_ci#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct pci_epf_header epf_ntb_header = { 15162306a36Sopenharmony_ci .vendorid = PCI_ANY_ID, 15262306a36Sopenharmony_ci .deviceid = PCI_ANY_ID, 15362306a36Sopenharmony_ci .baseclass_code = PCI_BASE_CLASS_MEMORY, 15462306a36Sopenharmony_ci .interrupt_pin = PCI_INTERRUPT_INTA, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/** 15862306a36Sopenharmony_ci * epf_ntb_link_up() - Raise link_up interrupt to Virtual Host (VHOST) 15962306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 16062306a36Sopenharmony_ci * @link_up: true or false indicating Link is UP or Down 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Once NTB function in HOST invoke ntb_link_enable(), 16362306a36Sopenharmony_ci * this NTB function driver will trigger a link event to VHOST. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (link_up) 17062306a36Sopenharmony_ci ntb->reg->link_status |= LINK_STATUS_UP; 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci ntb->reg->link_status &= ~LINK_STATUS_UP; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ntb_link_event(&ntb->ntb); 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/** 17962306a36Sopenharmony_ci * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST 18062306a36Sopenharmony_ci * to access the memory window of HOST 18162306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 18262306a36Sopenharmony_ci * @mw: Index of the memory window (either 0, 1, 2 or 3) 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * EP Outbound Window 18562306a36Sopenharmony_ci * +--------+ +-----------+ 18662306a36Sopenharmony_ci * | | | | 18762306a36Sopenharmony_ci * | | | | 18862306a36Sopenharmony_ci * | | | | 18962306a36Sopenharmony_ci * | | | | 19062306a36Sopenharmony_ci * | | +-----------+ 19162306a36Sopenharmony_ci * | Virtual| | Memory Win| 19262306a36Sopenharmony_ci * | NTB | -----------> | | 19362306a36Sopenharmony_ci * | Driver | | | 19462306a36Sopenharmony_ci * | | +-----------+ 19562306a36Sopenharmony_ci * | | | | 19662306a36Sopenharmony_ci * | | | | 19762306a36Sopenharmony_ci * +--------+ +-----------+ 19862306a36Sopenharmony_ci * VHOST PCI EP 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic int epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci phys_addr_t phys_addr; 20562306a36Sopenharmony_ci u8 func_no, vfunc_no; 20662306a36Sopenharmony_ci u64 addr, size; 20762306a36Sopenharmony_ci int ret = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci phys_addr = ntb->vpci_mw_phy[mw]; 21062306a36Sopenharmony_ci addr = ntb->reg->addr; 21162306a36Sopenharmony_ci size = ntb->reg->size; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci func_no = ntb->epf->func_no; 21462306a36Sopenharmony_ci vfunc_no = ntb->epf->vfunc_no; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = pci_epc_map_addr(ntb->epf->epc, func_no, vfunc_no, phys_addr, addr, size); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci dev_err(&ntb->epf->epc->dev, 21962306a36Sopenharmony_ci "Failed to map memory window %d address\n", mw); 22062306a36Sopenharmony_ci return ret; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * epf_ntb_teardown_mw() - Teardown the configured OB ATU 22562306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 22662306a36Sopenharmony_ci * @mw: Index of the memory window (either 0, 1, 2 or 3) 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using 22962306a36Sopenharmony_ci * pci_epc_unmap_addr() 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_cistatic void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci pci_epc_unmap_addr(ntb->epf->epc, 23462306a36Sopenharmony_ci ntb->epf->func_no, 23562306a36Sopenharmony_ci ntb->epf->vfunc_no, 23662306a36Sopenharmony_ci ntb->vpci_mw_phy[mw]); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * epf_ntb_cmd_handler() - Handle commands provided by the NTB HOST 24162306a36Sopenharmony_ci * @work: work_struct for the epf_ntb_epc 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Workqueue function that gets invoked for the two epf_ntb_epc 24462306a36Sopenharmony_ci * periodically (once every 5ms) to see if it has received any commands 24562306a36Sopenharmony_ci * from NTB HOST. The HOST can send commands to configure doorbell or 24662306a36Sopenharmony_ci * configure memory window or to update link status. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic void epf_ntb_cmd_handler(struct work_struct *work) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct epf_ntb_ctrl *ctrl; 25162306a36Sopenharmony_ci u32 command, argument; 25262306a36Sopenharmony_ci struct epf_ntb *ntb; 25362306a36Sopenharmony_ci struct device *dev; 25462306a36Sopenharmony_ci int ret; 25562306a36Sopenharmony_ci int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ntb = container_of(work, struct epf_ntb, cmd_handler.work); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci for (i = 1; i < ntb->db_count; i++) { 26062306a36Sopenharmony_ci if (ntb->epf_db[i]) { 26162306a36Sopenharmony_ci ntb->db |= 1 << (i - 1); 26262306a36Sopenharmony_ci ntb_db_event(&ntb->ntb, i); 26362306a36Sopenharmony_ci ntb->epf_db[i] = 0; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ctrl = ntb->reg; 26862306a36Sopenharmony_ci command = ctrl->command; 26962306a36Sopenharmony_ci if (!command) 27062306a36Sopenharmony_ci goto reset_handler; 27162306a36Sopenharmony_ci argument = ctrl->argument; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ctrl->command = 0; 27462306a36Sopenharmony_ci ctrl->argument = 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ctrl = ntb->reg; 27762306a36Sopenharmony_ci dev = &ntb->epf->dev; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci switch (command) { 28062306a36Sopenharmony_ci case COMMAND_CONFIGURE_DOORBELL: 28162306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case COMMAND_TEARDOWN_DOORBELL: 28462306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case COMMAND_CONFIGURE_MW: 28762306a36Sopenharmony_ci ret = epf_ntb_configure_mw(ntb, argument); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_ERROR; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case COMMAND_TEARDOWN_MW: 29462306a36Sopenharmony_ci epf_ntb_teardown_mw(ntb, argument); 29562306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case COMMAND_LINK_UP: 29862306a36Sopenharmony_ci ntb->linkup = true; 29962306a36Sopenharmony_ci ret = epf_ntb_link_up(ntb, true); 30062306a36Sopenharmony_ci if (ret < 0) 30162306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_ERROR; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 30462306a36Sopenharmony_ci goto reset_handler; 30562306a36Sopenharmony_ci case COMMAND_LINK_DOWN: 30662306a36Sopenharmony_ci ntb->linkup = false; 30762306a36Sopenharmony_ci ret = epf_ntb_link_up(ntb, false); 30862306a36Sopenharmony_ci if (ret < 0) 30962306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_ERROR; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci ctrl->command_status = COMMAND_STATUS_OK; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci dev_err(dev, "UNKNOWN command: %d\n", command); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cireset_handler: 31962306a36Sopenharmony_ci queue_delayed_work(kpcintb_workqueue, &ntb->cmd_handler, 32062306a36Sopenharmony_ci msecs_to_jiffies(5)); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/** 32462306a36Sopenharmony_ci * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR 32562306a36Sopenharmony_ci * @ntb: EPC associated with one of the HOST which holds peer's outbound 32662306a36Sopenharmony_ci * address. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and 32962306a36Sopenharmony_ci * self scratchpad region (removes inbound ATU configuration). While BAR0 is 33062306a36Sopenharmony_ci * the default self scratchpad BAR, an NTB could have other BARs for self 33162306a36Sopenharmony_ci * scratchpad (because of reserved BARs). This function can get the exact BAR 33262306a36Sopenharmony_ci * used for self scratchpad from epf_ntb_bar[BAR_CONFIG]. 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Please note the self scratchpad region and config region is combined to 33562306a36Sopenharmony_ci * a single region and mapped using the same BAR. Also note VHOST's peer 33662306a36Sopenharmony_ci * scratchpad is HOST's self scratchpad. 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Returns: void 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_cistatic void epf_ntb_config_sspad_bar_clear(struct epf_ntb *ntb) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct pci_epf_bar *epf_bar; 34362306a36Sopenharmony_ci enum pci_barno barno; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_CONFIG]; 34662306a36Sopenharmony_ci epf_bar = &ntb->epf->bar[barno]; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR 35362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Map BAR0 of EP CONTROLLER which contains the VHOST's config and 35662306a36Sopenharmony_ci * self scratchpad region. 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * Please note the self scratchpad region and config region is combined to 35962306a36Sopenharmony_ci * a single region and mapped using the same BAR. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistatic int epf_ntb_config_sspad_bar_set(struct epf_ntb *ntb) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct pci_epf_bar *epf_bar; 36662306a36Sopenharmony_ci enum pci_barno barno; 36762306a36Sopenharmony_ci u8 func_no, vfunc_no; 36862306a36Sopenharmony_ci struct device *dev; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci dev = &ntb->epf->dev; 37262306a36Sopenharmony_ci func_no = ntb->epf->func_no; 37362306a36Sopenharmony_ci vfunc_no = ntb->epf->vfunc_no; 37462306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_CONFIG]; 37562306a36Sopenharmony_ci epf_bar = &ntb->epf->bar[barno]; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = pci_epc_set_bar(ntb->epf->epc, func_no, vfunc_no, epf_bar); 37862306a36Sopenharmony_ci if (ret) { 37962306a36Sopenharmony_ci dev_err(dev, "inft: Config/Status/SPAD BAR set failed\n"); 38062306a36Sopenharmony_ci return ret; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * epf_ntb_config_spad_bar_free() - Free the physical memory associated with 38762306a36Sopenharmony_ci * config + scratchpad region 38862306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci enum pci_barno barno; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_CONFIG]; 39562306a36Sopenharmony_ci pci_epf_free_space(ntb->epf, ntb->reg, barno, 0); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/** 39962306a36Sopenharmony_ci * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad 40062306a36Sopenharmony_ci * region 40162306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * Allocate the Local Memory mentioned in the above diagram. The size of 40462306a36Sopenharmony_ci * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION 40562306a36Sopenharmony_ci * is obtained from "spad-count" configfs entry. 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistatic int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci size_t align; 41262306a36Sopenharmony_ci enum pci_barno barno; 41362306a36Sopenharmony_ci struct epf_ntb_ctrl *ctrl; 41462306a36Sopenharmony_ci u32 spad_size, ctrl_size; 41562306a36Sopenharmony_ci u64 size; 41662306a36Sopenharmony_ci struct pci_epf *epf = ntb->epf; 41762306a36Sopenharmony_ci struct device *dev = &epf->dev; 41862306a36Sopenharmony_ci u32 spad_count; 41962306a36Sopenharmony_ci void *base; 42062306a36Sopenharmony_ci int i; 42162306a36Sopenharmony_ci const struct pci_epc_features *epc_features = pci_epc_get_features(epf->epc, 42262306a36Sopenharmony_ci epf->func_no, 42362306a36Sopenharmony_ci epf->vfunc_no); 42462306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_CONFIG]; 42562306a36Sopenharmony_ci size = epc_features->bar_fixed_size[barno]; 42662306a36Sopenharmony_ci align = epc_features->align; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if ((!IS_ALIGNED(size, align))) 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci spad_count = ntb->spad_count; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ctrl_size = sizeof(struct epf_ntb_ctrl); 43462306a36Sopenharmony_ci spad_size = 2 * spad_count * sizeof(u32); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!align) { 43762306a36Sopenharmony_ci ctrl_size = roundup_pow_of_two(ctrl_size); 43862306a36Sopenharmony_ci spad_size = roundup_pow_of_two(spad_size); 43962306a36Sopenharmony_ci } else { 44062306a36Sopenharmony_ci ctrl_size = ALIGN(ctrl_size, align); 44162306a36Sopenharmony_ci spad_size = ALIGN(spad_size, align); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (!size) 44562306a36Sopenharmony_ci size = ctrl_size + spad_size; 44662306a36Sopenharmony_ci else if (size < ctrl_size + spad_size) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci base = pci_epf_alloc_space(epf, size, barno, align, 0); 45062306a36Sopenharmony_ci if (!base) { 45162306a36Sopenharmony_ci dev_err(dev, "Config/Status/SPAD alloc region fail\n"); 45262306a36Sopenharmony_ci return -ENOMEM; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ntb->reg = base; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ctrl = ntb->reg; 45862306a36Sopenharmony_ci ctrl->spad_offset = ctrl_size; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ctrl->spad_count = spad_count; 46162306a36Sopenharmony_ci ctrl->num_mws = ntb->num_mws; 46262306a36Sopenharmony_ci ntb->spad_size = spad_size; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ctrl->db_entry_size = sizeof(u32); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci for (i = 0; i < ntb->db_count; i++) { 46762306a36Sopenharmony_ci ntb->reg->db_data[i] = 1 + i; 46862306a36Sopenharmony_ci ntb->reg->db_offset[i] = 0; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/** 47562306a36Sopenharmony_ci * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capability 47662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * Configure MSI/MSI-X capability for each interface with number of 47962306a36Sopenharmony_ci * interrupts equal to "db_count" configfs entry. 48062306a36Sopenharmony_ci * 48162306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic int epf_ntb_configure_interrupt(struct epf_ntb *ntb) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci const struct pci_epc_features *epc_features; 48662306a36Sopenharmony_ci struct device *dev; 48762306a36Sopenharmony_ci u32 db_count; 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci dev = &ntb->epf->dev; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!(epc_features->msix_capable || epc_features->msi_capable)) { 49562306a36Sopenharmony_ci dev_err(dev, "MSI or MSI-X is required for doorbell\n"); 49662306a36Sopenharmony_ci return -EINVAL; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci db_count = ntb->db_count; 50062306a36Sopenharmony_ci if (db_count > MAX_DB_COUNT) { 50162306a36Sopenharmony_ci dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ntb->db_count = db_count; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (epc_features->msi_capable) { 50862306a36Sopenharmony_ci ret = pci_epc_set_msi(ntb->epf->epc, 50962306a36Sopenharmony_ci ntb->epf->func_no, 51062306a36Sopenharmony_ci ntb->epf->vfunc_no, 51162306a36Sopenharmony_ci 16); 51262306a36Sopenharmony_ci if (ret) { 51362306a36Sopenharmony_ci dev_err(dev, "MSI configuration failed\n"); 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * epf_ntb_db_bar_init() - Configure Doorbell window BARs 52362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_cistatic int epf_ntb_db_bar_init(struct epf_ntb *ntb) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci const struct pci_epc_features *epc_features; 53062306a36Sopenharmony_ci u32 align; 53162306a36Sopenharmony_ci struct device *dev = &ntb->epf->dev; 53262306a36Sopenharmony_ci int ret; 53362306a36Sopenharmony_ci struct pci_epf_bar *epf_bar; 53462306a36Sopenharmony_ci void __iomem *mw_addr; 53562306a36Sopenharmony_ci enum pci_barno barno; 53662306a36Sopenharmony_ci size_t size = sizeof(u32) * ntb->db_count; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci epc_features = pci_epc_get_features(ntb->epf->epc, 53962306a36Sopenharmony_ci ntb->epf->func_no, 54062306a36Sopenharmony_ci ntb->epf->vfunc_no); 54162306a36Sopenharmony_ci align = epc_features->align; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (size < 128) 54462306a36Sopenharmony_ci size = 128; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (align) 54762306a36Sopenharmony_ci size = ALIGN(size, align); 54862306a36Sopenharmony_ci else 54962306a36Sopenharmony_ci size = roundup_pow_of_two(size); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_DB]; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, align, 0); 55462306a36Sopenharmony_ci if (!mw_addr) { 55562306a36Sopenharmony_ci dev_err(dev, "Failed to allocate OB address\n"); 55662306a36Sopenharmony_ci return -ENOMEM; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci ntb->epf_db = mw_addr; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci epf_bar = &ntb->epf->bar[barno]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); 56462306a36Sopenharmony_ci if (ret) { 56562306a36Sopenharmony_ci dev_err(dev, "Doorbell BAR set failed\n"); 56662306a36Sopenharmony_ci goto err_alloc_peer_mem; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci return ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cierr_alloc_peer_mem: 57162306a36Sopenharmony_ci pci_epf_free_space(ntb->epf, mw_addr, barno, 0); 57262306a36Sopenharmony_ci return -1; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * epf_ntb_db_bar_clear() - Clear doorbell BAR and free memory 57962306a36Sopenharmony_ci * allocated in peer's outbound address space 58062306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic void epf_ntb_db_bar_clear(struct epf_ntb *ntb) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci enum pci_barno barno; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_DB]; 58762306a36Sopenharmony_ci pci_epf_free_space(ntb->epf, ntb->epf_db, barno, 0); 58862306a36Sopenharmony_ci pci_epc_clear_bar(ntb->epf->epc, 58962306a36Sopenharmony_ci ntb->epf->func_no, 59062306a36Sopenharmony_ci ntb->epf->vfunc_no, 59162306a36Sopenharmony_ci &ntb->epf->bar[barno]); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * epf_ntb_mw_bar_init() - Configure Memory window BARs 59662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 59762306a36Sopenharmony_ci * 59862306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_cistatic int epf_ntb_mw_bar_init(struct epf_ntb *ntb) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int ret = 0; 60362306a36Sopenharmony_ci int i; 60462306a36Sopenharmony_ci u64 size; 60562306a36Sopenharmony_ci enum pci_barno barno; 60662306a36Sopenharmony_ci struct device *dev = &ntb->epf->dev; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci for (i = 0; i < ntb->num_mws; i++) { 60962306a36Sopenharmony_ci size = ntb->mws_size[i]; 61062306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_MW0 + i]; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ntb->epf->bar[barno].barno = barno; 61362306a36Sopenharmony_ci ntb->epf->bar[barno].size = size; 61462306a36Sopenharmony_ci ntb->epf->bar[barno].addr = NULL; 61562306a36Sopenharmony_ci ntb->epf->bar[barno].phys_addr = 0; 61662306a36Sopenharmony_ci ntb->epf->bar[barno].flags |= upper_32_bits(size) ? 61762306a36Sopenharmony_ci PCI_BASE_ADDRESS_MEM_TYPE_64 : 61862306a36Sopenharmony_ci PCI_BASE_ADDRESS_MEM_TYPE_32; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = pci_epc_set_bar(ntb->epf->epc, 62162306a36Sopenharmony_ci ntb->epf->func_no, 62262306a36Sopenharmony_ci ntb->epf->vfunc_no, 62362306a36Sopenharmony_ci &ntb->epf->bar[barno]); 62462306a36Sopenharmony_ci if (ret) { 62562306a36Sopenharmony_ci dev_err(dev, "MW set failed\n"); 62662306a36Sopenharmony_ci goto err_alloc_mem; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Allocate EPC outbound memory windows to vpci vntb device */ 63062306a36Sopenharmony_ci ntb->vpci_mw_addr[i] = pci_epc_mem_alloc_addr(ntb->epf->epc, 63162306a36Sopenharmony_ci &ntb->vpci_mw_phy[i], 63262306a36Sopenharmony_ci size); 63362306a36Sopenharmony_ci if (!ntb->vpci_mw_addr[i]) { 63462306a36Sopenharmony_ci ret = -ENOMEM; 63562306a36Sopenharmony_ci dev_err(dev, "Failed to allocate source address\n"); 63662306a36Sopenharmony_ci goto err_set_bar; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cierr_set_bar: 64362306a36Sopenharmony_ci pci_epc_clear_bar(ntb->epf->epc, 64462306a36Sopenharmony_ci ntb->epf->func_no, 64562306a36Sopenharmony_ci ntb->epf->vfunc_no, 64662306a36Sopenharmony_ci &ntb->epf->bar[barno]); 64762306a36Sopenharmony_cierr_alloc_mem: 64862306a36Sopenharmony_ci epf_ntb_mw_bar_clear(ntb, i); 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * epf_ntb_mw_bar_clear() - Clear Memory window BARs 65462306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 65562306a36Sopenharmony_ci * @num_mws: the number of Memory window BARs that to be cleared 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_cistatic void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci enum pci_barno barno; 66062306a36Sopenharmony_ci int i; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (i = 0; i < num_mws; i++) { 66362306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_MW0 + i]; 66462306a36Sopenharmony_ci pci_epc_clear_bar(ntb->epf->epc, 66562306a36Sopenharmony_ci ntb->epf->func_no, 66662306a36Sopenharmony_ci ntb->epf->vfunc_no, 66762306a36Sopenharmony_ci &ntb->epf->bar[barno]); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci pci_epc_mem_free_addr(ntb->epf->epc, 67062306a36Sopenharmony_ci ntb->vpci_mw_phy[i], 67162306a36Sopenharmony_ci ntb->vpci_mw_addr[i], 67262306a36Sopenharmony_ci ntb->mws_size[i]); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/** 67762306a36Sopenharmony_ci * epf_ntb_epc_destroy() - Cleanup NTB EPC interface 67862306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_cistatic void epf_ntb_epc_destroy(struct epf_ntb *ntb) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci pci_epc_remove_epf(ntb->epf->epc, ntb->epf, 0); 68562306a36Sopenharmony_ci pci_epc_put(ntb->epf->epc); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB 69062306a36Sopenharmony_ci * constructs (scratchpad region, doorbell, memorywindow) 69162306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_cistatic int epf_ntb_init_epc_bar(struct epf_ntb *ntb) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci const struct pci_epc_features *epc_features; 69862306a36Sopenharmony_ci enum pci_barno barno; 69962306a36Sopenharmony_ci enum epf_ntb_bar bar; 70062306a36Sopenharmony_ci struct device *dev; 70162306a36Sopenharmony_ci u32 num_mws; 70262306a36Sopenharmony_ci int i; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci barno = BAR_0; 70562306a36Sopenharmony_ci num_mws = ntb->num_mws; 70662306a36Sopenharmony_ci dev = &ntb->epf->dev; 70762306a36Sopenharmony_ci epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* These are required BARs which are mandatory for NTB functionality */ 71062306a36Sopenharmony_ci for (bar = BAR_CONFIG; bar <= BAR_MW0; bar++, barno++) { 71162306a36Sopenharmony_ci barno = pci_epc_get_next_free_bar(epc_features, barno); 71262306a36Sopenharmony_ci if (barno < 0) { 71362306a36Sopenharmony_ci dev_err(dev, "Fail to get NTB function BAR\n"); 71462306a36Sopenharmony_ci return barno; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci ntb->epf_ntb_bar[bar] = barno; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* These are optional BARs which don't impact NTB functionality */ 72062306a36Sopenharmony_ci for (bar = BAR_MW1, i = 1; i < num_mws; bar++, barno++, i++) { 72162306a36Sopenharmony_ci barno = pci_epc_get_next_free_bar(epc_features, barno); 72262306a36Sopenharmony_ci if (barno < 0) { 72362306a36Sopenharmony_ci ntb->num_mws = i; 72462306a36Sopenharmony_ci dev_dbg(dev, "BAR not available for > MW%d\n", i + 1); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci ntb->epf_ntb_bar[bar] = barno; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/** 73362306a36Sopenharmony_ci * epf_ntb_epc_init() - Initialize NTB interface 73462306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * Wrapper to initialize a particular EPC interface and start the workqueue 73762306a36Sopenharmony_ci * to check for commands from HOST. This function will write to the 73862306a36Sopenharmony_ci * EP controller HW for configuring it. 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_cistatic int epf_ntb_epc_init(struct epf_ntb *ntb) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci u8 func_no, vfunc_no; 74562306a36Sopenharmony_ci struct pci_epc *epc; 74662306a36Sopenharmony_ci struct pci_epf *epf; 74762306a36Sopenharmony_ci struct device *dev; 74862306a36Sopenharmony_ci int ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci epf = ntb->epf; 75162306a36Sopenharmony_ci dev = &epf->dev; 75262306a36Sopenharmony_ci epc = epf->epc; 75362306a36Sopenharmony_ci func_no = ntb->epf->func_no; 75462306a36Sopenharmony_ci vfunc_no = ntb->epf->vfunc_no; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ret = epf_ntb_config_sspad_bar_set(ntb); 75762306a36Sopenharmony_ci if (ret) { 75862306a36Sopenharmony_ci dev_err(dev, "Config/self SPAD BAR init failed"); 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = epf_ntb_configure_interrupt(ntb); 76362306a36Sopenharmony_ci if (ret) { 76462306a36Sopenharmony_ci dev_err(dev, "Interrupt configuration failed\n"); 76562306a36Sopenharmony_ci goto err_config_interrupt; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci ret = epf_ntb_db_bar_init(ntb); 76962306a36Sopenharmony_ci if (ret) { 77062306a36Sopenharmony_ci dev_err(dev, "DB BAR init failed\n"); 77162306a36Sopenharmony_ci goto err_db_bar_init; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = epf_ntb_mw_bar_init(ntb); 77562306a36Sopenharmony_ci if (ret) { 77662306a36Sopenharmony_ci dev_err(dev, "MW BAR init failed\n"); 77762306a36Sopenharmony_ci goto err_mw_bar_init; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (vfunc_no <= 1) { 78162306a36Sopenharmony_ci ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header); 78262306a36Sopenharmony_ci if (ret) { 78362306a36Sopenharmony_ci dev_err(dev, "Configuration header write failed\n"); 78462306a36Sopenharmony_ci goto err_write_header; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); 78962306a36Sopenharmony_ci queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cierr_write_header: 79462306a36Sopenharmony_ci epf_ntb_mw_bar_clear(ntb, ntb->num_mws); 79562306a36Sopenharmony_cierr_mw_bar_init: 79662306a36Sopenharmony_ci epf_ntb_db_bar_clear(ntb); 79762306a36Sopenharmony_cierr_db_bar_init: 79862306a36Sopenharmony_cierr_config_interrupt: 79962306a36Sopenharmony_ci epf_ntb_config_sspad_bar_clear(ntb); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return ret; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/** 80662306a36Sopenharmony_ci * epf_ntb_epc_cleanup() - Cleanup all NTB interfaces 80762306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST and VHOST 80862306a36Sopenharmony_ci * 80962306a36Sopenharmony_ci * Wrapper to cleanup all NTB interfaces. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_cistatic void epf_ntb_epc_cleanup(struct epf_ntb *ntb) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci epf_ntb_db_bar_clear(ntb); 81462306a36Sopenharmony_ci epf_ntb_mw_bar_clear(ntb, ntb->num_mws); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci#define EPF_NTB_R(_name) \ 81862306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_show(struct config_item *item, \ 81962306a36Sopenharmony_ci char *page) \ 82062306a36Sopenharmony_ci{ \ 82162306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 82262306a36Sopenharmony_ci struct epf_ntb *ntb = to_epf_ntb(group); \ 82362306a36Sopenharmony_ci \ 82462306a36Sopenharmony_ci return sprintf(page, "%d\n", ntb->_name); \ 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci#define EPF_NTB_W(_name) \ 82862306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_store(struct config_item *item, \ 82962306a36Sopenharmony_ci const char *page, size_t len) \ 83062306a36Sopenharmony_ci{ \ 83162306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 83262306a36Sopenharmony_ci struct epf_ntb *ntb = to_epf_ntb(group); \ 83362306a36Sopenharmony_ci u32 val; \ 83462306a36Sopenharmony_ci int ret; \ 83562306a36Sopenharmony_ci \ 83662306a36Sopenharmony_ci ret = kstrtou32(page, 0, &val); \ 83762306a36Sopenharmony_ci if (ret) \ 83862306a36Sopenharmony_ci return ret; \ 83962306a36Sopenharmony_ci \ 84062306a36Sopenharmony_ci ntb->_name = val; \ 84162306a36Sopenharmony_ci \ 84262306a36Sopenharmony_ci return len; \ 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci#define EPF_NTB_MW_R(_name) \ 84662306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_show(struct config_item *item, \ 84762306a36Sopenharmony_ci char *page) \ 84862306a36Sopenharmony_ci{ \ 84962306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 85062306a36Sopenharmony_ci struct epf_ntb *ntb = to_epf_ntb(group); \ 85162306a36Sopenharmony_ci struct device *dev = &ntb->epf->dev; \ 85262306a36Sopenharmony_ci int win_no; \ 85362306a36Sopenharmony_ci \ 85462306a36Sopenharmony_ci if (sscanf(#_name, "mw%d", &win_no) != 1) \ 85562306a36Sopenharmony_ci return -EINVAL; \ 85662306a36Sopenharmony_ci \ 85762306a36Sopenharmony_ci if (win_no <= 0 || win_no > ntb->num_mws) { \ 85862306a36Sopenharmony_ci dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ 85962306a36Sopenharmony_ci return -EINVAL; \ 86062306a36Sopenharmony_ci } \ 86162306a36Sopenharmony_ci \ 86262306a36Sopenharmony_ci return sprintf(page, "%lld\n", ntb->mws_size[win_no - 1]); \ 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci#define EPF_NTB_MW_W(_name) \ 86662306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_store(struct config_item *item, \ 86762306a36Sopenharmony_ci const char *page, size_t len) \ 86862306a36Sopenharmony_ci{ \ 86962306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 87062306a36Sopenharmony_ci struct epf_ntb *ntb = to_epf_ntb(group); \ 87162306a36Sopenharmony_ci struct device *dev = &ntb->epf->dev; \ 87262306a36Sopenharmony_ci int win_no; \ 87362306a36Sopenharmony_ci u64 val; \ 87462306a36Sopenharmony_ci int ret; \ 87562306a36Sopenharmony_ci \ 87662306a36Sopenharmony_ci ret = kstrtou64(page, 0, &val); \ 87762306a36Sopenharmony_ci if (ret) \ 87862306a36Sopenharmony_ci return ret; \ 87962306a36Sopenharmony_ci \ 88062306a36Sopenharmony_ci if (sscanf(#_name, "mw%d", &win_no) != 1) \ 88162306a36Sopenharmony_ci return -EINVAL; \ 88262306a36Sopenharmony_ci \ 88362306a36Sopenharmony_ci if (win_no <= 0 || win_no > ntb->num_mws) { \ 88462306a36Sopenharmony_ci dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ 88562306a36Sopenharmony_ci return -EINVAL; \ 88662306a36Sopenharmony_ci } \ 88762306a36Sopenharmony_ci \ 88862306a36Sopenharmony_ci ntb->mws_size[win_no - 1] = val; \ 88962306a36Sopenharmony_ci \ 89062306a36Sopenharmony_ci return len; \ 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic ssize_t epf_ntb_num_mws_store(struct config_item *item, 89462306a36Sopenharmony_ci const char *page, size_t len) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 89762306a36Sopenharmony_ci struct epf_ntb *ntb = to_epf_ntb(group); 89862306a36Sopenharmony_ci u32 val; 89962306a36Sopenharmony_ci int ret; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = kstrtou32(page, 0, &val); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (val > MAX_MW) 90662306a36Sopenharmony_ci return -EINVAL; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci ntb->num_mws = val; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return len; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ciEPF_NTB_R(spad_count) 91462306a36Sopenharmony_ciEPF_NTB_W(spad_count) 91562306a36Sopenharmony_ciEPF_NTB_R(db_count) 91662306a36Sopenharmony_ciEPF_NTB_W(db_count) 91762306a36Sopenharmony_ciEPF_NTB_R(num_mws) 91862306a36Sopenharmony_ciEPF_NTB_R(vbus_number) 91962306a36Sopenharmony_ciEPF_NTB_W(vbus_number) 92062306a36Sopenharmony_ciEPF_NTB_R(vntb_pid) 92162306a36Sopenharmony_ciEPF_NTB_W(vntb_pid) 92262306a36Sopenharmony_ciEPF_NTB_R(vntb_vid) 92362306a36Sopenharmony_ciEPF_NTB_W(vntb_vid) 92462306a36Sopenharmony_ciEPF_NTB_MW_R(mw1) 92562306a36Sopenharmony_ciEPF_NTB_MW_W(mw1) 92662306a36Sopenharmony_ciEPF_NTB_MW_R(mw2) 92762306a36Sopenharmony_ciEPF_NTB_MW_W(mw2) 92862306a36Sopenharmony_ciEPF_NTB_MW_R(mw3) 92962306a36Sopenharmony_ciEPF_NTB_MW_W(mw3) 93062306a36Sopenharmony_ciEPF_NTB_MW_R(mw4) 93162306a36Sopenharmony_ciEPF_NTB_MW_W(mw4) 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, spad_count); 93462306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, db_count); 93562306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, num_mws); 93662306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw1); 93762306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw2); 93862306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw3); 93962306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw4); 94062306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, vbus_number); 94162306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, vntb_pid); 94262306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, vntb_vid); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic struct configfs_attribute *epf_ntb_attrs[] = { 94562306a36Sopenharmony_ci &epf_ntb_attr_spad_count, 94662306a36Sopenharmony_ci &epf_ntb_attr_db_count, 94762306a36Sopenharmony_ci &epf_ntb_attr_num_mws, 94862306a36Sopenharmony_ci &epf_ntb_attr_mw1, 94962306a36Sopenharmony_ci &epf_ntb_attr_mw2, 95062306a36Sopenharmony_ci &epf_ntb_attr_mw3, 95162306a36Sopenharmony_ci &epf_ntb_attr_mw4, 95262306a36Sopenharmony_ci &epf_ntb_attr_vbus_number, 95362306a36Sopenharmony_ci &epf_ntb_attr_vntb_pid, 95462306a36Sopenharmony_ci &epf_ntb_attr_vntb_vid, 95562306a36Sopenharmony_ci NULL, 95662306a36Sopenharmony_ci}; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic const struct config_item_type ntb_group_type = { 95962306a36Sopenharmony_ci .ct_attrs = epf_ntb_attrs, 96062306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 96162306a36Sopenharmony_ci}; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci/** 96462306a36Sopenharmony_ci * epf_ntb_add_cfs() - Add configfs directory specific to NTB 96562306a36Sopenharmony_ci * @epf: NTB endpoint function device 96662306a36Sopenharmony_ci * @group: A pointer to the config_group structure referencing a group of 96762306a36Sopenharmony_ci * config_items of a specific type that belong to a specific sub-system. 96862306a36Sopenharmony_ci * 96962306a36Sopenharmony_ci * Add configfs directory specific to NTB. This directory will hold 97062306a36Sopenharmony_ci * NTB specific properties like db_count, spad_count, num_mws etc., 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * Returns: Pointer to config_group 97362306a36Sopenharmony_ci */ 97462306a36Sopenharmony_cistatic struct config_group *epf_ntb_add_cfs(struct pci_epf *epf, 97562306a36Sopenharmony_ci struct config_group *group) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct epf_ntb *ntb = epf_get_drvdata(epf); 97862306a36Sopenharmony_ci struct config_group *ntb_group = &ntb->group; 97962306a36Sopenharmony_ci struct device *dev = &epf->dev; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci config_group_init_type_name(ntb_group, dev_name(dev), &ntb_group_type); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return ntb_group; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/*==== virtual PCI bus driver, which only load virtual NTB PCI driver ====*/ 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic u32 pci_space[] = { 98962306a36Sopenharmony_ci 0xffffffff, /* Device ID, Vendor ID */ 99062306a36Sopenharmony_ci 0, /* Status, Command */ 99162306a36Sopenharmony_ci 0xffffffff, /* Base Class, Subclass, Prog Intf, Revision ID */ 99262306a36Sopenharmony_ci 0x40, /* BIST, Header Type, Latency Timer, Cache Line Size */ 99362306a36Sopenharmony_ci 0, /* BAR 0 */ 99462306a36Sopenharmony_ci 0, /* BAR 1 */ 99562306a36Sopenharmony_ci 0, /* BAR 2 */ 99662306a36Sopenharmony_ci 0, /* BAR 3 */ 99762306a36Sopenharmony_ci 0, /* BAR 4 */ 99862306a36Sopenharmony_ci 0, /* BAR 5 */ 99962306a36Sopenharmony_ci 0, /* Cardbus CIS Pointer */ 100062306a36Sopenharmony_ci 0, /* Subsystem ID, Subsystem Vendor ID */ 100162306a36Sopenharmony_ci 0, /* ROM Base Address */ 100262306a36Sopenharmony_ci 0, /* Reserved, Capabilities Pointer */ 100362306a36Sopenharmony_ci 0, /* Reserved */ 100462306a36Sopenharmony_ci 0, /* Max_Lat, Min_Gnt, Interrupt Pin, Interrupt Line */ 100562306a36Sopenharmony_ci}; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci if (devfn == 0) { 101062306a36Sopenharmony_ci memcpy(val, ((u8 *)pci_space) + where, size); 101162306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci return 0; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic struct pci_ops vpci_ops = { 102262306a36Sopenharmony_ci .read = pci_read, 102362306a36Sopenharmony_ci .write = pci_write, 102462306a36Sopenharmony_ci}; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic int vpci_scan_bus(void *sysdata) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct pci_bus *vpci_bus; 102962306a36Sopenharmony_ci struct epf_ntb *ndev = sysdata; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata); 103262306a36Sopenharmony_ci if (vpci_bus) 103362306a36Sopenharmony_ci pr_err("create pci bus\n"); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci pci_bus_add_devices(vpci_bus); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci/*==================== Virtual PCIe NTB driver ==========================*/ 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic int vntb_epf_mw_count(struct ntb_dev *ntb, int pidx) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct epf_ntb *ndev = ntb_ndev(ntb); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return ndev->num_mws; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int vntb_epf_spad_count(struct ntb_dev *ntb) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci return ntb_ndev(ntb)->spad_count; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int vntb_epf_peer_mw_count(struct ntb_dev *ntb) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci return ntb_ndev(ntb)->num_mws; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci return 0; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx, 107062306a36Sopenharmony_ci dma_addr_t addr, resource_size_t size) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 107362306a36Sopenharmony_ci struct pci_epf_bar *epf_bar; 107462306a36Sopenharmony_ci enum pci_barno barno; 107562306a36Sopenharmony_ci int ret; 107662306a36Sopenharmony_ci struct device *dev; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci dev = &ntb->ntb.dev; 107962306a36Sopenharmony_ci barno = ntb->epf_ntb_bar[BAR_MW0 + idx]; 108062306a36Sopenharmony_ci epf_bar = &ntb->epf->bar[barno]; 108162306a36Sopenharmony_ci epf_bar->phys_addr = addr; 108262306a36Sopenharmony_ci epf_bar->barno = barno; 108362306a36Sopenharmony_ci epf_bar->size = size; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar); 108662306a36Sopenharmony_ci if (ret) { 108762306a36Sopenharmony_ci dev_err(dev, "failure set mw trans\n"); 108862306a36Sopenharmony_ci return ret; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci return 0; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int vntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx, 109962306a36Sopenharmony_ci phys_addr_t *base, resource_size_t *size) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (base) 110562306a36Sopenharmony_ci *base = ntb->vpci_mw_phy[idx]; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (size) 110862306a36Sopenharmony_ci *size = ntb->mws_size[idx]; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic int vntb_epf_link_enable(struct ntb_dev *ntb, 111462306a36Sopenharmony_ci enum ntb_speed max_speed, 111562306a36Sopenharmony_ci enum ntb_width max_width) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 112362306a36Sopenharmony_ci int off = ntb->reg->spad_offset, ct = ntb->reg->spad_count * sizeof(u32); 112462306a36Sopenharmony_ci u32 val; 112562306a36Sopenharmony_ci void __iomem *base = (void __iomem *)ntb->reg; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci val = readl(base + off + ct + idx * sizeof(u32)); 112862306a36Sopenharmony_ci return val; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic int vntb_epf_spad_write(struct ntb_dev *ndev, int idx, u32 val) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 113462306a36Sopenharmony_ci struct epf_ntb_ctrl *ctrl = ntb->reg; 113562306a36Sopenharmony_ci int off = ctrl->spad_offset, ct = ctrl->spad_count * sizeof(u32); 113662306a36Sopenharmony_ci void __iomem *base = (void __iomem *)ntb->reg; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci writel(val, base + off + ct + idx * sizeof(u32)); 113962306a36Sopenharmony_ci return 0; 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic u32 vntb_epf_peer_spad_read(struct ntb_dev *ndev, int pidx, int idx) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 114562306a36Sopenharmony_ci struct epf_ntb_ctrl *ctrl = ntb->reg; 114662306a36Sopenharmony_ci int off = ctrl->spad_offset; 114762306a36Sopenharmony_ci void __iomem *base = (void __iomem *)ntb->reg; 114862306a36Sopenharmony_ci u32 val; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci val = readl(base + off + idx * sizeof(u32)); 115162306a36Sopenharmony_ci return val; 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 val) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 115762306a36Sopenharmony_ci struct epf_ntb_ctrl *ctrl = ntb->reg; 115862306a36Sopenharmony_ci int off = ctrl->spad_offset; 115962306a36Sopenharmony_ci void __iomem *base = (void __iomem *)ntb->reg; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci writel(val, base + off + idx * sizeof(u32)); 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci u32 interrupt_num = ffs(db_bits) + 1; 116862306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 116962306a36Sopenharmony_ci u8 func_no, vfunc_no; 117062306a36Sopenharmony_ci int ret; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci func_no = ntb->epf->func_no; 117362306a36Sopenharmony_ci vfunc_no = ntb->epf->vfunc_no; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci ret = pci_epc_raise_irq(ntb->epf->epc, 117662306a36Sopenharmony_ci func_no, 117762306a36Sopenharmony_ci vfunc_no, 117862306a36Sopenharmony_ci PCI_EPC_IRQ_MSI, 117962306a36Sopenharmony_ci interrupt_num + 1); 118062306a36Sopenharmony_ci if (ret) 118162306a36Sopenharmony_ci dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci return ret; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic u64 vntb_epf_db_read(struct ntb_dev *ndev) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return ntb->db; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx, 119462306a36Sopenharmony_ci resource_size_t *addr_align, 119562306a36Sopenharmony_ci resource_size_t *size_align, 119662306a36Sopenharmony_ci resource_size_t *size_max) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (addr_align) 120162306a36Sopenharmony_ci *addr_align = SZ_4K; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (size_align) 120462306a36Sopenharmony_ci *size_align = 1; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (size_max) 120762306a36Sopenharmony_ci *size_max = ntb->mws_size[idx]; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic u64 vntb_epf_link_is_up(struct ntb_dev *ndev, 121362306a36Sopenharmony_ci enum ntb_speed *speed, 121462306a36Sopenharmony_ci enum ntb_width *width) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return ntb->reg->link_status; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct epf_ntb *ntb = ntb_ndev(ndev); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci ntb->db &= ~db_bits; 123162306a36Sopenharmony_ci return 0; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic int vntb_epf_link_disable(struct ntb_dev *ntb) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci return 0; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic const struct ntb_dev_ops vntb_epf_ops = { 124062306a36Sopenharmony_ci .mw_count = vntb_epf_mw_count, 124162306a36Sopenharmony_ci .spad_count = vntb_epf_spad_count, 124262306a36Sopenharmony_ci .peer_mw_count = vntb_epf_peer_mw_count, 124362306a36Sopenharmony_ci .db_valid_mask = vntb_epf_db_valid_mask, 124462306a36Sopenharmony_ci .db_set_mask = vntb_epf_db_set_mask, 124562306a36Sopenharmony_ci .mw_set_trans = vntb_epf_mw_set_trans, 124662306a36Sopenharmony_ci .mw_clear_trans = vntb_epf_mw_clear_trans, 124762306a36Sopenharmony_ci .peer_mw_get_addr = vntb_epf_peer_mw_get_addr, 124862306a36Sopenharmony_ci .link_enable = vntb_epf_link_enable, 124962306a36Sopenharmony_ci .spad_read = vntb_epf_spad_read, 125062306a36Sopenharmony_ci .spad_write = vntb_epf_spad_write, 125162306a36Sopenharmony_ci .peer_spad_read = vntb_epf_peer_spad_read, 125262306a36Sopenharmony_ci .peer_spad_write = vntb_epf_peer_spad_write, 125362306a36Sopenharmony_ci .peer_db_set = vntb_epf_peer_db_set, 125462306a36Sopenharmony_ci .db_read = vntb_epf_db_read, 125562306a36Sopenharmony_ci .mw_get_align = vntb_epf_mw_get_align, 125662306a36Sopenharmony_ci .link_is_up = vntb_epf_link_is_up, 125762306a36Sopenharmony_ci .db_clear_mask = vntb_epf_db_clear_mask, 125862306a36Sopenharmony_ci .db_clear = vntb_epf_db_clear, 125962306a36Sopenharmony_ci .link_disable = vntb_epf_link_disable, 126062306a36Sopenharmony_ci}; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci int ret; 126562306a36Sopenharmony_ci struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata; 126662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci ndev->ntb.pdev = pdev; 126962306a36Sopenharmony_ci ndev->ntb.topo = NTB_TOPO_NONE; 127062306a36Sopenharmony_ci ndev->ntb.ops = &vntb_epf_ops; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 127362306a36Sopenharmony_ci if (ret) { 127462306a36Sopenharmony_ci dev_err(dev, "Cannot set DMA mask\n"); 127562306a36Sopenharmony_ci return -EINVAL; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = ntb_register_device(&ndev->ntb); 127962306a36Sopenharmony_ci if (ret) { 128062306a36Sopenharmony_ci dev_err(dev, "Failed to register NTB device\n"); 128162306a36Sopenharmony_ci return ret; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci dev_dbg(dev, "PCI Virtual NTB driver loaded\n"); 128562306a36Sopenharmony_ci return 0; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic struct pci_device_id pci_vntb_table[] = { 128962306a36Sopenharmony_ci { 129062306a36Sopenharmony_ci PCI_DEVICE(0xffff, 0xffff), 129162306a36Sopenharmony_ci }, 129262306a36Sopenharmony_ci {}, 129362306a36Sopenharmony_ci}; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic struct pci_driver vntb_pci_driver = { 129662306a36Sopenharmony_ci .name = "pci-vntb", 129762306a36Sopenharmony_ci .id_table = pci_vntb_table, 129862306a36Sopenharmony_ci .probe = pci_vntb_probe, 129962306a36Sopenharmony_ci}; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci/* ============ PCIe EPF Driver Bind ====================*/ 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci/** 130462306a36Sopenharmony_ci * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality 130562306a36Sopenharmony_ci * @epf: NTB endpoint function device 130662306a36Sopenharmony_ci * 130762306a36Sopenharmony_ci * Initialize both the endpoint controllers associated with NTB function device. 130862306a36Sopenharmony_ci * Invoked when a primary interface or secondary interface is bound to EPC 130962306a36Sopenharmony_ci * device. This function will succeed only when EPC is bound to both the 131062306a36Sopenharmony_ci * interfaces. 131162306a36Sopenharmony_ci * 131262306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_cistatic int epf_ntb_bind(struct pci_epf *epf) 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci struct epf_ntb *ntb = epf_get_drvdata(epf); 131762306a36Sopenharmony_ci struct device *dev = &epf->dev; 131862306a36Sopenharmony_ci int ret; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (!epf->epc) { 132162306a36Sopenharmony_ci dev_dbg(dev, "PRIMARY EPC interface not yet bound\n"); 132262306a36Sopenharmony_ci return 0; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci ret = epf_ntb_init_epc_bar(ntb); 132662306a36Sopenharmony_ci if (ret) { 132762306a36Sopenharmony_ci dev_err(dev, "Failed to create NTB EPC\n"); 132862306a36Sopenharmony_ci goto err_bar_init; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ret = epf_ntb_config_spad_bar_alloc(ntb); 133262306a36Sopenharmony_ci if (ret) { 133362306a36Sopenharmony_ci dev_err(dev, "Failed to allocate BAR memory\n"); 133462306a36Sopenharmony_ci goto err_bar_alloc; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ret = epf_ntb_epc_init(ntb); 133862306a36Sopenharmony_ci if (ret) { 133962306a36Sopenharmony_ci dev_err(dev, "Failed to initialize EPC\n"); 134062306a36Sopenharmony_ci goto err_bar_alloc; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci epf_set_drvdata(epf, ntb); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci pci_space[0] = (ntb->vntb_pid << 16) | ntb->vntb_vid; 134662306a36Sopenharmony_ci pci_vntb_table[0].vendor = ntb->vntb_vid; 134762306a36Sopenharmony_ci pci_vntb_table[0].device = ntb->vntb_pid; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci ret = pci_register_driver(&vntb_pci_driver); 135062306a36Sopenharmony_ci if (ret) { 135162306a36Sopenharmony_ci dev_err(dev, "failure register vntb pci driver\n"); 135262306a36Sopenharmony_ci goto err_bar_alloc; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci vpci_scan_bus(ntb); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cierr_bar_alloc: 136062306a36Sopenharmony_ci epf_ntb_config_spad_bar_free(ntb); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cierr_bar_init: 136362306a36Sopenharmony_ci epf_ntb_epc_destroy(ntb); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return ret; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci/** 136962306a36Sopenharmony_ci * epf_ntb_unbind() - Cleanup the initialization from epf_ntb_bind() 137062306a36Sopenharmony_ci * @epf: NTB endpoint function device 137162306a36Sopenharmony_ci * 137262306a36Sopenharmony_ci * Cleanup the initialization from epf_ntb_bind() 137362306a36Sopenharmony_ci */ 137462306a36Sopenharmony_cistatic void epf_ntb_unbind(struct pci_epf *epf) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct epf_ntb *ntb = epf_get_drvdata(epf); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci epf_ntb_epc_cleanup(ntb); 137962306a36Sopenharmony_ci epf_ntb_config_spad_bar_free(ntb); 138062306a36Sopenharmony_ci epf_ntb_epc_destroy(ntb); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci pci_unregister_driver(&vntb_pci_driver); 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci// EPF driver probe 138662306a36Sopenharmony_cistatic struct pci_epf_ops epf_ntb_ops = { 138762306a36Sopenharmony_ci .bind = epf_ntb_bind, 138862306a36Sopenharmony_ci .unbind = epf_ntb_unbind, 138962306a36Sopenharmony_ci .add_cfs = epf_ntb_add_cfs, 139062306a36Sopenharmony_ci}; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci/** 139362306a36Sopenharmony_ci * epf_ntb_probe() - Probe NTB function driver 139462306a36Sopenharmony_ci * @epf: NTB endpoint function device 139562306a36Sopenharmony_ci * @id: NTB endpoint function device ID 139662306a36Sopenharmony_ci * 139762306a36Sopenharmony_ci * Probe NTB function driver when endpoint function bus detects a NTB 139862306a36Sopenharmony_ci * endpoint function. 139962306a36Sopenharmony_ci * 140062306a36Sopenharmony_ci * Returns: Zero for success, or an error code in case of failure 140162306a36Sopenharmony_ci */ 140262306a36Sopenharmony_cistatic int epf_ntb_probe(struct pci_epf *epf, 140362306a36Sopenharmony_ci const struct pci_epf_device_id *id) 140462306a36Sopenharmony_ci{ 140562306a36Sopenharmony_ci struct epf_ntb *ntb; 140662306a36Sopenharmony_ci struct device *dev; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci dev = &epf->dev; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); 141162306a36Sopenharmony_ci if (!ntb) 141262306a36Sopenharmony_ci return -ENOMEM; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci epf->header = &epf_ntb_header; 141562306a36Sopenharmony_ci ntb->epf = epf; 141662306a36Sopenharmony_ci ntb->vbus_number = 0xff; 141762306a36Sopenharmony_ci epf_set_drvdata(epf, ntb); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci dev_info(dev, "pci-ep epf driver loaded\n"); 142062306a36Sopenharmony_ci return 0; 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic const struct pci_epf_device_id epf_ntb_ids[] = { 142462306a36Sopenharmony_ci { 142562306a36Sopenharmony_ci .name = "pci_epf_vntb", 142662306a36Sopenharmony_ci }, 142762306a36Sopenharmony_ci {}, 142862306a36Sopenharmony_ci}; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic struct pci_epf_driver epf_ntb_driver = { 143162306a36Sopenharmony_ci .driver.name = "pci_epf_vntb", 143262306a36Sopenharmony_ci .probe = epf_ntb_probe, 143362306a36Sopenharmony_ci .id_table = epf_ntb_ids, 143462306a36Sopenharmony_ci .ops = &epf_ntb_ops, 143562306a36Sopenharmony_ci .owner = THIS_MODULE, 143662306a36Sopenharmony_ci}; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic int __init epf_ntb_init(void) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci int ret; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | 144362306a36Sopenharmony_ci WQ_HIGHPRI, 0); 144462306a36Sopenharmony_ci ret = pci_epf_register_driver(&epf_ntb_driver); 144562306a36Sopenharmony_ci if (ret) { 144662306a36Sopenharmony_ci destroy_workqueue(kpcintb_workqueue); 144762306a36Sopenharmony_ci pr_err("Failed to register pci epf ntb driver --> %d\n", ret); 144862306a36Sopenharmony_ci return ret; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_cimodule_init(epf_ntb_init); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic void __exit epf_ntb_exit(void) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci pci_epf_unregister_driver(&epf_ntb_driver); 145862306a36Sopenharmony_ci destroy_workqueue(kpcintb_workqueue); 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_cimodule_exit(epf_ntb_exit); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ciMODULE_DESCRIPTION("PCI EPF NTB DRIVER"); 146362306a36Sopenharmony_ciMODULE_AUTHOR("Frank Li <Frank.li@nxp.com>"); 146462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1465