df4ec2fa7a
Add support for Qualcomm Inline Crypto Engine (ICE) to ufs-qcom. The standards-compliant parts, such as querying the crypto capabilities and enabling crypto for individual UFS requests, are already handled by ufshcd-crypto.c, which itself is wired into the blk-crypto framework. However, ICE requires vendor-specific init, enable, and resume logic, and it requires that keys be programmed and evicted by vendor-specific SMC calls. Make the ufs-qcom driver handle these details. I tested this on Dragonboard 845c, which is a publicly available development board that uses the Snapdragon 845 SoC and runs the upstream Linux kernel. This is the same SoC used in the Pixel 3 and Pixel 3 XL phones. This testing included (among other things) verifying that the expected ciphertext was produced, both manually using ext4 encryption and automatically using a block layer self-test I've written. I've also tested that this driver works nearly as-is on the Snapdragon 765 and Snapdragon 865 SoCs. And others have tested it on Snapdragon 850, Snapdragon 855, and Snapdragon 865 (see the Tested-by tags). This is based very loosely on the vendor-provided driver in the kernel source code for the Pixel 3, but I've greatly simplified it. Also, for now I've only included support for major version 3 of ICE, since that's all I have the hardware to test with the mainline kernel. Plus it appears that version 3 is easier to use than older versions of ICE. For now, only allow using AES-256-XTS. The hardware also declares support for AES-128-XTS, AES-{128,256}-ECB, and AES-{128,256}-CBC (BitLocker variant). But none of these others are really useful, and they'd need to be individually tested to be sure they worked properly. This commit also changes the name of the loadable module from "ufs-qcom" to "ufs_qcom", as this is necessary to compile it from multiple source files (unless we were to rename ufs-qcom.c). Link: https://lore.kernel.org/r/20200710072013.177481-6-ebiggers@kernel.org Tested-by: Steev Klimaszewski <steev@kali.org> # Lenovo Yoga C630 Tested-by: Thara Gopinath <thara.gopinath@linaro.org> # db845c, sm8150-mtp, sm8250-mtp Reviewed-by: Avri Altman <avri.altman@wdc.com> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
295 lines
8.0 KiB
C
295 lines
8.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#ifndef UFS_QCOM_H_
|
|
#define UFS_QCOM_H_
|
|
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/reset.h>
|
|
|
|
#define MAX_UFS_QCOM_HOSTS 1
|
|
#define MAX_U32 (~(u32)0)
|
|
#define MPHY_TX_FSM_STATE 0x41
|
|
#define TX_FSM_HIBERN8 0x1
|
|
#define HBRN8_POLL_TOUT_MS 100
|
|
#define DEFAULT_CLK_RATE_HZ 1000000
|
|
#define BUS_VECTOR_NAME_LEN 32
|
|
|
|
#define UFS_HW_VER_MAJOR_SHFT (28)
|
|
#define UFS_HW_VER_MAJOR_MASK (0x000F << UFS_HW_VER_MAJOR_SHFT)
|
|
#define UFS_HW_VER_MINOR_SHFT (16)
|
|
#define UFS_HW_VER_MINOR_MASK (0x0FFF << UFS_HW_VER_MINOR_SHFT)
|
|
#define UFS_HW_VER_STEP_SHFT (0)
|
|
#define UFS_HW_VER_STEP_MASK (0xFFFF << UFS_HW_VER_STEP_SHFT)
|
|
|
|
/* vendor specific pre-defined parameters */
|
|
#define SLOW 1
|
|
#define FAST 2
|
|
|
|
#define UFS_QCOM_LIMIT_NUM_LANES_RX 2
|
|
#define UFS_QCOM_LIMIT_NUM_LANES_TX 2
|
|
#define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G3
|
|
#define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G3
|
|
#define UFS_QCOM_LIMIT_PWMGEAR_RX UFS_PWM_G4
|
|
#define UFS_QCOM_LIMIT_PWMGEAR_TX UFS_PWM_G4
|
|
#define UFS_QCOM_LIMIT_RX_PWR_PWM SLOW_MODE
|
|
#define UFS_QCOM_LIMIT_TX_PWR_PWM SLOW_MODE
|
|
#define UFS_QCOM_LIMIT_RX_PWR_HS FAST_MODE
|
|
#define UFS_QCOM_LIMIT_TX_PWR_HS FAST_MODE
|
|
#define UFS_QCOM_LIMIT_HS_RATE PA_HS_MODE_B
|
|
#define UFS_QCOM_LIMIT_DESIRED_MODE FAST
|
|
|
|
/* QCOM UFS host controller vendor specific registers */
|
|
enum {
|
|
REG_UFS_SYS1CLK_1US = 0xC0,
|
|
REG_UFS_TX_SYMBOL_CLK_NS_US = 0xC4,
|
|
REG_UFS_LOCAL_PORT_ID_REG = 0xC8,
|
|
REG_UFS_PA_ERR_CODE = 0xCC,
|
|
REG_UFS_RETRY_TIMER_REG = 0xD0,
|
|
REG_UFS_PA_LINK_STARTUP_TIMER = 0xD8,
|
|
REG_UFS_CFG1 = 0xDC,
|
|
REG_UFS_CFG2 = 0xE0,
|
|
REG_UFS_HW_VERSION = 0xE4,
|
|
|
|
UFS_TEST_BUS = 0xE8,
|
|
UFS_TEST_BUS_CTRL_0 = 0xEC,
|
|
UFS_TEST_BUS_CTRL_1 = 0xF0,
|
|
UFS_TEST_BUS_CTRL_2 = 0xF4,
|
|
UFS_UNIPRO_CFG = 0xF8,
|
|
|
|
/*
|
|
* QCOM UFS host controller vendor specific registers
|
|
* added in HW Version 3.0.0
|
|
*/
|
|
UFS_AH8_CFG = 0xFC,
|
|
};
|
|
|
|
/* QCOM UFS host controller vendor specific debug registers */
|
|
enum {
|
|
UFS_DBG_RD_REG_UAWM = 0x100,
|
|
UFS_DBG_RD_REG_UARM = 0x200,
|
|
UFS_DBG_RD_REG_TXUC = 0x300,
|
|
UFS_DBG_RD_REG_RXUC = 0x400,
|
|
UFS_DBG_RD_REG_DFC = 0x500,
|
|
UFS_DBG_RD_REG_TRLUT = 0x600,
|
|
UFS_DBG_RD_REG_TMRLUT = 0x700,
|
|
UFS_UFS_DBG_RD_REG_OCSC = 0x800,
|
|
|
|
UFS_UFS_DBG_RD_DESC_RAM = 0x1500,
|
|
UFS_UFS_DBG_RD_PRDT_RAM = 0x1700,
|
|
UFS_UFS_DBG_RD_RESP_RAM = 0x1800,
|
|
UFS_UFS_DBG_RD_EDTL_RAM = 0x1900,
|
|
};
|
|
|
|
#define UFS_CNTLR_2_x_x_VEN_REGS_OFFSET(x) (0x000 + x)
|
|
#define UFS_CNTLR_3_x_x_VEN_REGS_OFFSET(x) (0x400 + x)
|
|
|
|
/* bit definitions for REG_UFS_CFG1 register */
|
|
#define QUNIPRO_SEL 0x1
|
|
#define UTP_DBG_RAMS_EN 0x20000
|
|
#define TEST_BUS_EN BIT(18)
|
|
#define TEST_BUS_SEL GENMASK(22, 19)
|
|
#define UFS_REG_TEST_BUS_EN BIT(30)
|
|
|
|
/* bit definitions for REG_UFS_CFG2 register */
|
|
#define UAWM_HW_CGC_EN (1 << 0)
|
|
#define UARM_HW_CGC_EN (1 << 1)
|
|
#define TXUC_HW_CGC_EN (1 << 2)
|
|
#define RXUC_HW_CGC_EN (1 << 3)
|
|
#define DFC_HW_CGC_EN (1 << 4)
|
|
#define TRLUT_HW_CGC_EN (1 << 5)
|
|
#define TMRLUT_HW_CGC_EN (1 << 6)
|
|
#define OCSC_HW_CGC_EN (1 << 7)
|
|
|
|
/* bit definition for UFS_UFS_TEST_BUS_CTRL_n */
|
|
#define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */
|
|
|
|
#define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\
|
|
TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\
|
|
DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\
|
|
TMRLUT_HW_CGC_EN | OCSC_HW_CGC_EN)
|
|
|
|
/* bit offset */
|
|
enum {
|
|
OFFSET_UFS_PHY_SOFT_RESET = 1,
|
|
OFFSET_CLK_NS_REG = 10,
|
|
};
|
|
|
|
/* bit masks */
|
|
enum {
|
|
MASK_UFS_PHY_SOFT_RESET = 0x2,
|
|
MASK_TX_SYMBOL_CLK_1US_REG = 0x3FF,
|
|
MASK_CLK_NS_REG = 0xFFFC00,
|
|
};
|
|
|
|
/* QCOM UFS debug print bit mask */
|
|
#define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0)
|
|
#define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1)
|
|
#define UFS_QCOM_DBG_PRINT_TEST_BUS_EN BIT(2)
|
|
|
|
#define UFS_QCOM_DBG_PRINT_ALL \
|
|
(UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \
|
|
UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
|
|
|
/* QUniPro Vendor specific attributes */
|
|
#define PA_VS_CONFIG_REG1 0x9000
|
|
#define DME_VS_CORE_CLK_CTRL 0xD002
|
|
/* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */
|
|
#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
|
|
#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF
|
|
|
|
static inline void
|
|
ufs_qcom_get_controller_revision(struct ufs_hba *hba,
|
|
u8 *major, u16 *minor, u16 *step)
|
|
{
|
|
u32 ver = ufshcd_readl(hba, REG_UFS_HW_VERSION);
|
|
|
|
*major = (ver & UFS_HW_VER_MAJOR_MASK) >> UFS_HW_VER_MAJOR_SHFT;
|
|
*minor = (ver & UFS_HW_VER_MINOR_MASK) >> UFS_HW_VER_MINOR_SHFT;
|
|
*step = (ver & UFS_HW_VER_STEP_MASK) >> UFS_HW_VER_STEP_SHFT;
|
|
};
|
|
|
|
static inline void ufs_qcom_assert_reset(struct ufs_hba *hba)
|
|
{
|
|
ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
|
|
1 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
|
|
|
|
/*
|
|
* Make sure assertion of ufs phy reset is written to
|
|
* register before returning
|
|
*/
|
|
mb();
|
|
}
|
|
|
|
static inline void ufs_qcom_deassert_reset(struct ufs_hba *hba)
|
|
{
|
|
ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
|
|
0 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
|
|
|
|
/*
|
|
* Make sure de-assertion of ufs phy reset is written to
|
|
* register before returning
|
|
*/
|
|
mb();
|
|
}
|
|
|
|
struct ufs_qcom_bus_vote {
|
|
uint32_t client_handle;
|
|
uint32_t curr_vote;
|
|
int min_bw_vote;
|
|
int max_bw_vote;
|
|
int saved_vote;
|
|
bool is_max_bw_needed;
|
|
struct device_attribute max_bus_bw;
|
|
};
|
|
|
|
/* Host controller hardware version: major.minor.step */
|
|
struct ufs_hw_version {
|
|
u16 step;
|
|
u16 minor;
|
|
u8 major;
|
|
};
|
|
|
|
struct ufs_qcom_testbus {
|
|
u8 select_major;
|
|
u8 select_minor;
|
|
};
|
|
|
|
struct gpio_desc;
|
|
|
|
struct ufs_qcom_host {
|
|
/*
|
|
* Set this capability if host controller supports the QUniPro mode
|
|
* and if driver wants the Host controller to operate in QUniPro mode.
|
|
* Note: By default this capability will be kept enabled if host
|
|
* controller supports the QUniPro mode.
|
|
*/
|
|
#define UFS_QCOM_CAP_QUNIPRO 0x1
|
|
|
|
/*
|
|
* Set this capability if host controller can retain the secure
|
|
* configuration even after UFS controller core power collapse.
|
|
*/
|
|
#define UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE 0x2
|
|
u32 caps;
|
|
|
|
struct phy *generic_phy;
|
|
struct ufs_hba *hba;
|
|
struct ufs_qcom_bus_vote bus_vote;
|
|
struct ufs_pa_layer_attr dev_req_params;
|
|
struct clk *rx_l0_sync_clk;
|
|
struct clk *tx_l0_sync_clk;
|
|
struct clk *rx_l1_sync_clk;
|
|
struct clk *tx_l1_sync_clk;
|
|
bool is_lane_clks_enabled;
|
|
|
|
void __iomem *dev_ref_clk_ctrl_mmio;
|
|
bool is_dev_ref_clk_enabled;
|
|
struct ufs_hw_version hw_ver;
|
|
#ifdef CONFIG_SCSI_UFS_CRYPTO
|
|
void __iomem *ice_mmio;
|
|
#endif
|
|
|
|
u32 dev_ref_clk_en_mask;
|
|
|
|
/* Bitmask for enabling debug prints */
|
|
u32 dbg_print_en;
|
|
struct ufs_qcom_testbus testbus;
|
|
|
|
/* Reset control of HCI */
|
|
struct reset_control *core_reset;
|
|
struct reset_controller_dev rcdev;
|
|
|
|
struct gpio_desc *device_reset;
|
|
};
|
|
|
|
static inline u32
|
|
ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
|
|
{
|
|
if (host->hw_ver.major <= 0x02)
|
|
return UFS_CNTLR_2_x_x_VEN_REGS_OFFSET(reg);
|
|
|
|
return UFS_CNTLR_3_x_x_VEN_REGS_OFFSET(reg);
|
|
};
|
|
|
|
#define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
|
|
#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
|
|
#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
|
|
|
|
int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
|
|
|
|
static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
|
|
{
|
|
if (host->caps & UFS_QCOM_CAP_QUNIPRO)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* ufs-qcom-ice.c */
|
|
|
|
#ifdef CONFIG_SCSI_UFS_CRYPTO
|
|
int ufs_qcom_ice_init(struct ufs_qcom_host *host);
|
|
int ufs_qcom_ice_enable(struct ufs_qcom_host *host);
|
|
int ufs_qcom_ice_resume(struct ufs_qcom_host *host);
|
|
int ufs_qcom_ice_program_key(struct ufs_hba *hba,
|
|
const union ufs_crypto_cfg_entry *cfg, int slot);
|
|
#else
|
|
static inline int ufs_qcom_ice_init(struct ufs_qcom_host *host)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int ufs_qcom_ice_enable(struct ufs_qcom_host *host)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int ufs_qcom_ice_resume(struct ufs_qcom_host *host)
|
|
{
|
|
return 0;
|
|
}
|
|
#define ufs_qcom_ice_program_key NULL
|
|
#endif /* !CONFIG_SCSI_UFS_CRYPTO */
|
|
|
|
#endif /* UFS_QCOM_H_ */
|