1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu #include <endian.h> 9*91f16700Schasinglulu #include <errno.h> 10*91f16700Schasinglulu #include <stdint.h> 11*91f16700Schasinglulu #include <string.h> 12*91f16700Schasinglulu 13*91f16700Schasinglulu #include <platform_def.h> 14*91f16700Schasinglulu 15*91f16700Schasinglulu #include <arch_helpers.h> 16*91f16700Schasinglulu #include <common/debug.h> 17*91f16700Schasinglulu #include <drivers/delay_timer.h> 18*91f16700Schasinglulu #include <drivers/ufs.h> 19*91f16700Schasinglulu #include <lib/mmio.h> 20*91f16700Schasinglulu 21*91f16700Schasinglulu #define CDB_ADDR_MASK 127 22*91f16700Schasinglulu #define ALIGN_CDB(x) (((x) + CDB_ADDR_MASK) & ~CDB_ADDR_MASK) 23*91f16700Schasinglulu #define ALIGN_8(x) (((x) + 7) & ~7) 24*91f16700Schasinglulu 25*91f16700Schasinglulu #define UFS_DESC_SIZE 0x400 26*91f16700Schasinglulu #define MAX_UFS_DESC_SIZE 0x8000 /* 32 descriptors */ 27*91f16700Schasinglulu 28*91f16700Schasinglulu #define MAX_PRDT_SIZE 0x40000 /* 256KB */ 29*91f16700Schasinglulu 30*91f16700Schasinglulu static ufs_params_t ufs_params; 31*91f16700Schasinglulu static int nutrs; /* Number of UTP Transfer Request Slots */ 32*91f16700Schasinglulu 33*91f16700Schasinglulu /* 34*91f16700Schasinglulu * ufs_uic_error_handler - UIC error interrupts handler 35*91f16700Schasinglulu * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) 36*91f16700Schasinglulu * 37*91f16700Schasinglulu * Returns 38*91f16700Schasinglulu * 0 - ignore error 39*91f16700Schasinglulu * -EIO - fatal error, needs re-init 40*91f16700Schasinglulu * -EAGAIN - non-fatal error, retries are sufficient 41*91f16700Schasinglulu */ 42*91f16700Schasinglulu static int ufs_uic_error_handler(bool ignore_linereset) 43*91f16700Schasinglulu { 44*91f16700Schasinglulu uint32_t data; 45*91f16700Schasinglulu int result = 0; 46*91f16700Schasinglulu 47*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UECPA); 48*91f16700Schasinglulu if (data & UFS_UIC_PA_ERROR_MASK) { 49*91f16700Schasinglulu if (data & PA_LAYER_GEN_ERR) { 50*91f16700Schasinglulu if (!ignore_linereset) { 51*91f16700Schasinglulu return -EIO; 52*91f16700Schasinglulu } 53*91f16700Schasinglulu } else { 54*91f16700Schasinglulu result = -EAGAIN; 55*91f16700Schasinglulu } 56*91f16700Schasinglulu } 57*91f16700Schasinglulu 58*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UECDL); 59*91f16700Schasinglulu if (data & UFS_UIC_DL_ERROR_MASK) { 60*91f16700Schasinglulu if (data & PA_INIT_ERR) { 61*91f16700Schasinglulu return -EIO; 62*91f16700Schasinglulu } 63*91f16700Schasinglulu result = -EAGAIN; 64*91f16700Schasinglulu } 65*91f16700Schasinglulu 66*91f16700Schasinglulu /* NL/TL/DME error requires retries */ 67*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UECN); 68*91f16700Schasinglulu if (data & UFS_UIC_NL_ERROR_MASK) { 69*91f16700Schasinglulu result = -EAGAIN; 70*91f16700Schasinglulu } 71*91f16700Schasinglulu 72*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UECT); 73*91f16700Schasinglulu if (data & UFS_UIC_TL_ERROR_MASK) { 74*91f16700Schasinglulu result = -EAGAIN; 75*91f16700Schasinglulu } 76*91f16700Schasinglulu 77*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UECDME); 78*91f16700Schasinglulu if (data & UFS_UIC_DME_ERROR_MASK) { 79*91f16700Schasinglulu result = -EAGAIN; 80*91f16700Schasinglulu } 81*91f16700Schasinglulu 82*91f16700Schasinglulu return result; 83*91f16700Schasinglulu } 84*91f16700Schasinglulu 85*91f16700Schasinglulu /* 86*91f16700Schasinglulu * ufs_error_handler - error interrupts handler 87*91f16700Schasinglulu * @status: interrupt status 88*91f16700Schasinglulu * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) 89*91f16700Schasinglulu * 90*91f16700Schasinglulu * Returns 91*91f16700Schasinglulu * 0 - ignore error 92*91f16700Schasinglulu * -EIO - fatal error, needs re-init 93*91f16700Schasinglulu * -EAGAIN - non-fatal error, retries are sufficient 94*91f16700Schasinglulu */ 95*91f16700Schasinglulu static int ufs_error_handler(uint32_t status, bool ignore_linereset) 96*91f16700Schasinglulu { 97*91f16700Schasinglulu int result; 98*91f16700Schasinglulu 99*91f16700Schasinglulu if (status & UFS_INT_UE) { 100*91f16700Schasinglulu result = ufs_uic_error_handler(ignore_linereset); 101*91f16700Schasinglulu if (result != 0) { 102*91f16700Schasinglulu return result; 103*91f16700Schasinglulu } 104*91f16700Schasinglulu } 105*91f16700Schasinglulu 106*91f16700Schasinglulu /* Return I/O error on fatal error, it is upto the caller to re-init UFS */ 107*91f16700Schasinglulu if (status & UFS_INT_FATAL) { 108*91f16700Schasinglulu return -EIO; 109*91f16700Schasinglulu } 110*91f16700Schasinglulu 111*91f16700Schasinglulu /* retry for non-fatal errors */ 112*91f16700Schasinglulu return -EAGAIN; 113*91f16700Schasinglulu } 114*91f16700Schasinglulu 115*91f16700Schasinglulu /* 116*91f16700Schasinglulu * ufs_wait_for_int_status - wait for expected interrupt status 117*91f16700Schasinglulu * @expected: expected interrupt status bit 118*91f16700Schasinglulu * @timeout_ms: timeout in milliseconds to poll for 119*91f16700Schasinglulu * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) 120*91f16700Schasinglulu * 121*91f16700Schasinglulu * Returns 122*91f16700Schasinglulu * 0 - received expected interrupt and cleared it 123*91f16700Schasinglulu * -EIO - fatal error, needs re-init 124*91f16700Schasinglulu * -EAGAIN - non-fatal error, caller can retry 125*91f16700Schasinglulu * -ETIMEDOUT - timed out waiting for interrupt status 126*91f16700Schasinglulu */ 127*91f16700Schasinglulu static int ufs_wait_for_int_status(const uint32_t expected_status, 128*91f16700Schasinglulu unsigned int timeout_ms, 129*91f16700Schasinglulu bool ignore_linereset) 130*91f16700Schasinglulu { 131*91f16700Schasinglulu uint32_t interrupt_status, interrupts_enabled; 132*91f16700Schasinglulu int result = 0; 133*91f16700Schasinglulu 134*91f16700Schasinglulu interrupts_enabled = mmio_read_32(ufs_params.reg_base + IE); 135*91f16700Schasinglulu do { 136*91f16700Schasinglulu interrupt_status = mmio_read_32(ufs_params.reg_base + IS) & interrupts_enabled; 137*91f16700Schasinglulu if (interrupt_status & UFS_INT_ERR) { 138*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + IS, interrupt_status & UFS_INT_ERR); 139*91f16700Schasinglulu result = ufs_error_handler(interrupt_status, ignore_linereset); 140*91f16700Schasinglulu if (result != 0) { 141*91f16700Schasinglulu return result; 142*91f16700Schasinglulu } 143*91f16700Schasinglulu } 144*91f16700Schasinglulu 145*91f16700Schasinglulu if (interrupt_status & expected_status) { 146*91f16700Schasinglulu break; 147*91f16700Schasinglulu } 148*91f16700Schasinglulu mdelay(1); 149*91f16700Schasinglulu } while (timeout_ms-- > 0); 150*91f16700Schasinglulu 151*91f16700Schasinglulu if (!(interrupt_status & expected_status)) { 152*91f16700Schasinglulu return -ETIMEDOUT; 153*91f16700Schasinglulu } 154*91f16700Schasinglulu 155*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + IS, expected_status); 156*91f16700Schasinglulu 157*91f16700Schasinglulu return result; 158*91f16700Schasinglulu } 159*91f16700Schasinglulu 160*91f16700Schasinglulu 161*91f16700Schasinglulu int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd) 162*91f16700Schasinglulu { 163*91f16700Schasinglulu unsigned int data; 164*91f16700Schasinglulu int result, retries; 165*91f16700Schasinglulu 166*91f16700Schasinglulu if (base == 0 || cmd == NULL) 167*91f16700Schasinglulu return -EINVAL; 168*91f16700Schasinglulu 169*91f16700Schasinglulu for (retries = 0; retries < 100; retries++) { 170*91f16700Schasinglulu data = mmio_read_32(base + HCS); 171*91f16700Schasinglulu if ((data & HCS_UCRDY) != 0) { 172*91f16700Schasinglulu break; 173*91f16700Schasinglulu } 174*91f16700Schasinglulu mdelay(1); 175*91f16700Schasinglulu } 176*91f16700Schasinglulu if (retries >= 100) { 177*91f16700Schasinglulu return -EBUSY; 178*91f16700Schasinglulu } 179*91f16700Schasinglulu 180*91f16700Schasinglulu mmio_write_32(base + IS, ~0); 181*91f16700Schasinglulu mmio_write_32(base + UCMDARG1, cmd->arg1); 182*91f16700Schasinglulu mmio_write_32(base + UCMDARG2, cmd->arg2); 183*91f16700Schasinglulu mmio_write_32(base + UCMDARG3, cmd->arg3); 184*91f16700Schasinglulu mmio_write_32(base + UICCMD, cmd->op); 185*91f16700Schasinglulu 186*91f16700Schasinglulu result = ufs_wait_for_int_status(UFS_INT_UCCS, UIC_CMD_TIMEOUT_MS, 187*91f16700Schasinglulu cmd->op == DME_SET); 188*91f16700Schasinglulu if (result != 0) { 189*91f16700Schasinglulu return result; 190*91f16700Schasinglulu } 191*91f16700Schasinglulu 192*91f16700Schasinglulu return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK; 193*91f16700Schasinglulu } 194*91f16700Schasinglulu 195*91f16700Schasinglulu int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val) 196*91f16700Schasinglulu { 197*91f16700Schasinglulu uintptr_t base; 198*91f16700Schasinglulu int result, retries; 199*91f16700Schasinglulu uic_cmd_t cmd; 200*91f16700Schasinglulu 201*91f16700Schasinglulu assert(ufs_params.reg_base != 0); 202*91f16700Schasinglulu 203*91f16700Schasinglulu if (val == NULL) 204*91f16700Schasinglulu return -EINVAL; 205*91f16700Schasinglulu 206*91f16700Schasinglulu base = ufs_params.reg_base; 207*91f16700Schasinglulu cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx); 208*91f16700Schasinglulu cmd.arg2 = 0; 209*91f16700Schasinglulu cmd.arg3 = 0; 210*91f16700Schasinglulu cmd.op = DME_GET; 211*91f16700Schasinglulu 212*91f16700Schasinglulu for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) { 213*91f16700Schasinglulu result = ufshc_send_uic_cmd(base, &cmd); 214*91f16700Schasinglulu if (result == 0) 215*91f16700Schasinglulu break; 216*91f16700Schasinglulu /* -EIO requires UFS re-init */ 217*91f16700Schasinglulu if (result == -EIO) { 218*91f16700Schasinglulu return result; 219*91f16700Schasinglulu } 220*91f16700Schasinglulu } 221*91f16700Schasinglulu if (retries >= UFS_UIC_COMMAND_RETRIES) 222*91f16700Schasinglulu return -EIO; 223*91f16700Schasinglulu 224*91f16700Schasinglulu *val = mmio_read_32(base + UCMDARG3); 225*91f16700Schasinglulu return 0; 226*91f16700Schasinglulu } 227*91f16700Schasinglulu 228*91f16700Schasinglulu int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val) 229*91f16700Schasinglulu { 230*91f16700Schasinglulu uintptr_t base; 231*91f16700Schasinglulu int result, retries; 232*91f16700Schasinglulu uic_cmd_t cmd; 233*91f16700Schasinglulu 234*91f16700Schasinglulu assert((ufs_params.reg_base != 0)); 235*91f16700Schasinglulu 236*91f16700Schasinglulu base = ufs_params.reg_base; 237*91f16700Schasinglulu cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx); 238*91f16700Schasinglulu cmd.arg2 = 0; 239*91f16700Schasinglulu cmd.arg3 = val; 240*91f16700Schasinglulu cmd.op = DME_SET; 241*91f16700Schasinglulu 242*91f16700Schasinglulu for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) { 243*91f16700Schasinglulu result = ufshc_send_uic_cmd(base, &cmd); 244*91f16700Schasinglulu if (result == 0) 245*91f16700Schasinglulu break; 246*91f16700Schasinglulu /* -EIO requires UFS re-init */ 247*91f16700Schasinglulu if (result == -EIO) { 248*91f16700Schasinglulu return result; 249*91f16700Schasinglulu } 250*91f16700Schasinglulu } 251*91f16700Schasinglulu if (retries >= UFS_UIC_COMMAND_RETRIES) 252*91f16700Schasinglulu return -EIO; 253*91f16700Schasinglulu 254*91f16700Schasinglulu return 0; 255*91f16700Schasinglulu } 256*91f16700Schasinglulu 257*91f16700Schasinglulu static int ufshc_hce_enable(uintptr_t base) 258*91f16700Schasinglulu { 259*91f16700Schasinglulu unsigned int data; 260*91f16700Schasinglulu int retries; 261*91f16700Schasinglulu 262*91f16700Schasinglulu /* Enable Host Controller */ 263*91f16700Schasinglulu mmio_write_32(base + HCE, HCE_ENABLE); 264*91f16700Schasinglulu 265*91f16700Schasinglulu /* Wait until basic initialization sequence completed */ 266*91f16700Schasinglulu for (retries = 0; retries < HCE_ENABLE_INNER_RETRIES; ++retries) { 267*91f16700Schasinglulu data = mmio_read_32(base + HCE); 268*91f16700Schasinglulu if (data & HCE_ENABLE) { 269*91f16700Schasinglulu break; 270*91f16700Schasinglulu } 271*91f16700Schasinglulu udelay(HCE_ENABLE_TIMEOUT_US); 272*91f16700Schasinglulu } 273*91f16700Schasinglulu if (retries >= HCE_ENABLE_INNER_RETRIES) { 274*91f16700Schasinglulu return -ETIMEDOUT; 275*91f16700Schasinglulu } 276*91f16700Schasinglulu 277*91f16700Schasinglulu return 0; 278*91f16700Schasinglulu } 279*91f16700Schasinglulu 280*91f16700Schasinglulu static int ufshc_hce_disable(uintptr_t base) 281*91f16700Schasinglulu { 282*91f16700Schasinglulu unsigned int data; 283*91f16700Schasinglulu int timeout; 284*91f16700Schasinglulu 285*91f16700Schasinglulu /* Disable Host Controller */ 286*91f16700Schasinglulu mmio_write_32(base + HCE, HCE_DISABLE); 287*91f16700Schasinglulu timeout = HCE_DISABLE_TIMEOUT_US; 288*91f16700Schasinglulu do { 289*91f16700Schasinglulu data = mmio_read_32(base + HCE); 290*91f16700Schasinglulu if ((data & HCE_ENABLE) == HCE_DISABLE) { 291*91f16700Schasinglulu break; 292*91f16700Schasinglulu } 293*91f16700Schasinglulu udelay(1); 294*91f16700Schasinglulu } while (--timeout > 0); 295*91f16700Schasinglulu 296*91f16700Schasinglulu if (timeout <= 0) { 297*91f16700Schasinglulu return -ETIMEDOUT; 298*91f16700Schasinglulu } 299*91f16700Schasinglulu 300*91f16700Schasinglulu return 0; 301*91f16700Schasinglulu } 302*91f16700Schasinglulu 303*91f16700Schasinglulu 304*91f16700Schasinglulu static int ufshc_reset(uintptr_t base) 305*91f16700Schasinglulu { 306*91f16700Schasinglulu unsigned int data; 307*91f16700Schasinglulu int retries, result; 308*91f16700Schasinglulu 309*91f16700Schasinglulu /* disable controller if enabled */ 310*91f16700Schasinglulu if (mmio_read_32(base + HCE) & HCE_ENABLE) { 311*91f16700Schasinglulu result = ufshc_hce_disable(base); 312*91f16700Schasinglulu if (result != 0) { 313*91f16700Schasinglulu return -EIO; 314*91f16700Schasinglulu } 315*91f16700Schasinglulu } 316*91f16700Schasinglulu 317*91f16700Schasinglulu for (retries = 0; retries < HCE_ENABLE_OUTER_RETRIES; ++retries) { 318*91f16700Schasinglulu result = ufshc_hce_enable(base); 319*91f16700Schasinglulu if (result == 0) { 320*91f16700Schasinglulu break; 321*91f16700Schasinglulu } 322*91f16700Schasinglulu } 323*91f16700Schasinglulu if (retries >= HCE_ENABLE_OUTER_RETRIES) { 324*91f16700Schasinglulu return -EIO; 325*91f16700Schasinglulu } 326*91f16700Schasinglulu 327*91f16700Schasinglulu /* Enable UIC Interrupts alone. We can ignore other interrupts until 328*91f16700Schasinglulu * link is up as there might be spurious error interrupts during link-up 329*91f16700Schasinglulu */ 330*91f16700Schasinglulu data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS; 331*91f16700Schasinglulu mmio_write_32(base + IE, data); 332*91f16700Schasinglulu 333*91f16700Schasinglulu return 0; 334*91f16700Schasinglulu } 335*91f16700Schasinglulu 336*91f16700Schasinglulu static int ufshc_dme_link_startup(uintptr_t base) 337*91f16700Schasinglulu { 338*91f16700Schasinglulu uic_cmd_t cmd; 339*91f16700Schasinglulu 340*91f16700Schasinglulu memset(&cmd, 0, sizeof(cmd)); 341*91f16700Schasinglulu cmd.op = DME_LINKSTARTUP; 342*91f16700Schasinglulu return ufshc_send_uic_cmd(base, &cmd); 343*91f16700Schasinglulu } 344*91f16700Schasinglulu 345*91f16700Schasinglulu static int ufshc_link_startup(uintptr_t base) 346*91f16700Schasinglulu { 347*91f16700Schasinglulu int data, result; 348*91f16700Schasinglulu int retries; 349*91f16700Schasinglulu 350*91f16700Schasinglulu for (retries = DME_LINKSTARTUP_RETRIES; retries > 0; retries--) { 351*91f16700Schasinglulu result = ufshc_dme_link_startup(base); 352*91f16700Schasinglulu if (result != 0) { 353*91f16700Schasinglulu /* Reset controller before trying again */ 354*91f16700Schasinglulu result = ufshc_reset(base); 355*91f16700Schasinglulu if (result != 0) { 356*91f16700Schasinglulu return result; 357*91f16700Schasinglulu } 358*91f16700Schasinglulu continue; 359*91f16700Schasinglulu } 360*91f16700Schasinglulu assert(mmio_read_32(base + HCS) & HCS_DP); 361*91f16700Schasinglulu data = mmio_read_32(base + IS); 362*91f16700Schasinglulu if (data & UFS_INT_ULSS) 363*91f16700Schasinglulu mmio_write_32(base + IS, UFS_INT_ULSS); 364*91f16700Schasinglulu 365*91f16700Schasinglulu /* clear UE set due to line-reset */ 366*91f16700Schasinglulu if (data & UFS_INT_UE) { 367*91f16700Schasinglulu mmio_write_32(base + IS, UFS_INT_UE); 368*91f16700Schasinglulu } 369*91f16700Schasinglulu /* clearing line-reset, UECPA is cleared on read */ 370*91f16700Schasinglulu mmio_read_32(base + UECPA); 371*91f16700Schasinglulu return 0; 372*91f16700Schasinglulu } 373*91f16700Schasinglulu return -EIO; 374*91f16700Schasinglulu } 375*91f16700Schasinglulu 376*91f16700Schasinglulu /* Read Door Bell register to check if slot zero is available */ 377*91f16700Schasinglulu static int is_slot_available(void) 378*91f16700Schasinglulu { 379*91f16700Schasinglulu if (mmio_read_32(ufs_params.reg_base + UTRLDBR) & 0x1) { 380*91f16700Schasinglulu return -EBUSY; 381*91f16700Schasinglulu } 382*91f16700Schasinglulu return 0; 383*91f16700Schasinglulu } 384*91f16700Schasinglulu 385*91f16700Schasinglulu static void get_utrd(utp_utrd_t *utrd) 386*91f16700Schasinglulu { 387*91f16700Schasinglulu uintptr_t base; 388*91f16700Schasinglulu int result; 389*91f16700Schasinglulu utrd_header_t *hd; 390*91f16700Schasinglulu 391*91f16700Schasinglulu assert(utrd != NULL); 392*91f16700Schasinglulu result = is_slot_available(); 393*91f16700Schasinglulu assert(result == 0); 394*91f16700Schasinglulu 395*91f16700Schasinglulu /* clear utrd */ 396*91f16700Schasinglulu memset((void *)utrd, 0, sizeof(utp_utrd_t)); 397*91f16700Schasinglulu base = ufs_params.desc_base; 398*91f16700Schasinglulu /* clear the descriptor */ 399*91f16700Schasinglulu memset((void *)base, 0, UFS_DESC_SIZE); 400*91f16700Schasinglulu 401*91f16700Schasinglulu utrd->header = base; 402*91f16700Schasinglulu utrd->task_tag = 1; /* We always use the first slot */ 403*91f16700Schasinglulu /* CDB address should be aligned with 128 bytes */ 404*91f16700Schasinglulu utrd->upiu = ALIGN_CDB(utrd->header + sizeof(utrd_header_t)); 405*91f16700Schasinglulu utrd->resp_upiu = ALIGN_8(utrd->upiu + sizeof(cmd_upiu_t)); 406*91f16700Schasinglulu utrd->size_upiu = utrd->resp_upiu - utrd->upiu; 407*91f16700Schasinglulu utrd->size_resp_upiu = ALIGN_8(sizeof(resp_upiu_t)); 408*91f16700Schasinglulu utrd->prdt = utrd->resp_upiu + utrd->size_resp_upiu; 409*91f16700Schasinglulu 410*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 411*91f16700Schasinglulu hd->ucdba = utrd->upiu & UINT32_MAX; 412*91f16700Schasinglulu hd->ucdbau = (utrd->upiu >> 32) & UINT32_MAX; 413*91f16700Schasinglulu /* Both RUL and RUO is based on DWORD */ 414*91f16700Schasinglulu hd->rul = utrd->size_resp_upiu >> 2; 415*91f16700Schasinglulu hd->ruo = utrd->size_upiu >> 2; 416*91f16700Schasinglulu (void)result; 417*91f16700Schasinglulu } 418*91f16700Schasinglulu 419*91f16700Schasinglulu /* 420*91f16700Schasinglulu * Prepare UTRD, Command UPIU, Response UPIU. 421*91f16700Schasinglulu */ 422*91f16700Schasinglulu static int ufs_prepare_cmd(utp_utrd_t *utrd, uint8_t op, uint8_t lun, 423*91f16700Schasinglulu int lba, uintptr_t buf, size_t length) 424*91f16700Schasinglulu { 425*91f16700Schasinglulu utrd_header_t *hd; 426*91f16700Schasinglulu cmd_upiu_t *upiu; 427*91f16700Schasinglulu prdt_t *prdt; 428*91f16700Schasinglulu unsigned int ulba; 429*91f16700Schasinglulu unsigned int lba_cnt; 430*91f16700Schasinglulu uintptr_t desc_limit; 431*91f16700Schasinglulu uintptr_t prdt_end; 432*91f16700Schasinglulu 433*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 434*91f16700Schasinglulu upiu = (cmd_upiu_t *)utrd->upiu; 435*91f16700Schasinglulu 436*91f16700Schasinglulu hd->i = 1; 437*91f16700Schasinglulu hd->ct = CT_UFS_STORAGE; 438*91f16700Schasinglulu hd->ocs = OCS_MASK; 439*91f16700Schasinglulu 440*91f16700Schasinglulu upiu->trans_type = CMD_UPIU; 441*91f16700Schasinglulu upiu->task_tag = utrd->task_tag; 442*91f16700Schasinglulu upiu->cdb[0] = op; 443*91f16700Schasinglulu ulba = (unsigned int)lba; 444*91f16700Schasinglulu lba_cnt = (unsigned int)(length >> UFS_BLOCK_SHIFT); 445*91f16700Schasinglulu switch (op) { 446*91f16700Schasinglulu case CDBCMD_TEST_UNIT_READY: 447*91f16700Schasinglulu break; 448*91f16700Schasinglulu case CDBCMD_READ_CAPACITY_10: 449*91f16700Schasinglulu hd->dd = DD_OUT; 450*91f16700Schasinglulu upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S; 451*91f16700Schasinglulu upiu->lun = lun; 452*91f16700Schasinglulu break; 453*91f16700Schasinglulu case CDBCMD_READ_10: 454*91f16700Schasinglulu hd->dd = DD_OUT; 455*91f16700Schasinglulu upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S; 456*91f16700Schasinglulu upiu->lun = lun; 457*91f16700Schasinglulu upiu->cdb[1] = RW_WITHOUT_CACHE; 458*91f16700Schasinglulu /* set logical block address */ 459*91f16700Schasinglulu upiu->cdb[2] = (ulba >> 24) & 0xff; 460*91f16700Schasinglulu upiu->cdb[3] = (ulba >> 16) & 0xff; 461*91f16700Schasinglulu upiu->cdb[4] = (ulba >> 8) & 0xff; 462*91f16700Schasinglulu upiu->cdb[5] = ulba & 0xff; 463*91f16700Schasinglulu /* set transfer length */ 464*91f16700Schasinglulu upiu->cdb[7] = (lba_cnt >> 8) & 0xff; 465*91f16700Schasinglulu upiu->cdb[8] = lba_cnt & 0xff; 466*91f16700Schasinglulu break; 467*91f16700Schasinglulu case CDBCMD_WRITE_10: 468*91f16700Schasinglulu hd->dd = DD_IN; 469*91f16700Schasinglulu upiu->flags = UPIU_FLAGS_W | UPIU_FLAGS_ATTR_S; 470*91f16700Schasinglulu upiu->lun = lun; 471*91f16700Schasinglulu upiu->cdb[1] = RW_WITHOUT_CACHE; 472*91f16700Schasinglulu /* set logical block address */ 473*91f16700Schasinglulu upiu->cdb[2] = (ulba >> 24) & 0xff; 474*91f16700Schasinglulu upiu->cdb[3] = (ulba >> 16) & 0xff; 475*91f16700Schasinglulu upiu->cdb[4] = (ulba >> 8) & 0xff; 476*91f16700Schasinglulu upiu->cdb[5] = ulba & 0xff; 477*91f16700Schasinglulu /* set transfer length */ 478*91f16700Schasinglulu upiu->cdb[7] = (lba_cnt >> 8) & 0xff; 479*91f16700Schasinglulu upiu->cdb[8] = lba_cnt & 0xff; 480*91f16700Schasinglulu break; 481*91f16700Schasinglulu default: 482*91f16700Schasinglulu assert(0); 483*91f16700Schasinglulu break; 484*91f16700Schasinglulu } 485*91f16700Schasinglulu if (hd->dd == DD_IN) { 486*91f16700Schasinglulu flush_dcache_range(buf, length); 487*91f16700Schasinglulu } else if (hd->dd == DD_OUT) { 488*91f16700Schasinglulu inv_dcache_range(buf, length); 489*91f16700Schasinglulu } 490*91f16700Schasinglulu 491*91f16700Schasinglulu utrd->prdt_length = 0; 492*91f16700Schasinglulu if (length) { 493*91f16700Schasinglulu upiu->exp_data_trans_len = htobe32(length); 494*91f16700Schasinglulu assert(lba_cnt <= UINT16_MAX); 495*91f16700Schasinglulu prdt = (prdt_t *)utrd->prdt; 496*91f16700Schasinglulu 497*91f16700Schasinglulu desc_limit = ufs_params.desc_base + ufs_params.desc_size; 498*91f16700Schasinglulu while (length > 0) { 499*91f16700Schasinglulu if ((uintptr_t)prdt + sizeof(prdt_t) > desc_limit) { 500*91f16700Schasinglulu ERROR("UFS: Exceeded descriptor limit. Image is too large\n"); 501*91f16700Schasinglulu panic(); 502*91f16700Schasinglulu } 503*91f16700Schasinglulu prdt->dba = (unsigned int)(buf & UINT32_MAX); 504*91f16700Schasinglulu prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX); 505*91f16700Schasinglulu /* prdt->dbc counts from 0 */ 506*91f16700Schasinglulu if (length > MAX_PRDT_SIZE) { 507*91f16700Schasinglulu prdt->dbc = MAX_PRDT_SIZE - 1; 508*91f16700Schasinglulu length = length - MAX_PRDT_SIZE; 509*91f16700Schasinglulu } else { 510*91f16700Schasinglulu prdt->dbc = length - 1; 511*91f16700Schasinglulu length = 0; 512*91f16700Schasinglulu } 513*91f16700Schasinglulu buf += MAX_PRDT_SIZE; 514*91f16700Schasinglulu prdt++; 515*91f16700Schasinglulu utrd->prdt_length++; 516*91f16700Schasinglulu } 517*91f16700Schasinglulu hd->prdtl = utrd->prdt_length; 518*91f16700Schasinglulu hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2; 519*91f16700Schasinglulu } 520*91f16700Schasinglulu 521*91f16700Schasinglulu prdt_end = utrd->prdt + utrd->prdt_length * sizeof(prdt_t); 522*91f16700Schasinglulu flush_dcache_range(utrd->header, prdt_end - utrd->header); 523*91f16700Schasinglulu return 0; 524*91f16700Schasinglulu } 525*91f16700Schasinglulu 526*91f16700Schasinglulu static int ufs_prepare_query(utp_utrd_t *utrd, uint8_t op, uint8_t idn, 527*91f16700Schasinglulu uint8_t index, uint8_t sel, 528*91f16700Schasinglulu uintptr_t buf, size_t length) 529*91f16700Schasinglulu { 530*91f16700Schasinglulu utrd_header_t *hd; 531*91f16700Schasinglulu query_upiu_t *query_upiu; 532*91f16700Schasinglulu 533*91f16700Schasinglulu 534*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 535*91f16700Schasinglulu query_upiu = (query_upiu_t *)utrd->upiu; 536*91f16700Schasinglulu 537*91f16700Schasinglulu hd->i = 1; 538*91f16700Schasinglulu hd->ct = CT_UFS_STORAGE; 539*91f16700Schasinglulu hd->ocs = OCS_MASK; 540*91f16700Schasinglulu 541*91f16700Schasinglulu query_upiu->trans_type = QUERY_REQUEST_UPIU; 542*91f16700Schasinglulu query_upiu->task_tag = utrd->task_tag; 543*91f16700Schasinglulu query_upiu->data_segment_len = htobe16(length); 544*91f16700Schasinglulu query_upiu->ts.desc.opcode = op; 545*91f16700Schasinglulu query_upiu->ts.desc.idn = idn; 546*91f16700Schasinglulu query_upiu->ts.desc.index = index; 547*91f16700Schasinglulu query_upiu->ts.desc.selector = sel; 548*91f16700Schasinglulu switch (op) { 549*91f16700Schasinglulu case QUERY_READ_DESC: 550*91f16700Schasinglulu query_upiu->query_func = QUERY_FUNC_STD_READ; 551*91f16700Schasinglulu query_upiu->ts.desc.length = htobe16(length); 552*91f16700Schasinglulu break; 553*91f16700Schasinglulu case QUERY_WRITE_DESC: 554*91f16700Schasinglulu query_upiu->query_func = QUERY_FUNC_STD_WRITE; 555*91f16700Schasinglulu query_upiu->ts.desc.length = htobe16(length); 556*91f16700Schasinglulu memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)), 557*91f16700Schasinglulu (void *)buf, length); 558*91f16700Schasinglulu break; 559*91f16700Schasinglulu case QUERY_READ_ATTR: 560*91f16700Schasinglulu case QUERY_READ_FLAG: 561*91f16700Schasinglulu query_upiu->query_func = QUERY_FUNC_STD_READ; 562*91f16700Schasinglulu break; 563*91f16700Schasinglulu case QUERY_CLEAR_FLAG: 564*91f16700Schasinglulu case QUERY_SET_FLAG: 565*91f16700Schasinglulu query_upiu->query_func = QUERY_FUNC_STD_WRITE; 566*91f16700Schasinglulu break; 567*91f16700Schasinglulu case QUERY_WRITE_ATTR: 568*91f16700Schasinglulu query_upiu->query_func = QUERY_FUNC_STD_WRITE; 569*91f16700Schasinglulu query_upiu->ts.attr.value = htobe32(*((uint32_t *)buf)); 570*91f16700Schasinglulu break; 571*91f16700Schasinglulu default: 572*91f16700Schasinglulu assert(0); 573*91f16700Schasinglulu break; 574*91f16700Schasinglulu } 575*91f16700Schasinglulu flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE); 576*91f16700Schasinglulu return 0; 577*91f16700Schasinglulu } 578*91f16700Schasinglulu 579*91f16700Schasinglulu static void ufs_prepare_nop_out(utp_utrd_t *utrd) 580*91f16700Schasinglulu { 581*91f16700Schasinglulu utrd_header_t *hd; 582*91f16700Schasinglulu nop_out_upiu_t *nop_out; 583*91f16700Schasinglulu 584*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 585*91f16700Schasinglulu nop_out = (nop_out_upiu_t *)utrd->upiu; 586*91f16700Schasinglulu 587*91f16700Schasinglulu hd->i = 1; 588*91f16700Schasinglulu hd->ct = CT_UFS_STORAGE; 589*91f16700Schasinglulu hd->ocs = OCS_MASK; 590*91f16700Schasinglulu 591*91f16700Schasinglulu nop_out->trans_type = 0; 592*91f16700Schasinglulu nop_out->task_tag = utrd->task_tag; 593*91f16700Schasinglulu flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE); 594*91f16700Schasinglulu } 595*91f16700Schasinglulu 596*91f16700Schasinglulu static void ufs_send_request(int task_tag) 597*91f16700Schasinglulu { 598*91f16700Schasinglulu unsigned int data; 599*91f16700Schasinglulu int slot; 600*91f16700Schasinglulu 601*91f16700Schasinglulu slot = task_tag - 1; 602*91f16700Schasinglulu /* clear all interrupts */ 603*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + IS, ~0); 604*91f16700Schasinglulu 605*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRLRSR, 1); 606*91f16700Schasinglulu assert(mmio_read_32(ufs_params.reg_base + UTRLRSR) == 1); 607*91f16700Schasinglulu 608*91f16700Schasinglulu data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) | 609*91f16700Schasinglulu UTRIACR_IATOVAL(0xFF); 610*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRIACR, data); 611*91f16700Schasinglulu /* send request */ 612*91f16700Schasinglulu mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1U << slot); 613*91f16700Schasinglulu } 614*91f16700Schasinglulu 615*91f16700Schasinglulu static int ufs_check_resp(utp_utrd_t *utrd, int trans_type, unsigned int timeout_ms) 616*91f16700Schasinglulu { 617*91f16700Schasinglulu utrd_header_t *hd; 618*91f16700Schasinglulu resp_upiu_t *resp; 619*91f16700Schasinglulu sense_data_t *sense; 620*91f16700Schasinglulu unsigned int data; 621*91f16700Schasinglulu int slot, result; 622*91f16700Schasinglulu 623*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 624*91f16700Schasinglulu resp = (resp_upiu_t *)utrd->resp_upiu; 625*91f16700Schasinglulu 626*91f16700Schasinglulu result = ufs_wait_for_int_status(UFS_INT_UTRCS, timeout_ms, false); 627*91f16700Schasinglulu if (result != 0) { 628*91f16700Schasinglulu return result; 629*91f16700Schasinglulu } 630*91f16700Schasinglulu 631*91f16700Schasinglulu slot = utrd->task_tag - 1; 632*91f16700Schasinglulu 633*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UTRLDBR); 634*91f16700Schasinglulu assert((data & (1 << slot)) == 0); 635*91f16700Schasinglulu /* 636*91f16700Schasinglulu * Invalidate the header after DMA read operation has 637*91f16700Schasinglulu * completed to avoid cpu referring to the prefetched 638*91f16700Schasinglulu * data brought in before DMA completion. 639*91f16700Schasinglulu */ 640*91f16700Schasinglulu inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE); 641*91f16700Schasinglulu assert(hd->ocs == OCS_SUCCESS); 642*91f16700Schasinglulu assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type); 643*91f16700Schasinglulu 644*91f16700Schasinglulu sense = &resp->sd.sense; 645*91f16700Schasinglulu if (sense->resp_code == SENSE_DATA_VALID && 646*91f16700Schasinglulu sense->sense_key == SENSE_KEY_UNIT_ATTENTION && sense->asc == 0x29 && 647*91f16700Schasinglulu sense->ascq == 0) { 648*91f16700Schasinglulu WARN("Unit Attention Condition\n"); 649*91f16700Schasinglulu return -EAGAIN; 650*91f16700Schasinglulu } 651*91f16700Schasinglulu 652*91f16700Schasinglulu (void)resp; 653*91f16700Schasinglulu (void)slot; 654*91f16700Schasinglulu (void)data; 655*91f16700Schasinglulu return 0; 656*91f16700Schasinglulu } 657*91f16700Schasinglulu 658*91f16700Schasinglulu static void ufs_send_cmd(utp_utrd_t *utrd, uint8_t cmd_op, uint8_t lun, int lba, uintptr_t buf, 659*91f16700Schasinglulu size_t length) 660*91f16700Schasinglulu { 661*91f16700Schasinglulu int result, i; 662*91f16700Schasinglulu 663*91f16700Schasinglulu for (i = 0; i < UFS_CMD_RETRIES; ++i) { 664*91f16700Schasinglulu get_utrd(utrd); 665*91f16700Schasinglulu result = ufs_prepare_cmd(utrd, cmd_op, lun, lba, buf, length); 666*91f16700Schasinglulu assert(result == 0); 667*91f16700Schasinglulu ufs_send_request(utrd->task_tag); 668*91f16700Schasinglulu result = ufs_check_resp(utrd, RESPONSE_UPIU, CMD_TIMEOUT_MS); 669*91f16700Schasinglulu if (result == 0 || result == -EIO) { 670*91f16700Schasinglulu break; 671*91f16700Schasinglulu } 672*91f16700Schasinglulu } 673*91f16700Schasinglulu assert(result == 0); 674*91f16700Schasinglulu (void)result; 675*91f16700Schasinglulu } 676*91f16700Schasinglulu 677*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG 678*91f16700Schasinglulu static void dump_upiu(utp_utrd_t *utrd) 679*91f16700Schasinglulu { 680*91f16700Schasinglulu utrd_header_t *hd; 681*91f16700Schasinglulu int i; 682*91f16700Schasinglulu 683*91f16700Schasinglulu hd = (utrd_header_t *)utrd->header; 684*91f16700Schasinglulu INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n", 685*91f16700Schasinglulu (unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs, 686*91f16700Schasinglulu mmio_read_32(ufs_params.reg_base + UTRLDBR)); 687*91f16700Schasinglulu for (i = 0; i < sizeof(utrd_header_t); i += 4) { 688*91f16700Schasinglulu INFO("[%lx]:0x%x\n", 689*91f16700Schasinglulu (uintptr_t)utrd->header + i, 690*91f16700Schasinglulu *(unsigned int *)((uintptr_t)utrd->header + i)); 691*91f16700Schasinglulu } 692*91f16700Schasinglulu 693*91f16700Schasinglulu for (i = 0; i < sizeof(cmd_upiu_t); i += 4) { 694*91f16700Schasinglulu INFO("cmd[%lx]:0x%x\n", 695*91f16700Schasinglulu utrd->upiu + i, 696*91f16700Schasinglulu *(unsigned int *)(utrd->upiu + i)); 697*91f16700Schasinglulu } 698*91f16700Schasinglulu for (i = 0; i < sizeof(resp_upiu_t); i += 4) { 699*91f16700Schasinglulu INFO("resp[%lx]:0x%x\n", 700*91f16700Schasinglulu utrd->resp_upiu + i, 701*91f16700Schasinglulu *(unsigned int *)(utrd->resp_upiu + i)); 702*91f16700Schasinglulu } 703*91f16700Schasinglulu for (i = 0; i < sizeof(prdt_t); i += 4) { 704*91f16700Schasinglulu INFO("prdt[%lx]:0x%x\n", 705*91f16700Schasinglulu utrd->prdt + i, 706*91f16700Schasinglulu *(unsigned int *)(utrd->prdt + i)); 707*91f16700Schasinglulu } 708*91f16700Schasinglulu } 709*91f16700Schasinglulu #endif 710*91f16700Schasinglulu 711*91f16700Schasinglulu static void ufs_verify_init(void) 712*91f16700Schasinglulu { 713*91f16700Schasinglulu utp_utrd_t utrd; 714*91f16700Schasinglulu int result; 715*91f16700Schasinglulu 716*91f16700Schasinglulu get_utrd(&utrd); 717*91f16700Schasinglulu ufs_prepare_nop_out(&utrd); 718*91f16700Schasinglulu ufs_send_request(utrd.task_tag); 719*91f16700Schasinglulu result = ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS); 720*91f16700Schasinglulu assert(result == 0); 721*91f16700Schasinglulu (void)result; 722*91f16700Schasinglulu } 723*91f16700Schasinglulu 724*91f16700Schasinglulu static void ufs_verify_ready(void) 725*91f16700Schasinglulu { 726*91f16700Schasinglulu utp_utrd_t utrd; 727*91f16700Schasinglulu ufs_send_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0); 728*91f16700Schasinglulu } 729*91f16700Schasinglulu 730*91f16700Schasinglulu static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel, 731*91f16700Schasinglulu uintptr_t buf, size_t size) 732*91f16700Schasinglulu { 733*91f16700Schasinglulu utp_utrd_t utrd; 734*91f16700Schasinglulu query_resp_upiu_t *resp; 735*91f16700Schasinglulu int result; 736*91f16700Schasinglulu 737*91f16700Schasinglulu switch (op) { 738*91f16700Schasinglulu case QUERY_READ_FLAG: 739*91f16700Schasinglulu case QUERY_READ_ATTR: 740*91f16700Schasinglulu case QUERY_READ_DESC: 741*91f16700Schasinglulu case QUERY_WRITE_DESC: 742*91f16700Schasinglulu case QUERY_WRITE_ATTR: 743*91f16700Schasinglulu assert(((buf & 3) == 0) && (size != 0)); 744*91f16700Schasinglulu break; 745*91f16700Schasinglulu default: 746*91f16700Schasinglulu /* Do nothing in default case */ 747*91f16700Schasinglulu break; 748*91f16700Schasinglulu } 749*91f16700Schasinglulu get_utrd(&utrd); 750*91f16700Schasinglulu ufs_prepare_query(&utrd, op, idn, index, sel, buf, size); 751*91f16700Schasinglulu ufs_send_request(utrd.task_tag); 752*91f16700Schasinglulu result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU, QUERY_REQ_TIMEOUT_MS); 753*91f16700Schasinglulu assert(result == 0); 754*91f16700Schasinglulu resp = (query_resp_upiu_t *)utrd.resp_upiu; 755*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG 756*91f16700Schasinglulu dump_upiu(&utrd); 757*91f16700Schasinglulu #endif 758*91f16700Schasinglulu assert(resp->query_resp == QUERY_RESP_SUCCESS); 759*91f16700Schasinglulu 760*91f16700Schasinglulu switch (op) { 761*91f16700Schasinglulu case QUERY_READ_FLAG: 762*91f16700Schasinglulu *(uint32_t *)buf = (uint32_t)resp->ts.flag.value; 763*91f16700Schasinglulu break; 764*91f16700Schasinglulu case QUERY_READ_DESC: 765*91f16700Schasinglulu memcpy((void *)buf, 766*91f16700Schasinglulu (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)), 767*91f16700Schasinglulu size); 768*91f16700Schasinglulu break; 769*91f16700Schasinglulu case QUERY_READ_ATTR: 770*91f16700Schasinglulu *(uint32_t *)buf = htobe32(resp->ts.attr.value); 771*91f16700Schasinglulu break; 772*91f16700Schasinglulu default: 773*91f16700Schasinglulu /* Do nothing in default case */ 774*91f16700Schasinglulu break; 775*91f16700Schasinglulu } 776*91f16700Schasinglulu (void)result; 777*91f16700Schasinglulu } 778*91f16700Schasinglulu 779*91f16700Schasinglulu unsigned int ufs_read_attr(int idn) 780*91f16700Schasinglulu { 781*91f16700Schasinglulu unsigned int value; 782*91f16700Schasinglulu 783*91f16700Schasinglulu ufs_query(QUERY_READ_ATTR, idn, 0, 0, 784*91f16700Schasinglulu (uintptr_t)&value, sizeof(value)); 785*91f16700Schasinglulu return value; 786*91f16700Schasinglulu } 787*91f16700Schasinglulu 788*91f16700Schasinglulu void ufs_write_attr(int idn, unsigned int value) 789*91f16700Schasinglulu { 790*91f16700Schasinglulu ufs_query(QUERY_WRITE_ATTR, idn, 0, 0, 791*91f16700Schasinglulu (uintptr_t)&value, sizeof(value)); 792*91f16700Schasinglulu } 793*91f16700Schasinglulu 794*91f16700Schasinglulu unsigned int ufs_read_flag(int idn) 795*91f16700Schasinglulu { 796*91f16700Schasinglulu unsigned int value; 797*91f16700Schasinglulu 798*91f16700Schasinglulu ufs_query(QUERY_READ_FLAG, idn, 0, 0, 799*91f16700Schasinglulu (uintptr_t)&value, sizeof(value)); 800*91f16700Schasinglulu return value; 801*91f16700Schasinglulu } 802*91f16700Schasinglulu 803*91f16700Schasinglulu void ufs_set_flag(int idn) 804*91f16700Schasinglulu { 805*91f16700Schasinglulu ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0); 806*91f16700Schasinglulu } 807*91f16700Schasinglulu 808*91f16700Schasinglulu void ufs_clear_flag(int idn) 809*91f16700Schasinglulu { 810*91f16700Schasinglulu ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0); 811*91f16700Schasinglulu } 812*91f16700Schasinglulu 813*91f16700Schasinglulu void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size) 814*91f16700Schasinglulu { 815*91f16700Schasinglulu ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size); 816*91f16700Schasinglulu } 817*91f16700Schasinglulu 818*91f16700Schasinglulu void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size) 819*91f16700Schasinglulu { 820*91f16700Schasinglulu ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size); 821*91f16700Schasinglulu } 822*91f16700Schasinglulu 823*91f16700Schasinglulu static int ufs_read_capacity(int lun, unsigned int *num, unsigned int *size) 824*91f16700Schasinglulu { 825*91f16700Schasinglulu utp_utrd_t utrd; 826*91f16700Schasinglulu resp_upiu_t *resp; 827*91f16700Schasinglulu sense_data_t *sense; 828*91f16700Schasinglulu unsigned char data[CACHE_WRITEBACK_GRANULE << 1]; 829*91f16700Schasinglulu uintptr_t buf; 830*91f16700Schasinglulu int retries = UFS_READ_CAPACITY_RETRIES; 831*91f16700Schasinglulu 832*91f16700Schasinglulu assert((ufs_params.reg_base != 0) && 833*91f16700Schasinglulu (ufs_params.desc_base != 0) && 834*91f16700Schasinglulu (ufs_params.desc_size >= UFS_DESC_SIZE) && 835*91f16700Schasinglulu (num != NULL) && (size != NULL)); 836*91f16700Schasinglulu 837*91f16700Schasinglulu /* align buf address */ 838*91f16700Schasinglulu buf = (uintptr_t)data; 839*91f16700Schasinglulu buf = (buf + CACHE_WRITEBACK_GRANULE - 1) & 840*91f16700Schasinglulu ~(CACHE_WRITEBACK_GRANULE - 1); 841*91f16700Schasinglulu do { 842*91f16700Schasinglulu ufs_send_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0, 843*91f16700Schasinglulu buf, READ_CAPACITY_LENGTH); 844*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG 845*91f16700Schasinglulu dump_upiu(&utrd); 846*91f16700Schasinglulu #endif 847*91f16700Schasinglulu resp = (resp_upiu_t *)utrd.resp_upiu; 848*91f16700Schasinglulu sense = &resp->sd.sense; 849*91f16700Schasinglulu if (!((sense->resp_code == SENSE_DATA_VALID) && 850*91f16700Schasinglulu (sense->sense_key == SENSE_KEY_UNIT_ATTENTION) && 851*91f16700Schasinglulu (sense->asc == 0x29) && (sense->ascq == 0))) { 852*91f16700Schasinglulu inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE); 853*91f16700Schasinglulu /* last logical block address */ 854*91f16700Schasinglulu *num = be32toh(*(unsigned int *)buf); 855*91f16700Schasinglulu if (*num) 856*91f16700Schasinglulu *num += 1; 857*91f16700Schasinglulu /* logical block length in bytes */ 858*91f16700Schasinglulu *size = be32toh(*(unsigned int *)(buf + 4)); 859*91f16700Schasinglulu 860*91f16700Schasinglulu return 0; 861*91f16700Schasinglulu } 862*91f16700Schasinglulu 863*91f16700Schasinglulu } while (retries-- > 0); 864*91f16700Schasinglulu 865*91f16700Schasinglulu return -ETIMEDOUT; 866*91f16700Schasinglulu } 867*91f16700Schasinglulu 868*91f16700Schasinglulu size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size) 869*91f16700Schasinglulu { 870*91f16700Schasinglulu utp_utrd_t utrd; 871*91f16700Schasinglulu resp_upiu_t *resp; 872*91f16700Schasinglulu 873*91f16700Schasinglulu assert((ufs_params.reg_base != 0) && 874*91f16700Schasinglulu (ufs_params.desc_base != 0) && 875*91f16700Schasinglulu (ufs_params.desc_size >= UFS_DESC_SIZE)); 876*91f16700Schasinglulu 877*91f16700Schasinglulu ufs_send_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size); 878*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG 879*91f16700Schasinglulu dump_upiu(&utrd); 880*91f16700Schasinglulu #endif 881*91f16700Schasinglulu /* 882*91f16700Schasinglulu * Invalidate prefetched cache contents before cpu 883*91f16700Schasinglulu * accesses the buf. 884*91f16700Schasinglulu */ 885*91f16700Schasinglulu inv_dcache_range(buf, size); 886*91f16700Schasinglulu resp = (resp_upiu_t *)utrd.resp_upiu; 887*91f16700Schasinglulu return size - resp->res_trans_cnt; 888*91f16700Schasinglulu } 889*91f16700Schasinglulu 890*91f16700Schasinglulu size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size) 891*91f16700Schasinglulu { 892*91f16700Schasinglulu utp_utrd_t utrd; 893*91f16700Schasinglulu resp_upiu_t *resp; 894*91f16700Schasinglulu 895*91f16700Schasinglulu assert((ufs_params.reg_base != 0) && 896*91f16700Schasinglulu (ufs_params.desc_base != 0) && 897*91f16700Schasinglulu (ufs_params.desc_size >= UFS_DESC_SIZE)); 898*91f16700Schasinglulu 899*91f16700Schasinglulu ufs_send_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size); 900*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG 901*91f16700Schasinglulu dump_upiu(&utrd); 902*91f16700Schasinglulu #endif 903*91f16700Schasinglulu resp = (resp_upiu_t *)utrd.resp_upiu; 904*91f16700Schasinglulu return size - resp->res_trans_cnt; 905*91f16700Schasinglulu } 906*91f16700Schasinglulu 907*91f16700Schasinglulu static int ufs_set_fdevice_init(void) 908*91f16700Schasinglulu { 909*91f16700Schasinglulu unsigned int result; 910*91f16700Schasinglulu int timeout; 911*91f16700Schasinglulu 912*91f16700Schasinglulu ufs_set_flag(FLAG_DEVICE_INIT); 913*91f16700Schasinglulu 914*91f16700Schasinglulu timeout = FDEVICEINIT_TIMEOUT_MS; 915*91f16700Schasinglulu do { 916*91f16700Schasinglulu result = ufs_read_flag(FLAG_DEVICE_INIT); 917*91f16700Schasinglulu if (!result) { 918*91f16700Schasinglulu break; 919*91f16700Schasinglulu } 920*91f16700Schasinglulu mdelay(5); 921*91f16700Schasinglulu timeout -= 5; 922*91f16700Schasinglulu } while (timeout > 0); 923*91f16700Schasinglulu 924*91f16700Schasinglulu if (result != 0U) { 925*91f16700Schasinglulu return -ETIMEDOUT; 926*91f16700Schasinglulu } 927*91f16700Schasinglulu 928*91f16700Schasinglulu return 0; 929*91f16700Schasinglulu } 930*91f16700Schasinglulu 931*91f16700Schasinglulu static void ufs_enum(void) 932*91f16700Schasinglulu { 933*91f16700Schasinglulu unsigned int blk_num, blk_size; 934*91f16700Schasinglulu int i, result; 935*91f16700Schasinglulu 936*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRLBA, 937*91f16700Schasinglulu ufs_params.desc_base & UINT32_MAX); 938*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRLBAU, 939*91f16700Schasinglulu (ufs_params.desc_base >> 32) & UINT32_MAX); 940*91f16700Schasinglulu 941*91f16700Schasinglulu ufs_verify_init(); 942*91f16700Schasinglulu ufs_verify_ready(); 943*91f16700Schasinglulu 944*91f16700Schasinglulu result = ufs_set_fdevice_init(); 945*91f16700Schasinglulu assert(result == 0); 946*91f16700Schasinglulu 947*91f16700Schasinglulu blk_num = 0; 948*91f16700Schasinglulu blk_size = 0; 949*91f16700Schasinglulu 950*91f16700Schasinglulu /* dump available LUNs */ 951*91f16700Schasinglulu for (i = 0; i < UFS_MAX_LUNS; i++) { 952*91f16700Schasinglulu result = ufs_read_capacity(i, &blk_num, &blk_size); 953*91f16700Schasinglulu if (result != 0) { 954*91f16700Schasinglulu WARN("UFS LUN%d dump failed\n", i); 955*91f16700Schasinglulu } 956*91f16700Schasinglulu if (blk_num && blk_size) { 957*91f16700Schasinglulu INFO("UFS LUN%d contains %d blocks with %d-byte size\n", 958*91f16700Schasinglulu i, blk_num, blk_size); 959*91f16700Schasinglulu } 960*91f16700Schasinglulu } 961*91f16700Schasinglulu 962*91f16700Schasinglulu (void)result; 963*91f16700Schasinglulu } 964*91f16700Schasinglulu 965*91f16700Schasinglulu static void ufs_get_device_info(struct ufs_dev_desc *card_data) 966*91f16700Schasinglulu { 967*91f16700Schasinglulu uint8_t desc_buf[DESC_DEVICE_MAX_SIZE]; 968*91f16700Schasinglulu 969*91f16700Schasinglulu ufs_query(QUERY_READ_DESC, DESC_TYPE_DEVICE, 0, 0, 970*91f16700Schasinglulu (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE); 971*91f16700Schasinglulu 972*91f16700Schasinglulu /* 973*91f16700Schasinglulu * getting vendor (manufacturerID) and Bank Index in big endian 974*91f16700Schasinglulu * format 975*91f16700Schasinglulu */ 976*91f16700Schasinglulu card_data->wmanufacturerid = (uint16_t)((desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8) | 977*91f16700Schasinglulu (desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1])); 978*91f16700Schasinglulu } 979*91f16700Schasinglulu 980*91f16700Schasinglulu int ufs_init(const ufs_ops_t *ops, ufs_params_t *params) 981*91f16700Schasinglulu { 982*91f16700Schasinglulu int result; 983*91f16700Schasinglulu unsigned int data; 984*91f16700Schasinglulu uic_cmd_t cmd; 985*91f16700Schasinglulu struct ufs_dev_desc card = {0}; 986*91f16700Schasinglulu 987*91f16700Schasinglulu assert((params != NULL) && 988*91f16700Schasinglulu (params->reg_base != 0) && 989*91f16700Schasinglulu (params->desc_base != 0) && 990*91f16700Schasinglulu (params->desc_size >= UFS_DESC_SIZE)); 991*91f16700Schasinglulu 992*91f16700Schasinglulu memcpy(&ufs_params, params, sizeof(ufs_params_t)); 993*91f16700Schasinglulu 994*91f16700Schasinglulu /* 0 means 1 slot */ 995*91f16700Schasinglulu nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1; 996*91f16700Schasinglulu if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE)) { 997*91f16700Schasinglulu nutrs = ufs_params.desc_size / UFS_DESC_SIZE; 998*91f16700Schasinglulu } 999*91f16700Schasinglulu 1000*91f16700Schasinglulu 1001*91f16700Schasinglulu if (ufs_params.flags & UFS_FLAGS_SKIPINIT) { 1002*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRLBA, 1003*91f16700Schasinglulu ufs_params.desc_base & UINT32_MAX); 1004*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + UTRLBAU, 1005*91f16700Schasinglulu (ufs_params.desc_base >> 32) & UINT32_MAX); 1006*91f16700Schasinglulu 1007*91f16700Schasinglulu result = ufshc_dme_get(0x1571, 0, &data); 1008*91f16700Schasinglulu assert(result == 0); 1009*91f16700Schasinglulu result = ufshc_dme_get(0x41, 0, &data); 1010*91f16700Schasinglulu assert(result == 0); 1011*91f16700Schasinglulu if (data == 1) { 1012*91f16700Schasinglulu /* prepare to exit hibernate mode */ 1013*91f16700Schasinglulu memset(&cmd, 0, sizeof(uic_cmd_t)); 1014*91f16700Schasinglulu cmd.op = DME_HIBERNATE_EXIT; 1015*91f16700Schasinglulu result = ufshc_send_uic_cmd(ufs_params.reg_base, 1016*91f16700Schasinglulu &cmd); 1017*91f16700Schasinglulu assert(result == 0); 1018*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + UCMDARG2); 1019*91f16700Schasinglulu assert(data == 0); 1020*91f16700Schasinglulu do { 1021*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + IS); 1022*91f16700Schasinglulu } while ((data & UFS_INT_UHXS) == 0); 1023*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS); 1024*91f16700Schasinglulu data = mmio_read_32(ufs_params.reg_base + HCS); 1025*91f16700Schasinglulu assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL); 1026*91f16700Schasinglulu } 1027*91f16700Schasinglulu result = ufshc_dme_get(0x1568, 0, &data); 1028*91f16700Schasinglulu assert(result == 0); 1029*91f16700Schasinglulu assert((data > 0) && (data <= 3)); 1030*91f16700Schasinglulu } else { 1031*91f16700Schasinglulu assert((ops != NULL) && (ops->phy_init != NULL) && 1032*91f16700Schasinglulu (ops->phy_set_pwr_mode != NULL)); 1033*91f16700Schasinglulu 1034*91f16700Schasinglulu result = ufshc_reset(ufs_params.reg_base); 1035*91f16700Schasinglulu assert(result == 0); 1036*91f16700Schasinglulu ops->phy_init(&ufs_params); 1037*91f16700Schasinglulu result = ufshc_link_startup(ufs_params.reg_base); 1038*91f16700Schasinglulu assert(result == 0); 1039*91f16700Schasinglulu 1040*91f16700Schasinglulu /* enable all interrupts */ 1041*91f16700Schasinglulu data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS; 1042*91f16700Schasinglulu data |= UFS_INT_UTRCS | UFS_INT_ERR; 1043*91f16700Schasinglulu mmio_write_32(ufs_params.reg_base + IE, data); 1044*91f16700Schasinglulu 1045*91f16700Schasinglulu ufs_enum(); 1046*91f16700Schasinglulu 1047*91f16700Schasinglulu ufs_get_device_info(&card); 1048*91f16700Schasinglulu if (card.wmanufacturerid == UFS_VENDOR_SKHYNIX) { 1049*91f16700Schasinglulu ufs_params.flags |= UFS_FLAGS_VENDOR_SKHYNIX; 1050*91f16700Schasinglulu } 1051*91f16700Schasinglulu 1052*91f16700Schasinglulu ops->phy_set_pwr_mode(&ufs_params); 1053*91f16700Schasinglulu } 1054*91f16700Schasinglulu 1055*91f16700Schasinglulu (void)result; 1056*91f16700Schasinglulu return 0; 1057*91f16700Schasinglulu } 1058