Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ufshci/ufshci_uic_cmd.c
- This file was added.
| /*- | |||||
| * Copyright (c) 2025, Samsung Electronics Co., Ltd. | |||||
| * Written by Jaeyoon Choi | |||||
| * | |||||
| * SPDX-License-Identifier: BSD-2-Clause | |||||
| */ | |||||
| #include <sys/param.h> | |||||
| #include <sys/bus.h> | |||||
| #include <sys/conf.h> | |||||
| #include "ufshci_private.h" | |||||
| #include "ufshci_reg.h" | |||||
| int | |||||
| ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr) | |||||
| { | |||||
| uint32_t is; | |||||
| int timeout; | |||||
| /* Wait for the IS flag to change */ | |||||
| timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms); | |||||
| int delta = 10; | |||||
| while (1) { | |||||
| is = ufshci_mmio_read_4(ctrlr, is); | |||||
| if (UFSHCIV(UFSHCI_IS_REG_UPMS, is)) | |||||
| break; | |||||
| if (timeout - ticks < 0) { | |||||
| ufshci_printf(ctrlr, | |||||
| "Power mode is not changed " | |||||
| "within %d ms\n", | |||||
| ctrlr->device_init_timeout_in_ms); | |||||
| return (ENXIO); | |||||
| } | |||||
| DELAY(delta); | |||||
| delta = min(1000, delta * 2); | |||||
| } | |||||
| return (0); | |||||
imp: Hmmm... this can be quite hard on the CPU... What's the typical timeline here and what are the… | |||||
Done Inline ActionsThe timeout here is 500ms, and this logic only runs during driver initialization. I tried using pause_sbt(), but it caused issues due to sleeping while holding a lock, so I reverted to DELAY(). jaeyoon: The timeout here is 500ms, and this logic only runs during driver initialization.
Backoff… | |||||
Done Inline ActionsOK. Looks like you've added notes about this limitation. If it's just boot, it's OK for now, but we'll want a note for the future since it's important enough to optimize once we more widely deploy on real hardware. imp: OK. Looks like you've added notes about this limitation. If it's just boot, it's OK for now… | |||||
Done Inline ActionsI agree, I plan to prepare a patch for this within this month. jaeyoon: I agree, I plan to prepare a patch for this within this month. | |||||
| } | |||||
| int | |||||
| ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr) | |||||
| { | |||||
| uint32_t hcs; | |||||
| int timeout; | |||||
| /* Wait for the HCS flag to change */ | |||||
| timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms); | |||||
| int delta = 10; | |||||
| while (1) { | |||||
| hcs = ufshci_mmio_read_4(ctrlr, hcs); | |||||
| if (UFSHCIV(UFSHCI_HCS_REG_UCRDY, hcs)) | |||||
| break; | |||||
| if (timeout - ticks < 0) { | |||||
| ufshci_printf(ctrlr, | |||||
| "UIC command is not ready " | |||||
| "within %d ms\n", | |||||
| ctrlr->uic_cmd_timeout_in_ms); | |||||
| return (ENXIO); | |||||
| } | |||||
| DELAY(delta); | |||||
| delta = min(1000, delta * 2); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| ufshci_uic_wait_cmd(struct ufshci_controller *ctrlr, | |||||
| struct ufshci_uic_cmd *uic_cmd) | |||||
| { | |||||
| uint32_t is; | |||||
| int timeout; | |||||
| mtx_assert(&ctrlr->uic_cmd_lock, MA_OWNED); | |||||
| /* Wait for the IS flag to change */ | |||||
| timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms); | |||||
| int delta = 10; | |||||
| while (1) { | |||||
| is = ufshci_mmio_read_4(ctrlr, is); | |||||
| if (UFSHCIV(UFSHCI_IS_REG_UCCS, is)) | |||||
| break; | |||||
| if (timeout - ticks < 0) { | |||||
| ufshci_printf(ctrlr, | |||||
| "UIC command is not completed " | |||||
| "within %d ms\n", | |||||
| ctrlr->uic_cmd_timeout_in_ms); | |||||
| return (ENXIO); | |||||
| } | |||||
| DELAY(delta); | |||||
| delta = min(1000, delta * 2); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| ufshci_uic_send_cmd(struct ufshci_controller *ctrlr, | |||||
| struct ufshci_uic_cmd *uic_cmd, uint32_t *return_value) | |||||
| { | |||||
| int error; | |||||
| mtx_lock(&ctrlr->uic_cmd_lock); | |||||
| error = ufshci_uic_cmd_ready(ctrlr); | |||||
| if (error) { | |||||
| mtx_unlock(&ctrlr->uic_cmd_lock); | |||||
| return (ENXIO); | |||||
| } | |||||
| ufshci_mmio_write_4(ctrlr, ucmdarg1, uic_cmd->argument1); | |||||
| ufshci_mmio_write_4(ctrlr, ucmdarg2, uic_cmd->argument2); | |||||
| ufshci_mmio_write_4(ctrlr, ucmdarg3, uic_cmd->argument3); | |||||
| ufshci_mmio_write_4(ctrlr, uiccmd, uic_cmd->opcode); | |||||
| error = ufshci_uic_wait_cmd(ctrlr, uic_cmd); | |||||
| mtx_unlock(&ctrlr->uic_cmd_lock); | |||||
| if (error) | |||||
| return (ENXIO); | |||||
| if (return_value != NULL) | |||||
| *return_value = ufshci_mmio_read_4(ctrlr, ucmdarg3); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_link_startup(struct ufshci_controller *ctrlr) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_LINK_STARTUP; | |||||
| uic_cmd.argument1 = 0; | |||||
| uic_cmd.argument2 = 0; | |||||
| uic_cmd.argument3 = 0; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL)); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_get(struct ufshci_controller *ctrlr, uint16_t attribute, | |||||
| uint32_t *return_value) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_GET; | |||||
| uic_cmd.argument1 = attribute << 16; | |||||
| uic_cmd.argument2 = 0; | |||||
| uic_cmd.argument3 = 0; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value)); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_set(struct ufshci_controller *ctrlr, uint16_t attribute, | |||||
| uint32_t value) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_SET; | |||||
| uic_cmd.argument1 = attribute << 16; | |||||
| /* This drvier always sets only volatile values. */ | |||||
| uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16; | |||||
| uic_cmd.argument3 = value; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL)); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_peer_get(struct ufshci_controller *ctrlr, | |||||
| uint16_t attribute, uint32_t *return_value) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_PEER_GET; | |||||
| uic_cmd.argument1 = attribute << 16; | |||||
| uic_cmd.argument2 = 0; | |||||
| uic_cmd.argument3 = 0; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, return_value)); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_peer_set(struct ufshci_controller *ctrlr, | |||||
| uint16_t attribute, uint32_t value) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_PEER_SET; | |||||
| uic_cmd.argument1 = attribute << 16; | |||||
| /* This drvier always sets only volatile values. */ | |||||
| uic_cmd.argument2 = UFSHCI_ATTR_SET_TYPE_NORMAL << 16; | |||||
| uic_cmd.argument3 = value; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL)); | |||||
| } | |||||
| int | |||||
| ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr) | |||||
| { | |||||
| struct ufshci_uic_cmd uic_cmd; | |||||
| uic_cmd.opcode = UFSHCI_DME_ENDPOINT_RESET; | |||||
| uic_cmd.argument1 = 0; | |||||
| uic_cmd.argument2 = 0; | |||||
| uic_cmd.argument3 = 0; | |||||
| return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL)); | |||||
| } | |||||
Hmmm... this can be quite hard on the CPU... What's the typical timeline here and what are the typical timeouts? I see they are in ms. Also, what's the point of doing the backoff on the delay? wouldn't that just lengthen the time it takes to startup w/o there being a benefit? Or is there something in the spec that needs to do this.
Finally, is there not an interrupt that could be use so we could convert this to a pause() instead of a delay()? Same questions/feedback apply to the similar functions below.