156d77c9aSAbel Vesa // SPDX-License-Identifier: GPL-2.0 256d77c9aSAbel Vesa /* 356d77c9aSAbel Vesa * Copyright (c) 2023, Linaro Limited 456d77c9aSAbel Vesa */ 556d77c9aSAbel Vesa 656d77c9aSAbel Vesa #include <linux/module.h> 756d77c9aSAbel Vesa #include <linux/platform_device.h> 856d77c9aSAbel Vesa #include <linux/regulator/consumer.h> 956d77c9aSAbel Vesa #include <linux/regmap.h> 1056d77c9aSAbel Vesa #include <linux/of.h> 1156d77c9aSAbel Vesa #include <linux/phy/phy.h> 1256d77c9aSAbel Vesa 1356d77c9aSAbel Vesa /* eUSB2 status registers */ 1456d77c9aSAbel Vesa #define EUSB2_RPTR_STATUS 0x08 1556d77c9aSAbel Vesa #define RPTR_OK BIT(7) 1656d77c9aSAbel Vesa 1756d77c9aSAbel Vesa /* eUSB2 control registers */ 1856d77c9aSAbel Vesa #define EUSB2_EN_CTL1 0x46 1956d77c9aSAbel Vesa #define EUSB2_RPTR_EN BIT(7) 2056d77c9aSAbel Vesa 2156d77c9aSAbel Vesa #define EUSB2_FORCE_EN_5 0xe8 2256d77c9aSAbel Vesa #define F_CLK_19P2M_EN BIT(6) 2356d77c9aSAbel Vesa 2456d77c9aSAbel Vesa #define EUSB2_FORCE_VAL_5 0xeD 2556d77c9aSAbel Vesa #define V_CLK_19P2M_EN BIT(6) 2656d77c9aSAbel Vesa 2799a517a5SKonrad Dybcio #define EUSB2_TUNE_USB2_CROSSOVER 0x50 2856d77c9aSAbel Vesa #define EUSB2_TUNE_IUSB2 0x51 2999a517a5SKonrad Dybcio #define EUSB2_TUNE_RES_FSDIF 0x52 3099a517a5SKonrad Dybcio #define EUSB2_TUNE_HSDISC 0x53 3156d77c9aSAbel Vesa #define EUSB2_TUNE_SQUELCH_U 0x54 3299a517a5SKonrad Dybcio #define EUSB2_TUNE_USB2_SLEW 0x55 3399a517a5SKonrad Dybcio #define EUSB2_TUNE_USB2_EQU 0x56 3456d77c9aSAbel Vesa #define EUSB2_TUNE_USB2_PREEM 0x57 3599a517a5SKonrad Dybcio #define EUSB2_TUNE_USB2_HS_COMP_CUR 0x58 3699a517a5SKonrad Dybcio #define EUSB2_TUNE_EUSB_SLEW 0x59 3799a517a5SKonrad Dybcio #define EUSB2_TUNE_EUSB_EQU 0x5A 3899a517a5SKonrad Dybcio #define EUSB2_TUNE_EUSB_HS_COMP_CUR 0x5B 3956d77c9aSAbel Vesa 40734550d6SAbel Vesa enum eusb2_reg_layout { 41734550d6SAbel Vesa TUNE_EUSB_HS_COMP_CUR, 42734550d6SAbel Vesa TUNE_EUSB_EQU, 43734550d6SAbel Vesa TUNE_EUSB_SLEW, 44734550d6SAbel Vesa TUNE_USB2_HS_COMP_CUR, 45734550d6SAbel Vesa TUNE_USB2_PREEM, 46734550d6SAbel Vesa TUNE_USB2_EQU, 47734550d6SAbel Vesa TUNE_USB2_SLEW, 48734550d6SAbel Vesa TUNE_SQUELCH_U, 49734550d6SAbel Vesa TUNE_HSDISC, 50734550d6SAbel Vesa TUNE_RES_FSDIF, 51734550d6SAbel Vesa TUNE_IUSB2, 52734550d6SAbel Vesa TUNE_USB2_CROSSOVER, 53734550d6SAbel Vesa NUM_TUNE_FIELDS, 5456d77c9aSAbel Vesa 55734550d6SAbel Vesa FORCE_VAL_5 = NUM_TUNE_FIELDS, 56734550d6SAbel Vesa FORCE_EN_5, 574ba2e527SKonrad Dybcio 58734550d6SAbel Vesa EN_CTL1, 594ba2e527SKonrad Dybcio 60734550d6SAbel Vesa RPTR_STATUS, 61734550d6SAbel Vesa LAYOUT_SIZE, 624ba2e527SKonrad Dybcio }; 634ba2e527SKonrad Dybcio 6456d77c9aSAbel Vesa struct eusb2_repeater_cfg { 6599a517a5SKonrad Dybcio const u32 *init_tbl; 6656d77c9aSAbel Vesa int init_tbl_num; 6756d77c9aSAbel Vesa const char * const *vreg_list; 6856d77c9aSAbel Vesa int num_vregs; 6956d77c9aSAbel Vesa }; 7056d77c9aSAbel Vesa 7156d77c9aSAbel Vesa struct eusb2_repeater { 7256d77c9aSAbel Vesa struct device *dev; 73734550d6SAbel Vesa struct regmap *regmap; 7456d77c9aSAbel Vesa struct phy *phy; 7556d77c9aSAbel Vesa struct regulator_bulk_data *vregs; 7656d77c9aSAbel Vesa const struct eusb2_repeater_cfg *cfg; 77734550d6SAbel Vesa u32 base; 7856d77c9aSAbel Vesa enum phy_mode mode; 7956d77c9aSAbel Vesa }; 8056d77c9aSAbel Vesa 8156d77c9aSAbel Vesa static const char * const pm8550b_vreg_l[] = { 8256d77c9aSAbel Vesa "vdd18", "vdd3", 8356d77c9aSAbel Vesa }; 8456d77c9aSAbel Vesa 85734550d6SAbel Vesa static const u32 pm8550b_init_tbl[NUM_TUNE_FIELDS] = { 86734550d6SAbel Vesa [TUNE_IUSB2] = 0x8, 87734550d6SAbel Vesa [TUNE_SQUELCH_U] = 0x3, 88734550d6SAbel Vesa [TUNE_USB2_PREEM] = 0x5, 8956d77c9aSAbel Vesa }; 9056d77c9aSAbel Vesa 9167076749SAbel Vesa static const u32 smb2360_init_tbl[NUM_TUNE_FIELDS] = { 9267076749SAbel Vesa [TUNE_IUSB2] = 0x5, 9367076749SAbel Vesa [TUNE_SQUELCH_U] = 0x3, 9467076749SAbel Vesa [TUNE_USB2_PREEM] = 0x2, 9567076749SAbel Vesa }; 9667076749SAbel Vesa 9756d77c9aSAbel Vesa static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = { 9856d77c9aSAbel Vesa .init_tbl = pm8550b_init_tbl, 9956d77c9aSAbel Vesa .init_tbl_num = ARRAY_SIZE(pm8550b_init_tbl), 10056d77c9aSAbel Vesa .vreg_list = pm8550b_vreg_l, 10156d77c9aSAbel Vesa .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), 10256d77c9aSAbel Vesa }; 10356d77c9aSAbel Vesa 10467076749SAbel Vesa static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = { 10567076749SAbel Vesa .init_tbl = smb2360_init_tbl, 10667076749SAbel Vesa .init_tbl_num = ARRAY_SIZE(smb2360_init_tbl), 10767076749SAbel Vesa .vreg_list = pm8550b_vreg_l, 10867076749SAbel Vesa .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), 10967076749SAbel Vesa }; 11067076749SAbel Vesa 11156d77c9aSAbel Vesa static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr) 11256d77c9aSAbel Vesa { 11356d77c9aSAbel Vesa int num = rptr->cfg->num_vregs; 11456d77c9aSAbel Vesa struct device *dev = rptr->dev; 11556d77c9aSAbel Vesa int i; 11656d77c9aSAbel Vesa 11756d77c9aSAbel Vesa rptr->vregs = devm_kcalloc(dev, num, sizeof(*rptr->vregs), GFP_KERNEL); 11856d77c9aSAbel Vesa if (!rptr->vregs) 11956d77c9aSAbel Vesa return -ENOMEM; 12056d77c9aSAbel Vesa 12156d77c9aSAbel Vesa for (i = 0; i < num; i++) 12256d77c9aSAbel Vesa rptr->vregs[i].supply = rptr->cfg->vreg_list[i]; 12356d77c9aSAbel Vesa 12456d77c9aSAbel Vesa return devm_regulator_bulk_get(dev, num, rptr->vregs); 12556d77c9aSAbel Vesa } 12656d77c9aSAbel Vesa 12756d77c9aSAbel Vesa static int eusb2_repeater_init(struct phy *phy) 12856d77c9aSAbel Vesa { 12956d77c9aSAbel Vesa struct eusb2_repeater *rptr = phy_get_drvdata(phy); 13056156a76SKonrad Dybcio struct device_node *np = rptr->dev->of_node; 131734550d6SAbel Vesa struct regmap *regmap = rptr->regmap; 132734550d6SAbel Vesa const u32 *init_tbl = rptr->cfg->init_tbl; 133734550d6SAbel Vesa u8 tune_usb2_preem = init_tbl[TUNE_USB2_PREEM]; 134734550d6SAbel Vesa u8 tune_hsdisc = init_tbl[TUNE_HSDISC]; 135734550d6SAbel Vesa u8 tune_iusb2 = init_tbl[TUNE_IUSB2]; 136734550d6SAbel Vesa u32 base = rptr->base; 13756d77c9aSAbel Vesa u32 val; 13856d77c9aSAbel Vesa int ret; 139734550d6SAbel Vesa 140734550d6SAbel Vesa of_property_read_u8(np, "qcom,tune-usb2-amplitude", &tune_iusb2); 141734550d6SAbel Vesa of_property_read_u8(np, "qcom,tune-usb2-disc-thres", &tune_hsdisc); 142734550d6SAbel Vesa of_property_read_u8(np, "qcom,tune-usb2-preem", &tune_usb2_preem); 14356d77c9aSAbel Vesa 14456d77c9aSAbel Vesa ret = regulator_bulk_enable(rptr->cfg->num_vregs, rptr->vregs); 14556d77c9aSAbel Vesa if (ret) 14656d77c9aSAbel Vesa return ret; 14756d77c9aSAbel Vesa 148734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_EN_CTL1, EUSB2_RPTR_EN); 14956d77c9aSAbel Vesa 150734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_EUSB_HS_COMP_CUR, init_tbl[TUNE_EUSB_HS_COMP_CUR]); 151734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_EUSB_EQU, init_tbl[TUNE_EUSB_EQU]); 152734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_EUSB_SLEW, init_tbl[TUNE_EUSB_SLEW]); 153734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_USB2_HS_COMP_CUR, init_tbl[TUNE_USB2_HS_COMP_CUR]); 154734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_USB2_EQU, init_tbl[TUNE_USB2_EQU]); 155734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_USB2_SLEW, init_tbl[TUNE_USB2_SLEW]); 156734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_SQUELCH_U, init_tbl[TUNE_SQUELCH_U]); 157734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_RES_FSDIF, init_tbl[TUNE_RES_FSDIF]); 158734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_USB2_CROSSOVER, init_tbl[TUNE_USB2_CROSSOVER]); 15999a517a5SKonrad Dybcio 160734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_USB2_PREEM, tune_usb2_preem); 161734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_HSDISC, tune_hsdisc); 162734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_TUNE_IUSB2, tune_iusb2); 16356156a76SKonrad Dybcio 164734550d6SAbel Vesa ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, val, val & RPTR_OK, 10, 5); 16556d77c9aSAbel Vesa if (ret) 16656d77c9aSAbel Vesa dev_err(rptr->dev, "initialization timed-out\n"); 16756d77c9aSAbel Vesa 16856d77c9aSAbel Vesa return ret; 16956d77c9aSAbel Vesa } 17056d77c9aSAbel Vesa 17156d77c9aSAbel Vesa static int eusb2_repeater_set_mode(struct phy *phy, 17256d77c9aSAbel Vesa enum phy_mode mode, int submode) 17356d77c9aSAbel Vesa { 17456d77c9aSAbel Vesa struct eusb2_repeater *rptr = phy_get_drvdata(phy); 175734550d6SAbel Vesa struct regmap *regmap = rptr->regmap; 176734550d6SAbel Vesa u32 base = rptr->base; 17756d77c9aSAbel Vesa 17856d77c9aSAbel Vesa switch (mode) { 17956d77c9aSAbel Vesa case PHY_MODE_USB_HOST: 18056d77c9aSAbel Vesa /* 18156d77c9aSAbel Vesa * CM.Lx is prohibited when repeater is already into Lx state as 18256d77c9aSAbel Vesa * per eUSB 1.2 Spec. Below implement software workaround until 18356d77c9aSAbel Vesa * PHY and controller is fixing seen observation. 18456d77c9aSAbel Vesa */ 185734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_FORCE_EN_5, F_CLK_19P2M_EN); 186734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN); 18756d77c9aSAbel Vesa break; 18856d77c9aSAbel Vesa case PHY_MODE_USB_DEVICE: 18956d77c9aSAbel Vesa /* 19056d77c9aSAbel Vesa * In device mode clear host mode related workaround as there 19156d77c9aSAbel Vesa * is no repeater reset available, and enable/disable of 19256d77c9aSAbel Vesa * repeater doesn't clear previous value due to shared 19356d77c9aSAbel Vesa * regulators (say host <-> device mode switch). 19456d77c9aSAbel Vesa */ 195734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_FORCE_EN_5, 0); 196734550d6SAbel Vesa regmap_write(regmap, base + EUSB2_FORCE_VAL_5, 0); 19756d77c9aSAbel Vesa break; 19856d77c9aSAbel Vesa default: 19956d77c9aSAbel Vesa return -EINVAL; 20056d77c9aSAbel Vesa } 20156d77c9aSAbel Vesa 20256d77c9aSAbel Vesa return 0; 20356d77c9aSAbel Vesa } 20456d77c9aSAbel Vesa 20556d77c9aSAbel Vesa static int eusb2_repeater_exit(struct phy *phy) 20656d77c9aSAbel Vesa { 20756d77c9aSAbel Vesa struct eusb2_repeater *rptr = phy_get_drvdata(phy); 20856d77c9aSAbel Vesa 20956d77c9aSAbel Vesa return regulator_bulk_disable(rptr->cfg->num_vregs, rptr->vregs); 21056d77c9aSAbel Vesa } 21156d77c9aSAbel Vesa 21256d77c9aSAbel Vesa static const struct phy_ops eusb2_repeater_ops = { 21356d77c9aSAbel Vesa .init = eusb2_repeater_init, 21456d77c9aSAbel Vesa .exit = eusb2_repeater_exit, 21556d77c9aSAbel Vesa .set_mode = eusb2_repeater_set_mode, 21656d77c9aSAbel Vesa .owner = THIS_MODULE, 21756d77c9aSAbel Vesa }; 21856d77c9aSAbel Vesa 21956d77c9aSAbel Vesa static int eusb2_repeater_probe(struct platform_device *pdev) 22056d77c9aSAbel Vesa { 22156d77c9aSAbel Vesa struct eusb2_repeater *rptr; 22256d77c9aSAbel Vesa struct device *dev = &pdev->dev; 22356d77c9aSAbel Vesa struct phy_provider *phy_provider; 22456d77c9aSAbel Vesa struct device_node *np = dev->of_node; 22556d77c9aSAbel Vesa u32 res; 226734550d6SAbel Vesa int ret; 22756d77c9aSAbel Vesa 22856d77c9aSAbel Vesa rptr = devm_kzalloc(dev, sizeof(*rptr), GFP_KERNEL); 22956d77c9aSAbel Vesa if (!rptr) 23056d77c9aSAbel Vesa return -ENOMEM; 23156d77c9aSAbel Vesa 23256d77c9aSAbel Vesa rptr->dev = dev; 23356d77c9aSAbel Vesa dev_set_drvdata(dev, rptr); 23456d77c9aSAbel Vesa 23556d77c9aSAbel Vesa rptr->cfg = of_device_get_match_data(dev); 23656d77c9aSAbel Vesa if (!rptr->cfg) 23756d77c9aSAbel Vesa return -EINVAL; 23856d77c9aSAbel Vesa 239734550d6SAbel Vesa rptr->regmap = dev_get_regmap(dev->parent, NULL); 240734550d6SAbel Vesa if (!rptr->regmap) 24156d77c9aSAbel Vesa return -ENODEV; 24256d77c9aSAbel Vesa 24356d77c9aSAbel Vesa ret = of_property_read_u32(np, "reg", &res); 24456d77c9aSAbel Vesa if (ret < 0) 24556d77c9aSAbel Vesa return ret; 24656d77c9aSAbel Vesa 247734550d6SAbel Vesa rptr->base = res; 24856d77c9aSAbel Vesa 24956d77c9aSAbel Vesa ret = eusb2_repeater_init_vregs(rptr); 25056d77c9aSAbel Vesa if (ret < 0) { 25156d77c9aSAbel Vesa dev_err(dev, "unable to get supplies\n"); 25256d77c9aSAbel Vesa return ret; 25356d77c9aSAbel Vesa } 25456d77c9aSAbel Vesa 25556d77c9aSAbel Vesa rptr->phy = devm_phy_create(dev, np, &eusb2_repeater_ops); 25656d77c9aSAbel Vesa if (IS_ERR(rptr->phy)) { 25756d77c9aSAbel Vesa dev_err(dev, "failed to create PHY: %d\n", ret); 25856d77c9aSAbel Vesa return PTR_ERR(rptr->phy); 25956d77c9aSAbel Vesa } 26056d77c9aSAbel Vesa 26156d77c9aSAbel Vesa phy_set_drvdata(rptr->phy, rptr); 26256d77c9aSAbel Vesa 26356d77c9aSAbel Vesa phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 26456d77c9aSAbel Vesa if (IS_ERR(phy_provider)) 26556d77c9aSAbel Vesa return PTR_ERR(phy_provider); 26656d77c9aSAbel Vesa 26756d77c9aSAbel Vesa dev_info(dev, "Registered Qcom-eUSB2 repeater\n"); 26856d77c9aSAbel Vesa 26956d77c9aSAbel Vesa return 0; 27056d77c9aSAbel Vesa } 27156d77c9aSAbel Vesa 272e5ce6d9dSUwe Kleine-König static void eusb2_repeater_remove(struct platform_device *pdev) 27356d77c9aSAbel Vesa { 27456d77c9aSAbel Vesa struct eusb2_repeater *rptr = platform_get_drvdata(pdev); 27556d77c9aSAbel Vesa 27656d77c9aSAbel Vesa if (!rptr) 277e5ce6d9dSUwe Kleine-König return; 27856d77c9aSAbel Vesa 27956d77c9aSAbel Vesa eusb2_repeater_exit(rptr->phy); 28056d77c9aSAbel Vesa } 28156d77c9aSAbel Vesa 28256d77c9aSAbel Vesa static const struct of_device_id eusb2_repeater_of_match_table[] = { 28356d77c9aSAbel Vesa { 28456d77c9aSAbel Vesa .compatible = "qcom,pm8550b-eusb2-repeater", 28556d77c9aSAbel Vesa .data = &pm8550b_eusb2_cfg, 28656d77c9aSAbel Vesa }, 28767076749SAbel Vesa { 28867076749SAbel Vesa .compatible = "qcom,smb2360-eusb2-repeater", 28967076749SAbel Vesa .data = &smb2360_eusb2_cfg, 29067076749SAbel Vesa }, 29156d77c9aSAbel Vesa { }, 29256d77c9aSAbel Vesa }; 29356d77c9aSAbel Vesa MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table); 29456d77c9aSAbel Vesa 29556d77c9aSAbel Vesa static struct platform_driver eusb2_repeater_driver = { 29656d77c9aSAbel Vesa .probe = eusb2_repeater_probe, 297*54234e3aSUwe Kleine-König .remove = eusb2_repeater_remove, 29856d77c9aSAbel Vesa .driver = { 29956d77c9aSAbel Vesa .name = "qcom-eusb2-repeater", 30056d77c9aSAbel Vesa .of_match_table = eusb2_repeater_of_match_table, 30156d77c9aSAbel Vesa }, 30256d77c9aSAbel Vesa }; 30356d77c9aSAbel Vesa 30456d77c9aSAbel Vesa module_platform_driver(eusb2_repeater_driver); 30556d77c9aSAbel Vesa 30656d77c9aSAbel Vesa MODULE_DESCRIPTION("Qualcomm PMIC eUSB2 Repeater driver"); 30756d77c9aSAbel Vesa MODULE_LICENSE("GPL"); 308