From e4e01f80669b22662565c9fa545649dc44eb8fad Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 6 Aug 2023 20:18:42 +0200 Subject: ufs: cdns: Drop extra space Drop extra space before UCLASS. No functional change. Signed-off-by: Marek Vasut --- drivers/ufs/cdns-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/cdns-platform.c b/drivers/ufs/cdns-platform.c index bad1bf7de5f..1e62e252e7a 100644 --- a/drivers/ufs/cdns-platform.c +++ b/drivers/ufs/cdns-platform.c @@ -119,7 +119,7 @@ static const struct udevice_id cdns_ufs_pltfm_ids[] = { U_BOOT_DRIVER(cdns_ufs_pltfm) = { .name = "cdns-ufs-pltfm", - .id = UCLASS_UFS, + .id = UCLASS_UFS, .of_match = cdns_ufs_pltfm_ids, .probe = cdns_ufs_pltfm_probe, .bind = cdns_ufs_pltfm_bind, -- cgit v1.3.1 From 5dab730cac673c635a5107a28c0c4288a442f135 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:49 +0200 Subject: ufs: Convert quirks to BIT() macro Use BIT() macro for quirks, no functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma --- drivers/ufs/ufs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 8a38832b05f..5a5c13aefdf 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -717,7 +717,7 @@ struct ufs_hba { * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE * attribute of device to 0). */ -#define UFSHCD_QUIRK_BROKEN_LCC 0x1 +#define UFSHCD_QUIRK_BROKEN_LCC BIT(0) /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; -- cgit v1.3.1 From 91913a1aa2ffeb7278d7d4e4bc51565bb38cebeb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:50 +0200 Subject: ufs: Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS for host controllers which do not support 64-bit addressing. Ported from Linux kernel commit 6554400d6f66 ("scsi: ufs: core: Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS") with ufs_scsi_buffer_aligned() based on U-Boot generic bounce buffer. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 26 ++++++++++++++++++++++++++ drivers/ufs/ufs.h | 6 ++++++ 2 files changed, 32 insertions(+) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 3bf1a95e7f2..da0550d98c6 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -8,6 +8,7 @@ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com */ +#include #include #include #include @@ -1889,6 +1890,8 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) /* Read capabilties registers */ hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); + if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) + hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; /* Get UFS version supported by the controller */ hba->version = ufshcd_get_ufs_version(hba); @@ -1942,8 +1945,31 @@ int ufs_scsi_bind(struct udevice *ufs_dev, struct udevice **scsi_devp) return ret; } +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER) +static int ufs_scsi_buffer_aligned(struct udevice *scsi_dev, struct bounce_buffer *state) +{ +#ifdef CONFIG_PHYS_64BIT + struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent); + uintptr_t ubuf = (uintptr_t)state->user_buffer; + size_t len = state->len_aligned; + + /* Check if below 32bit boundary */ + if ((hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) && + ((ubuf >> 32) || (ubuf + len) >> 32)) { + dev_dbg(scsi_dev, "Buffer above 32bit boundary %lx-%lx\n", + ubuf, ubuf + len); + return 0; + } +#endif + return 1; +} +#endif /* CONFIG_BOUNCE_BUFFER */ + static struct scsi_ops ufs_ops = { .exec = ufs_scsi_exec, +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER) + .buffer_aligned = ufs_scsi_buffer_aligned, +#endif /* CONFIG_BOUNCE_BUFFER */ }; int ufs_probe_dev(int index) diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 5a5c13aefdf..e5ddb6f64a9 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -719,6 +719,12 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_BROKEN_LCC BIT(0) +/* + * This quirk needs to be enabled if the host controller has + * 64-bit addressing supported capability but it doesn't work. + */ +#define UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS BIT(1) + /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; struct utp_transfer_req_desc *utrdl; -- cgit v1.3.1 From f430151e105b055abd687a456c3980b3674eeb42 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:51 +0200 Subject: ufs: Add UFSHCD_QUIRK_HIBERN_FASTAUTO Add UFSHCD_QUIRK_HIBERN_FASTAUTO quirk for host controllers which supports auto-hibernate the capability but only FASTAUTO mode. Ported from Linux kernel commit 2f11bbc2c7f3 ("scsi: ufs: core: Add UFSHCD_QUIRK_HIBERN_FASTAUTO") Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 9 +++++++-- drivers/ufs/ufs.h | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index da0550d98c6..261ae2843c2 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -1631,8 +1631,13 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; - pwr_info->pwr_tx = FAST_MODE; - pwr_info->pwr_rx = FAST_MODE; + if (hba->quirks & UFSHCD_QUIRK_HIBERN_FASTAUTO) { + pwr_info->pwr_tx = FASTAUTO_MODE; + pwr_info->pwr_rx = FASTAUTO_MODE; + } else { + pwr_info->pwr_tx = FAST_MODE; + pwr_info->pwr_rx = FAST_MODE; + } pwr_info->hs_rate = PA_HS_MODE_B; /* Get the connected lane count */ diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index e5ddb6f64a9..638c10b5503 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -725,6 +725,12 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS BIT(1) +/* + * This quirk needs to be enabled if the host controller has + * auto-hibernate capability but it's FASTAUTO only. + */ +#define UFSHCD_QUIRK_HIBERN_FASTAUTO BIT(2) + /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; struct utp_transfer_req_desc *utrdl; -- cgit v1.3.1 From 2ff810ae5e823ba42c7b543b06d87e3dd7d1cb81 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:52 +0200 Subject: ufs: Handle UFS 3.0 controllers Extend the version check to handle UFS 3.0 controllers as well. Tested on R-Car S4 UFS 3.0 controller. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 3 ++- drivers/ufs/ufs.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 261ae2843c2..58830c8ddca 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -1903,7 +1903,8 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) if (hba->version != UFSHCI_VERSION_10 && hba->version != UFSHCI_VERSION_11 && hba->version != UFSHCI_VERSION_20 && - hba->version != UFSHCI_VERSION_21) + hba->version != UFSHCI_VERSION_21 && + hba->version != UFSHCI_VERSION_30) dev_err(hba->dev, "invalid UFS version 0x%x\n", hba->version); diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 638c10b5503..9daaf03d222 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -781,6 +781,7 @@ enum { UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ + UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ }; /* Interrupt disable masks */ -- cgit v1.3.1 From 7f26fcbea82caa57852fc93bee7ac7300a42c730 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:53 +0200 Subject: ufs: Pass hba pointer to ufshcd_prepare_req_desc_hdr() Pass the hba pointer itself to ufshcd_prepare_req_desc_hdr() instead of duplicating utp_transfer_req_desc access at each call site. No functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 58830c8ddca..da1009e2c14 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -696,10 +696,11 @@ static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) * ufshcd_prepare_req_desc_hdr() - Fills the requests header * descriptor according to request */ -static void ufshcd_prepare_req_desc_hdr(struct utp_transfer_req_desc *req_desc, +static void ufshcd_prepare_req_desc_hdr(struct ufs_hba *hba, u32 *upiu_flags, enum dma_data_direction cmd_dir) { + struct utp_transfer_req_desc *req_desc = hba->utrdl; u32 data_direction; u32 dword_0; @@ -793,11 +794,10 @@ static int ufshcd_comp_devman_upiu(struct ufs_hba *hba, { u32 upiu_flags; int ret = 0; - struct utp_transfer_req_desc *req_desc = hba->utrdl; hba->dev_cmd.type = cmd_type; - ufshcd_prepare_req_desc_hdr(req_desc, &upiu_flags, DMA_NONE); + ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, DMA_NONE); switch (cmd_type) { case DEV_CMD_TYPE_QUERY: ufshcd_prepare_utp_query_req_upiu(hba, upiu_flags); @@ -1449,12 +1449,11 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb) { struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent); - struct utp_transfer_req_desc *req_desc = hba->utrdl; u32 upiu_flags; int ocs, result = 0; u8 scsi_status; - ufshcd_prepare_req_desc_hdr(req_desc, &upiu_flags, pccb->dma_dir); + ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, pccb->dma_dir); ufshcd_prepare_utp_scsi_cmd_upiu(hba, pccb, upiu_flags); prepare_prdt_table(hba, pccb); -- cgit v1.3.1 From 12675cb100a860ec7f8fe6df36bb0f7602797e29 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:54 +0200 Subject: ufs: Use utp_transfer_req_desc pointer in ufshcd_get_tr_ocs Use utp_transfer_req_desc pointer to reference to utrdl queue instead of referencing the queue directly. This makes the code more consistent. No functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index da1009e2c14..041caee714f 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -858,7 +858,9 @@ static inline int ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr) */ static inline int ufshcd_get_tr_ocs(struct ufs_hba *hba) { - return le32_to_cpu(hba->utrdl->header.dword_2) & MASK_OCS; + struct utp_transfer_req_desc *req_desc = hba->utrdl; + + return le32_to_cpu(req_desc->header.dword_2) & MASK_OCS; } static inline int ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) -- cgit v1.3.1 From c5b3e5cd3d573bd3615c09f1aaa19291608e2093 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:55 +0200 Subject: ufs: Implement cache management Add function to flush and invalidate cache over request and response queue entries, and perform flush and optional invalidate over block layer data that are passed into the UFS layer. This makes it possible to use UFS with caches enabled. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 041caee714f..7c48d57f99d 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -692,6 +692,21 @@ static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7; } +/** + * ufshcd_cache_flush_and_invalidate - Flush and invalidate cache + * + * Flush and invalidate cache in aligned address..address+size range. + * The invalidation is in place to avoid stale data in cache. + */ +static void ufshcd_cache_flush_and_invalidate(void *addr, unsigned long size) +{ + uintptr_t aaddr = (uintptr_t)addr & ~(ARCH_DMA_MINALIGN - 1); + unsigned long asize = ALIGN(size, ARCH_DMA_MINALIGN); + + flush_dcache_range(aaddr, aaddr + asize); + invalidate_dcache_range(aaddr, aaddr + asize); +} + /** * ufshcd_prepare_req_desc_hdr() - Fills the requests header * descriptor according to request @@ -735,6 +750,8 @@ static void ufshcd_prepare_req_desc_hdr(struct ufs_hba *hba, req_desc->header.dword_3 = 0; req_desc->prd_table_length = 0; + + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); } static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, @@ -763,10 +780,15 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, memcpy(&ucd_req_ptr->qr, &query->request.upiu_req, QUERY_OSF_SIZE); /* Copy the Descriptor */ - if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) + if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) { memcpy(ucd_req_ptr + 1, query->descriptor, len); + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, 2 * sizeof(*ucd_req_ptr)); + } else { + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + } memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } static inline void ufshcd_prepare_utp_nop_upiu(struct ufs_hba *hba) @@ -783,6 +805,9 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufs_hba *hba) ucd_req_ptr->header.dword_2 = 0; memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } /** @@ -1409,6 +1434,8 @@ void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufs_hba *hba, memcpy(ucd_req_ptr->sc.cdb, pccb->cmd, cdb_len); memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } static inline void prepare_prdt_desc(struct ufshcd_sg_entry *entry, @@ -1423,6 +1450,7 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) { struct utp_transfer_req_desc *req_desc = hba->utrdl; struct ufshcd_sg_entry *prd_table = hba->ucd_prdt_ptr; + uintptr_t aaddr = (uintptr_t)(pccb->pdata) & ~(ARCH_DMA_MINALIGN - 1); ulong datalen = pccb->datalen; int table_length; u8 *buf; @@ -1430,9 +1458,19 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) if (!datalen) { req_desc->prd_table_length = 0; + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); return; } + if (pccb->dma_dir == DMA_TO_DEVICE) { /* Write to device */ + flush_dcache_range(aaddr, aaddr + + ALIGN(datalen, ARCH_DMA_MINALIGN)); + } + + /* In any case, invalidate cache to avoid stale data in it. */ + invalidate_dcache_range(aaddr, aaddr + + ALIGN(datalen, ARCH_DMA_MINALIGN)); + table_length = DIV_ROUND_UP(pccb->datalen, MAX_PRDT_ENTRY); buf = pccb->pdata; i = table_length; @@ -1446,6 +1484,8 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) prepare_prdt_desc(&prd_table[table_length - i - 1], buf, datalen - 1); req_desc->prd_table_length = table_length; + ufshcd_cache_flush_and_invalidate(prd_table, sizeof(*prd_table) * table_length); + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); } static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb) -- cgit v1.3.1 From 19f627eceaf80f048dad93c5e132787b46faa17a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 22 Aug 2023 03:47:13 +0200 Subject: ufs: ufs-renesas: Add support for Renesas R-Car UFS controller Add support for Renesas R-Car UFS controller which needs vendor-specific initialization. Ported from Linux kernel as of commit c2ab666072bc ("scsi: ufs: Explicitly include correct DT includes") Signed-off-by: Marek Vasut Reviewed-by: Tom Rini --- drivers/ufs/Kconfig | 9 + drivers/ufs/Makefile | 1 + drivers/ufs/ufs-renesas.c | 413 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 drivers/ufs/ufs-renesas.c (limited to 'drivers/ufs') diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig index 69ea18edf8d..0e0cc58e3d6 100644 --- a/drivers/ufs/Kconfig +++ b/drivers/ufs/Kconfig @@ -21,4 +21,13 @@ config TI_J721E_UFS This selects the glue layer driver for Cadence controller present on TI's J721E devices. +config UFS_RENESAS + bool "Renesas specific hooks to UFS controller platform driver" + depends on UFS + select BOUNCE_BUFFER + help + This selects the Renesas specific additions to UFSHCD platform driver. + UFS host on Renesas needs some vendor specific configuration before + accessing the hardware. + endmenu diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile index 62ed0166084..4f3344fd4e4 100644 --- a/drivers/ufs/Makefile +++ b/drivers/ufs/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o +obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o diff --git a/drivers/ufs/ufs-renesas.c b/drivers/ufs/ufs-renesas.c new file mode 100644 index 00000000000..f6086050cde --- /dev/null +++ b/drivers/ufs/ufs-renesas.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Renesas UFS host controller driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ufs.h" + +struct ufs_renesas_priv { + struct clk_bulk clks; + bool initialized; /* The hardware needs initialization once */ +}; + +enum { + SET_PHY_INDEX_LO = 0, + SET_PHY_INDEX_HI, + TIMER_INDEX, + MAX_INDEX +}; + +enum ufs_renesas_init_param_mode { + MODE_RESTORE, + MODE_SET, + MODE_SAVE, + MODE_POLL, + MODE_WAIT, + MODE_WRITE, +}; + +#define PARAM_RESTORE(_reg, _index) \ + { .mode = MODE_RESTORE, .reg = _reg, .index = _index } +#define PARAM_SET(_index, _set) \ + { .mode = MODE_SET, .index = _index, .u.set = _set } +#define PARAM_SAVE(_reg, _mask, _index) \ + { .mode = MODE_SAVE, .reg = _reg, .mask = (u32)(_mask), \ + .index = _index } +#define PARAM_POLL(_reg, _expected, _mask) \ + { .mode = MODE_POLL, .reg = _reg, .u.expected = _expected, \ + .mask = (u32)(_mask) } +#define PARAM_WAIT(_delay_us) \ + { .mode = MODE_WAIT, .u.delay_us = _delay_us } + +#define PARAM_WRITE(_reg, _val) \ + { .mode = MODE_WRITE, .reg = _reg, .u.val = _val } + +#define PARAM_WRITE_D0_D4(_d0, _d4) \ + PARAM_WRITE(0xd0, _d0), PARAM_WRITE(0xd4, _d4) + +#define PARAM_WRITE_800_80C_POLL(_addr, _data_800) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE_D0_D4(0x00000800, ((_data_800) << 16) | BIT(8) | (_addr)), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_RESTORE_800_80C_POLL(_index) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE(0xd0, 0x00000800), \ + PARAM_RESTORE(0xd4, (_index)), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_WRITE_804_80C_POLL(_addr, _data_804) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE_D0_D4(0x00000804, ((_data_804) << 16) | BIT(8) | (_addr)), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_WRITE_828_82C_POLL(_data_828) \ + PARAM_WRITE_D0_D4(0x0000082c, 0x0f000000), \ + PARAM_WRITE_D0_D4(0x00000828, _data_828), \ + PARAM_WRITE(0xd0, 0x0000082c), \ + PARAM_POLL(0xd4, _data_828, _data_828) + +#define PARAM_WRITE_PHY(_addr16, _data16) \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x18, (_data16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x19, ((_data16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_SET_PHY(_addr16, _data16) \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE_804_80C_POLL(0x1a, 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_SAVE(0xd4, 0xff, SET_PHY_INDEX_LO), \ + PARAM_WRITE_804_80C_POLL(0x1b, 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_SAVE(0xd4, 0xff, SET_PHY_INDEX_HI), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0), \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_SET(SET_PHY_INDEX_LO, (((_data16) & 0xff) << 16) | BIT(8) | 0x18), \ + PARAM_RESTORE_800_80C_POLL(SET_PHY_INDEX_LO), \ + PARAM_SET(SET_PHY_INDEX_HI, ((((_data16) >> 8) & 0xff) << 16) | BIT(8) | 0x19), \ + PARAM_RESTORE_800_80C_POLL(SET_PHY_INDEX_HI), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_INDIRECT_WRITE(_gpio, _addr, _data_800) \ + PARAM_WRITE(0xf0, _gpio), \ + PARAM_WRITE_800_80C_POLL((_addr), _data_800), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_INDIRECT_POLL(_gpio, _addr, _expected, _mask) \ + PARAM_WRITE(0xf0, _gpio), \ + PARAM_WRITE_800_80C_POLL((_addr), 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_POLL(0xd4, (_expected), (_mask)), \ + PARAM_WRITE(0xf0, 0) + +struct ufs_renesas_init_param { + enum ufs_renesas_init_param_mode mode; + u32 reg; + union { + u32 expected; + u32 delay_us; + u32 set; + u32 val; + } u; + u32 mask; + u32 index; +}; + +/* This setting is for SERIES B */ +static const struct ufs_renesas_init_param ufs_param[] = { + PARAM_WRITE(0xc0, 0x49425308), + PARAM_WRITE_D0_D4(0x00000104, 0x00000002), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000828, 0x00000200), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000828, 0x00000000), + PARAM_WRITE_D0_D4(0x00000104, 0x00000001), + PARAM_WRITE_D0_D4(0x00000940, 0x00000001), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000940, 0x00000000), + + PARAM_WRITE(0xc0, 0x49425308), + PARAM_WRITE(0xc0, 0x41584901), + + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), + PARAM_WRITE_D0_D4(0x00000804, 0x00000000), + PARAM_WRITE(0xd0, 0x0000080c), + PARAM_POLL(0xd4, BIT(8), BIT(8)), + + PARAM_WRITE(REG_CONTROLLER_ENABLE, 0x00000001), + + PARAM_WRITE(0xd0, 0x00000804), + PARAM_POLL(0xd4, BIT(8) | BIT(6) | BIT(0), BIT(8) | BIT(6) | BIT(0)), + + PARAM_WRITE(0xd0, 0x00000d00), + PARAM_SAVE(0xd4, 0x0000ffff, TIMER_INDEX), + PARAM_WRITE(0xd4, 0x00000000), + PARAM_WRITE_D0_D4(0x0000082c, 0x0f000000), + PARAM_WRITE_D0_D4(0x00000828, 0x08000000), + PARAM_WRITE(0xd0, 0x0000082c), + PARAM_POLL(0xd4, BIT(27), BIT(27)), + PARAM_WRITE(0xd0, 0x00000d2c), + PARAM_POLL(0xd4, BIT(0), BIT(0)), + + /* phy setup */ + PARAM_INDIRECT_WRITE(1, 0x01, 0x001f), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x0d, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x0e, 0x0007), + PARAM_INDIRECT_WRITE(7, 0x5f, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x60, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x5b, 0x00a6), + PARAM_INDIRECT_WRITE(7, 0x5c, 0x0003), + + PARAM_INDIRECT_POLL(7, 0x3c, 0, BIT(7)), + PARAM_INDIRECT_POLL(7, 0x4c, 0, BIT(4)), + + PARAM_INDIRECT_WRITE(1, 0x32, 0x0080), + PARAM_INDIRECT_WRITE(1, 0x1f, 0x0001), + PARAM_INDIRECT_WRITE(0, 0x2c, 0x0001), + PARAM_INDIRECT_WRITE(0, 0x32, 0x0087), + + PARAM_INDIRECT_WRITE(1, 0x4d, 0x0061), + PARAM_INDIRECT_WRITE(4, 0x9b, 0x0009), + PARAM_INDIRECT_WRITE(4, 0xa6, 0x0005), + PARAM_INDIRECT_WRITE(4, 0xa5, 0x0058), + PARAM_INDIRECT_WRITE(1, 0x39, 0x0027), + PARAM_INDIRECT_WRITE(1, 0x47, 0x004c), + + PARAM_INDIRECT_WRITE(7, 0x0d, 0x0002), + PARAM_INDIRECT_WRITE(7, 0x0e, 0x0007), + + PARAM_WRITE_PHY(0x0028, 0x0061), + PARAM_WRITE_PHY(0x4014, 0x0061), + PARAM_SET_PHY(0x401c, BIT(2)), + PARAM_WRITE_PHY(0x4000, 0x0000), + PARAM_WRITE_PHY(0x4001, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0000), + PARAM_WRITE_PHY(0x10af, 0x0001), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0000), + PARAM_WRITE_PHY(0x10af, 0x0002), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0080), + PARAM_WRITE_PHY(0x10af, 0x0000), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0080), + PARAM_WRITE_PHY(0x10af, 0x001a), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_INDIRECT_WRITE(7, 0x70, 0x0016), + PARAM_INDIRECT_WRITE(7, 0x71, 0x0016), + PARAM_INDIRECT_WRITE(7, 0x72, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x73, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x74, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x75, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x76, 0x0010), + PARAM_INDIRECT_WRITE(7, 0x77, 0x0010), + PARAM_INDIRECT_WRITE(7, 0x78, 0x00ff), + PARAM_INDIRECT_WRITE(7, 0x79, 0x0000), + + PARAM_INDIRECT_WRITE(7, 0x19, 0x0007), + + PARAM_INDIRECT_WRITE(7, 0x1a, 0x0007), + + PARAM_INDIRECT_WRITE(7, 0x24, 0x000c), + + PARAM_INDIRECT_WRITE(7, 0x25, 0x000c), + + PARAM_INDIRECT_WRITE(7, 0x62, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x63, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0017), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0004), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0017), + PARAM_INDIRECT_POLL(7, 0x55, 0, BIT(6)), + PARAM_INDIRECT_POLL(7, 0x41, 0, BIT(7)), + /* end of phy setup */ + + PARAM_WRITE(0xf0, 0), + PARAM_WRITE(0xd0, 0x00000d00), + PARAM_RESTORE(0xd4, TIMER_INDEX), +}; + +static void ufs_renesas_reg_control(struct ufs_hba *hba, + const struct ufs_renesas_init_param *p) +{ + static u32 save[MAX_INDEX]; + int ret; + u32 val; + + WARN_ON(p->index >= MAX_INDEX); + + switch (p->mode) { + case MODE_RESTORE: + ufshcd_writel(hba, save[p->index], p->reg); + break; + case MODE_SET: + save[p->index] |= p->u.set; + break; + case MODE_SAVE: + save[p->index] = ufshcd_readl(hba, p->reg) & p->mask; + break; + case MODE_POLL: + ret = readl_poll_timeout(hba->mmio_base + p->reg, val, + (val & p->mask) == p->u.expected, + 10000); + if (ret) + dev_err(hba->dev, "%s: poll failed %d (%08x, %08x, %08x)\n", + __func__, ret, val, p->mask, p->u.expected); + break; + case MODE_WAIT: + if (p->u.delay_us > 1000) + mdelay(DIV_ROUND_UP(p->u.delay_us, 1000)); + else + udelay(p->u.delay_us); + break; + case MODE_WRITE: + ufshcd_writel(hba, p->u.val, p->reg); + break; + default: + break; + } +} + +static void ufs_renesas_pre_init(struct ufs_hba *hba) +{ + const struct ufs_renesas_init_param *p = ufs_param; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ufs_param); i++) + ufs_renesas_reg_control(hba, &p[i]); +} + +static int ufs_renesas_hce_enable_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + struct ufs_renesas_priv *priv = dev_get_priv(hba->dev); + + if (priv->initialized) + return 0; + + if (status == PRE_CHANGE) + ufs_renesas_pre_init(hba); + + priv->initialized = true; + + return 0; +} + +static int ufs_renesas_init(struct ufs_hba *hba) +{ + hba->quirks |= UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS | UFSHCD_QUIRK_HIBERN_FASTAUTO; + + return 0; +} + +static struct ufs_hba_ops ufs_renesas_vops = { + .init = ufs_renesas_init, + .hce_enable_notify = ufs_renesas_hce_enable_notify, +}; + +static int ufs_renesas_pltfm_bind(struct udevice *dev) +{ + struct udevice *scsi_dev; + + return ufs_scsi_bind(dev, &scsi_dev); +} + +static int ufs_renesas_pltfm_probe(struct udevice *dev) +{ + struct ufs_renesas_priv *priv = dev_get_priv(dev); + int err; + + err = clk_get_bulk(dev, &priv->clks); + if (err < 0) + return err; + + err = clk_enable_bulk(&priv->clks); + if (err) + goto err_clk_enable; + + err = ufshcd_probe(dev, &ufs_renesas_vops); + if (err) { + dev_err(dev, "ufshcd_probe() failed %d\n", err); + goto err_ufshcd_probe; + } + + return 0; + +err_ufshcd_probe: + clk_disable_bulk(&priv->clks); +err_clk_enable: + clk_release_bulk(&priv->clks); + return err; +} + +static int ufs_renesas_pltfm_remove(struct udevice *dev) +{ + struct ufs_renesas_priv *priv = dev_get_priv(dev); + + clk_disable_bulk(&priv->clks); + clk_release_bulk(&priv->clks); + + return 0; +} + +static const struct udevice_id ufs_renesas_pltfm_ids[] = { + { .compatible = "renesas,r8a779f0-ufs" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(ufs_renesas) = { + .name = "ufs-renesas", + .id = UCLASS_UFS, + .of_match = ufs_renesas_pltfm_ids, + .bind = ufs_renesas_pltfm_bind, + .probe = ufs_renesas_pltfm_probe, + .remove = ufs_renesas_pltfm_remove, + .priv_auto = sizeof(struct ufs_renesas_priv), +}; -- cgit v1.3.1 From cad6abff2937b8f4e14c40fe82c36804e01ca38b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 9 Sep 2023 04:54:35 +0200 Subject: ufs: ufs-renesas: Drop include common.h The "#include " is being phased out in favor of more fine grained header management, i.e. ideally include a subset of headers that are really needed. Remove it from this driver. Signed-off-by: Marek Vasut Reviewed-by: Tom Rini --- drivers/ufs/ufs-renesas.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/ufs') diff --git a/drivers/ufs/ufs-renesas.c b/drivers/ufs/ufs-renesas.c index f6086050cde..ae05bdc8102 100644 --- a/drivers/ufs/ufs-renesas.c +++ b/drivers/ufs/ufs-renesas.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include -- cgit v1.3.1