Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | // SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2025, Linaro Limited */ #define pr_fmt(fmt) "qcom_usb_vbus: " fmt #include <bitfield.h> #include <errno.h> #include <dm.h> #include <fdtdec.h> #include <log.h> #include <asm/gpio.h> #include <linux/bitops.h> #include <linux/printk.h> #include <power/pmic.h> #include <power/regulator.h> enum pm8x50b_vbus { PM8150B, PM8550B, }; #define OTG_EN BIT(0) #define OTG_EN_SRC_CFG BIT(1) struct qcom_otg_regs { u32 otg_cmd; u32 otg_cfg; }; struct qcom_usb_vbus_priv { phys_addr_t base; struct qcom_otg_regs *regs; }; static const struct qcom_otg_regs qcom_otg[] = { [PM8150B] = { .otg_cmd = 0x40, .otg_cfg = 0x53, }, [PM8550B] = { .otg_cmd = 0x50, .otg_cfg = 0x56, }, }; static int qcom_usb_vbus_regulator_of_to_plat(struct udevice *dev) { struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); priv->base = dev_read_addr(dev); if (priv->base == FDT_ADDR_T_NONE) return -EINVAL; return 0; } static int qcom_usb_vbus_regulator_get_enable(struct udevice *dev) { const struct qcom_otg_regs *regs = &qcom_otg[dev_get_driver_data(dev)]; struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); int otg_en_reg = priv->base + regs->otg_cmd; int ret; ret = pmic_reg_read(dev->parent, otg_en_reg); if (ret < 0) log_err("failed to read usb vbus: %d\n", ret); else ret &= OTG_EN; return ret; } static int qcom_usb_vbus_regulator_set_enable(struct udevice *dev, bool enable) { const struct qcom_otg_regs *regs = &qcom_otg[dev_get_driver_data(dev)]; struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); int otg_en_reg = priv->base + regs->otg_cmd; int ret; if (enable) { ret = pmic_clrsetbits(dev->parent, otg_en_reg, 0, OTG_EN); if (ret < 0) { log_err("error enabling: %d\n", ret); return ret; } } else { ret = pmic_clrsetbits(dev->parent, otg_en_reg, OTG_EN, 0); if (ret < 0) { log_err("error disabling: %d\n", ret); return ret; } } return 0; } static int qcom_usb_vbus_regulator_probe(struct udevice *dev) { const struct qcom_otg_regs *regs = &qcom_otg[dev_get_driver_data(dev)]; struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); int otg_cfg_reg = priv->base + regs->otg_cfg; int ret; /* Disable HW logic for VBUS enable */ ret = pmic_clrsetbits(dev->parent, otg_cfg_reg, OTG_EN_SRC_CFG, 0); if (ret < 0) { log_err("error setting EN_SRC_CFG: %d\n", ret); return ret; } return 0; } static const struct dm_regulator_ops qcom_usb_vbus_regulator_ops = { .get_enable = qcom_usb_vbus_regulator_get_enable, .set_enable = qcom_usb_vbus_regulator_set_enable, }; static const struct udevice_id qcom_usb_vbus_regulator_ids[] = { { .compatible = "qcom,pm8150b-vbus-reg", .data = PM8150B }, { .compatible = "qcom,pm8550b-vbus-reg", .data = PM8550B }, { }, }; U_BOOT_DRIVER(qcom_usb_vbus_regulator) = { .name = "qcom-usb-vbus-regulator", .id = UCLASS_REGULATOR, .of_match = qcom_usb_vbus_regulator_ids, .of_to_plat = qcom_usb_vbus_regulator_of_to_plat, .ops = &qcom_usb_vbus_regulator_ops, .probe = qcom_usb_vbus_regulator_probe, .priv_auto = sizeof(struct qcom_usb_vbus_priv), }; |