Files
src/sys/dev/ufshci/ufshci_private.h
T
Aymeric Wibo 95b4436e98 power: Rename sleep types
Make sleep type names clearer and more consistent, and allow space for
something like "os_hibernate" once that gets added to FreeBSD.

Reviewed by:	jaeyoon, olce, markj
Approved by:	jaeyoon, olce, markj
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D56920
2026-05-11 18:11:39 +01:00

652 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*-
* Copyright (c) 2025, Samsung Electronics Co., Ltd.
* Written by Jaeyoon Choi
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#ifndef __UFSHCI_PRIVATE_H__
#define __UFSHCI_PRIVATE_H__
#ifdef _KERNEL
#include <sys/types.h>
#else /* !_KERNEL */
#include <stdbool.h>
#include <stdint.h>
#endif /* _KERNEL */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/counter.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/memdesc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/power.h>
#include <sys/rman.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <cam/cam.h>
#include <cam/scsi/scsi_all.h>
#include "ufshci.h"
MALLOC_DECLARE(M_UFSHCI);
#define UFSHCI_DEVICE_INIT_TIMEOUT_MS (2000) /* in milliseconds */
#define UFSHCI_UIC_CMD_TIMEOUT_MS (500) /* in milliseconds */
#define UFSHCI_DEFAULT_TIMEOUT_PERIOD (10) /* in seconds */
#define UFSHCI_MIN_TIMEOUT_PERIOD (5) /* in seconds */
#define UFSHCI_MAX_TIMEOUT_PERIOD (120) /* in seconds */
#define UFSHCI_DEFAULT_RETRY_COUNT (4)
#define UFSHCI_UTR_ENTRIES (32)
#define UFSHCI_UTRM_ENTRIES (8)
#define UFSHCI_SECTOR_SIZE (512)
struct ufshci_controller;
struct ufshci_completion_poll_status {
struct ufshci_completion cpl;
int done;
bool error;
};
struct ufshci_request {
struct ufshci_upiu request_upiu;
size_t request_size;
size_t response_size;
struct memdesc payload;
enum ufshci_data_direction data_direction;
ufshci_cb_fn_t cb_fn;
void *cb_arg;
bool is_admin;
int32_t retries;
bool payload_valid;
bool spare[2]; /* Future use */
STAILQ_ENTRY(ufshci_request) stailq;
};
enum ufshci_slot_state {
UFSHCI_SLOT_STATE_FREE = 0x0,
UFSHCI_SLOT_STATE_RESERVED = 0x1,
UFSHCI_SLOT_STATE_SCHEDULED = 0x2,
UFSHCI_SLOT_STATE_TIMEOUT = 0x3,
UFSHCI_SLOT_STATE_NEED_ERROR_HANDLING = 0x4,
};
struct ufshci_tracker {
TAILQ_ENTRY(ufshci_tracker) tailq;
struct ufshci_request *req;
struct ufshci_req_queue *req_queue;
struct ufshci_hw_queue *hwq;
uint8_t slot_num;
enum ufshci_slot_state slot_state;
size_t response_size;
sbintime_t deadline;
bus_dmamap_t payload_dma_map;
uint64_t payload_addr;
struct ufshci_utp_cmd_desc *ucd;
bus_addr_t ucd_bus_addr;
uint16_t prdt_off;
uint16_t prdt_entry_cnt;
};
enum ufshci_queue_mode {
UFSHCI_Q_MODE_SDB = 0x00, /* Single Doorbell Mode*/
UFSHCI_Q_MODE_MCQ = 0x01, /* Multi-Circular Queue Mode*/
};
/*
* UFS uses slot-based Single Doorbell (SDB) mode for request submission by
* default and additionally supports Multi-Circular Queue (MCQ) in UFS 4.0. To
* minimize duplicated code between SDB and MCQ, mode dependent operations are
* extracted into ufshci_qops.
*/
struct ufshci_qops {
int (*construct)(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue, uint32_t num_entries,
bool is_task_mgmt);
void (*destroy)(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
struct ufshci_hw_queue *(*get_hw_queue)(
struct ufshci_req_queue *req_queue);
int (*enable)(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
void (*disable)(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
int (*reserve_slot)(struct ufshci_req_queue *req_queue,
struct ufshci_tracker **tr);
int (*reserve_admin_slot)(struct ufshci_req_queue *req_queue,
struct ufshci_tracker **tr);
void (*ring_doorbell)(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
bool (*is_doorbell_cleared)(struct ufshci_controller *ctrlr,
uint8_t slot);
void (*clear_cpl_ntf)(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
bool (*process_cpl)(struct ufshci_req_queue *req_queue);
int (*get_inflight_io)(struct ufshci_controller *ctrlr);
};
#define UFSHCI_SDB_Q 0 /* Queue number for a single doorbell queue */
enum ufshci_recovery {
RECOVERY_NONE = 0, /* Normal operations */
RECOVERY_WAITING, /* waiting for the reset to complete */
};
/*
* Generic queue container used by both SDB (fixed 32-slot bitmap) and MCQ
* (ring buffer) modes. Fields are shared; some such as sq_head, sq_tail and
* cq_head are not used in SDB but used in MCQ.
*/
struct ufshci_hw_queue {
struct ufshci_controller *ctrlr;
struct ufshci_req_queue *req_queue;
uint32_t id;
int domain;
int cpu;
struct callout timer; /* recovery lock */
bool timer_armed; /* recovery lock */
enum ufshci_recovery recovery_state; /* recovery lock */
union {
struct ufshci_utp_xfer_req_desc *utrd;
struct ufshci_utp_task_mgmt_req_desc *utmrd;
};
bus_dma_tag_t dma_tag_queue;
bus_dmamap_t queuemem_map;
bus_addr_t req_queue_addr;
bus_addr_t *ucd_bus_addr;
uint32_t num_entries;
uint32_t num_trackers;
TAILQ_HEAD(, ufshci_tracker) free_tr;
TAILQ_HEAD(, ufshci_tracker) outstanding_tr;
/*
* A Request List using the single doorbell method uses a dedicated
* ufshci_tracker, one per slot.
*/
struct ufshci_tracker **act_tr;
uint32_t sq_head; /* MCQ mode */
uint32_t sq_tail; /* MCQ mode */
uint32_t cq_head; /* MCQ mode */
uint32_t phase;
int64_t num_cmds;
int64_t num_intr_handler_calls;
int64_t num_retries;
int64_t num_failures;
/*
* Each lock may be acquired independently.
* When both are required, acquire them in this order to avoid
* deadlocks. (recovery_lock -> qlock)
*/
struct mtx_padalign qlock;
struct mtx_padalign recovery_lock;
};
struct ufshci_req_queue {
struct ufshci_controller *ctrlr;
int domain;
/*
* queue_mode: active transfer scheme
* UFSHCI_Q_MODE_SDB legacy singledoorbell list
* UFSHCI_Q_MODE_MCQ modern multicircular queue (UFSHCI4.0+)
*/
enum ufshci_queue_mode queue_mode;
uint8_t num_q;
struct ufshci_hw_queue *hwq;
struct ufshci_qops qops;
bool is_task_mgmt;
uint32_t num_entries;
uint32_t num_trackers;
/* Shared DMA resource */
struct ufshci_utp_cmd_desc *ucd;
bus_dma_tag_t dma_tag_ucd;
bus_dma_tag_t dma_tag_payload;
bus_dmamap_t ucdmem_map;
};
enum ufshci_dev_pwr {
UFSHCI_DEV_PWR_ACTIVE = 0,
UFSHCI_DEV_PWR_SLEEP,
UFSHCI_DEV_PWR_POWERDOWN,
UFSHCI_DEV_PWR_DEEPSLEEP,
UFSHCI_DEV_PWR_COUNT,
};
enum ufshci_uic_link_state {
UFSHCI_UIC_LINK_STATE_OFF = 0,
UFSHCI_UIC_LINK_STATE_ACTIVE,
UFSHCI_UIC_LINK_STATE_HIBERNATE,
UFSHCI_UIC_LINK_STATE_BROKEN,
};
struct ufshci_power_entry {
enum ufshci_dev_pwr dev_pwr;
uint8_t ssu_pc; /* SSU Power Condition */
enum ufshci_uic_link_state link_state;
};
/* SSU Power Condition 0x40 is defined in the UFS specification */
static const struct ufshci_power_entry power_map[POWER_STYPE_COUNT] = {
[POWER_STYPE_AWAKE] = { UFSHCI_DEV_PWR_ACTIVE, SSS_PC_ACTIVE,
UFSHCI_UIC_LINK_STATE_ACTIVE },
[POWER_STYPE_STANDBY] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE,
UFSHCI_UIC_LINK_STATE_HIBERNATE },
[POWER_STYPE_FW_SUSPEND] = { UFSHCI_DEV_PWR_POWERDOWN,
SSS_PC_STANDBY, UFSHCI_UIC_LINK_STATE_HIBERNATE },
[POWER_STYPE_SUSPEND_TO_IDLE] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE,
UFSHCI_UIC_LINK_STATE_HIBERNATE },
[POWER_STYPE_FW_HIBERNATE] = { UFSHCI_DEV_PWR_DEEPSLEEP, 0x40,
UFSHCI_UIC_LINK_STATE_OFF },
[POWER_STYPE_POWEROFF] = { UFSHCI_DEV_PWR_POWERDOWN, SSS_PC_STANDBY,
UFSHCI_UIC_LINK_STATE_OFF },
};
struct ufshci_device {
uint32_t max_lun_count;
struct ufshci_device_descriptor dev_desc;
struct ufshci_geometry_descriptor geo_desc;
/* WriteBooster */
bool is_wb_enabled;
bool is_wb_flush_enabled;
uint32_t wb_buffer_type;
uint32_t wb_buffer_size_mb;
uint32_t wb_user_space_config_option;
uint8_t wb_dedicated_lu;
uint32_t write_booster_flush_threshold;
/* Power mode */
bool power_mode_supported;
enum ufshci_dev_pwr power_mode;
enum ufshci_uic_link_state link_state;
/* Auto Hibernation */
bool auto_hibernation_supported;
uint32_t ahit;
};
/*
* One of these per allocated device.
*/
struct ufshci_controller {
device_t dev;
uint32_t quirks;
#define UFSHCI_QUIRK_IGNORE_UIC_POWER_MODE \
1 /* QEMU does not support UIC POWER MODE */
#define UFSHCI_QUIRK_LONG_PEER_PA_TACTIVATE \
2 /* Need an additional 200 ms of PA_TActivate */
#define UFSHCI_QUIRK_WAIT_AFTER_POWER_MODE_CHANGE \
4 /* Need to wait 1250us after power mode change */
#define UFSHCI_QUIRK_CHANGE_LANE_AND_GEAR_SEPARATELY \
8 /* Need to change the number of lanes before changing HS-GEAR. */
#define UFSHCI_QUIRK_NOT_SUPPORT_ABORT_TASK \
16 /* QEMU does not support Task Management Request */
#define UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS \
32 /* QEMU does not support Well known logical units */
#define UFSHCI_QUIRK_BROKEN_AUTO_HIBERNATE \
64 /* Some controllers have the Auto hibernate feature enabled but it \
does not work. */
#define UFSHCI_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH \
128 /* Some controllers need to reinit the device after gear switch. \
*/
#define UFSHCI_QUIRK_BROKEN_LSDBS_MCQS_CAP \
256 /* Some controllers have their LSDB and MCQS fields reset to 0. */
uint32_t ref_clk;
struct cam_sim *ufshci_sim;
struct cam_path *ufshci_path;
struct cam_periph *ufs_device_wlun_periph;
struct mtx ufs_device_wlun_mtx;
struct mtx sc_mtx;
uint32_t sc_unit;
uint8_t sc_name[16];
struct ufshci_device ufs_dev;
bus_space_tag_t bus_tag;
bus_space_handle_t bus_handle;
int resource_id;
struct resource *resource;
/* Currently, there is no UFSHCI that supports MSI, MSI-X. */
int msi_count;
/* Fields for tracking progress during controller initialization. */
struct intr_config_hook config_hook;
struct task reset_task;
struct taskqueue *taskqueue;
/* For shared legacy interrupt. */
int rid;
struct resource *res;
void *tag;
uint32_t major_version;
uint32_t minor_version;
uint32_t enable_aborts;
uint32_t num_io_queues;
uint32_t max_hw_pend_io;
/* Maximum logical unit number */
uint32_t max_lun_count;
/* Maximum i/o size in bytes */
uint32_t max_xfer_size;
/* Controller capacity */
uint32_t cap;
/* Page size and log2(page_size) - 12 that we're currently using */
uint32_t page_size;
/* Timeout value on device initialization */
uint32_t device_init_timeout_in_ms;
/* Timeout value on UIC command */
uint32_t uic_cmd_timeout_in_ms;
/* UTMR/UTR queue timeout period in seconds */
uint32_t timeout_period;
/* UTMR/UTR queue retry count */
uint32_t retry_count;
/* UFS Host Controller Interface Registers */
struct ufshci_registers *regs;
/* UFS Transport Protocol Layer (UTP) */
struct ufshci_req_queue task_mgmt_req_queue;
struct ufshci_req_queue transfer_req_queue;
bool is_single_db_supported;
bool is_mcq_supported;
/* UFS Interconnect Layer (UIC) */
struct mtx uic_cmd_lock;
uint32_t tx_rx_power_mode;
uint32_t hs_gear;
uint32_t tx_lanes;
uint32_t rx_lanes;
uint32_t max_rx_hs_gear;
uint32_t max_tx_lanes;
uint32_t max_rx_lanes;
bool is_failed;
};
#define ufshci_mmio_offsetof(reg) offsetof(struct ufshci_registers, reg)
#define ufshci_mmio_read_4(sc, reg) \
bus_space_read_4((sc)->bus_tag, (sc)->bus_handle, \
ufshci_mmio_offsetof(reg))
#define ufshci_mmio_write_4(sc, reg, val) \
bus_space_write_4((sc)->bus_tag, (sc)->bus_handle, \
ufshci_mmio_offsetof(reg), val)
#define ufshci_printf(ctrlr, fmt, args...) \
device_printf(ctrlr->dev, fmt, ##args)
/* UFSHCI */
void ufshci_completion_poll_cb(void *arg, const struct ufshci_completion *cpl,
bool error);
/* SIM */
uint8_t ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun);
uint64_t ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun);
int ufshci_sim_attach(struct ufshci_controller *ctrlr);
void ufshci_sim_detach(struct ufshci_controller *ctrlr);
struct cam_periph *ufshci_sim_find_periph(struct ufshci_controller *ctrlr,
uint8_t wlun);
int ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
uint8_t pwr_cond, bool immed);
/* Controller */
int ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev);
void ufshci_ctrlr_destruct(struct ufshci_controller *ctrlr, device_t dev);
void ufshci_ctrlr_reset(struct ufshci_controller *ctrlr);
int ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr,
enum power_stype stype);
int ufshci_ctrlr_resume(struct ufshci_controller *ctrlr,
enum power_stype stype);
int ufshci_ctrlr_disable(struct ufshci_controller *ctrlr);
int ufshci_ctrlr_enable(struct ufshci_controller *ctrlr);
/* ctrlr defined as void * to allow use with config_intrhook. */
void ufshci_ctrlr_start_config_hook(void *arg);
void ufshci_ctrlr_poll(struct ufshci_controller *ctrlr);
int ufshci_ctrlr_submit_task_mgmt_request(struct ufshci_controller *ctrlr,
struct ufshci_request *req);
int ufshci_ctrlr_submit_transfer_request(struct ufshci_controller *ctrlr,
struct ufshci_request *req);
int ufshci_ctrlr_send_nop(struct ufshci_controller *ctrlr);
void ufshci_reg_dump(struct ufshci_controller *ctrlr);
/* Device */
int ufshci_dev_init(struct ufshci_controller *ctrlr);
int ufshci_dev_reset(struct ufshci_controller *ctrlr);
int ufshci_dev_init_reference_clock(struct ufshci_controller *ctrlr);
int ufshci_dev_init_unipro(struct ufshci_controller *ctrlr);
void ufshci_dev_enable_auto_hibernate(struct ufshci_controller *ctrlr);
void ufshci_dev_init_auto_hibernate(struct ufshci_controller *ctrlr);
int ufshci_dev_init_uic_power_mode(struct ufshci_controller *ctrlr);
void ufshci_dev_init_uic_link_state(struct ufshci_controller *ctrlr);
int ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr);
int ufshci_dev_get_descriptor(struct ufshci_controller *ctrlr);
int ufshci_dev_config_write_booster(struct ufshci_controller *ctrlr);
int ufshci_dev_get_current_power_mode(struct ufshci_controller *ctrlr,
uint8_t *power_mode);
int ufshci_dev_link_state_transition(struct ufshci_controller *ctrlr,
enum ufshci_uic_link_state target_state);
/* Controller Command */
void ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
ufshci_cb_fn_t cb_fn, void *cb_arg, uint8_t function, uint8_t lun,
uint8_t task_tag, uint8_t iid);
void ufshci_ctrlr_cmd_send_nop(struct ufshci_controller *ctrlr,
ufshci_cb_fn_t cb_fn, void *cb_arg);
void ufshci_ctrlr_cmd_send_query_request(struct ufshci_controller *ctrlr,
ufshci_cb_fn_t cb_fn, void *cb_arg, struct ufshci_query_param param);
void ufshci_ctrlr_cmd_send_scsi_command(struct ufshci_controller *ctrlr,
ufshci_cb_fn_t cb_fn, void *cb_arg, uint8_t *cmd_ptr, uint8_t cmd_len,
uint32_t data_len, uint8_t lun, bool is_write);
/* Request Queue */
bool ufshci_req_queue_process_completions(struct ufshci_req_queue *req_queue);
int ufshci_utmr_req_queue_construct(struct ufshci_controller *ctrlr);
int ufshci_utr_req_queue_construct(struct ufshci_controller *ctrlr);
void ufshci_utmr_req_queue_destroy(struct ufshci_controller *ctrlr);
void ufshci_utr_req_queue_destroy(struct ufshci_controller *ctrlr);
void ufshci_utmr_req_queue_disable(struct ufshci_controller *ctrlr);
int ufshci_utmr_req_queue_enable(struct ufshci_controller *ctrlr);
void ufshci_utr_req_queue_disable(struct ufshci_controller *ctrlr);
int ufshci_utr_req_queue_enable(struct ufshci_controller *ctrlr);
void ufshci_req_queue_fail(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
int ufshci_req_queue_submit_request(struct ufshci_req_queue *req_queue,
struct ufshci_request *req);
void ufshci_req_queue_complete_tracker(struct ufshci_tracker *tr);
/* Request Single Doorbell Queue */
int ufshci_req_sdb_construct(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue, uint32_t num_entries,
bool is_task_mgmt);
void ufshci_req_sdb_destroy(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
struct ufshci_hw_queue *ufshci_req_sdb_get_hw_queue(
struct ufshci_req_queue *req_queue);
void ufshci_req_sdb_disable(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
int ufshci_req_sdb_enable(struct ufshci_controller *ctrlr,
struct ufshci_req_queue *req_queue);
int ufshci_req_sdb_reserve_slot(struct ufshci_req_queue *req_queue,
struct ufshci_tracker **tr);
void ufshci_req_sdb_utmr_ring_doorbell(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
void ufshci_req_sdb_utr_ring_doorbell(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
bool ufshci_req_sdb_utmr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
uint8_t slot);
bool ufshci_req_sdb_utr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
uint8_t slot);
void ufshci_req_sdb_utmr_clear_cpl_ntf(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
void ufshci_req_sdb_utr_clear_cpl_ntf(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
bool ufshci_req_sdb_process_cpl(struct ufshci_req_queue *req_queue);
int ufshci_req_sdb_get_inflight_io(struct ufshci_controller *ctrlr);
/* UIC Command */
int ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr);
int ufshci_uic_hibernation_ready(struct ufshci_controller *ctrlr);
int ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr);
int ufshci_uic_send_dme_link_startup(struct ufshci_controller *ctrlr);
int ufshci_uic_send_dme_get(struct ufshci_controller *ctrlr, uint16_t attribute,
uint32_t *return_value);
int ufshci_uic_send_dme_set(struct ufshci_controller *ctrlr, uint16_t attribute,
uint32_t value);
int ufshci_uic_send_dme_peer_get(struct ufshci_controller *ctrlr,
uint16_t attribute, uint32_t *return_value);
int ufshci_uic_send_dme_peer_set(struct ufshci_controller *ctrlr,
uint16_t attribute, uint32_t value);
int ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr);
int ufshci_uic_send_dme_hibernate_enter(struct ufshci_controller *ctrlr);
int ufshci_uic_send_dme_hibernate_exit(struct ufshci_controller *ctrlr);
/* SYSCTL */
void ufshci_sysctl_initialize_ctrlr(struct ufshci_controller *ctrlr);
int ufshci_attach(device_t dev);
int ufshci_detach(device_t dev);
/*
* Wait for a command to complete using the ufshci_completion_poll_cb. Used in
* limited contexts where the caller knows it's OK to block briefly while the
* command runs. The ISR will run the callback which will set status->done to
* true, usually within microseconds. If not, then after one second timeout
* handler should reset the controller and abort all outstanding requests
* including this polled one. If still not after ten seconds, then something is
* wrong with the driver, and panic is the only way to recover.
*
* Most commands using this interface aren't actual I/O to the drive's media so
* complete within a few microseconds. Adaptively spin for one tick to catch the
* vast majority of these without waiting for a tick plus scheduling delays.
* Since these are on startup, this drastically reduces startup time.
*/
static __inline void
ufshci_completion_poll(struct ufshci_completion_poll_status *status)
{
int timeout = ticks + 10 * hz;
sbintime_t delta_t = SBT_1US;
while (!atomic_load_acq_int(&status->done)) {
if (timeout - ticks < 0)
panic(
"UFSHCI polled command failed to complete within 10s.");
pause_sbt("ufshci_cpl", delta_t, 0, C_PREL(1));
delta_t = min(SBT_1MS, delta_t * 3 / 2);
}
}
static __inline void
ufshci_single_map(void *arg, bus_dma_segment_t *seg, int nseg, int error)
{
uint64_t *bus_addr = (uint64_t *)arg;
KASSERT(nseg == 1, ("number of segments (%d) is not 1", nseg));
if (error != 0)
printf("ufshci_single_map err %d\n", error);
*bus_addr = seg[0].ds_addr;
}
static __inline struct ufshci_request *
_ufshci_allocate_request(const int how, ufshci_cb_fn_t cb_fn, void *cb_arg)
{
struct ufshci_request *req;
KASSERT(how == M_WAITOK || how == M_NOWAIT,
("ufshci_allocate_request: invalid how %d", how));
req = malloc(sizeof(*req), M_UFSHCI, how | M_ZERO);
if (req != NULL) {
req->cb_fn = cb_fn;
req->cb_arg = cb_arg;
}
return (req);
}
static __inline struct ufshci_request *
ufshci_allocate_request_vaddr(void *payload, uint32_t payload_size,
const int how, ufshci_cb_fn_t cb_fn, void *cb_arg)
{
struct ufshci_request *req;
req = _ufshci_allocate_request(how, cb_fn, cb_arg);
if (req != NULL) {
if (payload_size) {
req->payload = memdesc_vaddr(payload, payload_size);
req->payload_valid = true;
}
}
return (req);
}
static __inline struct ufshci_request *
ufshci_allocate_request_bio(struct bio *bio, const int how,
ufshci_cb_fn_t cb_fn, void *cb_arg)
{
struct ufshci_request *req;
req = _ufshci_allocate_request(how, cb_fn, cb_arg);
if (req != NULL) {
req->payload = memdesc_bio(bio);
req->payload_valid = true;
}
return (req);
}
#define ufshci_free_request(req) free(req, M_UFSHCI)
void ufshci_ctrlr_shared_handler(void *arg);
#endif /* __UFSHCI_PRIVATE_H__ */