Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ufshci/ufshci_uic_cmd.c
- This file was added.
| /*- | |||||
| * SPDX-License-Identifier: BSD-2-Clause | |||||
| * | |||||
| * Copyright (c) 2025, Samsung Electronics Co., Ltd. | |||||
| * All rights reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions | |||||
| * are met: | |||||
| * 1. Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * 2. Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in the | |||||
| * documentation and/or other materials provided with the distribution. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. | |||||
| */ | |||||
| #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; | |||||
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. | |||||
| /* Wait for the IS flag to change */ | |||||
| int timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms); | |||||
| sbintime_t delta_t = SBT_1US; | |||||
| 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); | |||||
| } | |||||
| pause_sbt("ufshci_uic_is", delta_t, 0, C_PREL(1)); | |||||
| delta_t = min(SBT_1MS, delta_t * 3 / 2); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr) | |||||
| { | |||||
| uint32_t hcs; | |||||
| /* Wait for the HCS flag to change */ | |||||
| int timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms); | |||||
| sbintime_t delta_t = SBT_1US; | |||||
| 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->device_init_timeout_in_ms); | |||||
| return (ENXIO); | |||||
| } | |||||
| pause_sbt("ufshci_uic_hcs", delta_t, 0, C_PREL(1)); | |||||
| delta_t = min(SBT_1MS, delta_t * 3 / 2); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| ufshci_uic_wait_cmd(struct ufshci_controller *ctrlr, | |||||
| struct ufshci_uic_cmd *uic_cmd) | |||||
| { | |||||
| mtx_assert(&ctrlr->uic_cmd_lock, MA_OWNED); | |||||
| uint32_t is; | |||||
| /* Wait for the IS flag to change */ | |||||
| int timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms); | |||||
| sbintime_t delta_t = SBT_1US; | |||||
| 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->device_init_timeout_in_ms); | |||||
| return (ENXIO); | |||||
| } | |||||
| pause_sbt("ufshci_uic_is", delta_t, 0, C_PREL(1)); | |||||
| delta_t = min(SBT_1MS, delta_t * 3 / 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_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.