// SPDX-License-Identifier: GPL-2.0 /* * Samsung EXYNOS SoC series USB DRD PHY driver * * Phy provider for USB 3.0 DRD controller on Exynos SoC series * * Copyright (c) 2021 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Vivek Gautam * Minho Lee * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 of * the License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #if 0 #include #endif #include #if IS_ENABLED(CONFIG_EXYNOS_OTP) #include #endif #ifdef CONFIG_OF #include #include #endif #include "phy-exynos-usbdrd.h" //#include "phy-exynos-debug.h" //#include static void __iomem *usbdp_combo_phy_reg; void __iomem *phycon_base_addr; EXPORT_SYMBOL_GPL(phycon_base_addr); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 struct usb_eom_result_s *eom_result; /*u32 get_speed_and_disu1u2(void);*/ static ssize_t exynos_usbdrd_eom_show(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; u32 test_cnt = 0; static int current_cnt = 0; if (eom_result == NULL) { len += snprintf(buf + len, PAGE_SIZE, "eom_result structure is NULL!!!\n"); goto exit; } while (current_cnt != EOM_PH_SEL_MAX * EOM_DEF_VREF_MAX) { len += snprintf(buf + len, PAGE_SIZE, "phase %d vref %d err %lu\n", eom_result[current_cnt].phase, eom_result[current_cnt].vref, (unsigned long)eom_result[current_cnt].err); current_cnt++; test_cnt++; if (test_cnt == 100) break; } if (current_cnt == EOM_PH_SEL_MAX * EOM_DEF_VREF_MAX) current_cnt = 0; exit: return len; } static ssize_t exynos_usbdrd_eom_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); int speed_val; kfree(eom_result); eom_result = kzalloc(sizeof(struct usb_eom_result_s) * EOM_PH_SEL_MAX * EOM_DEF_VREF_MAX, GFP_KERNEL); if (eom_result == NULL) { return -ENOMEM; } /* Disable U1/U2 & get speed */ { u32 reg; void __iomem *dctl_ctrl; /* Disable U1/U2 */ dctl_ctrl = ioremap(0x10E0c704, 0x4); reg = readl(dctl_ctrl); reg &= ~(BIT(9) | BIT(10) | BIT(11) | BIT(12)); writel(reg, dctl_ctrl); iounmap(dctl_ctrl); } speed_val = 0; if (speed_val == 0) speed_val = 1; /* Gen2 */ else speed_val = 0; /* Gen1 */ /* Start eom test */ phy_exynos_usbdp_g2_v4_eom(&phy_drd->usbphy_sub_info, eom_result, speed_val); return n; } static DEVICE_ATTR(eom, S_IWUSR | S_IRUSR | S_IRGRP, exynos_usbdrd_eom_show, exynos_usbdrd_eom_store); #endif static ssize_t exynos_usbdrd_hs_phy_tune_show(struct device *dev, struct device_attribute *attr, char *buf) { struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); int len = 0, i, ret; u32 tune_num = 0; struct device_node *tune_node; tune_node = of_parse_phandle(dev->of_node, "hs_tune_param", 0); ret = of_property_read_u32_array(tune_node, "hs_tune_cnt", &tune_num, 1); if (ret) { len += snprintf(buf + len, PAGE_SIZE, "Can't get tune value!!!\n"); goto exit; } len += snprintf(buf + len, PAGE_SIZE, "\t==== Print USB Tune Value ====\n"); len += snprintf(buf + len, PAGE_SIZE, "Tune value count : %d\n", tune_num); for (i = 0; i < tune_num; i++) { len += snprintf(buf + len, PAGE_SIZE, "%s\t\t\t: 0x%x, 0x%x\n", phy_drd->usbphy_info.tune_param[i].name, phy_drd->hs_tune_param_value[i][0], phy_drd->hs_tune_param_value[i][1]); } exit: return len; } static ssize_t exynos_usbdrd_hs_phy_tune_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct device_node *tune_node; struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); char *tune_name; int ret, i; u32 tune_val; u32 tune_num = 0; if (size > EXYNOS_DRD_TUNEPARAM_LEN) { pr_err("%s size(%zu) is too long.\n", __func__, size); return -EINVAL; } tune_name = kzalloc(size + 1, GFP_KERNEL); if (!tune_name) return -ENOMEM; if (sscanf(buf, "%29s %x", tune_name, &tune_val) != 2) goto exit; tune_node = of_parse_phandle(dev->of_node, "hs_tune_param", 0); ret = of_property_read_u32_array(tune_node, "hs_tune_cnt", &tune_num, 1); if (ret) { pr_err("Can't get hs_tune_cnt!!!\n"); goto exit; } for (i = 0; i < tune_num; i++) { if (!strncmp(phy_drd->usbphy_info.tune_param[i].name, tune_name, strlen(phy_drd->usbphy_info.tune_param[i].name))) { phy_drd->hs_tune_param_value[i][0] = tune_val; phy_drd->hs_tune_param_value[i][1] = tune_val; } } exit: kfree(tune_name); return size; } static DEVICE_ATTR(hs_phy_tune, S_IWUSR | S_IRUSR | S_IRGRP, exynos_usbdrd_hs_phy_tune_show, exynos_usbdrd_hs_phy_tune_store); static ssize_t exynos_usbdrd_phy_tune_show(struct device *dev, struct device_attribute *attr, char *buf) { struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); int len = 0, i, ret; u32 tune_num = 0; struct device_node *tune_node; tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0); ret = of_property_read_u32_array(tune_node, "ss_tune_cnt", &tune_num, 1); if (ret) { len += snprintf(buf + len, PAGE_SIZE, "Can't get tune value!!!\n"); goto exit; } len += snprintf(buf + len, PAGE_SIZE, "\t==== Print USB Tune Value ====\n"); len += snprintf(buf + len, PAGE_SIZE, "Tune value count : %d\n", tune_num); for (i = 0; i < tune_num; i++) { len += snprintf(buf + len, PAGE_SIZE, "%s\t\t\t: 0x%x, 0x%x\n", phy_drd->usbphy_sub_info.tune_param[i].name, phy_drd->ss_tune_param_value[i][0], phy_drd->ss_tune_param_value[i][1]); } exit: return len; } static ssize_t exynos_usbdrd_phy_tune_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct device_node *tune_node; struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); char *tune_name; int ret, i; u32 tune_num = 0; u32 tune_val = 0; if (size > EXYNOS_DRD_TUNEPARAM_LEN) { pr_err("%s size(%zu) is too long.\n", __func__, size); return -EINVAL; } tune_name = kzalloc(size + 1, GFP_KERNEL); if (!tune_name) return -ENOMEM; if (sscanf(buf, "%29s %x", tune_name, &tune_val) != 2) goto exit; tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0); ret = of_property_read_u32_array(tune_node, "ss_tune_cnt", &tune_num, 1); if (ret) { pr_err("Can't get ss_tune_cnt!!!\n"); goto exit; } for (i = 0; i < tune_num; i++) { if (!strncmp(phy_drd->usbphy_sub_info.tune_param[i].name, tune_name, strlen(phy_drd->usbphy_sub_info.tune_param[i].name))) { phy_drd->ss_tune_param_value[i][0] = tune_val; phy_drd->ss_tune_param_value[i][1] = tune_val; } } exit: kfree(tune_name); return size; } static DEVICE_ATTR(phy_tune, S_IWUSR | S_IRUSR | S_IRGRP, exynos_usbdrd_phy_tune_show, exynos_usbdrd_phy_tune_store); static int exynos_usbdrd_clk_prepare(struct exynos_usbdrd_phy *phy_drd) { int i; int ret; for (i = 0; phy_drd->clocks[i] != NULL; i++) { ret = clk_prepare(phy_drd->clocks[i]); if (ret) goto err; } if (phy_drd->use_phy_umux) { for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) { ret = clk_prepare(phy_drd->phy_clocks[i]); if (ret) goto err1; } } return 0; err: for (i = i - 1; i >= 0; i--) clk_unprepare(phy_drd->clocks[i]); return ret; err1: for (i = i - 1; i >= 0; i--) clk_unprepare(phy_drd->phy_clocks[i]); return ret; } static int exynos_usbdrd_clk_enable(struct exynos_usbdrd_phy *phy_drd, bool umux) { int i; int ret; if (!umux) { for (i = 0; phy_drd->clocks[i] != NULL; i++) { ret = clk_enable(phy_drd->clocks[i]); if (ret) goto err; } } else { for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) { ret = clk_enable(phy_drd->phy_clocks[i]); if (ret) goto err1; } } return 0; err: for (i = i - 1; i >= 0; i--) clk_disable(phy_drd->clocks[i]); return ret; err1: for (i = i - 1; i >= 0; i--) clk_disable(phy_drd->phy_clocks[i]); return ret; } static void exynos_usbdrd_clk_unprepare(struct exynos_usbdrd_phy *phy_drd) { int i; for (i = 0; phy_drd->clocks[i] != NULL; i++) clk_unprepare(phy_drd->clocks[i]); for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) clk_unprepare(phy_drd->phy_clocks[i]); } static void exynos_usbdrd_clk_disable(struct exynos_usbdrd_phy *phy_drd, bool umux) { int i; if (!umux) { for (i = 0; phy_drd->clocks[i] != NULL; i++) clk_disable(phy_drd->clocks[i]); } else { for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) clk_disable(phy_drd->phy_clocks[i]); } } static int exynos_usbdrd_phyclk_get(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; const char **phyclk_ids; const char **clk_ids; const char *refclk_name; struct clk *clk; int phyclk_count; int clk_count; bool is_phyclk = false; int clk_index = 0; int i, j, ret; phyclk_count = of_property_count_strings(dev->of_node, "phyclk_mux"); if (IS_ERR_VALUE((unsigned long)phyclk_count)) { dev_err(dev, "invalid phyclk list in %s node\n", dev->of_node->name); return -EINVAL; } phyclk_ids = (const char **)devm_kmalloc(dev, (phyclk_count+1) * sizeof(const char *), GFP_KERNEL); for (i = 0; i < phyclk_count; i++) { ret = of_property_read_string_index(dev->of_node, "phyclk_mux", i, &phyclk_ids[i]); if (ret) { dev_err(dev, "failed to read phyclk_mux name %d from %s node\n", i, dev->of_node->name); return ret; } } phyclk_ids[phyclk_count] = NULL; if (!strcmp("none", phyclk_ids[0])) { dev_info(dev, "don't need user Mux for phyclk\n"); phy_drd->use_phy_umux = false; phyclk_count = 0; } else { phy_drd->use_phy_umux = true; phy_drd->phy_clocks = (struct clk **) devm_kmalloc(dev, (phyclk_count+1) * sizeof(struct clk *), GFP_KERNEL); if (!phy_drd->phy_clocks) { dev_err(dev, "failed to alloc : phy clocks\n"); return -ENOMEM; } for (i = 0; phyclk_ids[i] != NULL; i++) { clk = devm_clk_get(dev, phyclk_ids[i]); if (IS_ERR_OR_NULL(clk)) { dev_err(dev, "couldn't get %s clock\n", phyclk_ids[i]); return -EINVAL; } phy_drd->phy_clocks[i] = clk; } phy_drd->phy_clocks[i] = NULL; } clk_count = of_property_count_strings(dev->of_node, "clock-names"); if (IS_ERR_VALUE((unsigned long)clk_count)) { dev_err(dev, "invalid clk list in %s node", dev->of_node->name); return -EINVAL; } clk_ids = (const char **)devm_kmalloc(dev, (clk_count + 1) * sizeof(const char *), GFP_KERNEL); for (i = 0; i < clk_count; i++) { ret = of_property_read_string_index(dev->of_node, "clock-names", i, &clk_ids[i]); if (ret) { dev_err(dev, "failed to read clocks name %d from %s node\n", i, dev->of_node->name); return ret; } } clk_ids[clk_count] = NULL; phy_drd->clocks = (struct clk **) devm_kmalloc(dev, (clk_count + 1) * sizeof(struct clk *), GFP_KERNEL); if (!phy_drd->clocks) { dev_err(dev, "failed to alloc for clocks\n"); return -ENOMEM; } for (i = 0; clk_ids[i] != NULL; i++) { if (phyclk_count) { for (j = 0; phyclk_ids[j] != NULL; j++) { if (!strcmp(phyclk_ids[j], clk_ids[i])) { is_phyclk = true; phyclk_count--; } } } if (!is_phyclk) { clk = devm_clk_get(dev, clk_ids[i]); if (IS_ERR_OR_NULL(clk)) { dev_err(dev, "couldn't get %s clock\n", clk_ids[i]); return -EINVAL; } phy_drd->clocks[clk_index] = clk; clk_index++; } is_phyclk = false; } phy_drd->clocks[clk_index] = NULL; ret = of_property_read_string_index(dev->of_node, "phy_refclk", 0, &refclk_name); if (ret) { dev_err(dev, "failed to read ref_clocks name from %s node\n", dev->of_node->name); return ret; } if (!strcmp("none", refclk_name)) { dev_err(dev, "phy reference clock shouldn't be omitted"); return -EINVAL; } for (i = 0; clk_ids[i] != NULL; i++) { if (!strcmp(clk_ids[i], refclk_name)) { phy_drd->ref_clk = devm_clk_get(dev, refclk_name); break; } } if (IS_ERR_OR_NULL(phy_drd->ref_clk)) { dev_err(dev, "%s couldn't get ref_clk", __func__); return -EINVAL; } devm_kfree(dev, phyclk_ids); devm_kfree(dev, clk_ids); return 0; } static int exynos_usbdrd_clk_get(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; int ret; ret = exynos_usbdrd_phyclk_get(phy_drd); if (ret < 0) { dev_err(dev, "failed to get clock for DRD USBPHY"); return ret; } return 0; } static inline struct exynos_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) { return container_of((inst), struct exynos_usbdrd_phy, phys[(inst)->index]); } #if IS_ENABLED(CONFIG_EXYNOS_OTP) void exynos_usbdrd_phy_get_otp_info(struct exynos_usbdrd_phy *phy_drd) { struct tune_bits *data; u16 magic; u8 type; u8 index_count; u8 i, j; phy_drd->otp_index[0] = phy_drd->otp_index[1] = 0; for (i = 0; i < OTP_SUPPORT_USBPHY_NUMBER; i++) { magic = i ? OTP_MAGIC_USB2: OTP_MAGIC_USB3; if (otp_tune_bits_parsed(magic, &type, &index_count, &data)) { dev_err(phy_drd->dev, "%s failed to get usb%d otp\n", __func__, i ? 2 : 3); continue; } dev_info(phy_drd->dev, "usb[%d] otp index_count: %d\n", i, index_count); if (!index_count) { phy_drd->otp_data[i] = NULL; continue; } phy_drd->otp_data[i] = devm_kzalloc(phy_drd->dev, sizeof(*data) * index_count, GFP_KERNEL); if (!phy_drd->otp_data[i]) { dev_err(phy_drd->dev, "%s failed to alloc for usb%d\n", __func__, i ? 2 : 3); continue; } phy_drd->otp_index[i] = index_count; phy_drd->otp_type[i] = type ? 4 : 1; dev_info(phy_drd->dev, "usb[%d] otp type: %d\n", i, type); for (j = 0; j < index_count; j++) { phy_drd->otp_data[i][j].index = data[j].index; phy_drd->otp_data[i][j].value = data[j].value; dev_dbg(phy_drd->dev, "usb[%d][%d] otp_data index:%d, value:0x%08x\n", i, j, phy_drd->otp_data[i][j].index, phy_drd->otp_data[i][j].value); } } } #endif /* * exynos_rate_to_clk() converts the supplied clock rate to the value that * can be written to the phy register. */ static unsigned int exynos_rate_to_clk(struct exynos_usbdrd_phy *phy_drd) { int ret; ret = clk_prepare_enable(phy_drd->ref_clk); if (ret) { dev_err(phy_drd->dev, "%s failed to enable ref_clk", __func__); return 0; } /* EXYNOS_FSEL_MASK */ switch (clk_get_rate(phy_drd->ref_clk)) { case 9600 * KHZ: phy_drd->extrefclk = EXYNOS_FSEL_9MHZ6; break; case 10 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_10MHZ; break; case 12 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_12MHZ; break; case 19200 * KHZ: phy_drd->extrefclk = EXYNOS_FSEL_19MHZ2; break; case 20 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_20MHZ; break; case 24 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_24MHZ; break; case 26 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_26MHZ; break; case 50 * MHZ: phy_drd->extrefclk = EXYNOS_FSEL_50MHZ; break; default: phy_drd->extrefclk = 0; clk_disable_unprepare(phy_drd->ref_clk); return -EINVAL; } clk_disable_unprepare(phy_drd->ref_clk); return 0; } static void exynos_usbdrd_pipe3_phy_isol(struct phy_usb_instance *inst, unsigned int on, unsigned int mask) { unsigned int val; if (!inst->reg_pmu) return; val = on ? 0 : mask; regmap_update_bits(inst->reg_pmu, inst->pmu_offset_dp, mask, val); } static void exynos_usbdrd_utmi_phy_isol(struct phy_usb_instance *inst, unsigned int on, unsigned int mask) { unsigned int val; if (!inst->reg_pmu) return; val = on ? 0 : mask; regmap_update_bits(inst->reg_pmu, inst->pmu_offset, mask, val); /* Control TCXO_BUF */ if (inst->pmu_mask_tcxobuf != 0) { val = on ? 0 : inst->pmu_mask_tcxobuf; regmap_update_bits(inst->reg_pmu, inst->pmu_offset_tcxobuf, inst->pmu_mask_tcxobuf, val); } } /* * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock * from clock core. Further sets multiplier values and spread spectrum * clock settings for SuperSpeed operations. */ static unsigned int exynos_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) { static u32 reg; struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); /* PHYCLKRST setting isn't required in Combo PHY */ if (phy_drd->usbphy_info.version >= EXYNOS_USBPHY_VER_02_0_0) return -EINVAL; /* restore any previous reference clock settings */ reg = readl(phy_drd->reg_phy + EXYNOS_DRD_PHYCLKRST); /* Use EXTREFCLK as ref clock */ reg &= ~PHYCLKRST_REFCLKSEL_MASK; reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; /* FSEL settings corresponding to reference clock */ reg &= ~PHYCLKRST_FSEL_PIPE_MASK | PHYCLKRST_MPLL_MULTIPLIER_MASK | PHYCLKRST_SSC_REFCLKSEL_MASK; switch (phy_drd->extrefclk) { case EXYNOS_FSEL_50MHZ: reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | PHYCLKRST_SSC_REFCLKSEL(0x00)); break; case EXYNOS_FSEL_24MHZ: reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | PHYCLKRST_SSC_REFCLKSEL(0x88)); break; case EXYNOS_FSEL_20MHZ: reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | PHYCLKRST_SSC_REFCLKSEL(0x00)); break; case EXYNOS_FSEL_19MHZ2: reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | PHYCLKRST_SSC_REFCLKSEL(0x88)); break; default: dev_dbg(phy_drd->dev, "unsupported ref clk\n"); break; } return reg; } /* * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock * from clock core. Further sets the FSEL values for HighSpeed operations. */ static unsigned int exynos_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) { static u32 reg; struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); /* PHYCLKRST setting isn't required in Combo PHY */ if(phy_drd->usbphy_info.version >= EXYNOS_USBPHY_VER_02_0_0) return EINVAL; /* restore any previous reference clock settings */ reg = readl(phy_drd->reg_phy + EXYNOS_DRD_PHYCLKRST); reg &= ~PHYCLKRST_REFCLKSEL_MASK; reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; reg &= ~PHYCLKRST_FSEL_UTMI_MASK | PHYCLKRST_MPLL_MULTIPLIER_MASK | PHYCLKRST_SSC_REFCLKSEL_MASK; reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); return reg; } /* * Sets the default PHY tuning values for high-speed connection. */ static int exynos_usbdrd_fill_hstune(struct exynos_usbdrd_phy *phy_drd, struct device_node *node) { struct device *dev = phy_drd->dev; struct exynos_usbphy_hs_tune *hs_tune = phy_drd->hs_value; int ret; u32 res[2]; u32 value; ret = of_property_read_u32_array(node, "tx_vref", res, 2); if (ret == 0) { hs_tune[0].tx_vref = res[0]; hs_tune[1].tx_vref = res[1]; } else { dev_err(dev, "can't get tx_vref value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_pre_emp", res, 2); if (ret == 0) { hs_tune[0].tx_pre_emp = res[0]; hs_tune[1].tx_pre_emp = res[1]; } else { dev_err(dev, "can't get tx_pre_emp value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_pre_emp_plus", res, 2); if (ret == 0) { hs_tune[0].tx_pre_emp_plus = res[0]; hs_tune[1].tx_pre_emp_plus = res[1]; } else { dev_err(dev, "can't get tx_pre_emp_plus value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_res", res, 2); if (ret == 0) { hs_tune[0].tx_res = res[0]; hs_tune[1].tx_res = res[1]; } else { dev_err(dev, "can't get tx_res value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_rise", res, 2); if (ret == 0) { hs_tune[0].tx_rise = res[0]; hs_tune[1].tx_rise = res[1]; } else { dev_err(dev, "can't get tx_rise value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_hsxv", res, 2); if (ret == 0) { hs_tune[0].tx_hsxv = res[0]; hs_tune[1].tx_hsxv = res[1]; } else { dev_err(dev, "can't get tx_hsxv value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_fsls", res, 2); if (ret == 0) { hs_tune[0].tx_fsls = res[0]; hs_tune[1].tx_fsls = res[1]; } else { dev_err(dev, "can't get tx_fsls value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "rx_sqrx", res, 2); if (ret == 0) { hs_tune[0].rx_sqrx = res[0]; hs_tune[1].rx_sqrx = res[1]; } else { dev_err(dev, "can't get tx_sqrx value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "compdis", res, 2); if (ret == 0) { hs_tune[0].compdis = res[0]; hs_tune[1].compdis = res[1]; } else { dev_err(dev, "can't get compdis value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "otg", res, 2); if (ret == 0) { hs_tune[0].otg = res[0]; hs_tune[1].otg = res[1]; } else { dev_err(dev, "can't get otg_tune value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "enable_user_imp", res, 2); if (ret == 0) { if (res[0]) { hs_tune[0].enable_user_imp = true; hs_tune[1].enable_user_imp = true; hs_tune[0].user_imp_value = res[1]; hs_tune[1].user_imp_value = res[1]; } else { hs_tune[0].enable_user_imp = false; hs_tune[1].enable_user_imp = false; } } else { dev_err(dev, "can't get enable_user_imp value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32(node, "is_phyclock", &value); if (ret == 0) { if ( value == 1) { hs_tune[0].utmi_clk = USBPHY_UTMI_PHYCLOCK; hs_tune[1].utmi_clk = USBPHY_UTMI_PHYCLOCK; } else { hs_tune[0].utmi_clk = USBPHY_UTMI_FREECLOCK; hs_tune[1].utmi_clk = USBPHY_UTMI_FREECLOCK; } } else { dev_err(dev, "can't get is_phyclock value, error = %d\n", ret); return -EINVAL; } return 0; } /* * Sets the default PHY tuning values for super-speed connection. */ static int exynos_usbdrd_fill_sstune(struct exynos_usbdrd_phy *phy_drd, struct device_node *node) { struct device *dev = phy_drd->dev; struct exynos_usbphy_ss_tune *ss_tune = phy_drd->ss_value; u32 res[2]; int ret; ret = of_property_read_u32_array(node, "tx_boost_level", res, 2); if (ret == 0) { ss_tune[0].tx_boost_level = res[0]; ss_tune[1].tx_boost_level = res[1]; } else { dev_err(dev, "can't get tx_boost_level value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_swing_level", res, 2); if (ret == 0) { ss_tune[0].tx_swing_level = res[0]; ss_tune[1].tx_swing_level = res[1]; } else { dev_err(dev, "can't get tx_swing_level value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_swing_full", res, 2); if (ret == 0) { ss_tune[0].tx_swing_full = res[0]; ss_tune[1].tx_swing_full = res[1]; } else { dev_err(dev, "can't get tx_swing_full value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_swing_low", res, 2); if (ret == 0) { ss_tune[0].tx_swing_low = res[0]; ss_tune[1].tx_swing_low = res[1]; } else { dev_err(dev, "can't get tx_swing_low value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_deemphasis_mode", res, 2); if (ret == 0) { ss_tune[0].tx_deemphasis_mode = res[0]; ss_tune[1].tx_deemphasis_mode = res[1]; } else { dev_err(dev, "can't get tx_deemphasis_mode value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_deemphasis_3p5db", res, 2); if (ret == 0) { ss_tune[0].tx_deemphasis_3p5db = res[0]; ss_tune[1].tx_deemphasis_3p5db = res[1]; } else { dev_err(dev, "can't get tx_deemphasis_3p5db value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "tx_deemphasis_6db", res, 2); if (ret == 0) { ss_tune[0].tx_deemphasis_6db = res[0]; ss_tune[1].tx_deemphasis_6db = res[1]; } else { dev_err(dev, "can't get tx_deemphasis_6db value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "enable_ssc", res, 2); if (ret == 0) { ss_tune[0].enable_ssc = res[0]; ss_tune[1].enable_ssc = res[1]; } else { dev_err(dev, "can't get enable_ssc value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "ssc_range", res, 2); if (ret == 0) { ss_tune[0].ssc_range = res[0]; ss_tune[1].ssc_range = res[1]; } else { dev_err(dev, "can't get ssc_range value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "los_bias", res, 2); if (ret == 0) { ss_tune[0].los_bias = res[0]; ss_tune[1].los_bias = res[1]; } else { dev_err(dev, "can't get los_bias value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "los_mask_val", res, 2); if (ret == 0) { ss_tune[0].los_mask_val = res[0]; ss_tune[1].los_mask_val = res[1]; } else { dev_err(dev, "can't get los_mask_val value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "enable_fixed_rxeq_mode", res, 2); if (ret == 0) { ss_tune[0].enable_fixed_rxeq_mode = res[0]; ss_tune[1].enable_fixed_rxeq_mode = res[1]; } else { dev_err(dev, "can't get enable_fixed_rxeq_mode value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "fix_rxeq_value", res, 2); if (ret == 0) { ss_tune[0].fix_rxeq_value = res[0]; ss_tune[1].fix_rxeq_value = res[1]; } else { dev_err(dev, "can't get fix_rxeq_value value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "set_crport_level_en", res, 2); if (ret == 0) { ss_tune[0].set_crport_level_en = res[0]; ss_tune[1].set_crport_level_en = res[1]; } else { dev_err(dev, "can't get set_crport_level_en value, error = %d\n", ret); return -EINVAL; } ret = of_property_read_u32_array(node, "set_crport_mpll_charge_pump", res, 2); if (ret == 0) { ss_tune[0].set_crport_mpll_charge_pump = res[0]; ss_tune[1].set_crport_mpll_charge_pump = res[1]; } else { dev_err(dev, "can't get set_crport_mpll_charge_pump value, error = %d\n", ret); return -EINVAL; } return 0; } static int exynos_usbdrd_fill_hstune_param(struct exynos_usbdrd_phy *phy_drd, struct device_node *node) { struct device *dev = phy_drd->dev; struct device_node *child = NULL; struct exynos_usb_tune_param *hs_tune_param; size_t size = sizeof(struct exynos_usb_tune_param); int ret; u32 res[2]; u32 param_index = 0; const char *name; ret = of_property_read_u32_array(node, "hs_tune_cnt", &res[0], 1); dev_info(dev, "%s hs tune cnt = %d\n", __func__, res[0]); hs_tune_param = devm_kzalloc(dev, size * (res[0] + 1), GFP_KERNEL); if (!hs_tune_param) return -ENOMEM; phy_drd->usbphy_info.tune_param = hs_tune_param; for_each_child_of_node(node, child) { ret = of_property_read_string(child, "tune_name", &name); if (ret == 0) { memcpy(hs_tune_param[param_index].name, name, strlen(name)); } else { dev_err(dev, "failed to read hs tune name from %s node\n", child->name); return ret; } ret = of_property_read_u32_array(child, "tune_value", res, 2); if (ret == 0) { phy_drd->hs_tune_param_value[param_index][0] = res[0]; phy_drd->hs_tune_param_value[param_index][1] = res[1]; } else { dev_err(dev, "failed to read hs tune value from %s node\n", child->name); return -EINVAL; } param_index++; } hs_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST; return 0; } /* * Sets the default PHY tuning values for super-speed connection. */ static int exynos_usbdrd_fill_sstune_param(struct exynos_usbdrd_phy *phy_drd, struct device_node *node) { struct device *dev = phy_drd->dev; struct device_node *child = NULL; struct exynos_usb_tune_param *ss_tune_param; size_t size = sizeof(struct exynos_usb_tune_param); int ret; u32 res[2]; u32 param_index = 0; const char *name; ret = of_property_read_u32_array(node, "ss_tune_cnt", &res[0], 1); dev_info(dev, "%s ss tune cnt = %d\n", __func__, res[0]); ss_tune_param = devm_kzalloc(dev, size * (res[0] + 1), GFP_KERNEL); if (!ss_tune_param) return -ENOMEM; phy_drd->usbphy_sub_info.tune_param = ss_tune_param; for_each_child_of_node(node, child) { ret = of_property_read_string(child, "tune_name", &name); if (ret == 0) { memcpy(ss_tune_param[param_index].name, name, strlen(name)); } else { dev_err(dev, "failed to read ss tune name from %s node\n", child->name); return ret; } ret = of_property_read_u32_array(child, "tune_value", res, 2); if (ret == 0) { phy_drd->ss_tune_param_value[param_index][0] = res[0]; phy_drd->ss_tune_param_value[param_index][1] = res[1]; if (phy_drd->use_default_tune_val) { phy_drd->ss_tune_param_value[param_index][0] = -1; phy_drd->ss_tune_param_value[param_index][1] = -1; } } else { dev_err(dev, "failed to read ss tune value from %s node\n", child->name); return -EINVAL; } param_index++; } ss_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST; return 0; } static int exynos_usbdrd_get_phy_refsel(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; struct device_node *node = dev->of_node; int value, ret; int check_flag = 0; ret = of_property_read_u32(node, "phy_refsel_clockcore", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_CLKCORE; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_CLKCORE; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_clockcore, error = %d\n", ret); return ret; } else { check_flag++; } ret = of_property_read_u32(node, "phy_refsel_ext_osc", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_OSC; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_OSC; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_ext_osc, error = %d\n", ret); return ret; } else { check_flag++; } ret = of_property_read_u32(node, "phy_refsel_xtal", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_XTAL; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_XTAL; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_xtal, error = %d\n", ret); return ret; } else { check_flag++; } ret = of_property_read_u32(node, "phy_refsel_diff_pad", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_PAD; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_PAD; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_diff_pad, error = %d\n", ret); return ret; } else { check_flag++; } ret = of_property_read_u32(node, "phy_refsel_diff_internal", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_diff_internal, error = %d\n", ret); return ret; } else { check_flag++; } ret = of_property_read_u32(node, "phy_refsel_diff_single", &value); if (ret == 0 && value == 1) { phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_SINGLE; phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_SINGLE; } else if (ret < 0) { dev_err(dev, "can't get phy_refsel_diff_single, error = %d\n", ret); return ret; } else { check_flag++; } if (check_flag > 5) { dev_err(dev, "USB refsel Must be choosed\n"); return -EINVAL; } return 0; } static int exynos_usbdrd_get_sub_phyinfo(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; struct device_node *tune_node; int ret; int value; if (!of_property_read_u32(dev->of_node, "sub_phy_version", &value)) { phy_drd->usbphy_sub_info.version = value; } else { dev_err(dev, "can't get sub_phy_version\n"); return -EINVAL; } phy_drd->usbphy_sub_info.refclk = phy_drd->extrefclk; phy_drd->usbphy_sub_info.regs_base = phy_drd->reg_phy2; /* Temporary WA, CAL code modification is needed */ phy_drd->usbphy_info.regs_base_2nd = phy_drd->reg_phy2; phy_drd->usbphy_sub_info.regs_base_2nd = phy_drd->reg_phy3; usbdp_combo_phy_reg = phy_drd->usbphy_sub_info.regs_base; tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0); if (tune_node != NULL) { ret = exynos_usbdrd_fill_sstune_param(phy_drd, tune_node); if (ret < 0) { dev_err(dev, "can't fill super speed tuning param\n"); return -EINVAL; } } else dev_info(dev, "don't need usbphy tuning param for high speed\n"); return 0; } static int exynos_usbdrd_get_phyinfo(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; struct device_node *tune_node; int ret; int value; phy_drd->usbphy_info.hs_rewa = 1; if (!of_property_read_u32(dev->of_node, "phy_version", &value)) { phy_drd->usbphy_info.version = value; } else { dev_err(dev, "can't get phy_version\n"); return -EINVAL; } if (!of_property_read_u32(dev->of_node, "use_io_for_ovc", &value)) { phy_drd->usbphy_info.use_io_for_ovc = value ? true : false; } else { dev_err(dev, "can't get io_for_ovc\n"); return -EINVAL; } if (!of_property_read_u32(dev->of_node, "common_block_disable", &value)) { phy_drd->usbphy_info.common_block_disable = value ? true : false; } else { dev_err(dev, "can't get common_block_disable\n"); return -EINVAL; } phy_drd->usbphy_info.refclk = phy_drd->extrefclk; phy_drd->usbphy_info.regs_base = phy_drd->reg_phy; if (!of_property_read_u32(dev->of_node, "is_not_vbus_pad", &value)) { phy_drd->usbphy_info.not_used_vbus_pad = value ? true : false; } else { dev_err(dev, "can't get vbus_pad\n"); return -EINVAL; } if (!of_property_read_u32(dev->of_node, "used_phy_port", &value)) { phy_drd->usbphy_info.used_phy_port = value ? true : false; } else { dev_err(dev, "can't get used_phy_port\n"); return -EINVAL; } ret = exynos_usbdrd_get_phy_refsel(phy_drd); if (ret < 0) dev_err(dev, "can't get phy refsel\n"); tune_node = of_parse_phandle(dev->of_node, "ss_tune_info", 0); if (tune_node == NULL) dev_info(dev, "don't need usbphy tuning value for super speed\n"); if (of_device_is_available(tune_node)) { ret = exynos_usbdrd_fill_sstune(phy_drd, tune_node); if (ret < 0) { dev_err(dev, "can't fill super speed tuning value\n"); return -EINVAL; } } tune_node = of_parse_phandle(dev->of_node, "hs_tune_info", 0); if (tune_node == NULL) dev_info(dev, "don't need usbphy tuning value for high speed\n"); if (of_device_is_available(tune_node)) { ret = exynos_usbdrd_fill_hstune(phy_drd, tune_node); if (ret < 0) { dev_err(dev, "can't fill high speed tuning value\n"); return -EINVAL; } } tune_node = of_parse_phandle(dev->of_node, "hs_tune_param", 0); if (tune_node != NULL) { ret = exynos_usbdrd_fill_hstune_param(phy_drd, tune_node); if (ret < 0) { dev_err(dev, "can't fill high speed tuning param\n"); return -EINVAL; } } else dev_info(dev, "don't need usbphy tuning param for high speed\n"); dev_info(phy_drd->dev, "usbphy info: version:0x%x, refclk:0x%x\n", phy_drd->usbphy_info.version, phy_drd->usbphy_info.refclk); return 0; } static int exynos_usbdrd_get_iptype(struct exynos_usbdrd_phy *phy_drd) { struct device *dev = phy_drd->dev; int ret, value; ret = of_property_read_u32(dev->of_node, "ip_type", &value); if (ret) { dev_err(dev, "can't get ip type"); return ret; } switch (value) { case TYPE_USB3DRD: phy_drd->ip_type = TYPE_USB3DRD; dev_info(dev, "It is TYPE USB3DRD"); break; case TYPE_USB3HOST: phy_drd->ip_type = TYPE_USB3HOST; dev_info(dev, "It is TYPE USB3HOST"); break; case TYPE_USB2DRD: phy_drd->ip_type = TYPE_USB2DRD; dev_info(dev, "It is TYPE USB2DRD"); break; case TYPE_USB2HOST: phy_drd->ip_type = TYPE_USB2HOST; dev_info(dev, "It is TYPE USB2HOST"); default: break; } return 0; } static void exynos_usbdrd_pipe3_exit(struct exynos_usbdrd_phy *phy_drd) { /* pipe3 phy diable is exucuted in utmi_exit. * Later divide the exit of main and sub phy if necessary */ return; } static void exynos_usbdrd_utmi_exit(struct exynos_usbdrd_phy *phy_drd) { if (phy_drd->use_phy_umux) { /*USB User MUX disable */ exynos_usbdrd_clk_disable(phy_drd, true); } phy_exynos_usb_v3p1_disable(&phy_drd->usbphy_info); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 phy_exynos_usbdp_g2_v4_disable(&phy_drd->usbphy_sub_info); #endif exynos_usbdrd_clk_disable(phy_drd, false); exynos_usbdrd_utmi_phy_isol(&phy_drd->phys[0], 1, phy_drd->phys[0].pmu_mask); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 exynos_usbdrd_pipe3_phy_isol(&phy_drd->phys[1], 1, phy_drd->phys[1].pmu_mask); #endif } static int exynos_usbdrd_phy_exit(struct phy *phy) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); /* UTMI or PIPE3 specific exit */ inst->phy_cfg->phy_exit(phy_drd); return 0; } #if IS_ENABLED(CONFIG_CHECK_CTYPE_SIDE) extern int usbpd_manager_get_side_check(void); #endif static void exynos_usbdrd_pipe3_init(struct exynos_usbdrd_phy *phy_drd) { int value; #if IS_ENABLED(CONFIG_CHECK_CTYPE_SIDE) /* USE external function to check typec side */ value = usbpd_manager_get_side_check(); if (value == 1) phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = 0; else if (value == 2) phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = 1; else { dev_err(phy_drd->dev, "Unexpected USB Typec side\n"); phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = 0; } dev_info(phy_drd->dev, "%s: phy port[%d]\n", __func__, phy_drd->usbphy_info.used_phy_port); #else if (gpio_is_valid(phy_drd->phy_port)) { if (phy_drd->reverse_phy_port) value = !gpio_get_value(phy_drd->phy_port); else value = gpio_get_value(phy_drd->phy_port); phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = value; dev_info(phy_drd->dev, "%s: phy port[%d]\n", __func__, phy_drd->usbphy_info.used_phy_port); } else { dev_info(phy_drd->dev, "%s: phy port fail retry\n", __func__); phy_drd->phy_port = of_get_named_gpio(phy_drd->dev->of_node, "phy,gpio_phy_port", 0); if (gpio_is_valid(phy_drd->phy_port)) { dev_err(phy_drd->dev, "PHY CON Selection OK\n"); if (gpio_request(phy_drd->phy_port, "PHY_CON")) dev_err(phy_drd->dev, "fail to request gpio %s\n", "PHY_CON"); else gpio_direction_input(phy_drd->phy_port); if (phy_drd->reverse_phy_port) value = !gpio_get_value(phy_drd->phy_port); else value = gpio_get_value(phy_drd->phy_port); phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = value; dev_info(phy_drd->dev, "%s: phy port1[%d]\n", __func__, phy_drd->usbphy_info.used_phy_port); } else { dev_err(phy_drd->dev, "non-DT: PHY CON Selection\n"); } } #endif /* Fill USBDP Combo phy init */ phy_exynos_usb_v3p1_g2_pma_ready(&phy_drd->usbphy_info); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 phy_exynos_usbdp_g2_v4_enable(&phy_drd->usbphy_sub_info); #endif } static void exynos_usbdrd_utmi_init(struct exynos_usbdrd_phy *phy_drd) { int ret; #if IS_ENABLED(CONFIG_EXYNOS_OTP) struct tune_bits *otp_data; u8 otp_type; u8 otp_index; u8 i; #endif pr_info("%s: +++\n", __func__); exynos_usbdrd_utmi_phy_isol(&phy_drd->phys[0], 0, phy_drd->phys[0].pmu_mask); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 exynos_usbdrd_pipe3_phy_isol(&phy_drd->phys[1], 0, phy_drd->phys[1].pmu_mask); #endif ret = exynos_usbdrd_clk_enable(phy_drd, false); if (ret) { dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__); return; } phy_exynos_usb_v3p1_enable(&phy_drd->usbphy_info); #if defined(CONFIG_OTG_CDP_SUPPORT) if (phy_drd->cdp_check == 1) { /* We check CDP in only Host mode */ ret = phy_exynos_usb3p1_bc_operate_cdp(&phy_drd->usbphy_info); if (ret) pr_info("%s : This device supports bc1.2\n", __func__); } #endif phy_exynos_usb_v3p1_pipe_ovrd(&phy_drd->usbphy_info); phy_exynos_usb3p1_set_fsv_out_en(&phy_drd->usbphy_info, 0); if (phy_drd->use_phy_umux) { /* USB User MUX enable */ ret = exynos_usbdrd_clk_enable(phy_drd, true); if (ret) { dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__); return; } } #if IS_ENABLED(CONFIG_EXYNOS_OTP) if (phy_drd->ip_type < TYPE_USB2DRD) { otp_type = phy_drd->otp_type[OTP_USB3PHY_INDEX]; otp_index = phy_drd->otp_index[OTP_USB3PHY_INDEX]; otp_data = phy_drd->otp_data[OTP_USB3PHY_INDEX]; } else { otp_type = phy_drd->otp_type[OTP_USB2PHY_INDEX]; otp_index = phy_drd->otp_index[OTP_USB2PHY_INDEX]; otp_data = phy_drd->otp_data[OTP_USB2PHY_INDEX]; } for (i = 0; i < otp_index; i++) { samsung_exynos_cal_usb3phy_write_register( &phy_drd->usbphy_info, otp_data[i].index * otp_type, otp_data[i].value); } #endif pr_info("%s: ---\n", __func__); } static int exynos_usbdrd_phy_init(struct phy *phy) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); /* UTMI or PIPE3 specific init */ inst->phy_cfg->phy_init(phy_drd); return 0; } static void exynos_usbdrd_utmi_ilbk(struct exynos_usbdrd_phy *phy_drd) { dev_info(phy_drd->dev, "%s\n", __func__); } static void exynos_usbdrd_pipe3_ilbk(struct exynos_usbdrd_phy *phy_drd) { dev_info(phy_drd->dev, "%s\n", __func__); //phy_exynos_usbdp_ilbk(&phy_drd->usbphy_sub_info); } static int exynos_usbdrd_pipe3_vendor_set(struct exynos_usbdrd_phy *phy_drd, int is_enable, int is_cancel) { if (is_cancel == 0) { dev_info(phy_drd->dev, "%s - SS ReWA Enable\n",__func__); phy_exynos_usb3p1_u3_rewa_enable(&phy_drd->usbphy_info, 0); enable_irq(phy_drd->usb3_irq_wakeup); } else { dev_info(phy_drd->dev, "%s - SS ReWA Disable\n",__func__); disable_irq_nosync(phy_drd->usb3_irq_wakeup); phy_exynos_usb3p1_u3_rewa_disable(&phy_drd->usbphy_info); } return 0; } static int exynos_usbdrd_utmi_vendor_set(struct exynos_usbdrd_phy *phy_drd, int is_enable, int is_cancel) { int ret = 0; dev_info(phy_drd->dev, "rewa irq : %d, enable: %d, cancel: %d\n", phy_drd->is_irq_enabled, is_enable, is_cancel); if (is_cancel) { if (is_enable) { if (phy_drd->is_irq_enabled == 1) { dev_info(phy_drd->dev, "[%s] REWA CANCEL\n", __func__); phy_exynos_usb3p1_rewa_cancel(&phy_drd->usbphy_info); } else { dev_info(phy_drd->dev, "Vendor set by interrupt, Do not REWA cancel\n"); } } } else { if (is_enable) { ret = phy_exynos_usb3p1_rewa_enable(&phy_drd->usbphy_info); if (ret) { dev_err(phy_drd->dev, "REWA ENABLE FAIL, ret : %d \n", ret); return ret; } /* inform what USB state is idle to IDLE_IP */ //exynos_update_ip_idle_status(phy_drd->idle_ip_idx, 1); dev_info(phy_drd->dev, "REWA ENABLE Complete\n"); if (phy_drd->is_irq_enabled == 0) { enable_irq(phy_drd->irq_wakeup); enable_irq(phy_drd->irq_conn); phy_drd->is_irq_enabled = 1; } else { dev_info(phy_drd->dev, "rewa irq already enabled\n"); } } else { dev_info(phy_drd->dev, "REWA Disconn & Wakeup IRQ DISABLE\n"); /* inform what USB state is not idle to IDLE_IP */ //exynos_update_ip_idle_status(phy_drd->idle_ip_idx, 0); ret = phy_exynos_usb3p1_rewa_disable(&phy_drd->usbphy_info); if (ret) { dev_err(phy_drd->dev, "REWA DISABLE FAIL, ret : %d \n", ret); return ret; } disable_irq_nosync(phy_drd->irq_wakeup); disable_irq_nosync(phy_drd->irq_conn); phy_drd->is_irq_enabled = 0; dev_info(phy_drd->dev, "REWA DISABLE Complete\n"); } } return ret; } static void exynos_usbdrd_pipe3_tune(struct exynos_usbdrd_phy *phy_drd, int phy_state) { struct exynos_usb_tune_param *ss_tune_param = phy_drd->usbphy_sub_info.tune_param; int i; dev_info(phy_drd->dev, "%s\n", __func__); if (phy_state >= OTG_STATE_A_IDLE) { /* for host mode */ for (i = 0; ss_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) { if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM) break; ss_tune_param[i].value = phy_drd->ss_tune_param_value[i][USBPHY_MODE_HOST]; } } else { /* for device mode */ for (i = 0; ss_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) { if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM) break; ss_tune_param[i].value = phy_drd->ss_tune_param_value[i][USBPHY_MODE_DEV]; } } #ifdef CONFIG_EXYNOS_USBDRD_PHY30 phy_exynos_usbdp_g2_v4_tune(&phy_drd->usbphy_sub_info); #endif } static void exynos_usbdrd_utmi_tune(struct exynos_usbdrd_phy *phy_drd, int phy_state) { struct exynos_usb_tune_param *hs_tune_param = phy_drd->usbphy_info.tune_param; int i; dev_info(phy_drd->dev, "%s phy_state %d\n", __func__, phy_state); if (phy_state >= OTG_STATE_A_IDLE) { /* for host mode */ for (i = 0; hs_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) { if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM) break; hs_tune_param[i].value = phy_drd->hs_tune_param_value[i][USBPHY_MODE_HOST]; } } else { /* for device mode */ for (i = 0; hs_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) { if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM) break; hs_tune_param[i].value = phy_drd->hs_tune_param_value[i][USBPHY_MODE_DEV]; } } phy_exynos_usb_v3p1_tune(&phy_drd->usbphy_info); } int exynos_usbdrd_phy_tune(struct phy *phy, int phy_state) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); inst->phy_cfg->phy_tune(phy_drd, phy_state); return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_phy_tune); void exynos_usbdrd_ldo_control(struct exynos_usbdrd_phy *phy_drd, int on) { int ret1, ret2, ret3; if (phy_drd->vdd075_usb == NULL || phy_drd->vdd18_usb == NULL || phy_drd->vdd33_usb == NULL) { dev_err(phy_drd->dev, "%s: not define regulator\n", __func__); goto out; } dev_info(phy_drd->dev, "Turn %s LDO\n", on ? "on" : "off"); if (on) { if (phy_drd->is_ldo_on) { dev_info(phy_drd->dev, "LDO already on! return\n"); goto out; } ret1 = regulator_enable(phy_drd->vdd075_usb); ret2 = regulator_enable(phy_drd->vdd18_usb); ret3 = regulator_enable(phy_drd->vdd33_usb); if (ret1 || ret2 || ret3) { dev_err(phy_drd->dev, "Failed to enable USB LDOs: %d %d %d\n", ret1, ret2, ret3); } phy_drd->is_ldo_on = 1; } else { if (!phy_drd->is_ldo_on) { dev_info(phy_drd->dev, "LDO already off! return\n"); goto out; } ret1 = regulator_disable(phy_drd->vdd075_usb); ret2 = regulator_disable(phy_drd->vdd18_usb); ret3 = regulator_disable(phy_drd->vdd33_usb); if (ret1 || ret2 || ret3) { dev_err(phy_drd->dev, "Failed to disable USB LDOs: %d %d %d\n", ret1, ret2, ret3); } phy_drd->is_ldo_on = 0; } out: return; } /* * USB LDO control was moved to phy_conn API from OTG * without adding one more phy interface */ void exynos_usbdrd_phy_conn(struct phy *phy, int is_conn) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); if (is_conn) { if (phy_drd->is_conn == 0) { dev_info(phy_drd->dev, "USB PHY isolation clear(ON)\n"); exynos_usbdrd_utmi_phy_isol(inst, 0, inst->pmu_mask); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 exynos_usbdrd_pipe3_phy_isol(inst, 0, inst->pmu_mask); #endif dev_info(phy_drd->dev, "USB PHY Conn Set\n"); //exynos_usbdrd_ldo_control(phy_drd, 1); phy_drd->is_conn = 1; } else dev_info(phy_drd->dev, "USB PHY Conn already Setted!!\n"); } else { if (phy_drd->is_conn == 1) { dev_info(phy_drd->dev, "USB PHY Conn Clear\n"); //exynos_usbdrd_ldo_control(phy_drd, 0); phy_drd->is_conn = 0; dev_info(phy_drd->dev, "USB PHY isolation set(OFF)\n"); exynos_usbdrd_utmi_phy_isol(inst, 1, inst->pmu_mask); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 exynos_usbdrd_pipe3_phy_isol(inst, 1, inst->pmu_mask); #endif } else dev_info(phy_drd->dev, "USB PHY Conn already cleared!!\n"); } return; } EXPORT_SYMBOL_GPL(exynos_usbdrd_phy_conn); void exynos_usbdrd_phy_vol_set(struct phy *phy, int voltage) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); regulator_set_voltage(phy_drd->vdd075_usb, voltage, voltage); dev_info(phy_drd->dev, "USB 0.85 PHY: %dmV\n", voltage); return; } EXPORT_SYMBOL_GPL(exynos_usbdrd_phy_vol_set); int exynos_usbdrd_dp_ilbk(struct phy *phy) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); inst->phy_cfg->phy_ilbk(phy_drd); return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_dp_ilbk); int exynos_usbdrd_phy_vendor_set(struct phy *phy, int is_enable, int is_cancel) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); int ret; ret = inst->phy_cfg->phy_vendor_set(phy_drd, is_enable, is_cancel); return ret; } EXPORT_SYMBOL_GPL(exynos_usbdrd_phy_vendor_set); static void exynos_usbdrd_pipe3_set(struct exynos_usbdrd_phy *phy_drd, int option, void *info) { /* Fill USBDP Combo phy set */ return; } static void exynos_usbdrd_utmi_set(struct exynos_usbdrd_phy *phy_drd, int option, void *info) { switch (option) { case SET_DPPULLUP_ENABLE: phy_exynos_usb_v3p1_enable_dp_pullup( &phy_drd->usbphy_info); break; case SET_DPPULLUP_DISABLE: phy_exynos_usb_v3p1_disable_dp_pullup( &phy_drd->usbphy_info); break; case SET_DPDM_PULLDOWN: phy_exynos_usb_v3p1_config_host_mode( &phy_drd->usbphy_info); default: break; } } int exynos_usbdrd_phy_link_rst(struct phy *phy) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); pr_info("%s\n", __func__); phy_exynos_usb_v3p1_link_sw_reset(&phy_drd->usbphy_info); return 0; } int exynos_usbdrd_phy_set(struct phy *phy, int option, void *info) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); inst->phy_cfg->phy_set(phy_drd, option, info); return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_phy_set); #if defined(CONFIG_OTG_CDP_SUPPORT) int exynos_usbdrd_cdp_set(struct phy *phy, int val) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); dev_dbg(phy_drd->dev, "Set CDP check flag to %d\n", val); phy_drd->cdp_check = val; return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_cdp_set); #endif static int exynos_usbdrd_phy_power_on(struct phy *phy) { int ret = 0; #ifdef SKIP_DWC3_CORE_POWER_CONTROL struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); /* Enable VBUS supply */ if (phy_drd->vbus) { ret = regulator_enable(phy_drd->vbus); if (ret) { dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); return ret; } } inst->phy_cfg->phy_isol(inst, 0, inst->pmu_mask); #endif return ret; } static struct device_node *exynos_usbdrd_parse_dt(void) { struct device_node *np = NULL; np = of_find_compatible_node(NULL, NULL, "samsung,exynos-usbdrd-phy"); if (!np) { pr_err("%s: failed to get the usbdrd phy device node\n", __func__); goto err; } return np; err: return NULL; } static struct exynos_usbdrd_phy *exynos_usbdrd_get_struct(void) { struct device_node *np = NULL; struct platform_device *pdev = NULL; struct device *dev; struct exynos_usbdrd_phy *phy_drd; np = exynos_usbdrd_parse_dt(); if (np) { pdev = of_find_device_by_node(np); dev = &pdev->dev; of_node_put(np); if (pdev) { pr_info("%s: get the %s platform_device\n", __func__, pdev->name); phy_drd = dev->driver_data; return phy_drd; } } pr_err("%s: failed to get the platform_device\n", __func__); return NULL; } /* static int exynos_usbdrd_get_idle_ip(void) { struct device_node *np = NULL; struct platform_device *pdev = NULL; struct device *dev; int idle_ip_idx; np = of_find_compatible_node(NULL, NULL, "samsung,exynos-dwusb"); if (np) { pdev = of_find_device_by_node(np); dev = &pdev->dev; of_node_put(np); if (pdev) { pr_info("%s: get the %s platform_device\n", __func__, pdev->name); idle_ip_idx = 1;//idle_ip_idx = exynos_get_idle_ip_index(dev_name(dev), 0); pr_info("%s, idle ip = %d\n", __func__, idle_ip_idx); return idle_ip_idx; } } pr_err("%s: failed to get the platform_device\n", __func__); return -1; } */ static int exynos_usbdrd_phy_power_off(struct phy *phy) { #ifdef SKIP_DWC3_CORE_POWER_CONTROL struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); dev_info(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); inst->phy_cfg->phy_isol(inst, 1, inst->pmu_mask); /* Disable VBUS supply */ if (phy_drd->vbus) regulator_disable(phy_drd->vbus); #endif return 0; } int exynos_usbdrd_ldo_manual_control(bool on) { struct exynos_usbdrd_phy *phy_drd; pr_info("%s ldo = %d\n", __func__, on); phy_drd = exynos_usbdrd_get_struct(); if (!phy_drd) { pr_err("[%s] exynos_usbdrd_get_struct error\n", __func__); return -ENODEV; } exynos_usbdrd_ldo_control(phy_drd, on); return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_ldo_manual_control); int exynos_usbdrd_ldo_external_control(bool on) { struct exynos_usbdrd_phy *phy_drd; phy_drd = exynos_usbdrd_get_struct(); if (!phy_drd) return -ENODEV; pr_info("%s ldo = %d\n", __func__, on); exynos_usbdrd_ldo_control(phy_drd, on); return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_ldo_external_control); int exynos_usbdrd_pipe3_enable(struct phy *phy) { struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); phy_exynos_usb_v3p1_g2_pma_ready(&phy_drd->usbphy_info); #ifdef CONFIG_EXYNOS_USBDRD_PHY30 phy_exynos_usbdp_g2_v4_enable(&phy_drd->usbphy_sub_info); #endif return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_pipe3_enable); int exynos_usbdrd_pipe3_disable(struct phy *phy) { #ifdef CONFIG_EXYNOS_USBDRD_PHY30 struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); #endif /* phy_exynos_usb_v3p1_pipe_ovrd(&phy_drd->usbphy_info); */ #ifdef CONFIG_EXYNOS_USBDRD_PHY30 phy_exynos_usbdp_g2_v4_disable(&phy_drd->usbphy_sub_info); #endif return 0; } EXPORT_SYMBOL_GPL(exynos_usbdrd_pipe3_disable); void exynos_usbdrd_shutdown_notice(int shutdown) { #ifdef CONFIG_EXYNOS_USBDRD_PHY30 struct exynos_usbdrd_phy *phy_drd; pr_info("%s\n", __func__); phy_drd = exynos_usbdrd_get_struct(); if (!phy_drd) pr_err("[%s] exynos_usbdrd_get_struct error\n", __func__); phy_drd->in_shutdown = shutdown; #endif } EXPORT_SYMBOL_GPL(exynos_usbdrd_shutdown_notice); static struct phy *exynos_usbdrd_phy_xlate(struct device *dev, struct of_phandle_args *args) { struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); if (WARN_ON(args->args[0] > EXYNOS_DRDPHYS_NUM)) return ERR_PTR(-ENODEV); return phy_drd->phys[args->args[0]].phy; } static irqreturn_t exynos_usbdrd_usb3_phy_wakeup_interrupt(int irq, void *_phydrd) { struct exynos_usbdrd_phy *phy_drd = (struct exynos_usbdrd_phy *)_phydrd; phy_exynos_usb3p1_u3_rewa_disable(&phy_drd->usbphy_info); dev_info(phy_drd->dev, "[%s] USB3 ReWA disabled...\n", __func__); return IRQ_HANDLED; } static irqreturn_t exynos_usbdrd_phy_wakeup_interrupt(int irq, void *_phydrd) { struct exynos_usbdrd_phy *phy_drd = (struct exynos_usbdrd_phy *)_phydrd; int ret; ret = phy_exynos_usb3p1_rewa_req_sys_valid(&phy_drd->usbphy_info); dev_info(phy_drd->dev, "[%s] rewa sys vaild set : %s \n", __func__, (ret == 1) ? "Disable" : "Disconnect"); return IRQ_HANDLED; } static irqreturn_t exynos_usbdrd_phy_conn_interrupt(int irq, void *_phydrd) { struct exynos_usbdrd_phy *phy_drd = (struct exynos_usbdrd_phy *)_phydrd; int ret; ret = phy_exynos_usb3p1_rewa_req_sys_valid(&phy_drd->usbphy_info); dev_info(phy_drd->dev, "[%s] rewa sys vaild set : %s \n", __func__, (ret == 1) ? "Disable" : "Disconnect"); return IRQ_HANDLED; } static 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, .reset = exynos_usbdrd_phy_link_rst, .owner = THIS_MODULE, }; static const struct exynos_usbdrd_phy_config phy_cfg_exynos[] = { { .id = EXYNOS_DRDPHY_UTMI, .phy_isol = exynos_usbdrd_utmi_phy_isol, .phy_init = exynos_usbdrd_utmi_init, .phy_exit = exynos_usbdrd_utmi_exit, .phy_tune = exynos_usbdrd_utmi_tune, .phy_vendor_set = exynos_usbdrd_utmi_vendor_set, .phy_ilbk = exynos_usbdrd_utmi_ilbk, .phy_set = exynos_usbdrd_utmi_set, .set_refclk = exynos_usbdrd_utmi_set_refclk, }, { .id = EXYNOS_DRDPHY_PIPE3, .phy_isol = exynos_usbdrd_pipe3_phy_isol, .phy_init = exynos_usbdrd_pipe3_init, .phy_exit = exynos_usbdrd_pipe3_exit, .phy_tune = exynos_usbdrd_pipe3_tune, .phy_vendor_set = exynos_usbdrd_pipe3_vendor_set, .phy_ilbk = exynos_usbdrd_pipe3_ilbk, .phy_set = exynos_usbdrd_pipe3_set, .set_refclk = exynos_usbdrd_pipe3_set_refclk, }, }; static const struct exynos_usbdrd_phy_drvdata exynos_usbdrd_phy = { .phy_cfg = phy_cfg_exynos, }; static const struct of_device_id exynos_usbdrd_phy_of_match[] = { { .compatible = "samsung,exynos-usbdrd-phy", .data = &exynos_usbdrd_phy }, { }, }; MODULE_DEVICE_TABLE(of, exynos_usbdrd_phy_of_match); void __iomem *phy_exynos_usbdp_get_address(void) { return usbdp_combo_phy_reg; } static int exynos_usbdrd_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct exynos_usbdrd_phy *phy_drd; struct phy_provider *phy_provider; struct resource *res; const struct of_device_id *match; const struct exynos_usbdrd_phy_drvdata *drv_data; struct regmap *reg_pmu; u32 pmu_offset, pmu_offset_dp, pmu_offset_tcxo, pmu_mask, pmu_mask_tcxo; int i, ret; pr_info("%s: +++ %s %s\n", __func__, dev->init_name, pdev->name); phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); if (!phy_drd) return -ENOMEM; dev_set_drvdata(dev, phy_drd); phy_drd->dev = dev; match = of_match_node(exynos_usbdrd_phy_of_match, pdev->dev.of_node); drv_data = match->data; phy_drd->drv_data = drv_data; phy_drd->irq_wakeup = platform_get_irq(pdev, 0); irq_set_status_flags(phy_drd->irq_wakeup, IRQ_NOAUTOEN); ret = devm_request_irq(dev, phy_drd->irq_wakeup, exynos_usbdrd_phy_wakeup_interrupt, 0, "phydrd-wakeup", phy_drd); if (ret) { dev_err(dev, "failed to request irq #%d --> %d\n", phy_drd->irq_wakeup, ret); return ret; } irq_set_irq_wake(phy_drd->irq_wakeup, 1); phy_drd->irq_conn = platform_get_irq(pdev, 1); irq_set_status_flags(phy_drd->irq_conn, IRQ_NOAUTOEN); ret = devm_request_irq(dev, phy_drd->irq_conn, exynos_usbdrd_phy_conn_interrupt, 0, "usb2-phydrd-conn", phy_drd); if (ret) { dev_err(dev, "failed to request irq #%d --> %d\n", phy_drd->irq_conn, ret); return ret; } irq_set_irq_wake(phy_drd->irq_conn, 1); phy_drd->usb3_irq_wakeup = platform_get_irq(pdev, 2); irq_set_status_flags(phy_drd->usb3_irq_wakeup, IRQ_NOAUTOEN); ret = devm_request_irq(dev, phy_drd->usb3_irq_wakeup, exynos_usbdrd_usb3_phy_wakeup_interrupt, 0, "usb3-phydrd-wakeup", phy_drd); if (ret) { dev_err(dev, "failed to request irq #%d --> %d (For SS ReWA)\n", phy_drd->usb3_irq_wakeup, ret); /* Don't return probe failure for compatibility */ dev_err(dev, "Don't return probe failure for compatibility.\n"); } else { irq_set_irq_wake(phy_drd->usb3_irq_wakeup, 1); } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); phy_drd->reg_phy = devm_ioremap_resource(dev, res); if (IS_ERR(phy_drd->reg_phy)) return PTR_ERR(phy_drd->reg_phy); phycon_base_addr = phy_drd->reg_phy; /* Both has_other_phy and has_combo_phy can't be enabled at the same time. It's alternative. */ if (!of_property_read_u32(dev->of_node, "has_other_phy", &ret)) { if (ret) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); phy_drd->reg_phy2 = devm_ioremap_resource(dev, res); if (IS_ERR(phy_drd->reg_phy2)) return PTR_ERR(phy_drd->reg_phy2); } else { dev_err(dev, "It has not the other phy\n"); } } ret = exynos_usbdrd_get_iptype(phy_drd); if (ret) { dev_err(dev, "%s: Failed to get ip_type\n", __func__); return ret; } ret = exynos_usbdrd_clk_get(phy_drd); if (ret) { dev_err(dev, "%s: Failed to get clocks\n", __func__); goto skip_clock; } ret = exynos_usbdrd_clk_prepare(phy_drd); if (ret) { dev_err(dev, "%s: Failed to prepare clocks\n", __func__); return ret; } ret = exynos_rate_to_clk(phy_drd); if (ret) { dev_err(phy_drd->dev, "%s: Not supported ref clock\n", __func__); goto err1; } skip_clock: reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, "samsung,pmu-syscon"); if (IS_ERR(reg_pmu)) { dev_err(dev, "Failed to lookup PMU regmap\n"); goto err1; } ret = of_property_read_u32(dev->of_node, "pmu_offset", &pmu_offset); if (ret < 0) { dev_err(dev, "couldn't read pmu_offset on %s node, error = %d\n", dev->of_node->name, ret); goto err1; } ret = of_property_read_u32(dev->of_node, "pmu_offset_dp", &pmu_offset_dp); if (ret < 0) { dev_err(dev, "couldn't read pmu_offset_dp on %s node, error = %d\n", dev->of_node->name, ret); goto err1; } ret = of_property_read_u32(dev->of_node, "pmu_mask", &pmu_mask); if (ret < 0) { dev_err(dev, "couldn't read pmu_mask on %s node, error = %d\n", dev->of_node->name, ret); goto err1; } ret = of_property_read_u32(dev->of_node, "pmu_offset_tcxobuf", &pmu_offset_tcxo); if (ret < 0) { dev_err(dev, "couldn't read pmu_offset_tcxo on %s node, error = %d\n", dev->of_node->name, ret); } ret = of_property_read_u32(dev->of_node, "pmu_mask_tcxobuf", &pmu_mask_tcxo); if (ret < 0) { dev_err(dev, "couldn't read pmu_mask_tcxo on %s node, error = %d\n", dev->of_node->name, ret); pmu_mask_tcxo = 0; } else pmu_mask_tcxo = (u32)BIT(pmu_mask_tcxo); pmu_mask = (u32)BIT(pmu_mask); dev_vdbg(dev, "Creating usbdrd_phy phy\n"); phy_drd->phy_port = of_get_named_gpio(dev->of_node, "phy,gpio_phy_port", 0); if (gpio_is_valid(phy_drd->phy_port)) { dev_err(dev, "PHY CON Selection OK\n"); ret = gpio_request(phy_drd->phy_port, "PHY_CON"); if (ret) dev_err(dev, "fail to request gpio %s:%d\n", "PHY_CON", ret); else gpio_direction_input(phy_drd->phy_port); } else dev_err(dev, "non-DT: PHY CON Selection\n"); ret = of_property_read_u32(dev->of_node, "reverse_con_dir", &phy_drd->reverse_phy_port); dev_info(dev, "reverse_con_dir = %d\n", phy_drd->reverse_phy_port); if (ret < 0) { phy_drd->reverse_phy_port = 0; } ret = exynos_usbdrd_get_phyinfo(phy_drd); if (ret) goto err1; if (!of_property_read_u32(dev->of_node, "use_default_tune_val", &ret)) { if (ret) { dev_info(dev, "Use default tune value for SS/SSP\n"); phy_drd->use_default_tune_val = 1; } else { phy_drd->use_default_tune_val = 0; } } if (!of_property_read_u32(dev->of_node, "has_combo_phy", &ret)) { if (ret) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); phy_drd->reg_phy2 = devm_ioremap_resource(dev, res); if (IS_ERR(phy_drd->reg_phy2)) return PTR_ERR(phy_drd->reg_phy2); res = platform_get_resource(pdev, IORESOURCE_MEM, 2); phy_drd->reg_phy3 = devm_ioremap_resource(dev, res); if (IS_ERR(phy_drd->reg_phy3)) return PTR_ERR(phy_drd->reg_phy3); res = platform_get_resource(pdev, IORESOURCE_MEM, 3); /* In case of phy driver, we use ioremap() function * because same address will be used at USB driver. */ phy_drd->reg_link = ioremap(res->start, resource_size(res)); if (IS_ERR(phy_drd->reg_link)) return PTR_ERR(phy_drd->reg_link); phy_drd->usbphy_sub_info.ctrl_base = phy_drd->reg_phy; phy_drd->usbphy_sub_info.pma_base = phy_drd->reg_phy2; phy_drd->usbphy_sub_info.pcs_base = phy_drd->reg_phy3; phy_drd->usbphy_sub_info.link_base = phy_drd->reg_link; exynos_usbdrd_get_sub_phyinfo(phy_drd); } else { dev_err(dev, "It has not combo phy\n"); } } #if IS_ENABLED(CONFIG_EXYNOS_OTP) exynos_usbdrd_phy_get_otp_info(phy_drd); #endif for (i = 0; i < EXYNOS_DRDPHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, &exynos_usbdrd_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); goto err1; } phy_drd->phys[i].phy = phy; phy_drd->phys[i].index = i; phy_drd->phys[i].reg_pmu = reg_pmu; phy_drd->phys[i].pmu_offset = pmu_offset; phy_drd->phys[i].pmu_offset_dp = pmu_offset_dp; phy_drd->phys[i].pmu_mask = pmu_mask; phy_drd->phys[i].pmu_offset_tcxobuf = pmu_offset_tcxo; phy_drd->phys[i].pmu_mask_tcxobuf = pmu_mask_tcxo; phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; phy_set_drvdata(phy, &phy_drd->phys[i]); } #if IS_ENABLED(CONFIG_PHY_EXYNOS_DEBUGFS) ret = exynos_usbdrd_debugfs_init(phy_drd); if (ret) { dev_err(dev, "Failed to initialize debugfs\n"); goto err1; } #endif #if IS_ENABLED(CONFIG_PHY_EXYNOS_DP_DEBUGFS) ret = exynos_usbdrd_dp_debugfs_init(phy_drd); if (ret) { dev_err(dev, "Failed to initialize dp debugfs\n"); goto err1; } #endif /* *phy_drd->idle_ip_idx = exynos_usbdrd_get_idle_ip(); *if (phy_drd->idle_ip_idx < 0) * dev_err(dev, "Failed to get idle ip index\n"); */ phy_provider = devm_of_phy_provider_register(dev, exynos_usbdrd_phy_xlate); if (IS_ERR(phy_provider)) { goto err1; } spin_lock_init(&phy_drd->lock); dev_info(dev, "Get USB LDO!\n"); phy_drd->vdd075_usb = regulator_get(dev, "vdd075_usb"); if (IS_ERR(phy_drd->vdd075_usb) || phy_drd->vdd075_usb == NULL) { dev_err(dev, "%s - vdd075_usb regulator_get fail %p %d\n", __func__, phy_drd->vdd075_usb, IS_ERR(phy_drd->vdd075_usb)); } phy_drd->vdd18_usb = regulator_get(dev, "vdd18_usb"); if (IS_ERR(phy_drd->vdd18_usb) || phy_drd->vdd18_usb == NULL) { dev_err(dev, "%s - vdd18_usb regulator_get fail %p %d\n", __func__, phy_drd->vdd18_usb, IS_ERR(phy_drd->vdd18_usb)); } for (i = 0; i < 5; i++) { phy_drd->vdd33_usb = regulator_get(dev, "vdd33_usb"); if (IS_ERR(phy_drd->vdd33_usb) || phy_drd->vdd33_usb == NULL) { dev_err(dev, "%s - vdd33_usb regulator_get fail %p %d\n", __func__, phy_drd->vdd33_usb, IS_ERR(phy_drd->vdd33_usb)); mdelay(100); } else break; } phy_drd->is_irq_enabled = 0; pm_runtime_enable(dev); ret = sysfs_create_file(&dev->kobj, &dev_attr_phy_tune.attr); if (ret) { dev_err(dev, "%s - Couldn't create sysfs for PHY tune\n", __func__); } ret = sysfs_create_file(&dev->kobj, &dev_attr_hs_phy_tune.attr); if (ret) { dev_err(dev, "%s - Couldn't create sysfs for HS PHY tune\n", __func__); } #ifdef CONFIG_EXYNOS_USBDRD_PHY30 ret = sysfs_create_file(&dev->kobj, &dev_attr_eom.attr); if (ret) { dev_err(dev, "%s - Couldn't create sysfs for PHY EOM\n", __func__); } #endif pr_info("%s: ---\n", __func__); return 0; err1: exynos_usbdrd_clk_unprepare(phy_drd); return ret; } #ifdef CONFIG_PM static int exynos_usbdrd_phy_resume(struct device *dev) { struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev); /* * There is issue, when USB3.0 PHY is in active state * after resume. This leads to increased power consumption * if no USB drivers use the PHY. * * The following code shutdowns the PHY, so it is in defined * state (OFF) after resume. If any USB driver already got * the PHY at this time, we do nothing and just exit. */ dev_info(dev, "%s\n", __func__); if ((!phy_drd->is_conn) && (phy_drd->is_irq_enabled == 0)) dev_info(dev, "USB wasn't connected\n"); else dev_info(dev, "USB was connected\n"); return 0; } static const struct dev_pm_ops exynos_usbdrd_phy_dev_pm_ops = { .resume = exynos_usbdrd_phy_resume, }; #define EXYNOS_USBDRD_PHY_PM_OPS &(exynos_usbdrd_phy_dev_pm_ops) #else #define EXYNOS_USBDRD_PHY_PM_OPS NULL #endif static struct platform_driver phy_exynos_usbdrd = { .probe = exynos_usbdrd_phy_probe, .driver = { .of_match_table = exynos_usbdrd_phy_of_match, .name = "phy_exynos_usbdrd", .pm = EXYNOS_USBDRD_PHY_PM_OPS, } }; module_platform_driver(phy_exynos_usbdrd); MODULE_SOFTDEP("post:dwc3-exynos-usb"); MODULE_DESCRIPTION("Samsung EXYNOS SoCs USB DRD controller PHY driver"); MODULE_AUTHOR("Vivek Gautam "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:phy_exynos_usbdrd");