diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 234 |
1 files changed, 204 insertions, 30 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 02b6016393a..84cc0bdda3a 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c | |||
@@ -146,9 +146,10 @@ | |||
146 | #include <plat/prcm.h> | 146 | #include <plat/prcm.h> |
147 | 147 | ||
148 | #include "cm2xxx_3xxx.h" | 148 | #include "cm2xxx_3xxx.h" |
149 | #include "cm44xx.h" | 149 | #include "cminst44xx.h" |
150 | #include "prm2xxx_3xxx.h" | 150 | #include "prm2xxx_3xxx.h" |
151 | #include "prm44xx.h" | 151 | #include "prm44xx.h" |
152 | #include "prminst44xx.h" | ||
152 | #include "mux.h" | 153 | #include "mux.h" |
153 | 154 | ||
154 | /* Maximum microseconds to wait for OMAP module to softreset */ | 155 | /* Maximum microseconds to wait for OMAP module to softreset */ |
@@ -679,6 +680,56 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) | |||
679 | } | 680 | } |
680 | 681 | ||
681 | /** | 682 | /** |
683 | * _enable_module - enable CLKCTRL modulemode on OMAP4 | ||
684 | * @oh: struct omap_hwmod * | ||
685 | * | ||
686 | * Enables the PRCM module mode related to the hwmod @oh. | ||
687 | * No return value. | ||
688 | */ | ||
689 | static void _enable_module(struct omap_hwmod *oh) | ||
690 | { | ||
691 | /* The module mode does not exist prior OMAP4 */ | ||
692 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | ||
693 | return; | ||
694 | |||
695 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) | ||
696 | return; | ||
697 | |||
698 | pr_debug("omap_hwmod: %s: _enable_module: %d\n", | ||
699 | oh->name, oh->prcm.omap4.modulemode); | ||
700 | |||
701 | omap4_cminst_module_enable(oh->prcm.omap4.modulemode, | ||
702 | oh->clkdm->prcm_partition, | ||
703 | oh->clkdm->cm_inst, | ||
704 | oh->clkdm->clkdm_offs, | ||
705 | oh->prcm.omap4.clkctrl_offs); | ||
706 | } | ||
707 | |||
708 | /** | ||
709 | * _disable_module - enable CLKCTRL modulemode on OMAP4 | ||
710 | * @oh: struct omap_hwmod * | ||
711 | * | ||
712 | * Disable the PRCM module mode related to the hwmod @oh. | ||
713 | * No return value. | ||
714 | */ | ||
715 | static void _disable_module(struct omap_hwmod *oh) | ||
716 | { | ||
717 | /* The module mode does not exist prior OMAP4 */ | ||
718 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | ||
719 | return; | ||
720 | |||
721 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) | ||
722 | return; | ||
723 | |||
724 | pr_debug("omap_hwmod: %s: _disable_module\n", oh->name); | ||
725 | |||
726 | omap4_cminst_module_disable(oh->clkdm->prcm_partition, | ||
727 | oh->clkdm->cm_inst, | ||
728 | oh->clkdm->clkdm_offs, | ||
729 | oh->prcm.omap4.clkctrl_offs); | ||
730 | } | ||
731 | |||
732 | /** | ||
682 | * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh | 733 | * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh |
683 | * @oh: struct omap_hwmod *oh | 734 | * @oh: struct omap_hwmod *oh |
684 | * | 735 | * |
@@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name) | |||
990 | 1041 | ||
991 | return oh; | 1042 | return oh; |
992 | } | 1043 | } |
1044 | /** | ||
1045 | * _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod | ||
1046 | * @oh: struct omap_hwmod * | ||
1047 | * | ||
1048 | * Convert a clockdomain name stored in a struct omap_hwmod into a | ||
1049 | * clockdomain pointer, and save it into the struct omap_hwmod. | ||
1050 | * return -EINVAL if clkdm_name does not exist or if the lookup failed. | ||
1051 | */ | ||
1052 | static int _init_clkdm(struct omap_hwmod *oh) | ||
1053 | { | ||
1054 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | ||
1055 | return 0; | ||
1056 | |||
1057 | if (!oh->clkdm_name) { | ||
1058 | pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name); | ||
1059 | return -EINVAL; | ||
1060 | } | ||
1061 | |||
1062 | oh->clkdm = clkdm_lookup(oh->clkdm_name); | ||
1063 | if (!oh->clkdm) { | ||
1064 | pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n", | ||
1065 | oh->name, oh->clkdm_name); | ||
1066 | return -EINVAL; | ||
1067 | } | ||
1068 | |||
1069 | pr_debug("omap_hwmod: %s: associated to clkdm %s\n", | ||
1070 | oh->name, oh->clkdm_name); | ||
1071 | |||
1072 | return 0; | ||
1073 | } | ||
993 | 1074 | ||
994 | /** | 1075 | /** |
995 | * _init_clocks - clk_get() all clocks associated with this hwmod | 1076 | * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as |
1077 | * well the clockdomain. | ||
996 | * @oh: struct omap_hwmod * | 1078 | * @oh: struct omap_hwmod * |
997 | * @data: not used; pass NULL | 1079 | * @data: not used; pass NULL |
998 | * | 1080 | * |
@@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) | |||
1012 | ret |= _init_main_clk(oh); | 1094 | ret |= _init_main_clk(oh); |
1013 | ret |= _init_interface_clks(oh); | 1095 | ret |= _init_interface_clks(oh); |
1014 | ret |= _init_opt_clks(oh); | 1096 | ret |= _init_opt_clks(oh); |
1097 | ret |= _init_clkdm(oh); | ||
1015 | 1098 | ||
1016 | if (!ret) | 1099 | if (!ret) |
1017 | oh->_state = _HWMOD_STATE_CLKS_INITED; | 1100 | oh->_state = _HWMOD_STATE_CLKS_INITED; |
@@ -1028,7 +1111,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) | |||
1028 | * Wait for a module @oh to leave slave idle. Returns 0 if the module | 1111 | * Wait for a module @oh to leave slave idle. Returns 0 if the module |
1029 | * does not have an IDLEST bit or if the module successfully leaves | 1112 | * does not have an IDLEST bit or if the module successfully leaves |
1030 | * slave idle; otherwise, pass along the return value of the | 1113 | * slave idle; otherwise, pass along the return value of the |
1031 | * appropriate *_cm_wait_module_ready() function. | 1114 | * appropriate *_cm*_wait_module_ready() function. |
1032 | */ | 1115 | */ |
1033 | static int _wait_target_ready(struct omap_hwmod *oh) | 1116 | static int _wait_target_ready(struct omap_hwmod *oh) |
1034 | { | 1117 | { |
@@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh) | |||
1055 | oh->prcm.omap2.idlest_reg_id, | 1138 | oh->prcm.omap2.idlest_reg_id, |
1056 | oh->prcm.omap2.idlest_idle_bit); | 1139 | oh->prcm.omap2.idlest_idle_bit); |
1057 | } else if (cpu_is_omap44xx()) { | 1140 | } else if (cpu_is_omap44xx()) { |
1058 | ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg); | 1141 | if (!oh->clkdm) |
1142 | return -EINVAL; | ||
1143 | |||
1144 | ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition, | ||
1145 | oh->clkdm->cm_inst, | ||
1146 | oh->clkdm->clkdm_offs, | ||
1147 | oh->prcm.omap4.clkctrl_offs); | ||
1059 | } else { | 1148 | } else { |
1060 | BUG(); | 1149 | BUG(); |
1061 | }; | 1150 | }; |
@@ -1064,6 +1153,36 @@ static int _wait_target_ready(struct omap_hwmod *oh) | |||
1064 | } | 1153 | } |
1065 | 1154 | ||
1066 | /** | 1155 | /** |
1156 | * _wait_target_disable - wait for a module to be disabled | ||
1157 | * @oh: struct omap_hwmod * | ||
1158 | * | ||
1159 | * Wait for a module @oh to enter slave idle. Returns 0 if the module | ||
1160 | * does not have an IDLEST bit or if the module successfully enters | ||
1161 | * slave idle; otherwise, pass along the return value of the | ||
1162 | * appropriate *_cm*_wait_module_idle() function. | ||
1163 | */ | ||
1164 | static int _wait_target_disable(struct omap_hwmod *oh) | ||
1165 | { | ||
1166 | /* TODO: For now just handle OMAP4+ */ | ||
1167 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | ||
1168 | return 0; | ||
1169 | |||
1170 | if (!oh) | ||
1171 | return -EINVAL; | ||
1172 | |||
1173 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT) | ||
1174 | return 0; | ||
1175 | |||
1176 | if (oh->flags & HWMOD_NO_IDLEST) | ||
1177 | return 0; | ||
1178 | |||
1179 | return omap4_cminst_wait_module_idle(oh->clkdm->prcm_partition, | ||
1180 | oh->clkdm->cm_inst, | ||
1181 | oh->clkdm->clkdm_offs, | ||
1182 | oh->prcm.omap4.clkctrl_offs); | ||
1183 | } | ||
1184 | |||
1185 | /** | ||
1067 | * _lookup_hardreset - fill register bit info for this hwmod/reset line | 1186 | * _lookup_hardreset - fill register bit info for this hwmod/reset line |
1068 | * @oh: struct omap_hwmod * | 1187 | * @oh: struct omap_hwmod * |
1069 | * @name: name of the reset line in the context of this hwmod | 1188 | * @name: name of the reset line in the context of this hwmod |
@@ -1119,8 +1238,10 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name) | |||
1119 | return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs, | 1238 | return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs, |
1120 | ohri.rst_shift); | 1239 | ohri.rst_shift); |
1121 | else if (cpu_is_omap44xx()) | 1240 | else if (cpu_is_omap44xx()) |
1122 | return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg, | 1241 | return omap4_prminst_assert_hardreset(ohri.rst_shift, |
1123 | ohri.rst_shift); | 1242 | oh->clkdm->pwrdm.ptr->prcm_partition, |
1243 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
1244 | oh->prcm.omap4.rstctrl_offs); | ||
1124 | else | 1245 | else |
1125 | return -EINVAL; | 1246 | return -EINVAL; |
1126 | } | 1247 | } |
@@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) | |||
1155 | if (ohri.st_shift) | 1276 | if (ohri.st_shift) |
1156 | pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", | 1277 | pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", |
1157 | oh->name, name); | 1278 | oh->name, name); |
1158 | ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg, | 1279 | ret = omap4_prminst_deassert_hardreset(ohri.rst_shift, |
1159 | ohri.rst_shift); | 1280 | oh->clkdm->pwrdm.ptr->prcm_partition, |
1281 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
1282 | oh->prcm.omap4.rstctrl_offs); | ||
1160 | } else { | 1283 | } else { |
1161 | return -EINVAL; | 1284 | return -EINVAL; |
1162 | } | 1285 | } |
@@ -1191,8 +1314,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) | |||
1191 | return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs, | 1314 | return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs, |
1192 | ohri.st_shift); | 1315 | ohri.st_shift); |
1193 | } else if (cpu_is_omap44xx()) { | 1316 | } else if (cpu_is_omap44xx()) { |
1194 | return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg, | 1317 | return omap4_prminst_is_hardreset_asserted(ohri.rst_shift, |
1195 | ohri.rst_shift); | 1318 | oh->clkdm->pwrdm.ptr->prcm_partition, |
1319 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
1320 | oh->prcm.omap4.rstctrl_offs); | ||
1196 | } else { | 1321 | } else { |
1197 | return -EINVAL; | 1322 | return -EINVAL; |
1198 | } | 1323 | } |
@@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh) | |||
1312 | static int _enable(struct omap_hwmod *oh) | 1437 | static int _enable(struct omap_hwmod *oh) |
1313 | { | 1438 | { |
1314 | int r; | 1439 | int r; |
1440 | int hwsup = 0; | ||
1315 | 1441 | ||
1316 | pr_debug("omap_hwmod: %s: enabling\n", oh->name); | 1442 | pr_debug("omap_hwmod: %s: enabling\n", oh->name); |
1317 | 1443 | ||
@@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh) | |||
1323 | return -EINVAL; | 1449 | return -EINVAL; |
1324 | } | 1450 | } |
1325 | 1451 | ||
1326 | /* Mux pins for device runtime if populated */ | ||
1327 | if (oh->mux && (!oh->mux->enabled || | ||
1328 | ((oh->_state == _HWMOD_STATE_IDLE) && | ||
1329 | oh->mux->pads_dynamic))) | ||
1330 | omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); | ||
1331 | |||
1332 | _add_initiator_dep(oh, mpu_oh); | ||
1333 | _enable_clocks(oh); | ||
1334 | 1452 | ||
1335 | /* | 1453 | /* |
1336 | * If an IP contains only one HW reset line, then de-assert it in order | 1454 | * If an IP contains only one HW reset line, then de-assert it in order |
@@ -1341,22 +1459,56 @@ static int _enable(struct omap_hwmod *oh) | |||
1341 | oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) | 1459 | oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) |
1342 | _deassert_hardreset(oh, oh->rst_lines[0].name); | 1460 | _deassert_hardreset(oh, oh->rst_lines[0].name); |
1343 | 1461 | ||
1344 | r = _wait_target_ready(oh); | 1462 | /* Mux pins for device runtime if populated */ |
1345 | if (r) { | 1463 | if (oh->mux && (!oh->mux->enabled || |
1346 | pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", | 1464 | ((oh->_state == _HWMOD_STATE_IDLE) && |
1347 | oh->name, r); | 1465 | oh->mux->pads_dynamic))) |
1348 | _disable_clocks(oh); | 1466 | omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); |
1349 | 1467 | ||
1350 | return r; | 1468 | _add_initiator_dep(oh, mpu_oh); |
1469 | |||
1470 | if (oh->clkdm) { | ||
1471 | /* | ||
1472 | * A clockdomain must be in SW_SUP before enabling | ||
1473 | * completely the module. The clockdomain can be set | ||
1474 | * in HW_AUTO only when the module become ready. | ||
1475 | */ | ||
1476 | hwsup = clkdm_in_hwsup(oh->clkdm); | ||
1477 | r = clkdm_hwmod_enable(oh->clkdm, oh); | ||
1478 | if (r) { | ||
1479 | WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", | ||
1480 | oh->name, oh->clkdm->name, r); | ||
1481 | return r; | ||
1482 | } | ||
1351 | } | 1483 | } |
1352 | 1484 | ||
1353 | oh->_state = _HWMOD_STATE_ENABLED; | 1485 | _enable_clocks(oh); |
1486 | _enable_module(oh); | ||
1354 | 1487 | ||
1355 | /* Access the sysconfig only if the target is ready */ | 1488 | r = _wait_target_ready(oh); |
1356 | if (oh->class->sysc) { | 1489 | if (!r) { |
1357 | if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) | 1490 | /* |
1358 | _update_sysc_cache(oh); | 1491 | * Set the clockdomain to HW_AUTO only if the target is ready, |
1359 | _enable_sysc(oh); | 1492 | * assuming that the previous state was HW_AUTO |
1493 | */ | ||
1494 | if (oh->clkdm && hwsup) | ||
1495 | clkdm_allow_idle(oh->clkdm); | ||
1496 | |||
1497 | oh->_state = _HWMOD_STATE_ENABLED; | ||
1498 | |||
1499 | /* Access the sysconfig only if the target is ready */ | ||
1500 | if (oh->class->sysc) { | ||
1501 | if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) | ||
1502 | _update_sysc_cache(oh); | ||
1503 | _enable_sysc(oh); | ||
1504 | } | ||
1505 | } else { | ||
1506 | _disable_clocks(oh); | ||
1507 | pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", | ||
1508 | oh->name, r); | ||
1509 | |||
1510 | if (oh->clkdm) | ||
1511 | clkdm_hwmod_disable(oh->clkdm, oh); | ||
1360 | } | 1512 | } |
1361 | 1513 | ||
1362 | return r; | 1514 | return r; |
@@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh) | |||
1372 | */ | 1524 | */ |
1373 | static int _idle(struct omap_hwmod *oh) | 1525 | static int _idle(struct omap_hwmod *oh) |
1374 | { | 1526 | { |
1527 | int ret; | ||
1528 | |||
1375 | pr_debug("omap_hwmod: %s: idling\n", oh->name); | 1529 | pr_debug("omap_hwmod: %s: idling\n", oh->name); |
1376 | 1530 | ||
1377 | if (oh->_state != _HWMOD_STATE_ENABLED) { | 1531 | if (oh->_state != _HWMOD_STATE_ENABLED) { |
@@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh) | |||
1383 | if (oh->class->sysc) | 1537 | if (oh->class->sysc) |
1384 | _idle_sysc(oh); | 1538 | _idle_sysc(oh); |
1385 | _del_initiator_dep(oh, mpu_oh); | 1539 | _del_initiator_dep(oh, mpu_oh); |
1540 | _disable_module(oh); | ||
1541 | ret = _wait_target_disable(oh); | ||
1542 | if (ret) | ||
1543 | pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", | ||
1544 | oh->name); | ||
1545 | /* | ||
1546 | * The module must be in idle mode before disabling any parents | ||
1547 | * clocks. Otherwise, the parent clock might be disabled before | ||
1548 | * the module transition is done, and thus will prevent the | ||
1549 | * transition to complete properly. | ||
1550 | */ | ||
1386 | _disable_clocks(oh); | 1551 | _disable_clocks(oh); |
1552 | if (oh->clkdm) | ||
1553 | clkdm_hwmod_disable(oh->clkdm, oh); | ||
1387 | 1554 | ||
1388 | /* Mux pins for device idle if populated */ | 1555 | /* Mux pins for device idle if populated */ |
1389 | if (oh->mux && oh->mux->pads_dynamic) | 1556 | if (oh->mux && oh->mux->pads_dynamic) |
@@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh) | |||
1475 | if (oh->_state == _HWMOD_STATE_ENABLED) { | 1642 | if (oh->_state == _HWMOD_STATE_ENABLED) { |
1476 | _del_initiator_dep(oh, mpu_oh); | 1643 | _del_initiator_dep(oh, mpu_oh); |
1477 | /* XXX what about the other system initiators here? dma, dsp */ | 1644 | /* XXX what about the other system initiators here? dma, dsp */ |
1645 | _disable_module(oh); | ||
1646 | ret = _wait_target_disable(oh); | ||
1647 | if (ret) | ||
1648 | pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", | ||
1649 | oh->name); | ||
1478 | _disable_clocks(oh); | 1650 | _disable_clocks(oh); |
1651 | if (oh->clkdm) | ||
1652 | clkdm_hwmod_disable(oh->clkdm, oh); | ||
1479 | } | 1653 | } |
1480 | /* XXX Should this code also force-disable the optional clocks? */ | 1654 | /* XXX Should this code also force-disable the optional clocks? */ |
1481 | 1655 | ||