644 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.4 */5 6#include <linux/clk.h>7#include <linux/delay.h>8#include <linux/err.h>9#include <linux/io.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17#include <linux/reset.h>18#include <linux/slab.h>19 20#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c)21#define SLEEPM BIT(0)22#define OPMODE_MASK GENMASK(4, 3)23#define OPMODE_NORMAL (0x00)24#define OPMODE_NONDRIVING BIT(3)25#define TERMSEL BIT(5)26 27#define USB2_PHY_USB_PHY_UTMI_CTRL1 (0x40)28#define XCVRSEL BIT(0)29 30#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50)31#define POR BIT(1)32 33#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)34#define SIDDQ BIT(2)35#define RETENABLEN BIT(3)36#define FSEL_MASK GENMASK(6, 4)37#define FSEL_DEFAULT (0x3 << 4)38 39#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58)40#define VBUSVLDEXTSEL0 BIT(4)41#define PLLBTUNE BIT(5)42 43#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c)44#define VREGBYPASS BIT(0)45 46#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60)47#define VBUSVLDEXT0 BIT(0)48 49#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64)50#define USB2_AUTO_RESUME BIT(0)51#define USB2_SUSPEND_N BIT(2)52#define USB2_SUSPEND_N_SEL BIT(3)53 54#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0 (0x6c)55#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1 (0x70)56#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2 (0x74)57#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3 (0x78)58#define PARAM_OVRD_MASK 0xFF59 60#define USB2_PHY_USB_PHY_CFG0 (0x94)61#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)62#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)63 64#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0)65#define REFCLK_SEL_MASK GENMASK(1, 0)66#define REFCLK_SEL_DEFAULT (0x2 << 0)67 68#define HS_DISCONNECT_MASK GENMASK(2, 0)69#define SQUELCH_DETECTOR_MASK GENMASK(7, 5)70 71#define HS_AMPLITUDE_MASK GENMASK(3, 0)72#define PREEMPHASIS_DURATION_MASK BIT(5)73#define PREEMPHASIS_AMPLITUDE_MASK GENMASK(7, 6)74 75#define HS_RISE_FALL_MASK GENMASK(1, 0)76#define HS_CROSSOVER_VOLTAGE_MASK GENMASK(3, 2)77#define HS_OUTPUT_IMPEDANCE_MASK GENMASK(5, 4)78 79#define LS_FS_OUTPUT_IMPEDANCE_MASK GENMASK(3, 0)80 81static const char * const qcom_snps_hsphy_vreg_names[] = {82 "vdda-pll", "vdda33", "vdda18",83};84 85#define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names)86 87struct override_param {88 s32 value;89 u8 reg_val;90};91 92struct override_param_map {93 const char *prop_name;94 const struct override_param *param_table;95 u8 table_size;96 u8 reg_offset;97 u8 param_mask;98};99 100struct phy_override_seq {101 bool need_update;102 u8 offset;103 u8 value;104 u8 mask;105};106 107#define NUM_HSPHY_TUNING_PARAMS (9)108 109/**110 * struct qcom_snps_hsphy - snps hs phy attributes111 *112 * @dev: device structure113 *114 * @phy: generic phy115 * @base: iomapped memory space for snps hs phy116 *117 * @num_clks: number of clocks118 * @clks: array of clocks119 * @phy_reset: phy reset control120 * @vregs: regulator supplies bulk data121 * @phy_initialized: if PHY has been initialized correctly122 * @mode: contains the current mode the PHY is in123 * @update_seq_cfg: tuning parameters for phy init124 */125struct qcom_snps_hsphy {126 struct device *dev;127 128 struct phy *phy;129 void __iomem *base;130 131 int num_clks;132 struct clk_bulk_data *clks;133 struct reset_control *phy_reset;134 struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];135 136 bool phy_initialized;137 enum phy_mode mode;138 struct phy_override_seq update_seq_cfg[NUM_HSPHY_TUNING_PARAMS];139};140 141static int qcom_snps_hsphy_clk_init(struct qcom_snps_hsphy *hsphy)142{143 struct device *dev = hsphy->dev;144 145 hsphy->num_clks = 2;146 hsphy->clks = devm_kcalloc(dev, hsphy->num_clks, sizeof(*hsphy->clks), GFP_KERNEL);147 if (!hsphy->clks)148 return -ENOMEM;149 150 /*151 * TODO: Currently no device tree instantiation of the PHY is using the clock.152 * This needs to be fixed in order for this code to be able to use devm_clk_bulk_get().153 */154 hsphy->clks[0].id = "cfg_ahb";155 hsphy->clks[0].clk = devm_clk_get_optional(dev, "cfg_ahb");156 if (IS_ERR(hsphy->clks[0].clk))157 return dev_err_probe(dev, PTR_ERR(hsphy->clks[0].clk),158 "failed to get cfg_ahb clk\n");159 160 hsphy->clks[1].id = "ref";161 hsphy->clks[1].clk = devm_clk_get(dev, "ref");162 if (IS_ERR(hsphy->clks[1].clk))163 return dev_err_probe(dev, PTR_ERR(hsphy->clks[1].clk),164 "failed to get ref clk\n");165 166 return 0;167}168 169static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,170 u32 mask, u32 val)171{172 u32 reg;173 174 reg = readl_relaxed(base + offset);175 reg &= ~mask;176 reg |= val & mask;177 writel_relaxed(reg, base + offset);178 179 /* Ensure above write is completed */180 readl_relaxed(base + offset);181}182 183static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy)184{185 dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n");186 187 if (hsphy->mode == PHY_MODE_USB_HOST) {188 /* Enable auto-resume to meet remote wakeup timing */189 qcom_snps_hsphy_write_mask(hsphy->base,190 USB2_PHY_USB_PHY_HS_PHY_CTRL2,191 USB2_AUTO_RESUME,192 USB2_AUTO_RESUME);193 usleep_range(500, 1000);194 qcom_snps_hsphy_write_mask(hsphy->base,195 USB2_PHY_USB_PHY_HS_PHY_CTRL2,196 0, USB2_AUTO_RESUME);197 }198 199 return 0;200}201 202static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy)203{204 dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n");205 206 return 0;207}208 209static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev)210{211 struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);212 213 if (!hsphy->phy_initialized)214 return 0;215 216 return qcom_snps_hsphy_suspend(hsphy);217}218 219static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev)220{221 struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);222 223 if (!hsphy->phy_initialized)224 return 0;225 226 return qcom_snps_hsphy_resume(hsphy);227}228 229static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,230 int submode)231{232 struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);233 234 hsphy->mode = mode;235 return 0;236}237 238static const struct override_param hs_disconnect_sc7280[] = {239 { -272, 0 },240 { 0, 1 },241 { 317, 2 },242 { 630, 3 },243 { 973, 4 },244 { 1332, 5 },245 { 1743, 6 },246 { 2156, 7 },247};248 249static const struct override_param squelch_det_threshold_sc7280[] = {250 { -2090, 7 },251 { -1560, 6 },252 { -1030, 5 },253 { -530, 4 },254 { 0, 3 },255 { 530, 2 },256 { 1060, 1 },257 { 1590, 0 },258};259 260static const struct override_param hs_amplitude_sc7280[] = {261 { -660, 0 },262 { -440, 1 },263 { -220, 2 },264 { 0, 3 },265 { 230, 4 },266 { 440, 5 },267 { 650, 6 },268 { 890, 7 },269 { 1110, 8 },270 { 1330, 9 },271 { 1560, 10 },272 { 1780, 11 },273 { 2000, 12 },274 { 2220, 13 },275 { 2430, 14 },276 { 2670, 15 },277};278 279static const struct override_param preemphasis_duration_sc7280[] = {280 { 10000, 1 },281 { 20000, 0 },282};283 284static const struct override_param preemphasis_amplitude_sc7280[] = {285 { 10000, 1 },286 { 20000, 2 },287 { 30000, 3 },288 { 40000, 0 },289};290 291static const struct override_param hs_rise_fall_time_sc7280[] = {292 { -4100, 3 },293 { 0, 2 },294 { 2810, 1 },295 { 5430, 0 },296};297 298static const struct override_param hs_crossover_voltage_sc7280[] = {299 { -31000, 1 },300 { 0, 3 },301 { 28000, 2 },302};303 304static const struct override_param hs_output_impedance_sc7280[] = {305 { -2300000, 3 },306 { 0, 2 },307 { 2600000, 1 },308 { 6100000, 0 },309};310 311static const struct override_param ls_fs_output_impedance_sc7280[] = {312 { -1053, 15 },313 { -557, 7 },314 { 0, 3 },315 { 612, 1 },316 { 1310, 0 },317};318 319static const struct override_param_map sc7280_snps_7nm_phy[] = {320 {321 "qcom,hs-disconnect-bp",322 hs_disconnect_sc7280,323 ARRAY_SIZE(hs_disconnect_sc7280),324 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,325 HS_DISCONNECT_MASK326 },327 {328 "qcom,squelch-detector-bp",329 squelch_det_threshold_sc7280,330 ARRAY_SIZE(squelch_det_threshold_sc7280),331 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,332 SQUELCH_DETECTOR_MASK333 },334 {335 "qcom,hs-amplitude-bp",336 hs_amplitude_sc7280,337 ARRAY_SIZE(hs_amplitude_sc7280),338 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,339 HS_AMPLITUDE_MASK340 },341 {342 "qcom,pre-emphasis-duration-bp",343 preemphasis_duration_sc7280,344 ARRAY_SIZE(preemphasis_duration_sc7280),345 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,346 PREEMPHASIS_DURATION_MASK,347 },348 {349 "qcom,pre-emphasis-amplitude-bp",350 preemphasis_amplitude_sc7280,351 ARRAY_SIZE(preemphasis_amplitude_sc7280),352 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,353 PREEMPHASIS_AMPLITUDE_MASK,354 },355 {356 "qcom,hs-rise-fall-time-bp",357 hs_rise_fall_time_sc7280,358 ARRAY_SIZE(hs_rise_fall_time_sc7280),359 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,360 HS_RISE_FALL_MASK361 },362 {363 "qcom,hs-crossover-voltage-microvolt",364 hs_crossover_voltage_sc7280,365 ARRAY_SIZE(hs_crossover_voltage_sc7280),366 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,367 HS_CROSSOVER_VOLTAGE_MASK368 },369 {370 "qcom,hs-output-impedance-micro-ohms",371 hs_output_impedance_sc7280,372 ARRAY_SIZE(hs_output_impedance_sc7280),373 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,374 HS_OUTPUT_IMPEDANCE_MASK,375 },376 {377 "qcom,ls-fs-output-impedance-bp",378 ls_fs_output_impedance_sc7280,379 ARRAY_SIZE(ls_fs_output_impedance_sc7280),380 USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3,381 LS_FS_OUTPUT_IMPEDANCE_MASK,382 },383 {},384};385 386static int qcom_snps_hsphy_init(struct phy *phy)387{388 struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);389 int ret, i;390 391 dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);392 393 ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);394 if (ret)395 return ret;396 397 ret = clk_bulk_prepare_enable(hsphy->num_clks, hsphy->clks);398 if (ret) {399 dev_err(&phy->dev, "failed to enable clocks, %d\n", ret);400 goto poweroff_phy;401 }402 403 ret = reset_control_assert(hsphy->phy_reset);404 if (ret) {405 dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);406 goto disable_clks;407 }408 409 usleep_range(100, 150);410 411 ret = reset_control_deassert(hsphy->phy_reset);412 if (ret) {413 dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);414 goto disable_clks;415 }416 417 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,418 UTMI_PHY_CMN_CTRL_OVERRIDE_EN,419 UTMI_PHY_CMN_CTRL_OVERRIDE_EN);420 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,421 POR, POR);422 qcom_snps_hsphy_write_mask(hsphy->base,423 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,424 FSEL_MASK, 0);425 qcom_snps_hsphy_write_mask(hsphy->base,426 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,427 PLLBTUNE, PLLBTUNE);428 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,429 REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);430 qcom_snps_hsphy_write_mask(hsphy->base,431 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,432 VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);433 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,434 VBUSVLDEXT0, VBUSVLDEXT0);435 436 for (i = 0; i < ARRAY_SIZE(hsphy->update_seq_cfg); i++) {437 if (hsphy->update_seq_cfg[i].need_update)438 qcom_snps_hsphy_write_mask(hsphy->base,439 hsphy->update_seq_cfg[i].offset,440 hsphy->update_seq_cfg[i].mask,441 hsphy->update_seq_cfg[i].value);442 }443 444 qcom_snps_hsphy_write_mask(hsphy->base,445 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,446 VREGBYPASS, VREGBYPASS);447 448 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,449 USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,450 USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);451 452 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,453 SLEEPM, SLEEPM);454 455 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,456 SIDDQ, 0);457 458 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,459 POR, 0);460 461 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,462 USB2_SUSPEND_N_SEL, 0);463 464 qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,465 UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);466 467 hsphy->phy_initialized = true;468 469 return 0;470 471disable_clks:472 clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);473poweroff_phy:474 regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);475 476 return ret;477}478 479static int qcom_snps_hsphy_exit(struct phy *phy)480{481 struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);482 483 reset_control_assert(hsphy->phy_reset);484 clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);485 regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);486 hsphy->phy_initialized = false;487 488 return 0;489}490 491static const struct phy_ops qcom_snps_hsphy_gen_ops = {492 .init = qcom_snps_hsphy_init,493 .exit = qcom_snps_hsphy_exit,494 .set_mode = qcom_snps_hsphy_set_mode,495 .owner = THIS_MODULE,496};497 498static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {499 { .compatible = "qcom,sm8150-usb-hs-phy", },500 { .compatible = "qcom,usb-snps-hs-5nm-phy", },501 {502 .compatible = "qcom,usb-snps-hs-7nm-phy",503 .data = &sc7280_snps_7nm_phy,504 },505 { .compatible = "qcom,usb-snps-femto-v2-phy", },506 { }507};508MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);509 510static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {511 SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend,512 qcom_snps_hsphy_runtime_resume, NULL)513};514 515static void qcom_snps_hsphy_override_param_update_val(516 const struct override_param_map map,517 s32 dt_val, struct phy_override_seq *seq_entry)518{519 int i;520 521 /*522 * Param table for each param is in increasing order523 * of dt values. We need to iterate over the list to524 * select the entry that matches the dt value and pick525 * up the corresponding register value.526 */527 for (i = 0; i < map.table_size - 1; i++) {528 if (map.param_table[i].value == dt_val)529 break;530 }531 532 seq_entry->need_update = true;533 seq_entry->offset = map.reg_offset;534 seq_entry->mask = map.param_mask;535 seq_entry->value = map.param_table[i].reg_val << __ffs(map.param_mask);536}537 538static void qcom_snps_hsphy_read_override_param_seq(struct device *dev)539{540 struct device_node *node = dev->of_node;541 s32 val;542 int ret, i;543 struct qcom_snps_hsphy *hsphy;544 const struct override_param_map *cfg = of_device_get_match_data(dev);545 546 if (!cfg)547 return;548 549 hsphy = dev_get_drvdata(dev);550 551 for (i = 0; cfg[i].prop_name != NULL; i++) {552 ret = of_property_read_s32(node, cfg[i].prop_name, &val);553 if (ret)554 continue;555 556 qcom_snps_hsphy_override_param_update_val(cfg[i], val,557 &hsphy->update_seq_cfg[i]);558 dev_dbg(&hsphy->phy->dev, "Read param: %s dt_val: %d reg_val: 0x%x\n",559 cfg[i].prop_name, val, hsphy->update_seq_cfg[i].value);560 561 }562}563 564static int qcom_snps_hsphy_probe(struct platform_device *pdev)565{566 struct device *dev = &pdev->dev;567 struct qcom_snps_hsphy *hsphy;568 struct phy_provider *phy_provider;569 struct phy *generic_phy;570 int ret, i;571 int num;572 573 hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);574 if (!hsphy)575 return -ENOMEM;576 577 hsphy->dev = dev;578 579 hsphy->base = devm_platform_ioremap_resource(pdev, 0);580 if (IS_ERR(hsphy->base))581 return PTR_ERR(hsphy->base);582 583 ret = qcom_snps_hsphy_clk_init(hsphy);584 if (ret)585 return dev_err_probe(dev, ret, "failed to initialize clocks\n");586 587 hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);588 if (IS_ERR(hsphy->phy_reset)) {589 dev_err(dev, "failed to get phy core reset\n");590 return PTR_ERR(hsphy->phy_reset);591 }592 593 num = ARRAY_SIZE(hsphy->vregs);594 for (i = 0; i < num; i++)595 hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];596 597 ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);598 if (ret)599 return dev_err_probe(dev, ret,600 "failed to get regulator supplies\n");601 602 pm_runtime_set_active(dev);603 pm_runtime_enable(dev);604 /*605 * Prevent runtime pm from being ON by default. Users can enable606 * it using power/control in sysfs.607 */608 pm_runtime_forbid(dev);609 610 generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);611 if (IS_ERR(generic_phy)) {612 ret = PTR_ERR(generic_phy);613 dev_err(dev, "failed to create phy, %d\n", ret);614 return ret;615 }616 hsphy->phy = generic_phy;617 618 dev_set_drvdata(dev, hsphy);619 phy_set_drvdata(generic_phy, hsphy);620 qcom_snps_hsphy_read_override_param_seq(dev);621 622 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);623 if (!IS_ERR(phy_provider))624 dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");625 else626 pm_runtime_disable(dev);627 628 return PTR_ERR_OR_ZERO(phy_provider);629}630 631static struct platform_driver qcom_snps_hsphy_driver = {632 .probe = qcom_snps_hsphy_probe,633 .driver = {634 .name = "qcom-snps-hs-femto-v2-phy",635 .pm = &qcom_snps_hsphy_pm_ops,636 .of_match_table = qcom_snps_hsphy_of_match_table,637 },638};639 640module_platform_driver(qcom_snps_hsphy_driver);641 642MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");643MODULE_LICENSE("GPL v2");644