1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright (c) 2017, 2019-2020, The Linux Foundation. All rights reserved. 3 // Copyright (c) 2023, Linaro Limited 4 5 #include <linux/bitfield.h> 6 #include <linux/clk.h> 7 #include <linux/module.h> 8 #include <linux/of.h> 9 #include <linux/platform_device.h> 10 #include <linux/regmap.h> 11 #include <linux/regulator/driver.h> 12 #include <linux/regulator/machine.h> 13 #include <linux/regulator/of_regulator.h> 14 15 #define REFGEN_REG_BIAS_EN 0x08 16 #define REFGEN_BIAS_EN_MASK GENMASK(2, 0) 17 #define REFGEN_BIAS_EN_ENABLE 0x7 18 #define REFGEN_BIAS_EN_DISABLE 0x6 19 20 #define REFGEN_REG_REFGEN_STATUS 0xc 21 #define REFGEN_STATUS_OUT_MASK BIT(3) 22 23 #define REFGEN_REG_BG_CTRL 0x14 24 #define REFGEN_BG_CTRL_MASK GENMASK(2, 1) 25 #define REFGEN_BG_CTRL_ENABLE 0x3 26 #define REFGEN_BG_CTRL_DISABLE 0x2 27 28 #define REFGEN_REG_PWRDWN_CTRL5 0x80 29 #define REFGEN_PWRDWN_CTRL5_MASK BIT(0) 30 #define REFGEN_PWRDWN_CTRL5_ENABLE 0x1 31 32 struct qcom_refgen_regulator_data { 33 const struct regulator_desc *rdesc; 34 bool has_clocks; 35 }; 36 37 struct qcom_refgen_drvdata { 38 struct clk_bulk_data *clks; 39 int num_clks; 40 bool is_enabled; 41 }; 42 43 static int qcom_sdm845_refgen_enable(struct regulator_dev *rdev) 44 { 45 regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK, 46 FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_ENABLE)); 47 48 regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN, 49 FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_ENABLE)); 50 51 return 0; 52 } 53 54 static int qcom_sdm845_refgen_disable(struct regulator_dev *rdev) 55 { 56 regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN, 57 FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_DISABLE)); 58 59 regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK, 60 FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_DISABLE)); 61 62 return 0; 63 } 64 65 static int qcom_sdm845_refgen_is_enabled(struct regulator_dev *rdev) 66 { 67 u32 val; 68 69 regmap_read(rdev->regmap, REFGEN_REG_BG_CTRL, &val); 70 if (FIELD_GET(REFGEN_BG_CTRL_MASK, val) != REFGEN_BG_CTRL_ENABLE) 71 return 0; 72 73 regmap_read(rdev->regmap, REFGEN_REG_BIAS_EN, &val); 74 if (FIELD_GET(REFGEN_BIAS_EN_MASK, val) != REFGEN_BIAS_EN_ENABLE) 75 return 0; 76 77 return 1; 78 } 79 80 static int qcom_ipq9650_refgen_enable(struct regulator_dev *rdev) 81 { 82 struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); 83 int ret; 84 85 ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); 86 if (ret) 87 return ret; 88 89 drvdata->is_enabled = true; 90 91 return 0; 92 } 93 94 static int qcom_ipq9650_refgen_disable(struct regulator_dev *rdev) 95 { 96 struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); 97 98 clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); 99 100 drvdata->is_enabled = false; 101 102 return 0; 103 } 104 105 static int qcom_ipq9650_refgen_is_enabled(struct regulator_dev *rdev) 106 { 107 struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); 108 109 return drvdata->is_enabled; 110 } 111 112 static int qcom_ipq9650_refgen_get_status(struct regulator_dev *rdev) 113 { 114 u32 val; 115 116 regmap_read(rdev->regmap, REFGEN_REG_REFGEN_STATUS, &val); 117 if (FIELD_GET(REFGEN_STATUS_OUT_MASK, val)) 118 return REGULATOR_STATUS_ON; 119 120 return REGULATOR_STATUS_OFF; 121 } 122 123 static const struct regulator_desc ipq9650_refgen_desc = { 124 .enable_time = 5, 125 .name = "refgen", 126 .owner = THIS_MODULE, 127 .type = REGULATOR_CURRENT, 128 .ops = &(const struct regulator_ops) { 129 .enable = qcom_ipq9650_refgen_enable, 130 .disable = qcom_ipq9650_refgen_disable, 131 .is_enabled = qcom_ipq9650_refgen_is_enabled, 132 .get_status = qcom_ipq9650_refgen_get_status, 133 }, 134 }; 135 136 static const struct regulator_desc sdm845_refgen_desc = { 137 .enable_time = 5, 138 .name = "refgen", 139 .owner = THIS_MODULE, 140 .type = REGULATOR_CURRENT, 141 .ops = &(const struct regulator_ops) { 142 .enable = qcom_sdm845_refgen_enable, 143 .disable = qcom_sdm845_refgen_disable, 144 .is_enabled = qcom_sdm845_refgen_is_enabled, 145 }, 146 }; 147 148 static const struct regulator_desc sm8250_refgen_desc = { 149 .enable_reg = REFGEN_REG_PWRDWN_CTRL5, 150 .enable_mask = REFGEN_PWRDWN_CTRL5_MASK, 151 .enable_val = REFGEN_PWRDWN_CTRL5_ENABLE, 152 .disable_val = 0, 153 .enable_time = 5, 154 .name = "refgen", 155 .owner = THIS_MODULE, 156 .type = REGULATOR_CURRENT, 157 .ops = &(const struct regulator_ops) { 158 .enable = regulator_enable_regmap, 159 .disable = regulator_disable_regmap, 160 .is_enabled = regulator_is_enabled_regmap, 161 }, 162 }; 163 164 static const struct qcom_refgen_regulator_data ipq9650_data = { 165 .rdesc = &ipq9650_refgen_desc, 166 .has_clocks = true, 167 }; 168 169 static const struct qcom_refgen_regulator_data sdm845_data = { 170 .rdesc = &sdm845_refgen_desc, 171 }; 172 173 static const struct qcom_refgen_regulator_data sm8250_data = { 174 .rdesc = &sm8250_refgen_desc, 175 }; 176 177 static const struct regmap_config qcom_refgen_regmap_config = { 178 .reg_bits = 32, 179 .reg_stride = 4, 180 .val_bits = 32, 181 }; 182 183 static int qcom_refgen_probe(struct platform_device *pdev) 184 { 185 const struct qcom_refgen_regulator_data *data; 186 struct qcom_refgen_drvdata *drvdata = NULL; 187 struct regulator_init_data *init_data; 188 struct regulator_config config = {}; 189 const struct regulator_desc *rdesc; 190 struct device *dev = &pdev->dev; 191 struct regulator_dev *rdev; 192 struct regmap *regmap; 193 void __iomem *base; 194 195 data = of_device_get_match_data(dev); 196 if (!data) 197 return -ENODATA; 198 199 if (data->has_clocks) { 200 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 201 if (!drvdata) 202 return -ENOMEM; 203 204 drvdata->num_clks = devm_clk_bulk_get_all(dev, &drvdata->clks); 205 if (drvdata->num_clks < 0) 206 return dev_err_probe(dev, drvdata->num_clks, 207 "failed to get clocks\n"); 208 } 209 210 rdesc = data->rdesc; 211 212 base = devm_platform_ioremap_resource(pdev, 0); 213 if (IS_ERR(base)) 214 return PTR_ERR(base); 215 216 regmap = devm_regmap_init_mmio(dev, base, &qcom_refgen_regmap_config); 217 if (IS_ERR(regmap)) 218 return PTR_ERR(regmap); 219 220 init_data = of_get_regulator_init_data(dev, dev->of_node, rdesc); 221 if (!init_data) 222 return -ENOMEM; 223 224 config.dev = dev; 225 config.init_data = init_data; 226 config.of_node = dev->of_node; 227 config.regmap = regmap; 228 config.driver_data = drvdata; 229 230 rdev = devm_regulator_register(dev, rdesc, &config); 231 if (IS_ERR(rdev)) 232 return PTR_ERR(rdev); 233 234 return 0; 235 } 236 237 static const struct of_device_id qcom_refgen_match_table[] = { 238 { .compatible = "qcom,ipq9650-refgen-regulator", .data = &ipq9650_data }, 239 { .compatible = "qcom,sdm845-refgen-regulator", .data = &sdm845_data }, 240 { .compatible = "qcom,sm8250-refgen-regulator", .data = &sm8250_data }, 241 { } 242 }; 243 MODULE_DEVICE_TABLE(of, qcom_refgen_match_table); 244 245 static struct platform_driver qcom_refgen_driver = { 246 .probe = qcom_refgen_probe, 247 .driver = { 248 .name = "qcom-refgen-regulator", 249 .of_match_table = qcom_refgen_match_table, 250 }, 251 }; 252 module_platform_driver(qcom_refgen_driver); 253 254 MODULE_LICENSE("GPL"); 255 MODULE_DESCRIPTION("Qualcomm REFGEN regulator driver"); 256