diff options
-rw-r--r-- | drivers/clk/clk.c | 67 |
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 | ||
1462 | out: | 1499 | out: |
1463 | clk_prepare_unlock(); | 1500 | clk_prepare_unlock(); |