diff options
author | Tarun Kanti DebBarma <tarun.kanti@ti.com> | 2011-11-23 16:14:29 -0500 |
---|---|---|
committer | Tarun Kanti DebBarma <tarun.kanti@ti.com> | 2012-02-06 06:10:48 -0500 |
commit | 2dc983c565e03f6f6f96c5fe7449b65d86af4dee (patch) | |
tree | 10603d101152685569c0dca7107ba733c9b6d133 /drivers/gpio/gpio-omap.c | |
parent | 065cd795d2721f8d2d1f2967ee6ed0aec07a4202 (diff) |
gpio/omap: cleanup prepare_for_idle and resume_after_idle
Since *_prepare_for_idle() and *_resume_after_idle() are called
with interrupts disabled they should be kept as simple as possible.
So, moving most of the stuff to *_runtime_suspend/resume() callbacks.
To avoid invalid context restore happening in *_runtime_resume()
callback as a result of *_get_sync() call in *_gpio_probe(), update
bank->context_loss_count. This would make context restore condition
check false in the callback and skip restore until further
initialization take place. The workaround_enabled static variable
is now a member of struct gpio_bank.
Unlike most GPIO registers the OE has 0xffffffff as the default value.
To make sure invalid context is not restored, updating the OE context
with default value.
Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
Signed-off-by: Charulatha V <charu@ti.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'drivers/gpio/gpio-omap.c')
-rw-r--r-- | drivers/gpio/gpio-omap.c | 245 |
1 files changed, 147 insertions, 98 deletions
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e9a30fa8715b..d483cc9f0c63 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include <asm/gpio.h> | 29 | #include <asm/gpio.h> |
30 | #include <asm/mach/irq.h> | 30 | #include <asm/mach/irq.h> |
31 | 31 | ||
32 | #define OFF_MODE 1 | ||
33 | |||
32 | static LIST_HEAD(omap_gpio_list); | 34 | static LIST_HEAD(omap_gpio_list); |
33 | 35 | ||
34 | struct gpio_regs { | 36 | struct gpio_regs { |
@@ -73,6 +75,8 @@ struct gpio_bank { | |||
73 | u32 width; | 75 | u32 width; |
74 | int context_loss_count; | 76 | int context_loss_count; |
75 | u16 id; | 77 | u16 id; |
78 | int power_mode; | ||
79 | bool workaround_enabled; | ||
76 | 80 | ||
77 | void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable); | 81 | void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable); |
78 | int (*get_context_loss_count)(struct device *dev); | 82 | int (*get_context_loss_count)(struct device *dev); |
@@ -905,6 +909,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) | |||
905 | if (bank->regs->debounce_en) | 909 | if (bank->regs->debounce_en) |
906 | _gpio_rmw(base, bank->regs->debounce_en, 0, 1); | 910 | _gpio_rmw(base, bank->regs->debounce_en, 0, 1); |
907 | 911 | ||
912 | /* Save OE default value (0xffffffff) in the context */ | ||
913 | bank->context.oe = __raw_readl(bank->base + bank->regs->direction); | ||
908 | /* Initialize interface clk ungated, module enabled */ | 914 | /* Initialize interface clk ungated, module enabled */ |
909 | if (bank->regs->ctrl) | 915 | if (bank->regs->ctrl) |
910 | _gpio_rmw(base, bank->regs->ctrl, 0, 1); | 916 | _gpio_rmw(base, bank->regs->ctrl, 0, 1); |
@@ -1127,141 +1133,179 @@ static int omap_gpio_resume(struct device *dev) | |||
1127 | } | 1133 | } |
1128 | #endif /* CONFIG_PM_SLEEP */ | 1134 | #endif /* CONFIG_PM_SLEEP */ |
1129 | 1135 | ||
1136 | #if defined(CONFIG_PM_RUNTIME) | ||
1130 | static void omap_gpio_save_context(struct gpio_bank *bank); | 1137 | static void omap_gpio_save_context(struct gpio_bank *bank); |
1131 | static void omap_gpio_restore_context(struct gpio_bank *bank); | 1138 | static void omap_gpio_restore_context(struct gpio_bank *bank); |
1132 | 1139 | ||
1133 | void omap2_gpio_prepare_for_idle(int off_mode) | 1140 | static int omap_gpio_runtime_suspend(struct device *dev) |
1134 | { | 1141 | { |
1135 | struct gpio_bank *bank; | 1142 | struct platform_device *pdev = to_platform_device(dev); |
1136 | 1143 | struct gpio_bank *bank = platform_get_drvdata(pdev); | |
1137 | list_for_each_entry(bank, &omap_gpio_list, node) { | 1144 | u32 l1 = 0, l2 = 0; |
1138 | u32 l1 = 0, l2 = 0; | 1145 | unsigned long flags; |
1139 | int j; | ||
1140 | |||
1141 | if (!bank->loses_context) | ||
1142 | continue; | ||
1143 | |||
1144 | for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) | ||
1145 | clk_disable(bank->dbck); | ||
1146 | 1146 | ||
1147 | if (!off_mode) | 1147 | spin_lock_irqsave(&bank->lock, flags); |
1148 | continue; | 1148 | if (bank->power_mode != OFF_MODE) { |
1149 | bank->power_mode = 0; | ||
1150 | goto save_gpio_context; | ||
1151 | } | ||
1152 | /* | ||
1153 | * If going to OFF, remove triggering for all | ||
1154 | * non-wakeup GPIOs. Otherwise spurious IRQs will be | ||
1155 | * generated. See OMAP2420 Errata item 1.101. | ||
1156 | */ | ||
1157 | if (!(bank->enabled_non_wakeup_gpios)) | ||
1158 | goto save_gpio_context; | ||
1149 | 1159 | ||
1150 | /* If going to OFF, remove triggering for all | 1160 | bank->saved_datain = __raw_readl(bank->base + |
1151 | * non-wakeup GPIOs. Otherwise spurious IRQs will be | 1161 | bank->regs->datain); |
1152 | * generated. See OMAP2420 Errata item 1.101. */ | 1162 | l1 = __raw_readl(bank->base + bank->regs->fallingdetect); |
1153 | if (!(bank->enabled_non_wakeup_gpios)) | 1163 | l2 = __raw_readl(bank->base + bank->regs->risingdetect); |
1154 | goto save_gpio_context; | ||
1155 | 1164 | ||
1156 | bank->saved_datain = __raw_readl(bank->base + | 1165 | bank->saved_fallingdetect = l1; |
1157 | bank->regs->datain); | 1166 | bank->saved_risingdetect = l2; |
1158 | l1 = __raw_readl(bank->base + bank->regs->fallingdetect); | 1167 | l1 &= ~bank->enabled_non_wakeup_gpios; |
1159 | l2 = __raw_readl(bank->base + bank->regs->risingdetect); | 1168 | l2 &= ~bank->enabled_non_wakeup_gpios; |
1160 | 1169 | ||
1161 | bank->saved_fallingdetect = l1; | 1170 | __raw_writel(l1, bank->base + bank->regs->fallingdetect); |
1162 | bank->saved_risingdetect = l2; | 1171 | __raw_writel(l2, bank->base + bank->regs->risingdetect); |
1163 | l1 &= ~bank->enabled_non_wakeup_gpios; | ||
1164 | l2 &= ~bank->enabled_non_wakeup_gpios; | ||
1165 | 1172 | ||
1166 | __raw_writel(l1, bank->base + bank->regs->fallingdetect); | 1173 | bank->workaround_enabled = true; |
1167 | __raw_writel(l2, bank->base + bank->regs->risingdetect); | ||
1168 | 1174 | ||
1169 | save_gpio_context: | 1175 | save_gpio_context: |
1170 | 1176 | if (bank->get_context_loss_count) | |
1171 | if (bank->get_context_loss_count) | 1177 | bank->context_loss_count = |
1172 | bank->context_loss_count = | ||
1173 | bank->get_context_loss_count(bank->dev); | 1178 | bank->get_context_loss_count(bank->dev); |
1174 | 1179 | ||
1175 | omap_gpio_save_context(bank); | 1180 | omap_gpio_save_context(bank); |
1181 | spin_unlock_irqrestore(&bank->lock, flags); | ||
1176 | 1182 | ||
1177 | if (!pm_runtime_suspended(bank->dev)) | 1183 | return 0; |
1178 | pm_runtime_put(bank->dev); | ||
1179 | } | ||
1180 | } | 1184 | } |
1181 | 1185 | ||
1182 | void omap2_gpio_resume_after_idle(void) | 1186 | static int omap_gpio_runtime_resume(struct device *dev) |
1183 | { | 1187 | { |
1184 | struct gpio_bank *bank; | 1188 | struct platform_device *pdev = to_platform_device(dev); |
1185 | 1189 | struct gpio_bank *bank = platform_get_drvdata(pdev); | |
1186 | list_for_each_entry(bank, &omap_gpio_list, node) { | 1190 | int context_lost_cnt_after; |
1187 | int context_lost_cnt_after; | 1191 | u32 l = 0, gen, gen0, gen1; |
1188 | u32 l = 0, gen, gen0, gen1; | 1192 | unsigned long flags; |
1189 | int j; | ||
1190 | |||
1191 | if (!bank->loses_context) | ||
1192 | continue; | ||
1193 | |||
1194 | for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) | ||
1195 | clk_enable(bank->dbck); | ||
1196 | 1193 | ||
1197 | if (pm_runtime_suspended(bank->dev)) | 1194 | spin_lock_irqsave(&bank->lock, flags); |
1198 | pm_runtime_get_sync(bank->dev); | 1195 | if (!bank->enabled_non_wakeup_gpios || !bank->workaround_enabled) { |
1196 | spin_unlock_irqrestore(&bank->lock, flags); | ||
1197 | return 0; | ||
1198 | } | ||
1199 | 1199 | ||
1200 | if (bank->get_context_loss_count) { | 1200 | if (bank->get_context_loss_count) { |
1201 | context_lost_cnt_after = | 1201 | context_lost_cnt_after = |
1202 | bank->get_context_loss_count(bank->dev); | 1202 | bank->get_context_loss_count(bank->dev); |
1203 | if (context_lost_cnt_after != bank->context_loss_count | 1203 | if (context_lost_cnt_after != bank->context_loss_count || |
1204 | || !context_lost_cnt_after) | 1204 | !context_lost_cnt_after) { |
1205 | omap_gpio_restore_context(bank); | 1205 | omap_gpio_restore_context(bank); |
1206 | } else { | ||
1207 | spin_unlock_irqrestore(&bank->lock, flags); | ||
1208 | return 0; | ||
1206 | } | 1209 | } |
1210 | } | ||
1207 | 1211 | ||
1208 | if (!(bank->enabled_non_wakeup_gpios)) | 1212 | __raw_writel(bank->saved_fallingdetect, |
1209 | continue; | 1213 | bank->base + bank->regs->fallingdetect); |
1214 | __raw_writel(bank->saved_risingdetect, | ||
1215 | bank->base + bank->regs->risingdetect); | ||
1216 | l = __raw_readl(bank->base + bank->regs->datain); | ||
1210 | 1217 | ||
1211 | __raw_writel(bank->saved_fallingdetect, | 1218 | /* |
1212 | bank->base + bank->regs->fallingdetect); | 1219 | * Check if any of the non-wakeup interrupt GPIOs have changed |
1213 | __raw_writel(bank->saved_risingdetect, | 1220 | * state. If so, generate an IRQ by software. This is |
1214 | bank->base + bank->regs->risingdetect); | 1221 | * horribly racy, but it's the best we can do to work around |
1215 | l = __raw_readl(bank->base + bank->regs->datain); | 1222 | * this silicon bug. |
1223 | */ | ||
1224 | l ^= bank->saved_datain; | ||
1225 | l &= bank->enabled_non_wakeup_gpios; | ||
1216 | 1226 | ||
1217 | /* Check if any of the non-wakeup interrupt GPIOs have changed | 1227 | /* |
1218 | * state. If so, generate an IRQ by software. This is | 1228 | * No need to generate IRQs for the rising edge for gpio IRQs |
1219 | * horribly racy, but it's the best we can do to work around | 1229 | * configured with falling edge only; and vice versa. |
1220 | * this silicon bug. */ | 1230 | */ |
1221 | l ^= bank->saved_datain; | 1231 | gen0 = l & bank->saved_fallingdetect; |
1222 | l &= bank->enabled_non_wakeup_gpios; | 1232 | gen0 &= bank->saved_datain; |
1223 | 1233 | ||
1224 | /* | 1234 | gen1 = l & bank->saved_risingdetect; |
1225 | * No need to generate IRQs for the rising edge for gpio IRQs | 1235 | gen1 &= ~(bank->saved_datain); |
1226 | * configured with falling edge only; and vice versa. | ||
1227 | */ | ||
1228 | gen0 = l & bank->saved_fallingdetect; | ||
1229 | gen0 &= bank->saved_datain; | ||
1230 | 1236 | ||
1231 | gen1 = l & bank->saved_risingdetect; | 1237 | /* FIXME: Consider GPIO IRQs with level detections properly! */ |
1232 | gen1 &= ~(bank->saved_datain); | 1238 | gen = l & (~(bank->saved_fallingdetect) & ~(bank->saved_risingdetect)); |
1239 | /* Consider all GPIO IRQs needed to be updated */ | ||
1240 | gen |= gen0 | gen1; | ||
1233 | 1241 | ||
1234 | /* FIXME: Consider GPIO IRQs with level detections properly! */ | 1242 | if (gen) { |
1235 | gen = l & (~(bank->saved_fallingdetect) & | 1243 | u32 old0, old1; |
1236 | ~(bank->saved_risingdetect)); | ||
1237 | /* Consider all GPIO IRQs needed to be updated */ | ||
1238 | gen |= gen0 | gen1; | ||
1239 | 1244 | ||
1240 | if (gen) { | 1245 | old0 = __raw_readl(bank->base + bank->regs->leveldetect0); |
1241 | u32 old0, old1; | 1246 | old1 = __raw_readl(bank->base + bank->regs->leveldetect1); |
1242 | 1247 | ||
1243 | old0 = __raw_readl(bank->base + | 1248 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) { |
1249 | __raw_writel(old0 | gen, bank->base + | ||
1244 | bank->regs->leveldetect0); | 1250 | bank->regs->leveldetect0); |
1245 | old1 = __raw_readl(bank->base + | 1251 | __raw_writel(old1 | gen, bank->base + |
1246 | bank->regs->leveldetect1); | 1252 | bank->regs->leveldetect1); |
1253 | } | ||
1247 | 1254 | ||
1248 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) { | 1255 | if (cpu_is_omap44xx()) { |
1249 | old0 |= gen; | 1256 | __raw_writel(old0 | l, bank->base + |
1250 | old1 |= gen; | ||
1251 | } | ||
1252 | |||
1253 | if (cpu_is_omap44xx()) { | ||
1254 | old0 |= l; | ||
1255 | old1 |= l; | ||
1256 | } | ||
1257 | __raw_writel(old0, bank->base + | ||
1258 | bank->regs->leveldetect0); | 1257 | bank->regs->leveldetect0); |
1259 | __raw_writel(old1, bank->base + | 1258 | __raw_writel(old1 | l, bank->base + |
1260 | bank->regs->leveldetect1); | 1259 | bank->regs->leveldetect1); |
1261 | } | 1260 | } |
1261 | __raw_writel(old0, bank->base + bank->regs->leveldetect0); | ||
1262 | __raw_writel(old1, bank->base + bank->regs->leveldetect1); | ||
1263 | } | ||
1264 | |||
1265 | bank->workaround_enabled = false; | ||
1266 | spin_unlock_irqrestore(&bank->lock, flags); | ||
1267 | |||
1268 | return 0; | ||
1269 | } | ||
1270 | #endif /* CONFIG_PM_RUNTIME */ | ||
1271 | |||
1272 | void omap2_gpio_prepare_for_idle(int pwr_mode) | ||
1273 | { | ||
1274 | struct gpio_bank *bank; | ||
1275 | |||
1276 | list_for_each_entry(bank, &omap_gpio_list, node) { | ||
1277 | int j; | ||
1278 | |||
1279 | if (!bank->mod_usage || !bank->loses_context) | ||
1280 | continue; | ||
1281 | |||
1282 | bank->power_mode = pwr_mode; | ||
1283 | |||
1284 | for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) | ||
1285 | clk_disable(bank->dbck); | ||
1286 | |||
1287 | pm_runtime_put_sync_suspend(bank->dev); | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | void omap2_gpio_resume_after_idle(void) | ||
1292 | { | ||
1293 | struct gpio_bank *bank; | ||
1294 | |||
1295 | list_for_each_entry(bank, &omap_gpio_list, node) { | ||
1296 | int j; | ||
1297 | |||
1298 | if (!bank->mod_usage || !bank->loses_context) | ||
1299 | continue; | ||
1300 | |||
1301 | for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) | ||
1302 | clk_enable(bank->dbck); | ||
1303 | |||
1304 | pm_runtime_get_sync(bank->dev); | ||
1262 | } | 1305 | } |
1263 | } | 1306 | } |
1264 | 1307 | ||
1308 | #if defined(CONFIG_PM_RUNTIME) | ||
1265 | static void omap_gpio_save_context(struct gpio_bank *bank) | 1309 | static void omap_gpio_save_context(struct gpio_bank *bank) |
1266 | { | 1310 | { |
1267 | bank->context.irqenable1 = | 1311 | bank->context.irqenable1 = |
@@ -1303,13 +1347,18 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) | |||
1303 | bank->base + bank->regs->fallingdetect); | 1347 | bank->base + bank->regs->fallingdetect); |
1304 | __raw_writel(bank->context.dataout, bank->base + bank->regs->dataout); | 1348 | __raw_writel(bank->context.dataout, bank->base + bank->regs->dataout); |
1305 | } | 1349 | } |
1350 | #endif /* CONFIG_PM_RUNTIME */ | ||
1306 | #else | 1351 | #else |
1307 | #define omap_gpio_suspend NULL | 1352 | #define omap_gpio_suspend NULL |
1308 | #define omap_gpio_resume NULL | 1353 | #define omap_gpio_resume NULL |
1354 | #define omap_gpio_runtime_suspend NULL | ||
1355 | #define omap_gpio_runtime_resume NULL | ||
1309 | #endif | 1356 | #endif |
1310 | 1357 | ||
1311 | static const struct dev_pm_ops gpio_pm_ops = { | 1358 | static const struct dev_pm_ops gpio_pm_ops = { |
1312 | SET_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) | 1359 | SET_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) |
1360 | SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, | ||
1361 | NULL) | ||
1313 | }; | 1362 | }; |
1314 | 1363 | ||
1315 | static struct platform_driver omap_gpio_driver = { | 1364 | static struct platform_driver omap_gpio_driver = { |