Skip to content

Commit 7b439aa

Browse files
jhovoldlag-linaro
authored andcommitted
mfd: qcom-spmi-pmic: Fix revid implementation
The Qualcomm SPMI PMIC revid implementation is broken in multiple ways. First, it assumes that just because the sibling base device has been registered that means that it is also bound to a driver, which may not be the case (e.g. due to probe deferral or asynchronous probe). This could trigger a NULL-pointer dereference when attempting to access the driver data of the unbound device. Second, it accesses driver data of a sibling device directly and without any locking, which means that the driver data may be freed while it is being accessed (e.g. on driver unbind). Third, it leaks a struct device reference to the sibling device which is looked up using the spmi_device_from_of() every time a function (child) device is calling the revid function (e.g. on probe). Fix this mess by reimplementing the revid lookup so that it is done only at probe of the PMIC device; the base device fetches the revid info from the hardware, while any secondary SPMI device fetches the information from the base device and caches it so that it can be accessed safely from its children. If the base device has not been probed yet then probe of a secondary device is deferred. Fixes: e9c11c6 ("mfd: qcom-spmi-pmic: expose the PMIC revid information to clients") Cc: [email protected] # 6.0 Signed-off-by: Johan Hovold <[email protected]> Acked-by: Caleb Connolly <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lee Jones <[email protected]>
1 parent a0fa44c commit 7b439aa

File tree

1 file changed

+53
-16
lines changed

1 file changed

+53
-16
lines changed

drivers/mfd/qcom-spmi-pmic.c

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ struct qcom_spmi_dev {
3030
struct qcom_spmi_pmic pmic;
3131
};
3232

33+
static DEFINE_MUTEX(pmic_spmi_revid_lock);
34+
3335
#define N_USIDS(n) ((void *)n)
3436

3537
static const struct of_device_id pmic_spmi_id_table[] = {
@@ -76,24 +78,21 @@ static const struct of_device_id pmic_spmi_id_table[] = {
7678
*
7779
* This only supports PMICs with 1 or 2 USIDs.
7880
*/
79-
static struct spmi_device *qcom_pmic_get_base_usid(struct device *dev)
81+
static struct spmi_device *qcom_pmic_get_base_usid(struct spmi_device *sdev, struct qcom_spmi_dev *ctx)
8082
{
81-
struct spmi_device *sdev;
82-
struct qcom_spmi_dev *ctx;
8383
struct device_node *spmi_bus;
8484
struct device_node *child;
8585
int function_parent_usid, ret;
8686
u32 pmic_addr;
8787

88-
sdev = to_spmi_device(dev);
89-
ctx = dev_get_drvdata(&sdev->dev);
90-
9188
/*
9289
* Quick return if the function device is already in the base
9390
* USID. This will always be hit for PMICs with only 1 USID.
9491
*/
95-
if (sdev->usid % ctx->num_usids == 0)
92+
if (sdev->usid % ctx->num_usids == 0) {
93+
get_device(&sdev->dev);
9694
return sdev;
95+
}
9796

9897
function_parent_usid = sdev->usid;
9998

@@ -118,10 +117,8 @@ static struct spmi_device *qcom_pmic_get_base_usid(struct device *dev)
118117
sdev = spmi_device_from_of(child);
119118
if (!sdev) {
120119
/*
121-
* If the base USID for this PMIC hasn't probed yet
122-
* but the secondary USID has, then we need to defer
123-
* the function driver so that it will attempt to
124-
* probe again when the base USID is ready.
120+
* If the base USID for this PMIC hasn't been
121+
* registered yet then we need to defer.
125122
*/
126123
sdev = ERR_PTR(-EPROBE_DEFER);
127124
}
@@ -135,6 +132,35 @@ static struct spmi_device *qcom_pmic_get_base_usid(struct device *dev)
135132
return sdev;
136133
}
137134

135+
static int pmic_spmi_get_base_revid(struct spmi_device *sdev, struct qcom_spmi_dev *ctx)
136+
{
137+
struct qcom_spmi_dev *base_ctx;
138+
struct spmi_device *base;
139+
int ret = 0;
140+
141+
base = qcom_pmic_get_base_usid(sdev, ctx);
142+
if (IS_ERR(base))
143+
return PTR_ERR(base);
144+
145+
/*
146+
* Copy revid info from base device if it has probed and is still
147+
* bound to its driver.
148+
*/
149+
mutex_lock(&pmic_spmi_revid_lock);
150+
base_ctx = spmi_device_get_drvdata(base);
151+
if (!base_ctx) {
152+
ret = -EPROBE_DEFER;
153+
goto out_unlock;
154+
}
155+
memcpy(&ctx->pmic, &base_ctx->pmic, sizeof(ctx->pmic));
156+
out_unlock:
157+
mutex_unlock(&pmic_spmi_revid_lock);
158+
159+
put_device(&base->dev);
160+
161+
return ret;
162+
}
163+
138164
static int pmic_spmi_load_revid(struct regmap *map, struct device *dev,
139165
struct qcom_spmi_pmic *pmic)
140166
{
@@ -210,11 +236,7 @@ const struct qcom_spmi_pmic *qcom_pmic_get(struct device *dev)
210236
if (!of_match_device(pmic_spmi_id_table, dev->parent))
211237
return ERR_PTR(-EINVAL);
212238

213-
sdev = qcom_pmic_get_base_usid(dev->parent);
214-
215-
if (IS_ERR(sdev))
216-
return ERR_CAST(sdev);
217-
239+
sdev = to_spmi_device(dev->parent);
218240
spmi = dev_get_drvdata(&sdev->dev);
219241

220242
return &spmi->pmic;
@@ -249,16 +271,31 @@ static int pmic_spmi_probe(struct spmi_device *sdev)
249271
ret = pmic_spmi_load_revid(regmap, &sdev->dev, &ctx->pmic);
250272
if (ret < 0)
251273
return ret;
274+
} else {
275+
ret = pmic_spmi_get_base_revid(sdev, ctx);
276+
if (ret)
277+
return ret;
252278
}
279+
280+
mutex_lock(&pmic_spmi_revid_lock);
253281
spmi_device_set_drvdata(sdev, ctx);
282+
mutex_unlock(&pmic_spmi_revid_lock);
254283

255284
return devm_of_platform_populate(&sdev->dev);
256285
}
257286

287+
static void pmic_spmi_remove(struct spmi_device *sdev)
288+
{
289+
mutex_lock(&pmic_spmi_revid_lock);
290+
spmi_device_set_drvdata(sdev, NULL);
291+
mutex_unlock(&pmic_spmi_revid_lock);
292+
}
293+
258294
MODULE_DEVICE_TABLE(of, pmic_spmi_id_table);
259295

260296
static struct spmi_driver pmic_spmi_driver = {
261297
.probe = pmic_spmi_probe,
298+
.remove = pmic_spmi_remove,
262299
.driver = {
263300
.name = "pmic-spmi",
264301
.of_match_table = pmic_spmi_id_table,

0 commit comments

Comments
 (0)