aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2013-04-02 17:09:38 -0400
committerMike Turquette <mturquette@linaro.org>2013-04-08 21:19:32 -0400
commit031dcc9bd4164a7482b89987d5b9ecb3af5e9033 (patch)
treeacab66af5c502cd7ad651de07356e33d3ad36b3b /drivers/clk
parentb33d212f4910ca44bd37d5e08422230687bd1378 (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.c56
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
1335static int __clk_set_parent(struct clk *clk, struct clk *parent) 1335static 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; 1361static 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
1391out:
1392 return ret; 1390 return ret;
1393} 1391}
1394 1392
@@ -1407,11 +1405,14 @@ out:
1407int clk_set_parent(struct clk *clk, struct clk *parent) 1405int 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) {