diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2013-04-02 17:09:38 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2013-04-08 21:19:32 -0400 |
commit | 031dcc9bd4164a7482b89987d5b9ecb3af5e9033 (patch) | |
tree | acab66af5c502cd7ad651de07356e33d3ad36b3b /drivers/clk | |
parent | b33d212f4910ca44bd37d5e08422230687bd1378 (diff) |
clk: Fixup errorhandling for clk_set_parent
Fixup the broken feature of allowing reparent of a clk to the
orhpan list and vice verse. When operating on a single-parent
clk, the .set_parent callback for the clk hw is optional to
implement, but for a multi-parent clk it is mandatory.
Moreover improve the errorhandling by verifying the prerequisites
before triggering clk notifiers. This will prevent unnecessary
rollback with ABORT_RATE_CHANGE.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Rajagopal Venkat <rajagopal.venkat@linaro.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/clk.c | 56 |
1 files changed, 36 insertions, 20 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 013a3c7fea5b..c83e8e543bab 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c | |||
@@ -1332,15 +1332,10 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) | |||
1332 | __clk_recalc_rates(clk, POST_RATE_CHANGE); | 1332 | __clk_recalc_rates(clk, POST_RATE_CHANGE); |
1333 | } | 1333 | } |
1334 | 1334 | ||
1335 | static int __clk_set_parent(struct clk *clk, struct clk *parent) | 1335 | static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) |
1336 | { | 1336 | { |
1337 | struct clk *old_parent; | ||
1338 | unsigned long flags; | ||
1339 | int ret = -EINVAL; | ||
1340 | u8 i; | 1337 | u8 i; |
1341 | 1338 | ||
1342 | old_parent = clk->parent; | ||
1343 | |||
1344 | if (!clk->parents) | 1339 | if (!clk->parents) |
1345 | clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), | 1340 | clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), |
1346 | GFP_KERNEL); | 1341 | GFP_KERNEL); |
@@ -1360,11 +1355,14 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) | |||
1360 | } | 1355 | } |
1361 | } | 1356 | } |
1362 | 1357 | ||
1363 | if (i == clk->num_parents) { | 1358 | return i; |
1364 | pr_debug("%s: clock %s is not a possible parent of clock %s\n", | 1359 | } |
1365 | __func__, parent->name, clk->name); | 1360 | |
1366 | goto out; | 1361 | static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) |
1367 | } | 1362 | { |
1363 | unsigned long flags; | ||
1364 | int ret = 0; | ||
1365 | struct clk *old_parent = clk->parent; | ||
1368 | 1366 | ||
1369 | /* migrate prepare and enable */ | 1367 | /* migrate prepare and enable */ |
1370 | if (clk->prepare_count) | 1368 | if (clk->prepare_count) |
@@ -1377,7 +1375,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) | |||
1377 | clk_enable_unlock(flags); | 1375 | clk_enable_unlock(flags); |
1378 | 1376 | ||
1379 | /* change clock input source */ | 1377 | /* change clock input source */ |
1380 | ret = clk->ops->set_parent(clk->hw, i); | 1378 | if (parent && clk->ops->set_parent) |
1379 | ret = clk->ops->set_parent(clk->hw, p_index); | ||
1381 | 1380 | ||
1382 | /* clean up old prepare and enable */ | 1381 | /* clean up old prepare and enable */ |
1383 | flags = clk_enable_lock(); | 1382 | flags = clk_enable_lock(); |
@@ -1388,7 +1387,6 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) | |||
1388 | if (clk->prepare_count) | 1387 | if (clk->prepare_count) |
1389 | __clk_unprepare(old_parent); | 1388 | __clk_unprepare(old_parent); |
1390 | 1389 | ||
1391 | out: | ||
1392 | return ret; | 1390 | return ret; |
1393 | } | 1391 | } |
1394 | 1392 | ||
@@ -1407,11 +1405,14 @@ out: | |||
1407 | int clk_set_parent(struct clk *clk, struct clk *parent) | 1405 | int clk_set_parent(struct clk *clk, struct clk *parent) |
1408 | { | 1406 | { |
1409 | int ret = 0; | 1407 | int ret = 0; |
1408 | u8 p_index = 0; | ||
1409 | unsigned long p_rate = 0; | ||
1410 | 1410 | ||
1411 | if (!clk || !clk->ops) | 1411 | if (!clk || !clk->ops) |
1412 | return -EINVAL; | 1412 | return -EINVAL; |
1413 | 1413 | ||
1414 | if (!clk->ops->set_parent) | 1414 | /* verify ops for for multi-parent clks */ |
1415 | if ((clk->num_parents > 1) && (!clk->ops->set_parent)) | ||
1415 | return -ENOSYS; | 1416 | return -ENOSYS; |
1416 | 1417 | ||
1417 | /* prevent racing with updates to the clock topology */ | 1418 | /* prevent racing with updates to the clock topology */ |
@@ -1420,19 +1421,34 @@ int clk_set_parent(struct clk *clk, struct clk *parent) | |||
1420 | if (clk->parent == parent) | 1421 | if (clk->parent == parent) |
1421 | goto out; | 1422 | goto out; |
1422 | 1423 | ||
1424 | /* check that we are allowed to re-parent if the clock is in use */ | ||
1425 | if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { | ||
1426 | ret = -EBUSY; | ||
1427 | goto out; | ||
1428 | } | ||
1429 | |||
1430 | /* try finding the new parent index */ | ||
1431 | if (parent) { | ||
1432 | p_index = clk_fetch_parent_index(clk, parent); | ||
1433 | p_rate = parent->rate; | ||
1434 | if (p_index == clk->num_parents) { | ||
1435 | pr_debug("%s: clk %s can not be parent of clk %s\n", | ||
1436 | __func__, parent->name, clk->name); | ||
1437 | ret = -EINVAL; | ||
1438 | goto out; | ||
1439 | } | ||
1440 | } | ||
1441 | |||
1423 | /* propagate PRE_RATE_CHANGE notifications */ | 1442 | /* propagate PRE_RATE_CHANGE notifications */ |
1424 | if (clk->notifier_count) | 1443 | if (clk->notifier_count) |
1425 | ret = __clk_speculate_rates(clk, parent->rate); | 1444 | ret = __clk_speculate_rates(clk, p_rate); |
1426 | 1445 | ||
1427 | /* abort if a driver objects */ | 1446 | /* abort if a driver objects */ |
1428 | if (ret == NOTIFY_STOP) | 1447 | if (ret == NOTIFY_STOP) |
1429 | goto out; | 1448 | goto out; |
1430 | 1449 | ||
1431 | /* only re-parent if the clock is not in use */ | 1450 | /* do the re-parent */ |
1432 | if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) | 1451 | ret = __clk_set_parent(clk, parent, p_index); |
1433 | ret = -EBUSY; | ||
1434 | else | ||
1435 | ret = __clk_set_parent(clk, parent); | ||
1436 | 1452 | ||
1437 | /* propagate ABORT_RATE_CHANGE if .set_parent failed */ | 1453 | /* propagate ABORT_RATE_CHANGE if .set_parent failed */ |
1438 | if (ret) { | 1454 | if (ret) { |