308 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2023, Linaro Limited4 */5 6#include <linux/module.h>7#include <linux/platform_device.h>8#include <linux/regulator/consumer.h>9#include <linux/regmap.h>10#include <linux/of.h>11#include <linux/phy/phy.h>12 13/* eUSB2 status registers */14#define EUSB2_RPTR_STATUS 0x0815#define RPTR_OK BIT(7)16 17/* eUSB2 control registers */18#define EUSB2_EN_CTL1 0x4619#define EUSB2_RPTR_EN BIT(7)20 21#define EUSB2_FORCE_EN_5 0xe822#define F_CLK_19P2M_EN BIT(6)23 24#define EUSB2_FORCE_VAL_5 0xeD25#define V_CLK_19P2M_EN BIT(6)26 27#define EUSB2_TUNE_USB2_CROSSOVER 0x5028#define EUSB2_TUNE_IUSB2 0x5129#define EUSB2_TUNE_RES_FSDIF 0x5230#define EUSB2_TUNE_HSDISC 0x5331#define EUSB2_TUNE_SQUELCH_U 0x5432#define EUSB2_TUNE_USB2_SLEW 0x5533#define EUSB2_TUNE_USB2_EQU 0x5634#define EUSB2_TUNE_USB2_PREEM 0x5735#define EUSB2_TUNE_USB2_HS_COMP_CUR 0x5836#define EUSB2_TUNE_EUSB_SLEW 0x5937#define EUSB2_TUNE_EUSB_EQU 0x5A38#define EUSB2_TUNE_EUSB_HS_COMP_CUR 0x5B39 40enum eusb2_reg_layout {41 TUNE_EUSB_HS_COMP_CUR,42 TUNE_EUSB_EQU,43 TUNE_EUSB_SLEW,44 TUNE_USB2_HS_COMP_CUR,45 TUNE_USB2_PREEM,46 TUNE_USB2_EQU,47 TUNE_USB2_SLEW,48 TUNE_SQUELCH_U,49 TUNE_HSDISC,50 TUNE_RES_FSDIF,51 TUNE_IUSB2,52 TUNE_USB2_CROSSOVER,53 NUM_TUNE_FIELDS,54 55 FORCE_VAL_5 = NUM_TUNE_FIELDS,56 FORCE_EN_5,57 58 EN_CTL1,59 60 RPTR_STATUS,61 LAYOUT_SIZE,62};63 64struct eusb2_repeater_cfg {65 const u32 *init_tbl;66 int init_tbl_num;67 const char * const *vreg_list;68 int num_vregs;69};70 71struct eusb2_repeater {72 struct device *dev;73 struct regmap *regmap;74 struct phy *phy;75 struct regulator_bulk_data *vregs;76 const struct eusb2_repeater_cfg *cfg;77 u32 base;78 enum phy_mode mode;79};80 81static const char * const pm8550b_vreg_l[] = {82 "vdd18", "vdd3",83};84 85static const u32 pm8550b_init_tbl[NUM_TUNE_FIELDS] = {86 [TUNE_IUSB2] = 0x8,87 [TUNE_SQUELCH_U] = 0x3,88 [TUNE_USB2_PREEM] = 0x5,89};90 91static const u32 smb2360_init_tbl[NUM_TUNE_FIELDS] = {92 [TUNE_IUSB2] = 0x5,93 [TUNE_SQUELCH_U] = 0x3,94 [TUNE_USB2_PREEM] = 0x2,95};96 97static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = {98 .init_tbl = pm8550b_init_tbl,99 .init_tbl_num = ARRAY_SIZE(pm8550b_init_tbl),100 .vreg_list = pm8550b_vreg_l,101 .num_vregs = ARRAY_SIZE(pm8550b_vreg_l),102};103 104static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = {105 .init_tbl = smb2360_init_tbl,106 .init_tbl_num = ARRAY_SIZE(smb2360_init_tbl),107 .vreg_list = pm8550b_vreg_l,108 .num_vregs = ARRAY_SIZE(pm8550b_vreg_l),109};110 111static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr)112{113 int num = rptr->cfg->num_vregs;114 struct device *dev = rptr->dev;115 int i;116 117 rptr->vregs = devm_kcalloc(dev, num, sizeof(*rptr->vregs), GFP_KERNEL);118 if (!rptr->vregs)119 return -ENOMEM;120 121 for (i = 0; i < num; i++)122 rptr->vregs[i].supply = rptr->cfg->vreg_list[i];123 124 return devm_regulator_bulk_get(dev, num, rptr->vregs);125}126 127static int eusb2_repeater_init(struct phy *phy)128{129 struct eusb2_repeater *rptr = phy_get_drvdata(phy);130 struct device_node *np = rptr->dev->of_node;131 struct regmap *regmap = rptr->regmap;132 const u32 *init_tbl = rptr->cfg->init_tbl;133 u8 tune_usb2_preem = init_tbl[TUNE_USB2_PREEM];134 u8 tune_hsdisc = init_tbl[TUNE_HSDISC];135 u8 tune_iusb2 = init_tbl[TUNE_IUSB2];136 u32 base = rptr->base;137 u32 val;138 int ret;139 140 of_property_read_u8(np, "qcom,tune-usb2-amplitude", &tune_iusb2);141 of_property_read_u8(np, "qcom,tune-usb2-disc-thres", &tune_hsdisc);142 of_property_read_u8(np, "qcom,tune-usb2-preem", &tune_usb2_preem);143 144 ret = regulator_bulk_enable(rptr->cfg->num_vregs, rptr->vregs);145 if (ret)146 return ret;147 148 regmap_write(regmap, base + EUSB2_EN_CTL1, EUSB2_RPTR_EN);149 150 regmap_write(regmap, base + EUSB2_TUNE_EUSB_HS_COMP_CUR, init_tbl[TUNE_EUSB_HS_COMP_CUR]);151 regmap_write(regmap, base + EUSB2_TUNE_EUSB_EQU, init_tbl[TUNE_EUSB_EQU]);152 regmap_write(regmap, base + EUSB2_TUNE_EUSB_SLEW, init_tbl[TUNE_EUSB_SLEW]);153 regmap_write(regmap, base + EUSB2_TUNE_USB2_HS_COMP_CUR, init_tbl[TUNE_USB2_HS_COMP_CUR]);154 regmap_write(regmap, base + EUSB2_TUNE_USB2_EQU, init_tbl[TUNE_USB2_EQU]);155 regmap_write(regmap, base + EUSB2_TUNE_USB2_SLEW, init_tbl[TUNE_USB2_SLEW]);156 regmap_write(regmap, base + EUSB2_TUNE_SQUELCH_U, init_tbl[TUNE_SQUELCH_U]);157 regmap_write(regmap, base + EUSB2_TUNE_RES_FSDIF, init_tbl[TUNE_RES_FSDIF]);158 regmap_write(regmap, base + EUSB2_TUNE_USB2_CROSSOVER, init_tbl[TUNE_USB2_CROSSOVER]);159 160 regmap_write(regmap, base + EUSB2_TUNE_USB2_PREEM, tune_usb2_preem);161 regmap_write(regmap, base + EUSB2_TUNE_HSDISC, tune_hsdisc);162 regmap_write(regmap, base + EUSB2_TUNE_IUSB2, tune_iusb2);163 164 ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, val, val & RPTR_OK, 10, 5);165 if (ret)166 dev_err(rptr->dev, "initialization timed-out\n");167 168 return ret;169}170 171static int eusb2_repeater_set_mode(struct phy *phy,172 enum phy_mode mode, int submode)173{174 struct eusb2_repeater *rptr = phy_get_drvdata(phy);175 struct regmap *regmap = rptr->regmap;176 u32 base = rptr->base;177 178 switch (mode) {179 case PHY_MODE_USB_HOST:180 /*181 * CM.Lx is prohibited when repeater is already into Lx state as182 * per eUSB 1.2 Spec. Below implement software workaround until183 * PHY and controller is fixing seen observation.184 */185 regmap_write(regmap, base + EUSB2_FORCE_EN_5, F_CLK_19P2M_EN);186 regmap_write(regmap, base + EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN);187 break;188 case PHY_MODE_USB_DEVICE:189 /*190 * In device mode clear host mode related workaround as there191 * is no repeater reset available, and enable/disable of192 * repeater doesn't clear previous value due to shared193 * regulators (say host <-> device mode switch).194 */195 regmap_write(regmap, base + EUSB2_FORCE_EN_5, 0);196 regmap_write(regmap, base + EUSB2_FORCE_VAL_5, 0);197 break;198 default:199 return -EINVAL;200 }201 202 return 0;203}204 205static int eusb2_repeater_exit(struct phy *phy)206{207 struct eusb2_repeater *rptr = phy_get_drvdata(phy);208 209 return regulator_bulk_disable(rptr->cfg->num_vregs, rptr->vregs);210}211 212static const struct phy_ops eusb2_repeater_ops = {213 .init = eusb2_repeater_init,214 .exit = eusb2_repeater_exit,215 .set_mode = eusb2_repeater_set_mode,216 .owner = THIS_MODULE,217};218 219static int eusb2_repeater_probe(struct platform_device *pdev)220{221 struct eusb2_repeater *rptr;222 struct device *dev = &pdev->dev;223 struct phy_provider *phy_provider;224 struct device_node *np = dev->of_node;225 u32 res;226 int ret;227 228 rptr = devm_kzalloc(dev, sizeof(*rptr), GFP_KERNEL);229 if (!rptr)230 return -ENOMEM;231 232 rptr->dev = dev;233 dev_set_drvdata(dev, rptr);234 235 rptr->cfg = of_device_get_match_data(dev);236 if (!rptr->cfg)237 return -EINVAL;238 239 rptr->regmap = dev_get_regmap(dev->parent, NULL);240 if (!rptr->regmap)241 return -ENODEV;242 243 ret = of_property_read_u32(np, "reg", &res);244 if (ret < 0)245 return ret;246 247 rptr->base = res;248 249 ret = eusb2_repeater_init_vregs(rptr);250 if (ret < 0) {251 dev_err(dev, "unable to get supplies\n");252 return ret;253 }254 255 rptr->phy = devm_phy_create(dev, np, &eusb2_repeater_ops);256 if (IS_ERR(rptr->phy)) {257 dev_err(dev, "failed to create PHY: %d\n", ret);258 return PTR_ERR(rptr->phy);259 }260 261 phy_set_drvdata(rptr->phy, rptr);262 263 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);264 if (IS_ERR(phy_provider))265 return PTR_ERR(phy_provider);266 267 dev_info(dev, "Registered Qcom-eUSB2 repeater\n");268 269 return 0;270}271 272static void eusb2_repeater_remove(struct platform_device *pdev)273{274 struct eusb2_repeater *rptr = platform_get_drvdata(pdev);275 276 if (!rptr)277 return;278 279 eusb2_repeater_exit(rptr->phy);280}281 282static const struct of_device_id eusb2_repeater_of_match_table[] = {283 {284 .compatible = "qcom,pm8550b-eusb2-repeater",285 .data = &pm8550b_eusb2_cfg,286 },287 {288 .compatible = "qcom,smb2360-eusb2-repeater",289 .data = &smb2360_eusb2_cfg,290 },291 { },292};293MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table);294 295static struct platform_driver eusb2_repeater_driver = {296 .probe = eusb2_repeater_probe,297 .remove_new = eusb2_repeater_remove,298 .driver = {299 .name = "qcom-eusb2-repeater",300 .of_match_table = eusb2_repeater_of_match_table,301 },302};303 304module_platform_driver(eusb2_repeater_driver);305 306MODULE_DESCRIPTION("Qualcomm PMIC eUSB2 Repeater driver");307MODULE_LICENSE("GPL");308