/* * This file is provided under a GPLv2 license. When using or * redistributing this file, you may do so under that license. * * GPL LICENSE SUMMARY * * Copyright (C) 2016-2018 T-Platforms JSC All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, one can be found http://www.gnu.org/licenses/. * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * IDT PCIe-switch NTB Linux driver * * Contact Information: * Serge Semin <[email protected]>, <[email protected]> */ #include <linux/stddef.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/bitops.h> #include <linux/sizes.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/pci.h> #include <linux/aer.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/debugfs.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/ntb.h> #include "ntb_hw_idt.h" #define NTB_NAME … #define NTB_DESC … #define NTB_VER … #define NTB_IRQNAME … MODULE_DESCRIPTION(…); MODULE_VERSION(…); MODULE_LICENSE(…) …; MODULE_AUTHOR(…) …; /* * NT Endpoint registers table simplifying a loop access to the functionally * related registers */ static const struct idt_ntb_regs ntdata_tbl = …; /* * NT Endpoint ports data table with the corresponding pcie command, link * status, control and BAR-related registers */ static const struct idt_ntb_port portdata_tbl[IDT_MAX_NR_PORTS] = …; /* * IDT PCIe-switch partitions table with the corresponding control, status * and messages control registers */ static const struct idt_ntb_part partdata_tbl[IDT_MAX_NR_PARTS] = …; /* * DebugFS directory to place the driver debug file */ static struct dentry *dbgfs_topdir; /*============================================================================= * 1. IDT PCIe-switch registers IO-functions * * Beside ordinary configuration space registers IDT PCIe-switch expose * global configuration registers, which are used to determine state of other * device ports as well as being notified of some switch-related events. * Additionally all the configuration space registers of all the IDT * PCIe-switch functions are mapped to the Global Address space, so each * function can determine a configuration of any other PCI-function. * Functions declared in this chapter are created to encapsulate access * to configuration and global registers, so the driver code just need to * provide IDT NTB hardware descriptor and a register address. *============================================================================= */ /* * idt_nt_write() - PCI configuration space registers write method * @ndev: IDT NTB hardware driver descriptor * @reg: Register to write data to * @data: Value to write to the register * * IDT PCIe-switch registers are all Little endian. */ static void idt_nt_write(struct idt_ntb_dev *ndev, const unsigned int reg, const u32 data) { … } /* * idt_nt_read() - PCI configuration space registers read method * @ndev: IDT NTB hardware driver descriptor * @reg: Register to write data to * * IDT PCIe-switch Global configuration registers are all Little endian. * * Return: register value */ static u32 idt_nt_read(struct idt_ntb_dev *ndev, const unsigned int reg) { … } /* * idt_sw_write() - Global registers write method * @ndev: IDT NTB hardware driver descriptor * @reg: Register to write data to * @data: Value to write to the register * * IDT PCIe-switch Global configuration registers are all Little endian. */ static void idt_sw_write(struct idt_ntb_dev *ndev, const unsigned int reg, const u32 data) { … } /* * idt_sw_read() - Global registers read method * @ndev: IDT NTB hardware driver descriptor * @reg: Register to write data to * * IDT PCIe-switch Global configuration registers are all Little endian. * * Return: register value */ static u32 idt_sw_read(struct idt_ntb_dev *ndev, const unsigned int reg) { … } /* * idt_reg_set_bits() - set bits of a passed register * @ndev: IDT NTB hardware driver descriptor * @reg: Register to change bits of * @reg_lock: Register access spin lock * @valid_mask: Mask of valid bits * @set_bits: Bitmask to set * * Helper method to check whether a passed bitfield is valid and set * corresponding bits of a register. * * WARNING! Make sure the passed register isn't accessed over plane * idt_nt_write() method (read method is ok to be used concurrently). * * Return: zero on success, negative error on invalid bitmask. */ static inline int idt_reg_set_bits(struct idt_ntb_dev *ndev, unsigned int reg, spinlock_t *reg_lock, u64 valid_mask, u64 set_bits) { … } /* * idt_reg_clear_bits() - clear bits of a passed register * @ndev: IDT NTB hardware driver descriptor * @reg: Register to change bits of * @reg_lock: Register access spin lock * @set_bits: Bitmask to clear * * Helper method to check whether a passed bitfield is valid and clear * corresponding bits of a register. * * NOTE! Invalid bits are always considered cleared so it's not an error * to clear them over. * * WARNING! Make sure the passed register isn't accessed over plane * idt_nt_write() method (read method is ok to use concurrently). */ static inline void idt_reg_clear_bits(struct idt_ntb_dev *ndev, unsigned int reg, spinlock_t *reg_lock, u64 clear_bits) { … } /*=========================================================================== * 2. Ports operations * * IDT PCIe-switches can have from 3 up to 8 ports with possible * NT-functions enabled. So all the possible ports need to be scanned looking * for NTB activated. NTB API will have enumerated only the ports with NTB. *=========================================================================== */ /* * idt_scan_ports() - scan IDT PCIe-switch ports collecting info in the tables * @ndev: Pointer to the PCI device descriptor * * Return: zero on success, otherwise a negative error number. */ static int idt_scan_ports(struct idt_ntb_dev *ndev) { … } /* * idt_ntb_port_number() - get the local port number * @ntb: NTB device context. * * Return: the local port number */ static int idt_ntb_port_number(struct ntb_dev *ntb) { … } /* * idt_ntb_peer_port_count() - get the number of peer ports * @ntb: NTB device context. * * Return the count of detected peer NT-functions. * * Return: number of peer ports */ static int idt_ntb_peer_port_count(struct ntb_dev *ntb) { … } /* * idt_ntb_peer_port_number() - get peer port by given index * @ntb: NTB device context. * @pidx: Peer port index. * * Return: peer port or negative error */ static int idt_ntb_peer_port_number(struct ntb_dev *ntb, int pidx) { … } /* * idt_ntb_peer_port_idx() - get peer port index by given port number * @ntb: NTB device context. * @port: Peer port number. * * Internal port -> index table is pre-initialized with -EINVAL values, * so we just need to return it value * * Return: peer NT-function port index or negative error */ static int idt_ntb_peer_port_idx(struct ntb_dev *ntb, int port) { … } /*=========================================================================== * 3. Link status operations * There is no any ready-to-use method to have peer ports notified if NTB * link is set up or got down. Instead global signal can be used instead. * In case if any one of ports changes local NTB link state, it sends * global signal and clears corresponding global state bit. Then all the ports * receive a notification of that, so to make client driver being aware of * possible NTB link change. * Additionally each of active NT-functions is subscribed to PCIe-link * state changes of peer ports. *=========================================================================== */ static void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev); /* * idt_init_link() - Initialize NTB link state notification subsystem * @ndev: IDT NTB hardware driver descriptor * * Function performs the basic initialization of some global registers * needed to enable IRQ-based notifications of PCIe Link Up/Down and * Global Signal events. * NOTE Since it's not possible to determine when all the NTB peer drivers are * unloaded as well as have those registers accessed concurrently, we must * preinitialize them with the same value and leave it uncleared on local * driver unload. */ static void idt_init_link(struct idt_ntb_dev *ndev) { … } /* * idt_deinit_link() - deinitialize link subsystem * @ndev: IDT NTB hardware driver descriptor * * Just disable the link back. */ static void idt_deinit_link(struct idt_ntb_dev *ndev) { … } /* * idt_se_isr() - switch events ISR * @ndev: IDT NTB hardware driver descriptor * @ntint_sts: NT-function interrupt status * * This driver doesn't support IDT PCIe-switch dynamic reconfigurations, * Failover capability, etc, so switch events are utilized to notify of * PCIe and NTB link events. * The method is called from PCIe ISR bottom-half routine. */ static void idt_se_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) { … } /* * idt_ntb_local_link_enable() - enable the local NTB link. * @ndev: IDT NTB hardware driver descriptor * * In order to enable the NTB link we need: * - enable Completion TLPs translation * - initialize mapping table to enable the Request ID translation * - notify peers of NTB link state change */ static void idt_ntb_local_link_enable(struct idt_ntb_dev *ndev) { … } /* * idt_ntb_local_link_disable() - disable the local NTB link. * @ndev: IDT NTB hardware driver descriptor * * In order to enable the NTB link we need: * - disable Completion TLPs translation * - clear corresponding mapping table entry * - notify peers of NTB link state change */ static void idt_ntb_local_link_disable(struct idt_ntb_dev *ndev) { … } /* * idt_ntb_local_link_is_up() - test wethter local NTB link is up * @ndev: IDT NTB hardware driver descriptor * * Local link is up under the following conditions: * - Bus mastering is enabled * - NTCTL has Completion TLPs translation enabled * - Mapping table permits Request TLPs translation * NOTE: We don't need to check PCIe link state since it's obviously * up while we are able to communicate with IDT PCIe-switch * * Return: true if link is up, otherwise false */ static bool idt_ntb_local_link_is_up(struct idt_ntb_dev *ndev) { … } /* * idt_ntb_peer_link_is_up() - test whether peer NTB link is up * @ndev: IDT NTB hardware driver descriptor * @pidx: Peer port index * * Peer link is up under the following conditions: * - PCIe link is up * - Bus mastering is enabled * - NTCTL has Completion TLPs translation enabled * - Mapping table permits Request TLPs translation * * Return: true if link is up, otherwise false */ static bool idt_ntb_peer_link_is_up(struct idt_ntb_dev *ndev, int pidx) { … } /* * idt_ntb_link_is_up() - get the current ntb link state (NTB API callback) * @ntb: NTB device context. * @speed: OUT - The link speed expressed as PCIe generation number. * @width: OUT - The link width expressed as the number of PCIe lanes. * * Get the bitfield of NTB link states for all peer ports * * Return: bitfield of indexed ports link state: bit is set/cleared if the * link is up/down respectively. */ static u64 idt_ntb_link_is_up(struct ntb_dev *ntb, enum ntb_speed *speed, enum ntb_width *width) { … } /* * idt_ntb_link_enable() - enable local port ntb link (NTB API callback) * @ntb: NTB device context. * @max_speed: The maximum link speed expressed as PCIe generation number. * @max_width: The maximum link width expressed as the number of PCIe lanes. * * Enable just local NTB link. PCIe link parameters are ignored. * * Return: always zero. */ static int idt_ntb_link_enable(struct ntb_dev *ntb, enum ntb_speed speed, enum ntb_width width) { … } /* * idt_ntb_link_disable() - disable local port ntb link (NTB API callback) * @ntb: NTB device context. * * Disable just local NTB link. * * Return: always zero. */ static int idt_ntb_link_disable(struct ntb_dev *ntb) { … } /*============================================================================= * 4. Memory Window operations * * IDT PCIe-switches have two types of memory windows: MWs with direct * address translation and MWs with LUT based translation. The first type of * MWs is simple map of corresponding BAR address space to a memory space * of specified target port. So it implemets just ont-to-one mapping. Lookup * table in its turn can map one BAR address space to up to 24 different * memory spaces of different ports. * NT-functions BARs can be turned on to implement either direct or lookup * table based address translations, so: * BAR0 - NT configuration registers space/direct address translation * BAR1 - direct address translation/upper address of BAR0x64 * BAR2 - direct address translation/Lookup table with either 12 or 24 entries * BAR3 - direct address translation/upper address of BAR2x64 * BAR4 - direct address translation/Lookup table with either 12 or 24 entries * BAR5 - direct address translation/upper address of BAR4x64 * Additionally BAR2 and BAR4 can't have 24-entries LUT enabled at the same * time. Since the BARs setup can be rather complicated this driver implements * a scanning algorithm to have all the possible memory windows configuration * covered. * * NOTE 1 BAR setup must be done before Linux kernel enumerated NT-function * of any port, so this driver would have memory windows configurations fixed. * In this way all initializations must be performed either by platform BIOS * or using EEPROM connected to IDT PCIe-switch master SMBus. * * NOTE 2 This driver expects BAR0 mapping NT-function configuration space. * Easy calculation can give us an upper boundary of 29 possible memory windows * per each NT-function if all the BARs are of 32bit type. *============================================================================= */ /* * idt_get_mw_count() - get memory window count * @mw_type: Memory window type * * Return: number of memory windows with respect to the BAR type */ static inline unsigned char idt_get_mw_count(enum idt_mw_type mw_type) { … } /* * idt_get_mw_name() - get memory window name * @mw_type: Memory window type * * Return: pointer to a string with name */ static inline char *idt_get_mw_name(enum idt_mw_type mw_type) { … } /* * idt_scan_mws() - scan memory windows of the port * @ndev: IDT NTB hardware driver descriptor * @port: Port to get number of memory windows for * @mw_cnt: Out - number of memory windows * * It walks over BAR setup registers of the specified port and determines * the memory windows parameters if any activated. * * Return: array of memory windows */ static struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port, unsigned char *mw_cnt) { … } /* * idt_init_mws() - initialize memory windows subsystem * @ndev: IDT NTB hardware driver descriptor * * Scan BAR setup registers of local and peer ports to determine the * outbound and inbound memory windows parameters * * Return: zero on success, otherwise a negative error number */ static int idt_init_mws(struct idt_ntb_dev *ndev) { … } /* * idt_ntb_mw_count() - number of inbound memory windows (NTB API callback) * @ntb: NTB device context. * @pidx: Port index of peer device. * * The value is returned for the specified peer, so generally speaking it can * be different for different port depending on the IDT PCIe-switch * initialization. * * Return: the number of memory windows. */ static int idt_ntb_mw_count(struct ntb_dev *ntb, int pidx) { … } /* * idt_ntb_mw_get_align() - inbound memory window parameters (NTB API callback) * @ntb: NTB device context. * @pidx: Port index of peer device. * @widx: Memory window index. * @addr_align: OUT - the base alignment for translating the memory window * @size_align: OUT - the size alignment for translating the memory window * @size_max: OUT - the maximum size of the memory window * * The peer memory window parameters have already been determined, so just * return the corresponding values, which mustn't change within session. * * Return: Zero on success, otherwise a negative error number. */ static int idt_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx, resource_size_t *addr_align, resource_size_t *size_align, resource_size_t *size_max) { … } /* * idt_ntb_peer_mw_count() - number of outbound memory windows * (NTB API callback) * @ntb: NTB device context. * * Outbound memory windows parameters have been determined based on the * BAR setup registers value, which are mostly constants within one session. * * Return: the number of memory windows. */ static int idt_ntb_peer_mw_count(struct ntb_dev *ntb) { … } /* * idt_ntb_peer_mw_get_addr() - get map address of an outbound memory window * (NTB API callback) * @ntb: NTB device context. * @widx: Memory window index (within ntb_peer_mw_count() return value). * @base: OUT - the base address of mapping region. * @size: OUT - the size of mapping region. * * Return just parameters of BAR resources mapping. Size reflects just the size * of the resource * * Return: Zero on success, otherwise a negative error number. */ static int idt_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int widx, phys_addr_t *base, resource_size_t *size) { … } /* * idt_ntb_peer_mw_set_trans() - set a translation address of a memory window * (NTB API callback) * @ntb: NTB device context. * @pidx: Port index of peer device the translation address received from. * @widx: Memory window index. * @addr: The dma address of the shared memory to access. * @size: The size of the shared memory to access. * * The Direct address translation and LUT base translation is initialized a * bit differenet. Although the parameters restriction are now determined by * the same code. * * Return: Zero on success, otherwise an error number. */ static int idt_ntb_peer_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, u64 addr, resource_size_t size) { … } /* * idt_ntb_peer_mw_clear_trans() - clear the outbound MW translation address * (NTB API callback) * @ntb: NTB device context. * @pidx: Port index of peer device. * @widx: Memory window index. * * It effectively disables the translation over the specified outbound MW. * * Return: Zero on success, otherwise an error number. */ static int idt_ntb_peer_mw_clear_trans(struct ntb_dev *ntb, int pidx, int widx) { … } /*============================================================================= * 5. Doorbell operations * * Doorbell functionality of IDT PCIe-switches is pretty unusual. First of * all there is global doorbell register which state can be changed by any * NT-function of the IDT device in accordance with global permissions. These * permissions configs are not supported by NTB API, so it must be done by * either BIOS or EEPROM settings. In the same way the state of the global * doorbell is reflected to the NT-functions local inbound doorbell registers. * It can lead to situations when client driver sets some peer doorbell bits * and get them bounced back to local inbound doorbell if permissions are * granted. * Secondly there is just one IRQ vector for Doorbell, Message, Temperature * and Switch events, so if client driver left any of Doorbell bits set and * some other event occurred, the driver will be notified of Doorbell event * again. *============================================================================= */ /* * idt_db_isr() - doorbell event ISR * @ndev: IDT NTB hardware driver descriptor * @ntint_sts: NT-function interrupt status * * Doorbell event happans when DBELL bit of NTINTSTS switches from 0 to 1. * It happens only when unmasked doorbell bits are set to ones on completely * zeroed doorbell register. * The method is called from PCIe ISR bottom-half routine. */ static void idt_db_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) { … } /* * idt_ntb_db_valid_mask() - get a mask of doorbell bits supported by the ntb * (NTB API callback) * @ntb: NTB device context. * * IDT PCIe-switches expose just one Doorbell register of DWORD size. * * Return: A mask of doorbell bits supported by the ntb. */ static u64 idt_ntb_db_valid_mask(struct ntb_dev *ntb) { … } /* * idt_ntb_db_read() - read the local doorbell register (NTB API callback) * @ntb: NTB device context. * * There is just on inbound doorbell register of each NT-function, so * this method return it value. * * Return: The bits currently set in the local doorbell register. */ static u64 idt_ntb_db_read(struct ntb_dev *ntb) { … } /* * idt_ntb_db_clear() - clear bits in the local doorbell register * (NTB API callback) * @ntb: NTB device context. * @db_bits: Doorbell bits to clear. * * Clear bits of inbound doorbell register by writing ones to it. * * NOTE! Invalid bits are always considered cleared so it's not an error * to clear them over. * * Return: always zero as success. */ static int idt_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) { … } /* * idt_ntb_db_read_mask() - read the local doorbell mask (NTB API callback) * @ntb: NTB device context. * * Each inbound doorbell bit can be masked from generating IRQ by setting * the corresponding bit in inbound doorbell mask. So this method returns * the value of the register. * * Return: The bits currently set in the local doorbell mask register. */ static u64 idt_ntb_db_read_mask(struct ntb_dev *ntb) { … } /* * idt_ntb_db_set_mask() - set bits in the local doorbell mask * (NTB API callback) * @ntb: NTB device context. * @db_bits: Doorbell mask bits to set. * * The inbound doorbell register mask value must be read, then OR'ed with * passed field and only then set back. * * Return: zero on success, negative error if invalid argument passed. */ static int idt_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) { … } /* * idt_ntb_db_clear_mask() - clear bits in the local doorbell mask * (NTB API callback) * @ntb: NTB device context. * @db_bits: Doorbell bits to clear. * * The method just clears the set bits up in accordance with the passed * bitfield. IDT PCIe-switch shall generate an interrupt if there hasn't * been any unmasked bit set before current unmasking. Otherwise IRQ won't * be generated since there is only one IRQ vector for all doorbells. * * Return: always zero as success */ static int idt_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) { … } /* * idt_ntb_peer_db_set() - set bits in the peer doorbell register * (NTB API callback) * @ntb: NTB device context. * @db_bits: Doorbell bits to set. * * IDT PCIe-switches exposes local outbound doorbell register to change peer * inbound doorbell register state. * * Return: zero on success, negative error if invalid argument passed. */ static int idt_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) { … } /*============================================================================= * 6. Messaging operations * * Each NT-function of IDT PCIe-switch has four inbound and four outbound * message registers. Each outbound message register can be connected to one or * even more than one peer inbound message registers by setting global * configurations. Since NTB API permits one-on-one message registers mapping * only, the driver acts in according with that restriction. *============================================================================= */ /* * idt_init_msg() - initialize messaging interface * @ndev: IDT NTB hardware driver descriptor * * Just initialize the message registers routing tables locker. */ static void idt_init_msg(struct idt_ntb_dev *ndev) { … } /* * idt_msg_isr() - message event ISR * @ndev: IDT NTB hardware driver descriptor * @ntint_sts: NT-function interrupt status * * Message event happens when MSG bit of NTINTSTS switches from 0 to 1. * It happens only when unmasked message status bits are set to ones on * completely zeroed message status register. * The method is called from PCIe ISR bottom-half routine. */ static void idt_msg_isr(struct idt_ntb_dev *ndev, u32 ntint_sts) { … } /* * idt_ntb_msg_count() - get the number of message registers (NTB API callback) * @ntb: NTB device context. * * IDT PCIe-switches support four message registers. * * Return: the number of message registers. */ static int idt_ntb_msg_count(struct ntb_dev *ntb) { … } /* * idt_ntb_msg_inbits() - get a bitfield of inbound message registers status * (NTB API callback) * @ntb: NTB device context. * * NT message status register is shared between inbound and outbound message * registers status * * Return: bitfield of inbound message registers. */ static u64 idt_ntb_msg_inbits(struct ntb_dev *ntb) { … } /* * idt_ntb_msg_outbits() - get a bitfield of outbound message registers status * (NTB API callback) * @ntb: NTB device context. * * NT message status register is shared between inbound and outbound message * registers status * * Return: bitfield of outbound message registers. */ static u64 idt_ntb_msg_outbits(struct ntb_dev *ntb) { … } /* * idt_ntb_msg_read_sts() - read the message registers status (NTB API callback) * @ntb: NTB device context. * * IDT PCIe-switches expose message status registers to notify drivers of * incoming data and failures in case if peer message register isn't freed. * * Return: status bits of message registers */ static u64 idt_ntb_msg_read_sts(struct ntb_dev *ntb) { … } /* * idt_ntb_msg_clear_sts() - clear status bits of message registers * (NTB API callback) * @ntb: NTB device context. * @sts_bits: Status bits to clear. * * Clear bits in the status register by writing ones. * * NOTE! Invalid bits are always considered cleared so it's not an error * to clear them over. * * Return: always zero as success. */ static int idt_ntb_msg_clear_sts(struct ntb_dev *ntb, u64 sts_bits) { … } /* * idt_ntb_msg_set_mask() - set mask of message register status bits * (NTB API callback) * @ntb: NTB device context. * @mask_bits: Mask bits. * * Mask the message status bits from raising an IRQ. * * Return: zero on success, negative error if invalid argument passed. */ static int idt_ntb_msg_set_mask(struct ntb_dev *ntb, u64 mask_bits) { … } /* * idt_ntb_msg_clear_mask() - clear message registers mask * (NTB API callback) * @ntb: NTB device context. * @mask_bits: Mask bits. * * Clear mask of message status bits IRQs. * * Return: always zero as success. */ static int idt_ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits) { … } /* * idt_ntb_msg_read() - read message register with specified index * (NTB API callback) * @ntb: NTB device context. * @pidx: OUT - Port index of peer device a message retrieved from * @midx: Message register index * * Read data from the specified message register and source register. * * Return: inbound message register value. */ static u32 idt_ntb_msg_read(struct ntb_dev *ntb, int *pidx, int midx) { … } /* * idt_ntb_peer_msg_write() - write data to the specified message register * (NTB API callback) * @ntb: NTB device context. * @pidx: Port index of peer device a message being sent to * @midx: Message register index * @msg: Data to send * * Just try to send data to a peer. Message status register should be * checked by client driver. * * Return: zero on success, negative error if invalid argument passed. */ static int idt_ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx, u32 msg) { … } /*============================================================================= * 7. Temperature sensor operations * * IDT PCIe-switch has an embedded temperature sensor, which can be used to * check current chip core temperature. Since a workload environment can be * different on different platforms, an offset and ADC/filter settings can be * specified. Although the offset configuration is only exposed to the sysfs * hwmon interface at the moment. The rest of the settings can be adjusted * for instance by the BIOS/EEPROM firmware. *============================================================================= */ /* * idt_get_deg() - convert millidegree Celsius value to just degree * @mdegC: IN - millidegree Celsius value * * Return: Degree corresponding to the passed millidegree value */ static inline s8 idt_get_deg(long mdegC) { … } /* * idt_get_frac() - retrieve 0/0.5 fraction of the millidegree Celsius value * @mdegC: IN - millidegree Celsius value * * Return: 0/0.5 degree fraction of the passed millidegree value */ static inline u8 idt_get_deg_frac(long mdegC) { … } /* * idt_get_temp_fmt() - convert millidegree Celsius value to 0:7:1 format * @mdegC: IN - millidegree Celsius value * * Return: 0:7:1 format acceptable by the IDT temperature sensor */ static inline u8 idt_temp_get_fmt(long mdegC) { … } /* * idt_get_temp_sval() - convert temp sample to signed millidegree Celsius * @data: IN - shifted to LSB 8-bits temperature sample * * Return: signed millidegree Celsius */ static inline long idt_get_temp_sval(u32 data) { … } /* * idt_get_temp_sval() - convert temp sample to unsigned millidegree Celsius * @data: IN - shifted to LSB 8-bits temperature sample * * Return: unsigned millidegree Celsius */ static inline long idt_get_temp_uval(u32 data) { … } /* * idt_read_temp() - read temperature from chip sensor * @ntb: NTB device context. * @type: IN - type of the temperature value to read * @val: OUT - integer value of temperature in millidegree Celsius */ static void idt_read_temp(struct idt_ntb_dev *ndev, const enum idt_temp_val type, long *val) { … } /* * idt_write_temp() - write temperature to the chip sensor register * @ntb: NTB device context. * @type: IN - type of the temperature value to change * @val: IN - integer value of temperature in millidegree Celsius */ static void idt_write_temp(struct idt_ntb_dev *ndev, const enum idt_temp_val type, const long val) { … } /* * idt_sysfs_show_temp() - printout corresponding temperature value * @dev: Pointer to the NTB device structure * @da: Sensor device attribute structure * @buf: Buffer to print temperature out * * Return: Number of written symbols or negative error */ static ssize_t idt_sysfs_show_temp(struct device *dev, struct device_attribute *da, char *buf) { … } /* * idt_sysfs_set_temp() - set corresponding temperature value * @dev: Pointer to the NTB device structure * @da: Sensor device attribute structure * @buf: Buffer to print temperature out * @count: Size of the passed buffer * * Return: Number of written symbols or negative error */ static ssize_t idt_sysfs_set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * idt_sysfs_reset_hist() - reset temperature history * @dev: Pointer to the NTB device structure * @da: Sensor device attribute structure * @buf: Buffer to print temperature out * @count: Size of the passed buffer * * Return: Number of written symbols or negative error */ static ssize_t idt_sysfs_reset_hist(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Hwmon IDT sysfs attributes */ static SENSOR_DEVICE_ATTR(temp1_input, 0444, idt_sysfs_show_temp, NULL, IDT_TEMP_CUR); static SENSOR_DEVICE_ATTR(temp1_lowest, 0444, idt_sysfs_show_temp, NULL, IDT_TEMP_LOW); static SENSOR_DEVICE_ATTR(temp1_highest, 0444, idt_sysfs_show_temp, NULL, IDT_TEMP_HIGH); static SENSOR_DEVICE_ATTR(temp1_offset, 0644, idt_sysfs_show_temp, idt_sysfs_set_temp, IDT_TEMP_OFFSET); static DEVICE_ATTR(temp1_reset_history, 0200, NULL, idt_sysfs_reset_hist); /* * Hwmon IDT sysfs attributes group */ static struct attribute *idt_temp_attrs[] = …; ATTRIBUTE_GROUPS(…); /* * idt_init_temp() - initialize temperature sensor interface * @ndev: IDT NTB hardware driver descriptor * * Simple sensor initializarion method is responsible for device switching * on and resource management based hwmon interface registration. Note, that * since the device is shared we won't disable it on remove, but leave it * working until the system is powered off. */ static void idt_init_temp(struct idt_ntb_dev *ndev) { … } /*============================================================================= * 8. ISRs related operations * * IDT PCIe-switch has strangely developed IRQ system. There is just one * interrupt vector for doorbell and message registers. So the hardware driver * can't determine actual source of IRQ if, for example, message event happened * while any of unmasked doorbell is still set. The similar situation may be if * switch or temperature sensor events pop up. The difference is that SEVENT * and TMPSENSOR bits of NT interrupt status register can be cleaned by * IRQ handler so a next interrupt request won't have false handling of * corresponding events. * The hardware driver has only bottom-half handler of the IRQ, since if any * of events happened the device won't raise it again before the last one is * handled by clearing of corresponding NTINTSTS bit. *============================================================================= */ static irqreturn_t idt_thread_isr(int irq, void *devid); /* * idt_init_isr() - initialize PCIe interrupt handler * @ndev: IDT NTB hardware driver descriptor * * Return: zero on success, otherwise a negative error number. */ static int idt_init_isr(struct idt_ntb_dev *ndev) { … } /* * idt_deinit_ist() - deinitialize PCIe interrupt handler * @ndev: IDT NTB hardware driver descriptor * * Disable corresponding interrupts and free allocated IRQ vectors. */ static void idt_deinit_isr(struct idt_ntb_dev *ndev) { … } /* * idt_thread_isr() - NT function interrupts handler * @irq: IRQ number * @devid: Custom buffer * * It reads current NT interrupts state register and handles all the event * it declares. * The method is bottom-half routine of actual default PCIe IRQ handler. */ static irqreturn_t idt_thread_isr(int irq, void *devid) { … } /*=========================================================================== * 9. NTB hardware driver initialization *=========================================================================== */ /* * NTB API operations */ static const struct ntb_dev_ops idt_ntb_ops = …; /* * idt_register_device() - register IDT NTB device * @ndev: IDT NTB hardware driver descriptor * * Return: zero on success, otherwise a negative error number. */ static int idt_register_device(struct idt_ntb_dev *ndev) { … } /* * idt_unregister_device() - unregister IDT NTB device * @ndev: IDT NTB hardware driver descriptor */ static void idt_unregister_device(struct idt_ntb_dev *ndev) { … } /*============================================================================= * 10. DebugFS node initialization *============================================================================= */ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp); /* * Driver DebugFS info file operations */ static const struct file_operations idt_dbgfs_info_ops = …; /* * idt_dbgfs_info_read() - DebugFS read info node callback * @file: File node descriptor. * @ubuf: User-space buffer to put data to * @count: Size of the buffer * @offp: Offset within the buffer */ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp) { … } /* * idt_init_dbgfs() - initialize DebugFS node * @ndev: IDT NTB hardware driver descriptor * * Return: zero on success, otherwise a negative error number. */ static int idt_init_dbgfs(struct idt_ntb_dev *ndev) { … } /* * idt_deinit_dbgfs() - deinitialize DebugFS node * @ndev: IDT NTB hardware driver descriptor * * Just discard the info node from DebugFS */ static void idt_deinit_dbgfs(struct idt_ntb_dev *ndev) { … } /*============================================================================= * 11. Basic PCIe device initialization *============================================================================= */ /* * idt_check_setup() - Check whether the IDT PCIe-swtich is properly * pre-initialized * @pdev: Pointer to the PCI device descriptor * * Return: zero on success, otherwise a negative error number. */ static int idt_check_setup(struct pci_dev *pdev) { … } /* * Create the IDT PCIe-switch driver descriptor * @pdev: Pointer to the PCI device descriptor * @id: IDT PCIe-device configuration * * It just allocates a memory for IDT PCIe-switch device structure and * initializes some commonly used fields. * * No need of release method, since managed device resource is used for * memory allocation. * * Return: pointer to the descriptor, otherwise a negative error number. */ static struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev, const struct pci_device_id *id) { … } /* * idt_init_pci() - initialize the basic PCI-related subsystem * @ndev: Pointer to the IDT PCIe-switch driver descriptor * * Managed device resources will be freed automatically in case of failure or * driver detachment. * * Return: zero on success, otherwise negative error number. */ static int idt_init_pci(struct idt_ntb_dev *ndev) { … } /* * idt_deinit_pci() - deinitialize the basic PCI-related subsystem * @ndev: Pointer to the IDT PCIe-switch driver descriptor * * Managed resources will be freed on the driver detachment */ static void idt_deinit_pci(struct idt_ntb_dev *ndev) { … } /*=========================================================================== * 12. PCI bus callback functions *=========================================================================== */ /* * idt_pci_probe() - PCI device probe callback * @pdev: Pointer to PCI device structure * @id: PCIe device custom descriptor * * Return: zero on success, otherwise negative error number */ static int idt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { … } /* * idt_pci_probe() - PCI device remove callback * @pdev: Pointer to PCI device structure */ static void idt_pci_remove(struct pci_dev *pdev) { … } /* * IDT PCIe-switch models ports configuration structures */ static const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = …; static const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = …; static const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = …; static const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = …; static const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = …; static const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = …; static const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = …; static const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = …; /* * PCI-ids table of the supported IDT PCIe-switch devices */ static const struct pci_device_id idt_pci_tbl[] = …; MODULE_DEVICE_TABLE(pci, idt_pci_tbl); /* * IDT PCIe-switch NT-function device driver structure definition */ static struct pci_driver idt_pci_driver = …; static int __init idt_pci_driver_init(void) { … } module_init(…) …; static void __exit idt_pci_driver_exit(void) { … } module_exit(idt_pci_driver_exit);