btrfs: fix race between quota disable and quota assign ioctls
[platform/kernel/linux-rpi.git] / drivers / regulator / qcom_smd-regulator.c
index 198fcc6..2fe13c7 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/soc/qcom/smd-rpm.h>
 
 struct qcom_rpm_reg {
@@ -356,10 +357,10 @@ static const struct regulator_desc pm8941_switch = {
 
 static const struct regulator_desc pm8916_pldo = {
        .linear_ranges = (struct linear_range[]) {
-               REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500),
+               REGULATOR_LINEAR_RANGE(1750000, 0, 127, 12500),
        },
        .n_linear_ranges = 1,
-       .n_voltages = 209,
+       .n_voltages = 128,
        .ops = &rpm_smps_ldo_ops,
 };
 
@@ -925,32 +926,31 @@ static const struct rpm_regulator_data rpm_pm8950_regulators[] = {
        { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8950_hfsmps, "vdd_s2" },
        { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8950_hfsmps, "vdd_s3" },
        { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8950_hfsmps, "vdd_s4" },
-       { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8950_ftsmps2p5, "vdd_s5" },
+       /* S5 is managed via SPMI. */
        { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8950_hfsmps, "vdd_s6" },
 
        { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8950_ult_nldo, "vdd_l1_l19" },
        { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8950_ult_nldo, "vdd_l2_l23" },
        { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8950_ult_nldo, "vdd_l3" },
-       { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16" },
-       { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8950_pldo_lv, "vdd_l4_l5_l6_l7_l16" },
-       { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8950_pldo_lv, "vdd_l4_l5_l6_l7_l16" },
-       { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8950_pldo_lv, "vdd_l4_l5_l6_l7_l16" },
+       /* L4 seems not to exist. */
+       { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8950_pldo_lv, "vdd_l5_l6_l7_l16" },
+       { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8950_pldo_lv, "vdd_l5_l6_l7_l16" },
+       { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8950_pldo_lv, "vdd_l5_l6_l7_l16" },
        { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22" },
        { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18" },
        { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8950_ult_nldo, "vdd_l9_l10_l13_l14_l15_l18"},
-       { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22"},
-       { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22"},
-       { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18"},
-       { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18"},
-       { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18"},
-       { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16"},
-       { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22"},
-       { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18"},
-       { "l19", QCOM_SMD_RPM_LDOA, 18, &pm8950_pldo, "vdd_l1_l19"},
-       { "l20", QCOM_SMD_RPM_LDOA, 18, &pm8950_pldo, "vdd_l20"},
-       { "l21", QCOM_SMD_RPM_LDOA, 18, &pm8950_pldo, "vdd_l21"},
-       { "l22", QCOM_SMD_RPM_LDOA, 18, &pm8950_pldo, "vdd_l8_l11_l12_l17_l22"},
-       { "l23", QCOM_SMD_RPM_LDOA, 18, &pm8950_pldo, "vdd_l2_l23"},
+       { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22" },
+       { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22" },
+       { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18" },
+       { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18" },
+       { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8950_ult_pldo, "vdd_l9_l10_l13_l14_l15_l18" },
+       { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8950_ult_pldo, "vdd_l5_l6_l7_l16" },
+       { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8950_ult_pldo, "vdd_l8_l11_l12_l17_l22" },
+       /* L18 seems not to exist. */
+       { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8950_pldo, "vdd_l1_l19" },
+       /* L20 & L21 seem not to exist. */
+       { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8950_pldo, "vdd_l8_l11_l12_l17_l22" },
+       { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8950_pldo, "vdd_l2_l23" },
        {}
 };
 
@@ -1190,52 +1190,93 @@ static const struct of_device_id rpm_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rpm_of_match);
 
-static int rpm_reg_probe(struct platform_device *pdev)
+/**
+ * rpm_regulator_init_vreg() - initialize all attributes of a qcom_smd-regulator
+ * @vreg:              Pointer to the individual qcom_smd-regulator resource
+ * @dev:               Pointer to the top level qcom_smd-regulator PMIC device
+ * @node:              Pointer to the individual qcom_smd-regulator resource
+ *                     device node
+ * @rpm:               Pointer to the rpm bus node
+ * @pmic_rpm_data:     Pointer to a null-terminated array of qcom_smd-regulator
+ *                     resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpm_regulator_init_vreg(struct qcom_rpm_reg *vreg, struct device *dev,
+                                  struct device_node *node, struct qcom_smd_rpm *rpm,
+                                  const struct rpm_regulator_data *pmic_rpm_data)
 {
-       const struct rpm_regulator_data *reg;
-       const struct of_device_id *match;
-       struct regulator_config config = { };
+       struct regulator_config config = {};
+       const struct rpm_regulator_data *rpm_data;
        struct regulator_dev *rdev;
+       int ret;
+
+       for (rpm_data = pmic_rpm_data; rpm_data->name; rpm_data++)
+               if (of_node_name_eq(node, rpm_data->name))
+                       break;
+
+       if (!rpm_data->name) {
+               dev_err(dev, "Unknown regulator %pOFn\n", node);
+               return -EINVAL;
+       }
+
+       vreg->dev       = dev;
+       vreg->rpm       = rpm;
+       vreg->type      = rpm_data->type;
+       vreg->id        = rpm_data->id;
+
+       memcpy(&vreg->desc, rpm_data->desc, sizeof(vreg->desc));
+       vreg->desc.name = rpm_data->name;
+       vreg->desc.supply_name = rpm_data->supply;
+       vreg->desc.owner = THIS_MODULE;
+       vreg->desc.type = REGULATOR_VOLTAGE;
+       vreg->desc.of_match = rpm_data->name;
+
+       config.dev              = dev;
+       config.of_node          = node;
+       config.driver_data      = vreg;
+
+       rdev = devm_regulator_register(dev, &vreg->desc, &config);
+       if (IS_ERR(rdev)) {
+               ret = PTR_ERR(rdev);
+               dev_err(dev, "%pOFn: devm_regulator_register() failed, ret=%d\n", node, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rpm_reg_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct rpm_regulator_data *vreg_data;
+       struct device_node *node;
        struct qcom_rpm_reg *vreg;
        struct qcom_smd_rpm *rpm;
+       int ret;
 
        rpm = dev_get_drvdata(pdev->dev.parent);
        if (!rpm) {
-               dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
+               dev_err(&pdev->dev, "Unable to retrieve handle to rpm\n");
                return -ENODEV;
        }
 
-       match = of_match_device(rpm_of_match, &pdev->dev);
-       if (!match) {
-               dev_err(&pdev->dev, "failed to match device\n");
+       vreg_data = of_device_get_match_data(dev);
+       if (!vreg_data)
                return -ENODEV;
-       }
 
-       for (reg = match->data; reg->name; reg++) {
+       for_each_available_child_of_node(dev->of_node, node) {
                vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
-               if (!vreg)
+               if (!vreg) {
+                       of_node_put(node);
                        return -ENOMEM;
+               }
+
+               ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data);
 
-               vreg->dev = &pdev->dev;
-               vreg->type = reg->type;
-               vreg->id = reg->id;
-               vreg->rpm = rpm;
-
-               memcpy(&vreg->desc, reg->desc, sizeof(vreg->desc));
-
-               vreg->desc.id = -1;
-               vreg->desc.owner = THIS_MODULE;
-               vreg->desc.type = REGULATOR_VOLTAGE;
-               vreg->desc.name = reg->name;
-               vreg->desc.supply_name = reg->supply;
-               vreg->desc.of_match = reg->name;
-
-               config.dev = &pdev->dev;
-               config.driver_data = vreg;
-               rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config);
-               if (IS_ERR(rdev)) {
-                       dev_err(&pdev->dev, "failed to register %s\n", reg->name);
-                       return PTR_ERR(rdev);
+               if (ret < 0) {
+                       of_node_put(node);
+                       return ret;
                }
        }