qcom-pm8008-regulator.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  5. * Copyright (c) 2024 Linaro Limited
  6. */
  7. #include <linux/array_size.h>
  8. #include <linux/bits.h>
  9. #include <linux/device.h>
  10. #include <linux/math.h>
  11. #include <linux/module.h>
  12. #include <linux/of.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/regmap.h>
  15. #include <linux/regulator/driver.h>
  16. #include <asm/byteorder.h>
  17. #define DEFAULT_VOLTAGE_STEPPER_RATE 38400
  18. #define LDO_STEPPER_CTL_REG 0x3b
  19. #define STEP_RATE_MASK GENMASK(1, 0)
  20. #define LDO_VSET_LB_REG 0x40
  21. #define LDO_ENABLE_REG 0x46
  22. #define ENABLE_BIT BIT(7)
  23. struct pm8008_regulator {
  24. struct regmap *regmap;
  25. struct regulator_desc desc;
  26. unsigned int base;
  27. };
  28. struct pm8008_regulator_data {
  29. const char *name;
  30. const char *supply_name;
  31. unsigned int base;
  32. int min_dropout_uV;
  33. const struct linear_range *voltage_range;
  34. };
  35. static const struct linear_range nldo_ranges[] = {
  36. REGULATOR_LINEAR_RANGE(528000, 0, 122, 8000),
  37. };
  38. static const struct linear_range pldo_ranges[] = {
  39. REGULATOR_LINEAR_RANGE(1504000, 0, 237, 8000),
  40. };
  41. static const struct pm8008_regulator_data pm8008_reg_data[] = {
  42. { "ldo1", "vdd-l1-l2", 0x4000, 225000, nldo_ranges, },
  43. { "ldo2", "vdd-l1-l2", 0x4100, 225000, nldo_ranges, },
  44. { "ldo3", "vdd-l3-l4", 0x4200, 300000, pldo_ranges, },
  45. { "ldo4", "vdd-l3-l4", 0x4300, 300000, pldo_ranges, },
  46. { "ldo5", "vdd-l5", 0x4400, 200000, pldo_ranges, },
  47. { "ldo6", "vdd-l6", 0x4500, 200000, pldo_ranges, },
  48. { "ldo7", "vdd-l7", 0x4600, 200000, pldo_ranges, },
  49. };
  50. static int pm8008_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
  51. {
  52. struct pm8008_regulator *preg = rdev_get_drvdata(rdev);
  53. unsigned int mV;
  54. __le16 val;
  55. int ret;
  56. ret = regulator_list_voltage_linear_range(rdev, sel);
  57. if (ret < 0)
  58. return ret;
  59. mV = DIV_ROUND_UP(ret, 1000);
  60. val = cpu_to_le16(mV);
  61. ret = regmap_bulk_write(preg->regmap, preg->base + LDO_VSET_LB_REG,
  62. &val, sizeof(val));
  63. if (ret < 0)
  64. return ret;
  65. return 0;
  66. }
  67. static int pm8008_regulator_get_voltage_sel(struct regulator_dev *rdev)
  68. {
  69. struct pm8008_regulator *preg = rdev_get_drvdata(rdev);
  70. unsigned int uV;
  71. __le16 val;
  72. int ret;
  73. ret = regmap_bulk_read(preg->regmap, preg->base + LDO_VSET_LB_REG,
  74. &val, sizeof(val));
  75. if (ret < 0)
  76. return ret;
  77. uV = le16_to_cpu(val) * 1000;
  78. return (uV - preg->desc.min_uV) / preg->desc.uV_step;
  79. }
  80. static const struct regulator_ops pm8008_regulator_ops = {
  81. .list_voltage = regulator_list_voltage_linear,
  82. .set_voltage_sel = pm8008_regulator_set_voltage_sel,
  83. .get_voltage_sel = pm8008_regulator_get_voltage_sel,
  84. .enable = regulator_enable_regmap,
  85. .disable = regulator_disable_regmap,
  86. .is_enabled = regulator_is_enabled_regmap,
  87. };
  88. static int pm8008_regulator_probe(struct platform_device *pdev)
  89. {
  90. const struct pm8008_regulator_data *data;
  91. struct regulator_config config = {};
  92. struct device *dev = &pdev->dev;
  93. struct pm8008_regulator *preg;
  94. struct regulator_desc *desc;
  95. struct regulator_dev *rdev;
  96. struct regmap *regmap;
  97. unsigned int val;
  98. int ret, i;
  99. regmap = dev_get_regmap(dev->parent, "secondary");
  100. if (!regmap)
  101. return -EINVAL;
  102. for (i = 0; i < ARRAY_SIZE(pm8008_reg_data); i++) {
  103. data = &pm8008_reg_data[i];
  104. preg = devm_kzalloc(dev, sizeof(*preg), GFP_KERNEL);
  105. if (!preg)
  106. return -ENOMEM;
  107. preg->regmap = regmap;
  108. preg->base = data->base;
  109. desc = &preg->desc;
  110. desc->name = data->name;
  111. desc->supply_name = data->supply_name;
  112. desc->of_match = data->name;
  113. desc->regulators_node = of_match_ptr("regulators");
  114. desc->ops = &pm8008_regulator_ops;
  115. desc->type = REGULATOR_VOLTAGE;
  116. desc->owner = THIS_MODULE;
  117. desc->linear_ranges = data->voltage_range;
  118. desc->n_linear_ranges = 1;
  119. desc->uV_step = desc->linear_ranges[0].step;
  120. desc->min_uV = desc->linear_ranges[0].min;
  121. desc->n_voltages = linear_range_values_in_range(&desc->linear_ranges[0]);
  122. ret = regmap_read(regmap, preg->base + LDO_STEPPER_CTL_REG, &val);
  123. if (ret < 0) {
  124. dev_err(dev, "failed to read step rate: %d\n", ret);
  125. return ret;
  126. }
  127. val &= STEP_RATE_MASK;
  128. desc->ramp_delay = DEFAULT_VOLTAGE_STEPPER_RATE >> val;
  129. desc->min_dropout_uV = data->min_dropout_uV;
  130. desc->enable_reg = preg->base + LDO_ENABLE_REG;
  131. desc->enable_mask = ENABLE_BIT;
  132. config.dev = dev->parent;
  133. config.driver_data = preg;
  134. config.regmap = regmap;
  135. rdev = devm_regulator_register(dev, desc, &config);
  136. if (IS_ERR(rdev)) {
  137. ret = PTR_ERR(rdev);
  138. dev_err(dev, "failed to register regulator %s: %d\n",
  139. desc->name, ret);
  140. return ret;
  141. }
  142. }
  143. return 0;
  144. }
  145. static const struct platform_device_id pm8008_regulator_id_table[] = {
  146. { "pm8008-regulator" },
  147. { }
  148. };
  149. MODULE_DEVICE_TABLE(platform, pm8008_regulator_id_table);
  150. static struct platform_driver pm8008_regulator_driver = {
  151. .driver = {
  152. .name = "qcom-pm8008-regulator",
  153. },
  154. .probe = pm8008_regulator_probe,
  155. .id_table = pm8008_regulator_id_table,
  156. };
  157. module_platform_driver(pm8008_regulator_driver);
  158. MODULE_DESCRIPTION("Qualcomm PM8008 PMIC regulator driver");
  159. MODULE_LICENSE("GPL");