brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 68cc8e2 Raw
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