aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/clk.c67
1 files changed, 52 insertions, 15 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c83e8e543bab..de6b459de78e 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1363,31 +1363,71 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
1363 unsigned long flags; 1363 unsigned long flags;
1364 int ret = 0; 1364 int ret = 0;
1365 struct clk *old_parent = clk->parent; 1365 struct clk *old_parent = clk->parent;
1366 bool migrated_enable = false;
1366 1367
1367 /* migrate prepare and enable */ 1368 /* migrate prepare */
1368 if (clk->prepare_count) 1369 if (clk->prepare_count)
1369 __clk_prepare(parent); 1370 __clk_prepare(parent);
1370 1371
1371 /* FIXME replace with clk_is_enabled(clk) someday */
1372 flags = clk_enable_lock(); 1372 flags = clk_enable_lock();
1373 if (clk->enable_count) 1373
1374 /* migrate enable */
1375 if (clk->enable_count) {
1374 __clk_enable(parent); 1376 __clk_enable(parent);
1377 migrated_enable = true;
1378 }
1379
1380 /* update the clk tree topology */
1381 clk_reparent(clk, parent);
1382
1375 clk_enable_unlock(flags); 1383 clk_enable_unlock(flags);
1376 1384
1377 /* change clock input source */ 1385 /* change clock input source */
1378 if (parent && clk->ops->set_parent) 1386 if (parent && clk->ops->set_parent)
1379 ret = clk->ops->set_parent(clk->hw, p_index); 1387 ret = clk->ops->set_parent(clk->hw, p_index);
1380 1388
1381 /* clean up old prepare and enable */ 1389 if (ret) {
1382 flags = clk_enable_lock(); 1390 /*
1383 if (clk->enable_count) 1391 * The error handling is tricky due to that we need to release
1392 * the spinlock while issuing the .set_parent callback. This
1393 * means the new parent might have been enabled/disabled in
1394 * between, which must be considered when doing rollback.
1395 */
1396 flags = clk_enable_lock();
1397
1398 clk_reparent(clk, old_parent);
1399
1400 if (migrated_enable && clk->enable_count) {
1401 __clk_disable(parent);
1402 } else if (migrated_enable && (clk->enable_count == 0)) {
1403 __clk_disable(old_parent);
1404 } else if (!migrated_enable && clk->enable_count) {
1405 __clk_disable(parent);
1406 __clk_enable(old_parent);
1407 }
1408
1409 clk_enable_unlock(flags);
1410
1411 if (clk->prepare_count)
1412 __clk_unprepare(parent);
1413
1414 return ret;
1415 }
1416
1417 /* clean up enable for old parent if migration was done */
1418 if (migrated_enable) {
1419 flags = clk_enable_lock();
1384 __clk_disable(old_parent); 1420 __clk_disable(old_parent);
1385 clk_enable_unlock(flags); 1421 clk_enable_unlock(flags);
1422 }
1386 1423
1424 /* clean up prepare for old parent if migration was done */
1387 if (clk->prepare_count) 1425 if (clk->prepare_count)
1388 __clk_unprepare(old_parent); 1426 __clk_unprepare(old_parent);
1389 1427
1390 return ret; 1428 /* update debugfs with new clk tree topology */
1429 clk_debug_reparent(clk, parent);
1430 return 0;
1391} 1431}
1392 1432
1393/** 1433/**
@@ -1450,14 +1490,11 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
1450 /* do the re-parent */ 1490 /* do the re-parent */
1451 ret = __clk_set_parent(clk, parent, p_index); 1491 ret = __clk_set_parent(clk, parent, p_index);
1452 1492
1453 /* propagate ABORT_RATE_CHANGE if .set_parent failed */ 1493 /* propagate rate recalculation accordingly */
1454 if (ret) { 1494 if (ret)
1455 __clk_recalc_rates(clk, ABORT_RATE_CHANGE); 1495 __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
1456 goto out; 1496 else
1457 } 1497 __clk_recalc_rates(clk, POST_RATE_CHANGE);
1458
1459 /* propagate rate recalculation downstream */
1460 __clk_reparent(clk, parent);
1461 1498
1462out: 1499out:
1463 clk_prepare_unlock(); 1500 clk_prepare_unlock();