diff options
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 9 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/cadence/phy-cadence-torrent.c | 225 | ||||
-rw-r--r-- | drivers/phy/phy-exynos-usbdrd.c | 386 | ||||
-rw-r--r-- | drivers/phy/phy-stm32-usbphyc.c | 19 | ||||
-rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-ufs.c | 128 | ||||
-rw-r--r-- | drivers/phy/rockchip/phy-rockchip-typec.c | 2 |
7 files changed, 674 insertions, 96 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index d3fe90d939e..c297fa03ea7 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -259,6 +259,15 @@ config MT76X8_USB_PHY This PHY is found on MT76x8 devices supporting USB. +config PHY_EXYNOS_USBDRD + bool "Exynos SoC series USB DRD PHY driver" + depends on PHY && CLK + depends on ARCH_EXYNOS + select REGMAP + select SYSCON + help + Enable USB DRD PHY support for Exynos SoC series. + config PHY_MTK_TPHY bool "MediaTek T-PHY Driver" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b4d01fc700d..98c1ef8683b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT7620_USB_PHY) += mt7620-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o obj-$(CONFIG_PHY_IMX8MQ_USB) += phy-imx8mq-usb.o diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index d4e8ece4935..1f566d082f9 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -240,6 +240,7 @@ struct cdns_torrent_inst { struct cdns_torrent_phy { void __iomem *sd_base; /* SD0801 register base */ + u32 protocol_bitmask; size_t size; struct reset_control *phy_rst; struct udevice *dev; @@ -432,124 +433,155 @@ static int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_ph struct cdns_reg_pairs *reg_pairs; enum cdns_torrent_ssc_mode ssc; struct regmap *regmap; - u32 num_regs; + u32 num_regs, num_protocols, protocol; + + num_protocols = hweight32(cdns_phy->protocol_bitmask); - /* Maximum 2 links (subnodes) are supported */ - if (cdns_phy->nsubnodes != 2) + /* Maximum 2 protocols are supported */ + if (num_protocols > 2) { + dev_err(cdns_phy->dev, "at most 2 protocols are supported\n"); return -EINVAL; + } - phy_t1 = cdns_phy->phys[0].phy_type; - phy_t2 = cdns_phy->phys[1].phy_type; + if (cdns_phy->nsubnodes == 2) { + phy_t1 = cdns_phy->phys[0].phy_type; + phy_t2 = cdns_phy->phys[1].phy_type; + } else { + if (num_protocols != 2) { + dev_err(cdns_phy->dev, "incorrect representation of link\n"); + return -EINVAL; + } + phy_t1 = __ffs(cdns_phy->protocol_bitmask); + phy_t2 = __fls(cdns_phy->protocol_bitmask); + } - /* - * First configure the PHY for first link with phy_t1. Geth the array - * values are [phy_t1][phy_t2][ssc]. + /** + * Configure all links with the protocol phy_t1 first followed by + * configuring all links with the protocol phy_t2. + * + * When phy_t1 = phy_t2, it is a single protocol and configuration + * is performed with a single iteration of the protocol and multiple + * iterations over the sub-nodes (links). + * + * When phy_t1 != phy_t2, there are two protocols and configuration + * is performed by iterating over all sub-nodes matching the first + * protocol and configuring them first, followed by iterating over + * all sub-nodes matching the second protocol and configuring them + * next. */ - for (node = 0; node < cdns_phy->nsubnodes; node++) { - if (node == 1) { - /* - * If fist link with phy_t1 is configured, then - * configure the PHY for second link with phy_t2. - * Get the array values as [phy_t2][phy_t1][ssc] - */ + + for (protocol = 0; protocol < num_protocols; protocol++) { + /** + * For the case where num_protocols is 1, + * phy_t1 = phy_t2 and the swap is unnecessary. + * + * Swapping phy_t1 and phy_t2 is only required when the + * number of protocols is 2 and there are 2 or more links. + */ + if (protocol == 1) { tmp_phy_type = phy_t1; phy_t1 = phy_t2; phy_t2 = tmp_phy_type; } - mlane = cdns_phy->phys[node].mlane; - ssc = cdns_phy->phys[node].ssc_mode; - num_lanes = cdns_phy->phys[node].num_lanes; + for (node = 0; node < cdns_phy->nsubnodes; node++) { + if (cdns_phy->phys[node].phy_type != phy_t1) + continue; - /** - * PHY configuration specific registers: - * link_cmn_vals depend on combination of PHY types being - * configured and are common for both PHY types, so array - * values should be same for [phy_t1][phy_t2][ssc] and - * [phy_t2][phy_t1][ssc]. - * xcvr_diag_vals also depend on combination of PHY types - * being configured, but these can be different for particular - * PHY type and are per lane. - */ - link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc]; - if (link_cmn_vals) { - reg_pairs = link_cmn_vals->reg_pairs; - num_regs = link_cmn_vals->num_regs; - regmap = cdns_phy->regmap_common_cdb; + mlane = cdns_phy->phys[node].mlane; + ssc = cdns_phy->phys[node].ssc_mode; + num_lanes = cdns_phy->phys[node].num_lanes; /** - * First array value in link_cmn_vals must be of - * PHY_PLL_CFG register + * PHY configuration specific registers: + * link_cmn_vals depend on combination of PHY types being + * configured and are common for both PHY types, so array + * values should be same for [phy_t1][phy_t2][ssc] and + * [phy_t2][phy_t1][ssc]. + * xcvr_diag_vals also depend on combination of PHY types + * being configured, but these can be different for particular + * PHY type and are per lane. */ - regmap_field_write(cdns_phy->phy_pll_cfg, - reg_pairs[0].val); - - for (i = 1; i < num_regs; i++) - regmap_write(regmap, reg_pairs[i].off, - reg_pairs[i].val); - } + link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc]; + if (link_cmn_vals) { + reg_pairs = link_cmn_vals->reg_pairs; + num_regs = link_cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + + /** + * First array value in link_cmn_vals must be of + * PHY_PLL_CFG register + */ + regmap_field_write(cdns_phy->phy_pll_cfg, + reg_pairs[0].val); + + for (i = 1; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } - xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc]; - if (xcvr_diag_vals) { - reg_pairs = xcvr_diag_vals->reg_pairs; - num_regs = xcvr_diag_vals->num_regs; - for (i = 0; i < num_lanes; i++) { - regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; - for (j = 0; j < num_regs; j++) - regmap_write(regmap, reg_pairs[j].off, - reg_pairs[j].val); + xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc]; + if (xcvr_diag_vals) { + reg_pairs = xcvr_diag_vals->reg_pairs; + num_regs = xcvr_diag_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } } - } - /* PHY PCS common registers configurations */ - pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; - if (pcs_cmn_vals) { - reg_pairs = pcs_cmn_vals->reg_pairs; - num_regs = pcs_cmn_vals->num_regs; - regmap = cdns_phy->regmap_phy_pcs_common_cdb; - for (i = 0; i < num_regs; i++) - regmap_write(regmap, reg_pairs[i].off, - reg_pairs[i].val); - } + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } - /* PMA common registers configurations */ - cmn_vals = init_data->cmn_vals[phy_t1][phy_t2][ssc]; - if (cmn_vals) { - reg_pairs = cmn_vals->reg_pairs; - num_regs = cmn_vals->num_regs; - regmap = cdns_phy->regmap_common_cdb; - for (i = 0; i < num_regs; i++) - regmap_write(regmap, reg_pairs[i].off, - reg_pairs[i].val); - } + /* PMA common registers configurations */ + cmn_vals = init_data->cmn_vals[phy_t1][phy_t2][ssc]; + if (cmn_vals) { + reg_pairs = cmn_vals->reg_pairs; + num_regs = cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } - /* PMA TX lane registers configurations */ - tx_ln_vals = init_data->tx_ln_vals[phy_t1][phy_t2][ssc]; - if (tx_ln_vals) { - reg_pairs = tx_ln_vals->reg_pairs; - num_regs = tx_ln_vals->num_regs; - for (i = 0; i < num_lanes; i++) { - regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; - for (j = 0; j < num_regs; j++) - regmap_write(regmap, reg_pairs[j].off, - reg_pairs[j].val); + /* PMA TX lane registers configurations */ + tx_ln_vals = init_data->tx_ln_vals[phy_t1][phy_t2][ssc]; + if (tx_ln_vals) { + reg_pairs = tx_ln_vals->reg_pairs; + num_regs = tx_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } } - } - /* PMA RX lane registers configurations */ - rx_ln_vals = init_data->rx_ln_vals[phy_t1][phy_t2][ssc]; - if (rx_ln_vals) { - reg_pairs = rx_ln_vals->reg_pairs; - num_regs = rx_ln_vals->num_regs; - for (i = 0; i < num_lanes; i++) { - regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane]; - for (j = 0; j < num_regs; j++) - regmap_write(regmap, reg_pairs[j].off, - reg_pairs[j].val); + /* PMA RX lane registers configurations */ + rx_ln_vals = init_data->rx_ln_vals[phy_t1][phy_t2][ssc]; + if (rx_ln_vals) { + reg_pairs = rx_ln_vals->reg_pairs; + num_regs = rx_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } } - } - reset_deassert_bulk(cdns_phy->phys[node].lnk_rst); + reset_deassert_bulk(cdns_phy->phys[node].lnk_rst); + } } /* Take the PHY out of reset */ @@ -575,6 +607,7 @@ static int cdns_torrent_phy_probe(struct udevice *dev) /* Get init data for this phy */ data = (struct cdns_torrent_data *)dev_get_driver_data(dev); cdns_phy->init_data = data; + cdns_phy->protocol_bitmask = 0; cdns_phy->phy_rst = devm_reset_control_get_by_index(dev, 0); if (IS_ERR(cdns_phy->phy_rst)) { @@ -677,6 +710,8 @@ static int cdns_torrent_phy_probe(struct udevice *dev) /* Get SSC mode */ ofnode_read_u32(child, "cdns,ssc-mode", &cdns_phy->phys[node].ssc_mode); + + cdns_phy->protocol_bitmask |= BIT(cdns_phy->phys[node].phy_type); node++; } diff --git a/drivers/phy/phy-exynos-usbdrd.c b/drivers/phy/phy-exynos-usbdrd.c new file mode 100644 index 00000000000..db5815ed184 --- /dev/null +++ b/drivers/phy/phy-exynos-usbdrd.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + * + * Samsung Exynos SoC series USB DRD PHY driver. + * Based on Linux kernel PHY driver: drivers/phy/samsung/phy-exynos5-usbdrd.c + */ + +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +/* Offset of PMU register controlling USB PHY output isolation */ +#define EXYNOS_USBDRD_PHY_CONTROL 0x0704 +#define EXYNOS_PHY_ENABLE BIT(0) + +/* Exynos USB PHY registers */ +#define EXYNOS5_FSEL_9MHZ6 0x0 +#define EXYNOS5_FSEL_10MHZ 0x1 +#define EXYNOS5_FSEL_12MHZ 0x2 +#define EXYNOS5_FSEL_19MHZ2 0x3 +#define EXYNOS5_FSEL_20MHZ 0x4 +#define EXYNOS5_FSEL_24MHZ 0x5 +#define EXYNOS5_FSEL_26MHZ 0x6 +#define EXYNOS5_FSEL_50MHZ 0x7 + +/* Exynos850: USB DRD PHY registers */ +#define EXYNOS850_DRD_LINKCTRL 0x04 +#define LINKCTRL_FORCE_QACT BIT(8) +#define LINKCTRL_BUS_FILTER_BYPASS GENMASK(7, 4) + +#define EXYNOS850_DRD_CLKRST 0x20 +#define CLKRST_LINK_SW_RST BIT(0) +#define CLKRST_PORT_RST BIT(1) +#define CLKRST_PHY_SW_RST BIT(3) + +#define EXYNOS850_DRD_SSPPLLCTL 0x30 +#define SSPPLLCTL_FSEL GENMASK(2, 0) + +#define EXYNOS850_DRD_UTMI 0x50 +#define UTMI_FORCE_SLEEP BIT(0) +#define UTMI_FORCE_SUSPEND BIT(1) +#define UTMI_DM_PULLDOWN BIT(2) +#define UTMI_DP_PULLDOWN BIT(3) +#define UTMI_FORCE_BVALID BIT(4) +#define UTMI_FORCE_VBUSVALID BIT(5) + +#define EXYNOS850_DRD_HSP 0x54 +#define HSP_COMMONONN BIT(8) +#define HSP_EN_UTMISUSPEND BIT(9) +#define HSP_VBUSVLDEXT BIT(12) +#define HSP_VBUSVLDEXTSEL BIT(13) +#define HSP_FSV_OUT_EN BIT(24) + +#define EXYNOS850_DRD_HSP_TEST 0x5c +#define HSP_TEST_SIDDQ BIT(24) + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +/** + * struct exynos_usbdrd_phy - driver data for Exynos USB PHY + * @reg_phy: USB PHY controller register memory base + * @clk: clock for register access + * @core_clk: core clock for phy (ref clock) + * @reg_pmu: regmap for PMU block + * @extrefclk: frequency select settings when using 'separate reference clocks' + */ +struct exynos_usbdrd_phy { + void __iomem *reg_phy; + struct clk *clk; + struct clk *core_clk; + struct regmap *reg_pmu; + u32 extrefclk; +}; + +static void exynos_usbdrd_phy_isol(struct regmap *reg_pmu, bool isolate) +{ + unsigned int val; + + if (!reg_pmu) + return; + + val = isolate ? 0 : EXYNOS_PHY_ENABLE; + regmap_update_bits(reg_pmu, EXYNOS_USBDRD_PHY_CONTROL, + EXYNOS_PHY_ENABLE, val); +} + +/* + * Convert the supplied clock rate to the value that can be written to the PHY + * register. + */ +static unsigned int exynos_rate_to_clk(unsigned long rate, u32 *reg) +{ + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS5_FSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS5_FSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS5_FSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS5_FSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS5_FSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS5_FSEL_24MHZ; + break; + case 26 * MHZ: + *reg = EXYNOS5_FSEL_26MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS5_FSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos850_usbdrd_utmi_init(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* + * Disable HWACG (hardware auto clock gating control). This will force + * QACTIVE signal in Q-Channel interface to HIGH level, to make sure + * the PHY clock is not gated by the hardware. + */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= LINKCTRL_FORCE_QACT; + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + /* Start PHY Reset (POR=high) */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_PHY_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + + /* Enable UTMI+ */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN | + UTMI_DM_PULLDOWN); + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + /* Set VBUS Valid and D+ pull-up control by VBUS pad usage */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= FIELD_PREP(LINKCTRL_BUS_FILTER_BYPASS, 0xf); + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + reg = readl(regs_base + EXYNOS850_DRD_SSPPLLCTL); + reg &= ~SSPPLLCTL_FSEL; + switch (phy_drd->extrefclk) { + case EXYNOS5_FSEL_50MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 7); + break; + case EXYNOS5_FSEL_26MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 6); + break; + case EXYNOS5_FSEL_24MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 2); + break; + case EXYNOS5_FSEL_20MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 1); + break; + case EXYNOS5_FSEL_19MHZ2: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 0); + break; + default: + dev_warn(phy->dev, "unsupported ref clk: %#.2x\n", + phy_drd->extrefclk); + break; + } + writel(reg, regs_base + EXYNOS850_DRD_SSPPLLCTL); + + /* Power up PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg &= ~HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Finish PHY reset (POR=low) */ + udelay(10); /* required before doing POR=low */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST); + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(75); /* required after POR=low for guaranteed PHY clock */ + + /* Disable single ended signal out */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg &= ~HSP_FSV_OUT_EN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); +} + +static void exynos850_usbdrd_utmi_exit(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN); + reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Power down PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg |= HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Link reset */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(10); /* required before doing POR=low */ + reg &= ~CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); +} + +static int exynos_usbdrd_phy_init(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + exynos850_usbdrd_utmi_init(phy); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos_usbdrd_phy_exit(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + exynos850_usbdrd_utmi_exit(phy); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos_usbdrd_phy_power_on(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + dev_dbg(phy->dev, "Request to power_on usbdrd_phy phy\n"); + + ret = clk_prepare_enable(phy_drd->core_clk); + if (ret) + return ret; + + /* Power-on PHY */ + exynos_usbdrd_phy_isol(phy_drd->reg_pmu, false); + + return 0; +} + +static int exynos_usbdrd_phy_power_off(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + + dev_dbg(phy->dev, "Request to power_off usbdrd_phy phy\n"); + + /* Power-off the PHY */ + exynos_usbdrd_phy_isol(phy_drd->reg_pmu, true); + + clk_disable_unprepare(phy_drd->core_clk); + + return 0; +} + +static int exynos_usbdrd_phy_init_clk(struct udevice *dev) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(dev); + unsigned long ref_rate; + int err; + + phy_drd->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + err = PTR_ERR(phy_drd->clk); + dev_err(dev, "Failed to get phy clock (err=%d)\n", err); + return err; + } + + phy_drd->core_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(phy_drd->core_clk)) { + err = PTR_ERR(phy_drd->core_clk); + dev_err(dev, "Failed to get ref clock (err=%d)\n", err); + return err; + } + + ref_rate = clk_get_rate(phy_drd->core_clk); + err = exynos_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (err) { + dev_err(dev, "Clock rate %lu not supported\n", ref_rate); + return err; + } + + return 0; +} + +static int exynos_usbdrd_phy_probe(struct udevice *dev) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(dev); + int err; + + phy_drd->reg_phy = dev_read_addr_ptr(dev); + if (!phy_drd->reg_phy) + return -EINVAL; + + err = exynos_usbdrd_phy_init_clk(dev); + if (err) + return err; + + phy_drd->reg_pmu = syscon_regmap_lookup_by_phandle(dev, + "samsung,pmu-syscon"); + if (IS_ERR(phy_drd->reg_pmu)) { + err = PTR_ERR(phy_drd->reg_pmu); + dev_err(dev, "Failed to lookup PMU regmap\n"); + return err; + } + + return 0; +} + +static const struct phy_ops exynos_usbdrd_phy_ops = { + .init = exynos_usbdrd_phy_init, + .exit = exynos_usbdrd_phy_exit, + .power_on = exynos_usbdrd_phy_power_on, + .power_off = exynos_usbdrd_phy_power_off, +}; + +static const struct udevice_id exynos_usbdrd_phy_of_match[] = { + { + .compatible = "samsung,exynos850-usbdrd-phy", + }, + { } +}; + +U_BOOT_DRIVER(exynos_usbdrd_phy) = { + .name = "exynos-usbdrd-phy", + .id = UCLASS_PHY, + .of_match = exynos_usbdrd_phy_of_match, + .probe = exynos_usbdrd_phy_probe, + .ops = &exynos_usbdrd_phy_ops, + .priv_auto = sizeof(struct exynos_usbdrd_phy), +}; diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c index 8d643b762f9..fcf8617ee9b 100644 --- a/drivers/phy/phy-stm32-usbphyc.c +++ b/drivers/phy/phy-stm32-usbphyc.c @@ -16,7 +16,9 @@ #include <syscon.h> #include <usb.h> #include <asm/io.h> +#include <dm/device.h> #include <dm/device_compat.h> +#include <dm/device-internal.h> #include <dm/lists.h> #include <dm/of_access.h> #include <linux/bitfield.h> @@ -633,6 +635,7 @@ U_BOOT_DRIVER(stm32_usb_phyc) = { struct stm32_usbphyc_clk { bool enable; + struct clk clkp; }; static ulong stm32_usbphyc_clk48_get_rate(struct clk *clk) @@ -687,9 +690,25 @@ const struct clk_ops usbphyc_clk48_ops = { .disable = stm32_usbphyc_clk48_disable, }; +int usbphyc_clk48_probe(struct udevice *dev) +{ + struct stm32_usbphyc_clk *priv = dev_get_priv(dev); + + /* prepare clkp to correctly register clock with CCF */ + priv->clkp.dev = dev; + priv->clkp.id = CLK_ID(dev, 0); + + /* Store back pointer to clk from udevice */ + /* FIXME: This is not allowed...should be allocated by driver model */ + dev_set_uclass_priv(dev, &priv->clkp); + + return 0; +} + U_BOOT_DRIVER(stm32_usb_phyc_clk) = { .name = "stm32-usbphyc-clk", .id = UCLASS_CLK, .ops = &usbphyc_clk48_ops, + .probe = &usbphyc_clk48_probe, .priv_auto = sizeof(struct stm32_usbphyc_clk), }; diff --git a/drivers/phy/qcom/phy-qcom-qmp-ufs.c b/drivers/phy/qcom/phy-qcom-qmp-ufs.c index 449b9767778..f3c606847fb 100644 --- a/drivers/phy/qcom/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qcom/phy-qcom-qmp-ufs.c @@ -86,6 +86,12 @@ enum qphy_reg_layout { QPHY_LAYOUT_SIZE }; +static const unsigned int ufsphy_v2_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = QPHY_V2_PCS_UFS_PHY_START, + [QPHY_PCS_READY_STATUS] = QPHY_V2_PCS_UFS_READY_STATUS, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V2_PCS_UFS_POWER_DOWN_CONTROL, +}; + static const unsigned int ufsphy_v3_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = QPHY_V3_PCS_UFS_PHY_START, [QPHY_PCS_READY_STATUS] = QPHY_V3_PCS_UFS_READY_STATUS, @@ -715,6 +721,98 @@ static const struct qmp_ufs_init_tbl sc7280_ufsphy_hs_g4_rx[] = { QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x0f), }; +static const struct qmp_ufs_init_tbl sm6115_ufsphy_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), +}; + +static const struct qmp_ufs_init_tbl sm6115_ufsphy_hs_b_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +static const struct qmp_ufs_init_tbl sm6115_ufsphy_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_ufs_init_tbl sm6115_ufsphy_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5b), +}; + +static const struct qmp_ufs_init_tbl sm6115_ufsphy_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_RX_PWM_GEAR_BAND, 0x15), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_RX_SYM_RESYNC_CTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_TX_LARGE_AMP_POST_EMP_LVL, 0x12), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_TX_SMALL_AMP_POST_EMP_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_UFS_RX_MIN_HIBERN8_TIME, 0x9a), /* 8 us */ +}; + struct qmp_ufs_offsets { u16 serdes; u16 pcs; @@ -1079,6 +1177,34 @@ static const struct qmp_ufs_cfg sa8775p_ufsphy_cfg = { .regs = ufsphy_v5_regs_layout, }; +static const struct qmp_ufs_cfg sm6115_ufsphy_cfg = { + .lanes = 1, + + .offsets = &qmp_ufs_offsets, + + .tbls = { + .serdes = sm6115_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(sm6115_ufsphy_serdes), + .tx = sm6115_ufsphy_tx, + .tx_num = ARRAY_SIZE(sm6115_ufsphy_tx), + .rx = sm6115_ufsphy_rx, + .rx_num = ARRAY_SIZE(sm6115_ufsphy_rx), + .pcs = sm6115_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(sm6115_ufsphy_pcs), + }, + .tbls_hs_b = { + .serdes = sm6115_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm6115_ufsphy_hs_b_serdes), + }, + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_ufs_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_ufs_vreg_l), + .regs = ufsphy_v2_regs_layout, + + .no_pcs_sw_reset = true, +}; + static void qmp_ufs_configure_lane(void __iomem *base, const struct qmp_ufs_init_tbl tbl[], int num, @@ -1469,9 +1595,11 @@ static const struct udevice_id qmp_ufs_ids[] = { { .compatible = "qcom,sdm845-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg }, { .compatible = "qcom,sm8150-qmp-ufs-phy", .data = (ulong)&sm8150_ufsphy_cfg }, { .compatible = "qcom,sm8250-qmp-ufs-phy", .data = (ulong)&sm8250_ufsphy_cfg }, + { .compatible = "qcom,qcs8300-qmp-ufs-phy", .data = (ulong)&sa8775p_ufsphy_cfg }, { .compatible = "qcom,sm8550-qmp-ufs-phy", .data = (ulong)&sm8550_ufsphy_cfg }, { .compatible = "qcom,sm8650-qmp-ufs-phy", .data = (ulong)&sm8650_ufsphy_cfg }, { .compatible = "qcom,sc7280-qmp-ufs-phy", .data = (ulong)&sc7280_ufsphy_cfg, }, + { .compatible = "qcom,qcs615-qmp-ufs-phy", .data = (ulong)&sm6115_ufsphy_cfg, }, { } }; diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index c7459dbc5fc..c48a5cd5267 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -284,7 +284,7 @@ DECLARE_GLOBAL_DATA_PTR; * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL_CONFIG 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) |