diff --git a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi index 8d4871135fa6..227e7d6ff898 100644 --- a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi +++ b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi @@ -18,7 +18,7 @@ }; &usb_1 { - dr_mode = "host"; + dr_mode = "peripheral"; }; // RAM Entry 0 : Base 0x0080000000 Size 0x003A800000 diff --git a/arch/arm/mach-snapdragon/of_fixup.c b/arch/arm/mach-snapdragon/of_fixup.c index 5b6076ea8e57..002fd45a10a5 100644 --- a/arch/arm/mach-snapdragon/of_fixup.c +++ b/arch/arm/mach-snapdragon/of_fixup.c @@ -4,7 +4,7 @@ * * This file implements runtime fixups for Qualcomm DT to improve * compatibility with U-Boot. This includes adjusting the USB nodes - * to only use USB high-speed. + * to only use USB high-speed if SSPHY driver is not available. * * We use OF_LIVE for this rather than early FDT fixup for a couple * of reasons: it has a much nicer API, is most likely more efficient, @@ -21,12 +21,101 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include #include +/** + * find_ssphy_node() - Find the super-speed PHY node referenced by DWC3 + * @dwc3: DWC3 device node + * + * Returns: Pointer to SS-PHY node if found, NULL otherwise + */ +static struct device_node *find_ssphy_node(struct device_node *dwc3) +{ + const __be32 *phandles; + const char *phy_name; + int len, i, ret; + + phandles = of_get_property(dwc3, "phys", &len); + if (!phandles) + return NULL; + + len /= sizeof(*phandles); + + /* Iterate through PHY phandles to find the SS-PHY */ + for (i = 0; i < len; i++) { + ret = of_property_read_string_index(dwc3, "phy-names", i, &phy_name); + if (ret) + continue; + + /* Check if this is the super-speed PHY */ + if (!strncmp("usb3-phy", phy_name, strlen("usb3-phy")) || + !strncmp("usb3_phy", phy_name, strlen("usb3_phy"))) { + return of_find_node_by_phandle(NULL, be32_to_cpu(phandles[i])); + } + } + + return NULL; +} + +/** + * has_driver_for_node() - Check if any PHY driver can bind to this node + * @np: Device node to check + * + * Returns: true if a PHY driver with matching compatible string exists, false otherwise + */ +static bool has_driver_for_node(struct device_node *np) +{ + struct driver *driver = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + const char *compat_list, *compat; + int compat_length, i; + struct driver *entry; + + if (!np) + return false; + + /* Get compatible strings from the node */ + compat_list = of_get_property(np, "compatible", &compat_length); + if (!compat_list) + return false; + + /* Check each compatible string against PHY drivers only */ + for (i = 0; i < compat_length; i += strlen(compat) + 1) { + compat = compat_list + i; + + /* Iterate through all registered drivers */ + for (entry = driver; entry != driver + n_ents; entry++) { + const struct udevice_id *of_match = entry->of_match; + + /* Skip non-PHY drivers to improve performance */ + if (entry->id != UCLASS_PHY) + continue; + + if (!of_match) + continue; + + while (of_match->compatible) { + if (!strcmp(of_match->compatible, compat)) { + debug("Found PHY driver '%s' for SS-PHY compatible '%s'\n", + entry->name, compat); + return true; + } + of_match++; + } + } + } + + return false; +} + /* U-Boot only supports USB high-speed mode on Qualcomm platforms with DWC3 * USB controllers. Rather than requiring source level DT changes, we fix up * DT here. This improves compatibility with upstream DT and simplifies the @@ -34,7 +123,7 @@ */ static int fixup_qcom_dwc3(struct device_node *root, struct device_node *glue_np, bool flat) { - struct device_node *dwc3; + struct device_node *dwc3, *ssphy_np; int ret, len, hsphy_idx = 1; const __be32 *phandles; const char *second_phy_name; @@ -54,30 +143,43 @@ static int fixup_qcom_dwc3(struct device_node *root, struct device_node *glue_np } } - /* Tell the glue driver to configure the wrapper for high-speed only operation */ - ret = of_write_prop(glue_np, "qcom,select-utmi-as-pipe-clk", 0, NULL); - if (ret) { - log_err("Failed to add property 'qcom,select-utmi-as-pipe-clk': %d\n", ret); - return ret; - } + debug("Checking USB configuration for %s\n", glue_np->name); phandles = of_get_property(dwc3, "phys", &len); len /= sizeof(*phandles); if (len == 1) { - log_debug("Only one phy, not a superspeed controller\n"); + debug("Only one phy, not a superspeed controller\n"); return 0; } - /* Figure out if the superspeed phy is present and if so then which phy is it? */ + /* Figure out if the superspeed phy is present */ ret = of_property_read_string_index(dwc3, "phy-names", 1, &second_phy_name); if (ret == -ENODATA) { - log_debug("Only one phy, not a super-speed controller\n"); + debug("Only one phy, not a super-speed controller\n"); return 0; } else if (ret) { log_err("Failed to read second phy name: %d\n", ret); return ret; } + /* Find the super-speed PHY node and check if a driver is available */ + ssphy_np = find_ssphy_node(dwc3); + if (ssphy_np && has_driver_for_node(ssphy_np)) { + debug("Skipping USB fixup for %s (SS-PHY driver available)\n", + glue_np->name); + return 0; + } + + /* No driver available - apply the fixup */ + debug("Applying USB high-speed fixup to %s\n", glue_np->name); + + /* Tell the glue driver to configure the wrapper for high-speed only operation */ + ret = of_write_prop(glue_np, "qcom,select-utmi-as-pipe-clk", 0, NULL); + if (ret) { + log_err("Failed to add property 'qcom,select-utmi-as-pipe-clk': %d\n", ret); + return ret; + } + /* * Determine which phy is the superspeed phy by checking the name of the second phy * since it is typically the superspeed one. @@ -164,11 +266,42 @@ static void fixup_power_domains(struct device_node *root) } } +static void add_optee_node(struct device_node *root) +{ + struct device_node *fw = NULL, *optee = NULL; + int ret; + + fw = of_find_node_by_path("/firmware"); + if (!fw) { + log_err("Failed to find /firmware node\n"); + return; + } + + ret = of_add_subnode(fw, "optee", strlen("optee") + 1, &optee); + if (ret) { + log_err("Failed to add 'optee' subnode: %d\n", ret); + return; + } + + ret = of_write_prop(optee, "compatible", strlen("linaro,optee-tz") + 1, + "linaro,optee-tz"); + if (ret) { + log_err("Failed to add optee 'compatible' property: %d\n", ret); + return; + } + + ret = of_write_prop(optee, "method", strlen("smc") + 1, "smc"); + if (ret) { + log_err("Failed to add optee 'method' property: %d\n", ret); + return; + } +} + #define time_call(func, ...) \ do { \ u64 start = timer_get_us(); \ func(__VA_ARGS__); \ - debug(#func " took %lluus\n", timer_get_us() - start); \ + printf(#func " took %lluus\n", timer_get_us() - start); \ } while (0) static int qcom_of_fixup_nodes(void * __maybe_unused ctx, struct event *event) @@ -178,6 +311,9 @@ static int qcom_of_fixup_nodes(void * __maybe_unused ctx, struct event *event) time_call(fixup_usb_nodes, root); time_call(fixup_power_domains, root); + if (IS_ENABLED(CONFIG_OPTEE) && is_optee_smc_api()) + time_call(add_optee_node, root); + return 0; } diff --git a/board/qualcomm/tfa-optee.config b/board/qualcomm/tfa-optee.config new file mode 100644 index 000000000000..1e8364c114f9 --- /dev/null +++ b/board/qualcomm/tfa-optee.config @@ -0,0 +1,4 @@ +# Enables support for TF-A based OP-TEE as the open +# source TrustZone stack on Qcom platforms +CONFIG_TEE=y +CONFIG_OPTEE=y diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index 618098c8860e..9756687d8f91 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -15,3 +15,5 @@ CONFIG_REMAKE_ELF=y CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2" CONFIG_FASTBOOT_BUF_ADDR=0xd8800000 + +CONFIG_PHY_QCOM_QMP_COMBO=y diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c index 7b6ed8260236..403995e5a0aa 100644 --- a/drivers/clk/qcom/clock-sc7280.c +++ b/drivers/clk/qcom/clock-sc7280.c @@ -116,6 +116,7 @@ static const struct gate_clk sc7280_clks[] = { GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1), GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1), GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1), + GATE_CLK(GCC_USB3_PRIM_PHY_PIPE_CLK, 0xf05c, 1), GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1), GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1), GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1), diff --git a/drivers/phy/qcom/Kconfig b/drivers/phy/qcom/Kconfig index 0dd69f7ffd0c..49f830abf01a 100644 --- a/drivers/phy/qcom/Kconfig +++ b/drivers/phy/qcom/Kconfig @@ -12,6 +12,14 @@ config PHY_QCOM_IPQ4019_USB help Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. +config PHY_QCOM_QMP_COMBO + bool "Qualcomm QMP USB3-DP Combo PHY driver" + depends on PHY && ARCH_SNAPDRAGON + help + Enable this to support the USB3-DP Combo QMP PHY on various Qualcomm + chipsets. This driver supports the USB3 PHY functionality of the combo + PHY (USB3 + DisplayPort). Currently only USB3 mode is supported. + config PHY_QCOM_QMP_PCIE tristate "Qualcomm QMP PCIe PHY driver" depends on PHY && ARCH_SNAPDRAGON diff --git a/drivers/phy/qcom/Makefile b/drivers/phy/qcom/Makefile index 1c4e7d8d3917..714013dc572d 100644 --- a/drivers/phy/qcom/Makefile +++ b/drivers/phy/qcom/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o +obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o diff --git a/drivers/phy/qcom/phy-qcom-qmp-combo.c b/drivers/phy/qcom/phy-qcom-qmp-combo.c new file mode 100644 index 000000000000..0095cbf8b4c3 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-combo.c @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-qmp-common.h" + +#include "phy-qcom-qmp.h" +#include "phy-qcom-qmp-pcs-misc-v3.h" +#include "phy-qcom-qmp-pcs-usb-v4.h" +#include "phy-qcom-qmp-dp-com-v3.h" + +/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ +/* DP PHY soft reset */ +#define SW_DPPHY_RESET BIT(0) +/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ +#define SW_DPPHY_RESET_MUX BIT(1) +/* USB3 PHY soft reset */ +#define SW_USB3PHY_RESET BIT(2) +/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ +#define SW_USB3PHY_RESET_MUX BIT(3) + +/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ +#define USB3_MODE BIT(0) /* enables USB3 mode */ +#define DP_MODE BIT(1) /* enables DP mode */ + +/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */ +#define SW_PORTSELECT_MUX BIT(1) + +/* PHY slot identifiers for device tree phandle arguments */ +#define QMP_USB43DP_USB3_PHY 0 +#define QMP_USB43DP_DP_PHY 1 + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 + +struct qmp_combo_offsets { + u16 com; + u16 txa; + u16 rxa; + u16 txb; + u16 rxb; + u16 usb3_serdes; + u16 usb3_pcs_misc; + u16 usb3_pcs; + u16 usb3_pcs_usb; +}; + +/* + * Initialisation tables + */ + +static const struct qmp_combo_offsets qmp_combo_offsets_v3 = { + .com = 0x0000, + .txa = 0x1200, + .rxa = 0x1400, + .txb = 0x1600, + .rxb = 0x1800, + .usb3_serdes = 0x1000, + .usb3_pcs_misc = 0x1a00, + .usb3_pcs = 0x1c00, + .usb3_pcs_usb = 0x1f00, +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +struct qmp_phy_cfg { + const struct qmp_combo_offsets *offsets; + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_usb_tbl; + int pcs_usb_tbl_num; + const char * const *vreg_list; + int num_vregs; + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; +}; + +/* list of clocks required by phy */ +static const char * const qmp_combo_phy_clk_l[] = { + "aux", "com_aux", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy-supply", + "vdda-pll-supply", +}; + +struct qmp_combo { + struct udevice *dev; + void __iomem *com; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs; + void __iomem *pcs_usb; + void __iomem *pcs_misc; + struct clk *clks; + struct clk *pipe_clk; + int num_clks; + struct reset_ctl_bulk resets; + int num_resets; + struct udevice **vregs; + int num_vregs; + const struct qmp_phy_cfg *cfg; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static int qmp_combo_com_exit(struct qmp_combo *qmp) +{ + int i, ret; + + for (i = 0; i < qmp->num_clks; i++) + clk_disable(&qmp->clks[i]); + + reset_assert_bulk(&qmp->resets); + + for (i = qmp->num_vregs - 1; i >= 0; i--) { + ret = regulator_set_enable(qmp->vregs[i], false); + if (ret) + dev_warn(qmp->dev, "failed to disable %s: %d\n", + qmp->cfg->vreg_list[i], ret); + } + + return 0; +} + +static int qmp_combo_com_init(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *com = qmp->com; + void __iomem *pcs = qmp->pcs; + u32 val; + int ret, i; + + ret = reset_assert_bulk(&qmp->resets); + if (ret) { + printf("Failed to assert resets: %d\n", ret); + return ret; + } + + ret = reset_deassert_bulk(&qmp->resets); + if (ret) { + printf("Failed to deassert resets: %d\n", ret); + return ret; + } + + for (i = 0; i < qmp->num_vregs; i++) { + ret = regulator_set_enable(qmp->vregs[i], true); + if (ret) { + dev_err(qmp->dev, "Failed to enable regulator %d: %d\n", i, ret); + while (--i >= 0) + regulator_set_enable(qmp->vregs[i], false); + reset_assert_bulk(&qmp->resets); + return ret; + } + } + + for (i = 0; i < qmp->num_clks; i++) { + ret = clk_enable(&qmp->clks[i]); + if (ret) { + printf("Failed to enable clock %d: %d\n", i, ret); + while (--i >= 0) + clk_disable(&qmp->clks[i]); + for (i = qmp->num_vregs - 1; i >= 0; i--) + regulator_set_enable(qmp->vregs[i], false); + reset_assert_bulk(&qmp->resets); + return ret; + } + } + + /* Common block register writes */ + qphy_setbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); + qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + val = SW_PORTSELECT_MUX; + writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL); + + writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); + + qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); + + qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); + + qphy_setbits(pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN); + + return 0; +} + +static int qmp_combo_usb_power_on(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *serdes = qmp->serdes; + void __iomem *tx = qmp->tx; + void __iomem *rx = qmp->rx; + void __iomem *tx2 = qmp->tx2; + void __iomem *rx2 = qmp->rx2; + void __iomem *pcs = qmp->pcs; + void __iomem *pcs_usb = qmp->pcs_usb; + u32 val; + int ret; + + /* Serdes configuration */ + qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num); + + ret = clk_prepare_enable(qmp->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx configurations */ + qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1); + qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2); + + qmp_configure_lane(qmp->dev, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1); + qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2); + + /* PCS configuration */ + qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + if (pcs_usb) { + qmp_configure(qmp->dev, pcs_usb, + cfg->pcs_usb_tbl, + cfg->pcs_usb_tbl_num); + } + + if (cfg->has_pwrdn_delay) + udelay(20); + + /* Pull PHY out of reset */ + qphy_clrbits(pcs, QPHY_V4_PCS_SW_RESET, SW_RESET); + + /* Start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, QPHY_V4_PCS_START_CONTROL, + SERDES_START | PCS_START); + + /* Wait for PHY initialization */ + ret = readl_poll_timeout(pcs + QPHY_V4_PCS_PCS_STATUS1, val, + !(val & PHYSTATUS), PHY_INIT_COMPLETE_TIMEOUT); + + if (ret) { + printf("QMP USB3 PHY initialization timeout\n"); + clk_disable(qmp->pipe_clk); + return ret; + } + + return 0; +} + +static int qmp_combo_power_on(struct phy *phy) +{ + struct qmp_combo *qmp = dev_get_priv(phy->dev); + int ret; + + /* Initialize common block */ + ret = qmp_combo_com_init(qmp); + if (ret) + return ret; + + /* Initialize USB3-specific configuration */ + ret = qmp_combo_usb_power_on(qmp); + if (ret) { + qmp_combo_com_exit(qmp); + return ret; + } + + return 0; +} + +static int qmp_combo_power_off(struct phy *phy) +{ + struct qmp_combo *qmp = dev_get_priv(phy->dev); + void __iomem *com = qmp->com; + + clk_disable(qmp->pipe_clk); + + /* PHY reset */ + qphy_setbits(qmp->pcs, QPHY_V4_PCS_SW_RESET, SW_RESET); + + /* Stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qmp->pcs, QPHY_V4_PCS_START_CONTROL, + SERDES_START | PCS_START); + + /* Put PHY into POWER DOWN state: active low */ + qphy_clrbits(qmp->pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN); + + /* Power down common block */ + qphy_clrbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); + + return qmp_combo_com_exit(qmp); +} + +static int qmp_combo_reset_init(struct qmp_combo *qmp) +{ + struct udevice *dev = qmp->dev; + int ret; + + ret = reset_get_bulk(dev, &qmp->resets); + if (ret) { + printf("Failed to get resets: %d\n", ret); + return ret; + } + + qmp->num_resets = qmp->resets.count; + + return 0; +} + +static int qmp_combo_clk_init(struct qmp_combo *qmp) +{ + struct udevice *dev = qmp->dev; + int num = ARRAY_SIZE(qmp_combo_phy_clk_l); + int i, ret; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) { + ret = clk_get_by_name(dev, qmp_combo_phy_clk_l[i], &qmp->clks[i]); + if (ret) { + dev_err(dev, "failed to get %s clock: %d\n", + qmp_combo_phy_clk_l[i], ret); + return ret; + } + } + + qmp->num_clks = num; + return 0; +} + +static int qmp_combo_vreg_init(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct udevice *dev = qmp->dev; + int num = cfg->num_vregs; + int i, ret; + + if (!num) + return 0; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) { + ret = device_get_supply_regulator(dev, cfg->vreg_list[i], + &qmp->vregs[i]); + if (ret) { + dev_err(dev, "failed to get regulator %s: %d\n", + cfg->vreg_list[i], ret); + return ret; + } + } + + qmp->num_vregs = num; + return 0; +} + +static int qmp_combo_parse_dt(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + const struct qmp_combo_offsets *offs = cfg->offsets; + struct udevice *dev = qmp->dev; + void __iomem *base; + int ret; + + if (!offs) + return -EINVAL; + + base = (void __iomem *)dev_read_addr(dev); + if (IS_ERR(base)) + return PTR_ERR(base); + + qmp->com = base + offs->com; + qmp->serdes = base + offs->usb3_serdes; + qmp->tx = base + offs->txa; + qmp->rx = base + offs->rxa; + qmp->tx2 = base + offs->txb; + qmp->rx2 = base + offs->rxb; + qmp->pcs = base + offs->usb3_pcs; + qmp->pcs_usb = base + offs->usb3_pcs_usb; + qmp->pcs_misc = base + offs->usb3_pcs_misc; + + ret = qmp_combo_clk_init(qmp); + if (ret) + return ret; + + qmp->pipe_clk = devm_clk_get(dev, "usb3_pipe"); + if (IS_ERR(qmp->pipe_clk)) { + dev_err(dev, "failed to get pipe clock (%ld)\n", + PTR_ERR(qmp->pipe_clk)); + return ret; + } + + ret = qmp_combo_reset_init(qmp); + if (ret) + return ret; + + ret = qmp_combo_vreg_init(qmp); + if (ret) + return ret; + + return 0; +} + +static int qmp_combo_probe(struct udevice *dev) +{ + struct qmp_combo *qmp = dev_get_priv(dev); + int ret; + + qmp->dev = dev; + qmp->cfg = (const struct qmp_phy_cfg *)dev_get_driver_data(dev); + if (!qmp->cfg) { + printf("Failed to get PHY configuration\n"); + return -EINVAL; + } + + ret = qmp_combo_parse_dt(qmp); + + return ret; +} + +static const struct qmp_phy_cfg sc7280_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + + .has_pwrdn_delay = true, +}; + +static int qmp_combo_xlate(struct phy *phy, struct ofnode_phandle_args *args) +{ + if (args->args_count != 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + /* We only support the USB3 phy at slot 0 */ + if (args->args[0] == QMP_USB43DP_DP_PHY) + return -EINVAL; + + phy->id = QMP_USB43DP_USB3_PHY; + + return 0; +} + +static struct phy_ops qmp_combo_ops = { + .init = qmp_combo_power_on, + .exit = qmp_combo_power_off, + .of_xlate = qmp_combo_xlate, +}; + +static const struct udevice_id qmp_combo_ids[] = { + { + .compatible = "qcom,sc7280-qmp-usb3-dp-phy", + .data = (ulong)&sc7280_usb3dpphy_cfg, + }, + { } +}; + +U_BOOT_DRIVER(qmp_combo) = { + .name = "qcom-qmp-usb3-dp-phy", + .id = UCLASS_PHY, + .of_match = qmp_combo_ids, + .ops = &qmp_combo_ops, + .probe = qmp_combo_probe, + .priv_auto = sizeof(struct qmp_combo), +}; diff --git a/drivers/phy/qcom/phy-qcom-qmp-common.h b/drivers/phy/qcom/phy-qcom-qmp-common.h new file mode 100644 index 000000000000..71356fb7dd02 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-common.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_COMMON_H_ +#define QCOM_PHY_QMP_COMMON_H_ + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + char *name; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .name = #o, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .name = #o, \ + .lane_mask = l, \ + } + +static inline void qmp_configure_lane(struct udevice *dev, void __iomem *base, + const struct qmp_phy_init_tbl tbl[], + int num, u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + dev_dbg(dev, "Writing Reg: %s Offset: 0x%04x Val: 0x%02x\n", + t->name, t->offset, t->val); + writel(t->val, base + t->offset); + } +} + +static inline void qmp_configure(struct udevice *dev, void __iomem *base, + const struct qmp_phy_init_tbl tbl[], int num) +{ + qmp_configure_lane(dev, base, tbl, num, 0xff); +} + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h new file mode 100644 index 000000000000..396179ef38b0 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_DP_COM_V3_H_ +#define QCOM_PHY_QMP_DP_COM_V3_H_ + +/* Only for QMP V3 & V4 PHY - DP COM registers */ +#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 +#define QPHY_V3_DP_COM_SW_RESET 0x04 +#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08 +#define QPHY_V3_DP_COM_SWI_CTRL 0x0c +#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10 +#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14 +#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h new file mode 100644 index 000000000000..d7fd4ac0fc55 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V4_H_ +#define QCOM_PHY_QMP_PCS_USB_V4_H_ + +/* Only for QMP V4 PHY - USB3 PCS registers */ +#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x000 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010 +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014 +#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018 +#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x01c +#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x020 +#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x028 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x02c +#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x030 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x034 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x038 +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x03c +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x040 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x044 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x048 +#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x04c +#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x050 +#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x054 +#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x058 + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp.h b/drivers/phy/qcom/phy-qcom-qmp.h index 99f4d447caf6..06dac21ddc4a 100644 --- a/drivers/phy/qcom/phy-qcom-qmp.h +++ b/drivers/phy/qcom/phy-qcom-qmp.h @@ -12,12 +12,17 @@ #include "phy-qcom-qmp-qserdes-com-v3.h" #include "phy-qcom-qmp-qserdes-txrx-v3.h" +#include "phy-qcom-qmp-qserdes-com-v4.h" +#include "phy-qcom-qmp-qserdes-txrx-v4.h" + #include "phy-qcom-qmp-qserdes-pll.h" #include "phy-qcom-qmp-pcs-v2.h" #include "phy-qcom-qmp-pcs-v3.h" +#include "phy-qcom-qmp-pcs-v4.h" + /* Only for QMP V3 & V4 PHY - DP COM registers */ #define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 #define QPHY_V3_DP_COM_SW_RESET 0x04 @@ -112,4 +117,16 @@ #define QSERDES_V6_DP_PHY_AUX_INTERRUPT_STATUS 0x0e0 #define QSERDES_V6_DP_PHY_STATUS 0x0e4 +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) + +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) + +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) + #endif diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 5fc0505c788d..4d67c948ec19 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -795,6 +795,11 @@ static optee_invoke_fn *get_invoke_func(struct udevice *dev) return ERR_PTR(-EINVAL); } +bool is_optee_smc_api(void) +{ + return is_optee_api(optee_smccc_smc); +} + static int optee_of_to_plat(struct udevice *dev) { struct optee_pdata *pdata = dev_get_plat(dev); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 847fa1f82c37..ff0bca0dd8e8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -94,6 +94,8 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) reg &= ~DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); + mdelay(100); + return 0; } diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 9ce5df59f9ba..05acd24d8bd4 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -45,24 +45,214 @@ static void downcase(char *str, size_t len) static struct blk_desc *cur_dev; static struct disk_partition cur_part_info; +static int fat_sect_size; #define DOS_BOOT_MAGIC_OFFSET 0x1fe #define DOS_FS_TYPE_OFFSET 0x36 #define DOS_FS32_TYPE_OFFSET 0x52 -static int disk_read(__u32 block, __u32 nr_blocks, void *buf) +inline __u32 sect_to_block(__u32 sect, __u32 *off) { - ulong ret; + *off = 0; + if (fat_sect_size && fat_sect_size < cur_part_info.blksz) { + int div = cur_part_info.blksz / fat_sect_size; + + *off = sect % div; + return sect / div; + } else if (fat_sect_size && (fat_sect_size > cur_part_info.blksz)) { + return sect * (fat_sect_size / cur_part_info.blksz); + } - if (!cur_dev) - return -1; + return sect; +} - ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf); +inline __u32 size_to_blocks(__u32 size) +{ + return (size + (cur_part_info.blksz - 1)) / cur_part_info.blksz; +} - if (ret != nr_blocks) - return -1; +static int disk_read(__u32 sect, __u32 nr_sect, void *buf) +{ + int ret; + __u8 *block = NULL; + __u32 rem, size; + __u32 s, n; - return ret; + rem = nr_sect * fat_sect_size; + /* + * block N block N + 1 block N + 2 + * +-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | |s|e|c|t|o|r| | |s|e|c|t|o|r| | |s|e|c|t|o|r| | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . . . | | | | . . . + * ------+---------------+---------------+---------------+------ + * |<--- FAT reads in sectors --->| + * + * | part 1 | part 2 | part 3 | + * + */ + + /* Do part 1 */ + if (fat_sect_size) { + __u32 offset; + + /* Read one block and copy out the leading sectors */ + block = malloc_cache_aligned(cur_dev->blksz); + if (!block) { + printf("Error: allocating block: %lu\n", cur_dev->blksz); + return -1; + } + + s = sect_to_block(sect, &offset); + offset = offset * fat_sect_size; + + ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } + + if (rem > (cur_part_info.blksz - offset)) + size = cur_part_info.blksz - offset; + else + size = rem; + + memcpy(buf, block + offset, size); + rem -= size; + buf += size; + s++; + } else { + /* + * fat_sect_size not being set implies, this is the first read + * to partition. The first sector is being read to get the + * FS meta data. The FAT sector size is got from this meta data. + */ + ret = blk_dread(cur_dev, cur_part_info.start + s, 1, buf); + if (ret != 1) + return -1; + } + + /* Do part 2, read directly into the given buffer */ + if (rem > cur_part_info.blksz) { + n = rem / cur_part_info.blksz; + ret = blk_dread(cur_dev, cur_part_info.start + s, n, buf); + if (ret != n) { + ret = -1; + goto exit; + } + buf += n * cur_part_info.blksz; + rem = rem % cur_part_info.blksz; + s += n; + } + + /* Do part 3, read a block and copy the trailing sectors */ + if (rem) { + ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } else { + memcpy(buf, block, rem); + } + } +exit: + if (block) + free(block); + + return (ret == -1) ? -1 : nr_sect; +} + +int disk_write(__u32 sect, __u32 nr_sect, void *buf) +{ + int ret; + __u8 *block = NULL; + __u32 rem, size; + __u32 s, n; + + rem = nr_sect * fat_sect_size; + /* + * block N block N + 1 block N + 2 + * +-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | |s|e|c|t|o|r| | |s|e|c|t|o|r| | |s|e|c|t|o|r| | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . . . | | | | . . . + * ------+---------------+---------------+---------------+------ + * |<--- FAT reads in sectors --->| + * + * | part 1 | part 2 | part 3 | + * + */ + + /* Do part 1 */ + if (fat_sect_size) { + __u32 offset; + + /* Read one block and overwrite the leading sectors */ + block = malloc_cache_aligned(cur_dev->blksz); + if (!block) { + printf("Error: allocating block: %lu\n", cur_dev->blksz); + return -1; + } + + s = sect_to_block(sect, &offset); + offset = offset * fat_sect_size; + + ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } + + if (rem > (cur_part_info.blksz - offset)) + size = cur_part_info.blksz - offset; + else + size = rem; + + memcpy(block + offset, buf, size); + ret = blk_dwrite(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } + + rem -= size; + buf += size; + s++; + } + + /* Do part 2, write directly from the given buffer */ + if (rem > cur_part_info.blksz) { + n = rem / cur_part_info.blksz; + ret = blk_dwrite(cur_dev, cur_part_info.start + s, n, buf); + if (ret != n) { + ret = -1; + goto exit; + } + buf += n * cur_part_info.blksz; + rem = rem % cur_part_info.blksz; + s += n; + } + + /* Do part 3, read a block and copy the trailing sectors */ + if (rem) { + ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } else { + memcpy(block, buf, rem); + } + ret = blk_dwrite(cur_dev, cur_part_info.start + s, 1, block); + if (ret != 1) { + ret = -1; + goto exit; + } + } +exit: + if (block) + free(block); + + return (ret == -1) ? -1 : nr_sect; } int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info) @@ -581,6 +771,8 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) return -1; } + fat_sect_size = 0; + if (disk_read(0, 1, block) < 0) { debug("Error: reading block\n"); ret = -1; @@ -651,12 +843,12 @@ static int get_fs_info(fsdata *mydata) mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats; mydata->sect_size = get_unaligned_le16(bs.sector_size); + fat_sect_size = mydata->sect_size; mydata->clust_size = bs.cluster_size; - if (mydata->sect_size != cur_part_info.blksz) { - log_err("FAT sector size mismatch (fs=%u, dev=%lu)\n", - mydata->sect_size, cur_part_info.blksz); - return -1; - } + if (mydata->sect_size != cur_part_info.blksz) + log_info("FAT sector size mismatch (fs=%u, dev=%lu)\n", + mydata->sect_size, cur_part_info.blksz); + if (mydata->clust_size == 0) { log_err("FAT cluster size not set\n"); return -1; diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 0b924541187c..b686afd32c69 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -192,25 +192,6 @@ static int set_name(fat_itr *itr, const char *filename, char *shortname) } static int total_sector; -static int disk_write(__u32 block, __u32 nr_blocks, void *buf) -{ - ulong ret; - - if (!cur_dev) - return -1; - - if (cur_part_info.start + block + nr_blocks > - cur_part_info.start + total_sector) { - printf("error: overflow occurs\n"); - return -1; - } - - ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf); - if (nr_blocks && ret == 0) - return -1; - - return ret; -} /* * Write fat buffer into block device diff --git a/include/tee/optee.h b/include/tee/optee.h index 77729450bb6b..d11944937808 100644 --- a/include/tee/optee.h +++ b/include/tee/optee.h @@ -65,4 +65,13 @@ static inline int optee_copy_fdt_nodes(void *new_blob) } #endif +#if defined(CONFIG_OPTEE) +bool is_optee_smc_api(void); +#else +static inline bool is_optee_smc_api(void) +{ + return false; +} +#endif + #endif /* _OPTEE_H */