// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Broadcom MPI3 Storage Controllers * * Copyright (C) 2017-2023 Broadcom Inc. * (mailto: [email protected]) * */ #include "mpi3mr.h" #include <linux/bsg-lib.h> #include <uapi/scsi/scsi_bsg_mpi3mr.h> /** * mpi3mr_alloc_trace_buffer: Allocate trace buffer * @mrioc: Adapter instance reference * @trace_size: Trace buffer size * * Allocate trace buffer * Return: 0 on success, non-zero on failure. */ static int mpi3mr_alloc_trace_buffer(struct mpi3mr_ioc *mrioc, u32 trace_size) { … } /** * mpi3mr_alloc_diag_bufs - Allocate memory for diag buffers * @mrioc: Adapter instance reference * * This functions checks whether the driver defined buffer sizes * are greater than IOCFacts provided controller local buffer * sizes and if the driver defined sizes are more then the * driver allocates the specific buffer by reading driver page1 * * Return: Nothing. */ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_issue_diag_buf_post - Send diag buffer post req * @mrioc: Adapter instance reference * @diag_buffer: Diagnostic buffer descriptor * * Issue diagnostic buffer post MPI request through admin queue * and wait for the completion of it or time out. * * Return: 0 on success, non-zero on failures. */ int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc, struct diag_buffer_desc *diag_buffer) { … } /** * mpi3mr_post_diag_bufs - Post diag buffers to the controller * @mrioc: Adapter instance reference * * This function calls helper function to post both trace and * firmware buffers to the controller. * * Return: None */ int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_issue_diag_buf_release - Send diag buffer release req * @mrioc: Adapter instance reference * @diag_buffer: Diagnostic buffer descriptor * * Issue diagnostic buffer manage MPI request with release * action request through admin queue and wait for the * completion of it or time out. * * Return: 0 on success, non-zero on failures. */ int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc, struct diag_buffer_desc *diag_buffer) { … } /** * mpi3mr_process_trigger - Generic HDB Trigger handler * @mrioc: Adapter instance reference * @trigger_type: Trigger type * @trigger_data: Trigger data * @trigger_flags: Trigger flags * * This function checks validity of HDB, triggers and based on * trigger information, creates an event to be processed in the * firmware event worker thread . * * This function should be called with trigger spinlock held * * Return: Nothing */ static void mpi3mr_process_trigger(struct mpi3mr_ioc *mrioc, u8 trigger_type, union mpi3mr_trigger_data *trigger_data, u8 trigger_flags) { … } /** * mpi3mr_global_trigger - Global HDB trigger handler * @mrioc: Adapter instance reference * @trigger_data: Trigger data * * This function checks whether the given global trigger is * enabled in the driver page 2 and if so calls generic trigger * handler to queue event for HDB release. * * Return: Nothing */ void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data) { … } /** * mpi3mr_scsisense_trigger - SCSI sense HDB trigger handler * @mrioc: Adapter instance reference * @sensekey: Sense Key * @asc: Additional Sense Code * @ascq: Additional Sense Code Qualifier * * This function compares SCSI sense trigger values with driver * page 2 values and calls generic trigger handler to release * HDBs if match found * * Return: Nothing */ void mpi3mr_scsisense_trigger(struct mpi3mr_ioc *mrioc, u8 sensekey, u8 asc, u8 ascq) { … } /** * mpi3mr_event_trigger - MPI event HDB trigger handler * @mrioc: Adapter instance reference * @event: MPI Event * * This function compares event trigger values with driver page * 2 values and calls generic trigger handler to release * HDBs if match found. * * Return: Nothing */ void mpi3mr_event_trigger(struct mpi3mr_ioc *mrioc, u8 event) { … } /** * mpi3mr_reply_trigger - MPI Reply HDB trigger handler * @mrioc: Adapter instance reference * @ioc_status: Masked value of IOC Status from MPI Reply * @ioc_loginfo: IOC Log Info from MPI Reply * * This function compares IOC status and IOC log info trigger * values with driver page 2 values and calls generic trigger * handler to release HDBs if match found. * * Return: Nothing */ void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 ioc_status, u32 ioc_loginfo) { … } /** * mpi3mr_get_num_trigger - Gets number of HDB triggers * @mrioc: Adapter instance reference * @num_triggers: Number of triggers * @page_action: Page action * * This function reads number of triggers by reading driver page * 2 * * Return: 0 on success and proper error codes on failure */ static int mpi3mr_get_num_trigger(struct mpi3mr_ioc *mrioc, u8 *num_triggers, u8 page_action) { … } /** * mpi3mr_refresh_trigger - Handler for Refresh trigger BSG * @mrioc: Adapter instance reference * @page_action: Page action * * This function caches the driver page 2 in the driver's memory * by reading driver page 2 from the controller for a given page * type and updates the HDB trigger values * * Return: 0 on success and proper error codes on failure */ int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_action) { … } /** * mpi3mr_release_diag_bufs - Release diag buffers * @mrioc: Adapter instance reference * @skip_rel_action: Skip release action and set buffer state * * This function calls helper function to release both trace and * firmware buffers from the controller. * * Return: None */ void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action) { … } /** * mpi3mr_set_trigger_data_in_hdb - Updates HDB trigger type and * trigger data * * @hdb: HDB pointer * @type: Trigger type * @data: Trigger data * @force: Trigger overwrite flag * @trigger_data: Pointer to trigger data information * * Updates trigger type and trigger data based on parameter * passed to this function * * Return: Nothing */ void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb, u8 type, union mpi3mr_trigger_data *trigger_data, bool force) { … } /** * mpi3mr_set_trigger_data_in_all_hdb - Updates HDB trigger type * and trigger data for all HDB * * @mrioc: Adapter instance reference * @type: Trigger type * @data: Trigger data * @force: Trigger overwrite flag * @trigger_data: Pointer to trigger data information * * Updates trigger type and trigger data based on parameter * passed to this function * * Return: Nothing */ void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc, u8 type, union mpi3mr_trigger_data *trigger_data, bool force) { … } /** * mpi3mr_hdbstatuschg_evt_th - HDB status change evt tophalf * @mrioc: Adapter instance reference * @event_reply: event data * * Modifies the status of the applicable diag buffer descriptors * * Return: Nothing */ void mpi3mr_hdbstatuschg_evt_th(struct mpi3mr_ioc *mrioc, struct mpi3_event_notification_reply *event_reply) { … } /** * mpi3mr_diag_buffer_for_type - returns buffer desc for type * @mrioc: Adapter instance reference * @buf_type: Diagnostic buffer type * * Identifies matching diag descriptor from mrioc for given diag * buffer type. * * Return: diag buffer descriptor on success, NULL on failures. */ struct diag_buffer_desc * mpi3mr_diag_buffer_for_type(struct mpi3mr_ioc *mrioc, u8 buf_type) { … } /** * mpi3mr_bsg_pel_abort - sends PEL abort request * @mrioc: Adapter instance reference * * This function sends PEL abort request to the firmware through * admin request queue. * * Return: 0 on success, -1 on failure */ static int mpi3mr_bsg_pel_abort(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_bsg_verify_adapter - verify adapter number is valid * @ioc_number: Adapter number * * This function returns the adapter instance pointer of given * adapter number. If adapter number does not match with the * driver's adapter list, driver returns NULL. * * Return: adapter instance reference */ static struct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number) { … } /** * mpi3mr_bsg_refresh_hdb_triggers - Refresh HDB trigger data * @mrioc: Adapter instance reference * @job: BSG Job pointer * * This function reads the controller trigger config page as * defined by the input page type and refreshes the driver's * local trigger information structures with the controller's * config page data. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_refresh_hdb_triggers(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_upload_hdb - Upload a specific HDB to user space * @mrioc: Adapter instance reference * @job: BSG Job pointer * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_upload_hdb(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_repost_hdb - Re-post HDB * @mrioc: Adapter instance reference * @job: BSG job pointer * * This function retrieves the HDB descriptor corresponding to a * given buffer type and if the HDB is in released status then * posts the HDB with the firmware. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_repost_hdb(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_query_hdb - Handler for query HDB command * @mrioc: Adapter instance reference * @job: BSG job pointer * * This function prepares and copies the host diagnostic buffer * entries to the user buffer. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_query_hdb(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_enable_logdata - Handler for log data enable * @mrioc: Adapter instance reference * @job: BSG job reference * * This function enables log data caching in the driver if not * already enabled and return the maximum number of log data * entries that can be cached in the driver. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_enable_logdata(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_get_logdata - Handler for get log data * @mrioc: Adapter instance reference * @job: BSG job pointer * This function copies the log data entries to the user buffer * when log caching is enabled in the driver. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_get_logdata(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_pel_enable - Handler for PEL enable driver * @mrioc: Adapter instance reference * @job: BSG job pointer * * This function is the handler for PEL enable driver. * Validates the application given class and locale and if * requires aborts the existing PEL wait request and/or issues * new PEL wait request to the firmware and returns. * * Return: 0 on success and proper error codes on failure. */ static long mpi3mr_bsg_pel_enable(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_get_all_tgt_info - Get all target information * @mrioc: Adapter instance reference * @job: BSG job reference * * This function copies the driver managed target devices device * handle, persistent ID, bus ID and taret ID to the user * provided buffer for the specific controller. This function * also provides the number of devices managed by the driver for * the specific controller. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_get_all_tgt_info(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_get_change_count - Get topology change count * @mrioc: Adapter instance reference * @job: BSG job reference * * This function copies the toplogy change count provided by the * driver in events and cached in the driver to the user * provided buffer for the specific controller. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_get_change_count(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_adp_reset - Issue controller reset * @mrioc: Adapter instance reference * @job: BSG job reference * * This function identifies the user provided reset type and * issues approporiate reset to the controller and wait for that * to complete and reinitialize the controller and then returns * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_adp_reset(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_populate_adpinfo - Get adapter info command handler * @mrioc: Adapter instance reference * @job: BSG job reference * * This function provides adapter information for the given * controller * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_populate_adpinfo(struct mpi3mr_ioc *mrioc, struct bsg_job *job) { … } /** * mpi3mr_bsg_process_drv_cmds - Driver Command handler * @job: BSG job reference * * This function is the top level handler for driver commands, * this does basic validation of the buffer and identifies the * opcode and switches to correct sub handler. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_process_drv_cmds(struct bsg_job *job) { … } /** * mpi3mr_total_num_ioctl_sges - Count number of SGEs required * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * * This function returns total number of data SGEs required * including zero length SGEs and excluding management request * and response buffer for the given list of data buffer * descriptors * * Return: Number of SGE elements needed */ static inline u16 mpi3mr_total_num_ioctl_sges(struct mpi3mr_buf_map *drv_bufs, u8 bufcnt) { … } /** * mpi3mr_bsg_build_sgl - SGL construction for MPI commands * @mrioc: Adapter instance reference * @mpi_req: MPI request * @sgl_offset: offset to start sgl in the MPI request * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * @is_rmc: Does the buffer list has management command buffer * @is_rmr: Does the buffer list has management response buffer * @num_datasges: Number of data buffers in the list * * This function places the DMA address of the given buffers in * proper format as SGEs in the given MPI request. * * Return: 0 on success,-1 on failure */ static int mpi3mr_bsg_build_sgl(struct mpi3mr_ioc *mrioc, u8 *mpi_req, u32 sgl_offset, struct mpi3mr_buf_map *drv_bufs, u8 bufcnt, u8 is_rmc, u8 is_rmr, u8 num_datasges) { … } /** * mpi3mr_get_nvme_data_fmt - returns the NVMe data format * @nvme_encap_request: NVMe encapsulated MPI request * * This function returns the type of the data format specified * in user provided NVMe command in NVMe encapsulated request. * * Return: Data format of the NVMe command (PRP/SGL etc) */ static unsigned int mpi3mr_get_nvme_data_fmt( struct mpi3_nvme_encapsulated_request *nvme_encap_request) { … } /** * mpi3mr_build_nvme_sgl - SGL constructor for NVME * encapsulated request * @mrioc: Adapter instance reference * @nvme_encap_request: NVMe encapsulated MPI request * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * * This function places the DMA address of the given buffers in * proper format as SGEs in the given NVMe encapsulated request. * * Return: 0 on success, -1 on failure */ static int mpi3mr_build_nvme_sgl(struct mpi3mr_ioc *mrioc, struct mpi3_nvme_encapsulated_request *nvme_encap_request, struct mpi3mr_buf_map *drv_bufs, u8 bufcnt) { … } /** * mpi3mr_build_nvme_prp - PRP constructor for NVME * encapsulated request * @mrioc: Adapter instance reference * @nvme_encap_request: NVMe encapsulated MPI request * @drv_bufs: DMA address of the buffers to be placed in SGL * @bufcnt: Number of DMA buffers * * This function places the DMA address of the given buffers in * proper format as PRP entries in the given NVMe encapsulated * request. * * Return: 0 on success, -1 on failure */ static int mpi3mr_build_nvme_prp(struct mpi3mr_ioc *mrioc, struct mpi3_nvme_encapsulated_request *nvme_encap_request, struct mpi3mr_buf_map *drv_bufs, u8 bufcnt) { … } /** * mpi3mr_map_data_buffer_dma - build dma descriptors for data * buffers * @mrioc: Adapter instance reference * @drv_buf: buffer map descriptor * @desc_count: Number of already consumed dma descriptors * * This function computes how many pre-allocated DMA descriptors * are required for the given data buffer and if those number of * descriptors are free, then setup the mapping of the scattered * DMA address to the given data buffer, if the data direction * of the buffer is DMA_TO_DEVICE then the actual data is copied to * the DMA buffers * * Return: 0 on success, -1 on failure */ static int mpi3mr_map_data_buffer_dma(struct mpi3mr_ioc *mrioc, struct mpi3mr_buf_map *drv_buf, u16 desc_count) { … } /** * mpi3mr_bsg_process_mpt_cmds - MPI Pass through BSG handler * @job: BSG job reference * * This function is the top level handler for MPI Pass through * command, this does basic validation of the input data buffers, * identifies the given buffer types and MPI command, allocates * DMAable memory for user given buffers, construstcs SGL * properly and passes the command to the firmware. * * Once the MPI command is completed the driver copies the data * if any and reply, sense information to user provided buffers. * If the command is timed out then issues controller reset * prior to returning. * * Return: 0 on success and proper error codes on failure */ static long mpi3mr_bsg_process_mpt_cmds(struct bsg_job *job) { … } /** * mpi3mr_app_save_logdata - Save Log Data events * @mrioc: Adapter instance reference * @event_data: event data associated with log data event * @event_data_size: event data size to copy * * If log data event caching is enabled by the applicatiobns, * then this function saves the log data in the circular queue * and Sends async signal SIGIO to indicate there is an async * event from the firmware to the event monitoring applications. * * Return:Nothing */ void mpi3mr_app_save_logdata(struct mpi3mr_ioc *mrioc, char *event_data, u16 event_data_size) { … } /** * mpi3mr_bsg_request - bsg request entry point * @job: BSG job reference * * This is driver's entry point for bsg requests * * Return: 0 on success and proper error codes on failure */ static int mpi3mr_bsg_request(struct bsg_job *job) { … } /** * mpi3mr_bsg_exit - de-registration from bsg layer * @mrioc: Adapter instance reference * * This will be called during driver unload and all * bsg resources allocated during load will be freed. * * Return:Nothing */ void mpi3mr_bsg_exit(struct mpi3mr_ioc *mrioc) { … } /** * mpi3mr_bsg_node_release -release bsg device node * @dev: bsg device node * * decrements bsg dev parent reference count * * Return:Nothing */ static void mpi3mr_bsg_node_release(struct device *dev) { … } /** * mpi3mr_bsg_init - registration with bsg layer * @mrioc: Adapter instance reference * * This will be called during driver load and it will * register driver with bsg layer * * Return:Nothing */ void mpi3mr_bsg_init(struct mpi3mr_ioc *mrioc) { … } /** * version_fw_show - SysFS callback for firmware version read * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying firmware version */ static ssize_t version_fw_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(version_fw); /** * fw_queue_depth_show - SysFS callback for firmware max cmds * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying firmware max commands */ static ssize_t fw_queue_depth_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(fw_queue_depth); /** * op_req_q_count_show - SysFS callback for request queue count * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying request queue count */ static ssize_t op_req_q_count_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(op_req_q_count); /** * reply_queue_count_show - SysFS callback for reply queue count * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying reply queue count */ static ssize_t reply_queue_count_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(reply_queue_count); /** * logging_level_show - Show controller debug level * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * A sysfs 'read/write' shost attribute, to show the current * debug log level used by the driver for the specific * controller. * * Return: sysfs_emit() return */ static ssize_t logging_level_show(struct device *dev, struct device_attribute *attr, char *buf) { … } /** * logging_level_store- Change controller debug level * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * @count: size of the buffer * * A sysfs 'read/write' shost attribute, to change the current * debug log level used by the driver for the specific * controller. * * Return: strlen() return */ static ssize_t logging_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(logging_level); /** * adp_state_show() - SysFS callback for adapter state show * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying adapter state */ static ssize_t adp_state_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(adp_state); static struct attribute *mpi3mr_host_attrs[] = …; static const struct attribute_group mpi3mr_host_attr_group = …; const struct attribute_group *mpi3mr_host_groups[] = …; /* * SCSI Device attributes under sysfs */ /** * sas_address_show - SysFS callback for dev SASaddress display * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying SAS address of the * specific SAS/SATA end device. */ static ssize_t sas_address_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(sas_address); /** * device_handle_show - SysFS callback for device handle display * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying firmware internal * device handle of the specific device. */ static ssize_t device_handle_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(device_handle); /** * persistent_id_show - SysFS callback for persisten ID display * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Return: sysfs_emit() return after copying persistent ID of the * of the specific device. */ static ssize_t persistent_id_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(persistent_id); /** * sas_ncq_prio_supported_show - Indicate if device supports NCQ priority * @dev: pointer to embedded device * @attr: sas_ncq_prio_supported attribute descriptor * @buf: the buffer returned * * A sysfs 'read-only' sdev attribute, only works with SATA devices */ static ssize_t sas_ncq_prio_supported_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(sas_ncq_prio_supported); /** * sas_ncq_prio_enable_show - send prioritized io commands to device * @dev: pointer to embedded device * @attr: sas_ncq_prio_enable attribute descriptor * @buf: the buffer returned * * A sysfs 'read/write' sdev attribute, only works with SATA devices */ static ssize_t sas_ncq_prio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t sas_ncq_prio_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(sas_ncq_prio_enable); static struct attribute *mpi3mr_dev_attrs[] = …; static const struct attribute_group mpi3mr_dev_attr_group = …; const struct attribute_group *mpi3mr_dev_groups[] = …;