diff options
| author | Narayanan G <narayanan.gopalakrishnan@stericsson.com> | 2012-02-09 02:11:37 -0500 |
|---|---|---|
| committer | Vinod Koul <vinod.koul@linux.intel.com> | 2012-04-23 08:26:17 -0400 |
| commit | 1bdae6f49c52af3a58998cdb051dbd5b942f9273 (patch) | |
| tree | 4b3ad9b496be8755fb3571593286b3144d364c7b | |
| parent | ed8b0d67f33518a16c6b2450fe5ebebf180c2d04 (diff) | |
dma40: Improve the logic of stopping logical chan
can be directly stopped by issuing a SUSPEND_REQ on the EE
bits. There is no need to suspend the physical channel and
restart it.
Also, the support for pre-V2 hw is discontinued.
EE bits for writing:
00: disable only if AS=11 or AS=00
01: enable
10: suspend_req only if AS=01 & EE=01 or EE=11
11: round / no change for writing
Signed-off-by: Narayanan G <narayanan.gopalakrishnan@stericsson.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
| -rw-r--r-- | drivers/dma/ste_dma40.c | 320 | ||||
| -rw-r--r-- | drivers/dma/ste_dma40_ll.h | 2 |
2 files changed, 199 insertions, 123 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index bdd41d4bfa8d..c5f26cc2c277 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c | |||
| @@ -69,6 +69,22 @@ enum d40_command { | |||
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| 71 | /* | 71 | /* |
| 72 | * enum d40_events - The different Event Enables for the event lines. | ||
| 73 | * | ||
| 74 | * @D40_DEACTIVATE_EVENTLINE: De-activate Event line, stopping the logical chan. | ||
| 75 | * @D40_ACTIVATE_EVENTLINE: Activate the Event line, to start a logical chan. | ||
| 76 | * @D40_SUSPEND_REQ_EVENTLINE: Requesting for suspending a event line. | ||
| 77 | * @D40_ROUND_EVENTLINE: Status check for event line. | ||
| 78 | */ | ||
| 79 | |||
| 80 | enum d40_events { | ||
| 81 | D40_DEACTIVATE_EVENTLINE = 0, | ||
| 82 | D40_ACTIVATE_EVENTLINE = 1, | ||
| 83 | D40_SUSPEND_REQ_EVENTLINE = 2, | ||
| 84 | D40_ROUND_EVENTLINE = 3 | ||
| 85 | }; | ||
| 86 | |||
| 87 | /* | ||
| 72 | * These are the registers that has to be saved and later restored | 88 | * These are the registers that has to be saved and later restored |
| 73 | * when the DMA hw is powered off. | 89 | * when the DMA hw is powered off. |
| 74 | * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. | 90 | * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. |
| @@ -870,8 +886,8 @@ static void d40_save_restore_registers(struct d40_base *base, bool save) | |||
| 870 | } | 886 | } |
| 871 | #endif | 887 | #endif |
| 872 | 888 | ||
| 873 | static int d40_channel_execute_command(struct d40_chan *d40c, | 889 | static int __d40_execute_command_phy(struct d40_chan *d40c, |
| 874 | enum d40_command command) | 890 | enum d40_command command) |
| 875 | { | 891 | { |
| 876 | u32 status; | 892 | u32 status; |
| 877 | int i; | 893 | int i; |
| @@ -880,6 +896,12 @@ static int d40_channel_execute_command(struct d40_chan *d40c, | |||
| 880 | unsigned long flags; | 896 | unsigned long flags; |
| 881 | u32 wmask; | 897 | u32 wmask; |
| 882 | 898 | ||
| 899 | if (command == D40_DMA_STOP) { | ||
| 900 | ret = __d40_execute_command_phy(d40c, D40_DMA_SUSPEND_REQ); | ||
| 901 | if (ret) | ||
| 902 | return ret; | ||
| 903 | } | ||
| 904 | |||
| 883 | spin_lock_irqsave(&d40c->base->execmd_lock, flags); | 905 | spin_lock_irqsave(&d40c->base->execmd_lock, flags); |
| 884 | 906 | ||
| 885 | if (d40c->phy_chan->num % 2 == 0) | 907 | if (d40c->phy_chan->num % 2 == 0) |
| @@ -973,67 +995,109 @@ static void d40_term_all(struct d40_chan *d40c) | |||
| 973 | } | 995 | } |
| 974 | 996 | ||
| 975 | d40c->pending_tx = 0; | 997 | d40c->pending_tx = 0; |
| 976 | d40c->busy = false; | ||
| 977 | } | 998 | } |
| 978 | 999 | ||
| 979 | static void __d40_config_set_event(struct d40_chan *d40c, bool enable, | 1000 | static void __d40_config_set_event(struct d40_chan *d40c, |
| 980 | u32 event, int reg) | 1001 | enum d40_events event_type, u32 event, |
| 1002 | int reg) | ||
| 981 | { | 1003 | { |
| 982 | void __iomem *addr = chan_base(d40c) + reg; | 1004 | void __iomem *addr = chan_base(d40c) + reg; |
| 983 | int tries; | 1005 | int tries; |
| 1006 | u32 status; | ||
| 1007 | |||
| 1008 | switch (event_type) { | ||
| 1009 | |||
| 1010 | case D40_DEACTIVATE_EVENTLINE: | ||
| 984 | 1011 | ||
| 985 | if (!enable) { | ||
| 986 | writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) | 1012 | writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) |
| 987 | | ~D40_EVENTLINE_MASK(event), addr); | 1013 | | ~D40_EVENTLINE_MASK(event), addr); |
| 988 | return; | 1014 | break; |
| 989 | } | 1015 | |
| 1016 | case D40_SUSPEND_REQ_EVENTLINE: | ||
| 1017 | status = (readl(addr) & D40_EVENTLINE_MASK(event)) >> | ||
| 1018 | D40_EVENTLINE_POS(event); | ||
| 1019 | |||
| 1020 | if (status == D40_DEACTIVATE_EVENTLINE || | ||
| 1021 | status == D40_SUSPEND_REQ_EVENTLINE) | ||
| 1022 | break; | ||
| 990 | 1023 | ||
| 1024 | writel((D40_SUSPEND_REQ_EVENTLINE << D40_EVENTLINE_POS(event)) | ||
| 1025 | | ~D40_EVENTLINE_MASK(event), addr); | ||
| 1026 | |||
| 1027 | for (tries = 0 ; tries < D40_SUSPEND_MAX_IT; tries++) { | ||
| 1028 | |||
| 1029 | status = (readl(addr) & D40_EVENTLINE_MASK(event)) >> | ||
| 1030 | D40_EVENTLINE_POS(event); | ||
| 1031 | |||
| 1032 | cpu_relax(); | ||
| 1033 | /* | ||
| 1034 | * Reduce the number of bus accesses while | ||
| 1035 | * waiting for the DMA to suspend. | ||
| 1036 | */ | ||
| 1037 | udelay(3); | ||
| 1038 | |||
| 1039 | if (status == D40_DEACTIVATE_EVENTLINE) | ||
| 1040 | break; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | if (tries == D40_SUSPEND_MAX_IT) { | ||
| 1044 | chan_err(d40c, | ||
| 1045 | "unable to stop the event_line chl %d (log: %d)" | ||
| 1046 | "status %x\n", d40c->phy_chan->num, | ||
| 1047 | d40c->log_num, status); | ||
| 1048 | } | ||
| 1049 | break; | ||
| 1050 | |||
| 1051 | case D40_ACTIVATE_EVENTLINE: | ||
| 991 | /* | 1052 | /* |
| 992 | * The hardware sometimes doesn't register the enable when src and dst | 1053 | * The hardware sometimes doesn't register the enable when src and dst |
| 993 | * event lines are active on the same logical channel. Retry to ensure | 1054 | * event lines are active on the same logical channel. Retry to ensure |
| 994 | * it does. Usually only one retry is sufficient. | 1055 | * it does. Usually only one retry is sufficient. |
| 995 | */ | 1056 | */ |
| 996 | tries = 100; | 1057 | tries = 100; |
| 997 | while (--tries) { | 1058 | while (--tries) { |
| 998 | writel((D40_ACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) | 1059 | writel((D40_ACTIVATE_EVENTLINE << |
| 999 | | ~D40_EVENTLINE_MASK(event), addr); | 1060 | D40_EVENTLINE_POS(event)) | |
| 1061 | ~D40_EVENTLINE_MASK(event), addr); | ||
| 1000 | 1062 | ||
| 1001 | if (readl(addr) & D40_EVENTLINE_MASK(event)) | 1063 | if (readl(addr) & D40_EVENTLINE_MASK(event)) |
| 1002 | break; | 1064 | break; |
| 1003 | } | 1065 | } |
| 1004 | 1066 | ||
| 1005 | if (tries != 99) | 1067 | if (tries != 99) |
| 1006 | dev_dbg(chan2dev(d40c), | 1068 | dev_dbg(chan2dev(d40c), |
| 1007 | "[%s] workaround enable S%cLNK (%d tries)\n", | 1069 | "[%s] workaround enable S%cLNK (%d tries)\n", |
| 1008 | __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D', | 1070 | __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D', |
| 1009 | 100 - tries); | 1071 | 100 - tries); |
| 1010 | 1072 | ||
| 1011 | WARN_ON(!tries); | 1073 | WARN_ON(!tries); |
| 1012 | } | 1074 | break; |
| 1013 | 1075 | ||
| 1014 | static void d40_config_set_event(struct d40_chan *d40c, bool do_enable) | 1076 | case D40_ROUND_EVENTLINE: |
| 1015 | { | 1077 | BUG(); |
| 1016 | unsigned long flags; | 1078 | break; |
| 1017 | 1079 | ||
| 1018 | spin_lock_irqsave(&d40c->phy_chan->lock, flags); | 1080 | } |
| 1081 | } | ||
| 1019 | 1082 | ||
| 1083 | static void d40_config_set_event(struct d40_chan *d40c, | ||
| 1084 | enum d40_events event_type) | ||
| 1085 | { | ||
| 1020 | /* Enable event line connected to device (or memcpy) */ | 1086 | /* Enable event line connected to device (or memcpy) */ |
| 1021 | if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) || | 1087 | if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) || |
| 1022 | (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) { | 1088 | (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) { |
| 1023 | u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); | 1089 | u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); |
| 1024 | 1090 | ||
| 1025 | __d40_config_set_event(d40c, do_enable, event, | 1091 | __d40_config_set_event(d40c, event_type, event, |
| 1026 | D40_CHAN_REG_SSLNK); | 1092 | D40_CHAN_REG_SSLNK); |
| 1027 | } | 1093 | } |
| 1028 | 1094 | ||
| 1029 | if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) { | 1095 | if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) { |
| 1030 | u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); | 1096 | u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); |
| 1031 | 1097 | ||
| 1032 | __d40_config_set_event(d40c, do_enable, event, | 1098 | __d40_config_set_event(d40c, event_type, event, |
| 1033 | D40_CHAN_REG_SDLNK); | 1099 | D40_CHAN_REG_SDLNK); |
| 1034 | } | 1100 | } |
| 1035 | |||
| 1036 | spin_unlock_irqrestore(&d40c->phy_chan->lock, flags); | ||
| 1037 | } | 1101 | } |
| 1038 | 1102 | ||
| 1039 | static u32 d40_chan_has_events(struct d40_chan *d40c) | 1103 | static u32 d40_chan_has_events(struct d40_chan *d40c) |
| @@ -1047,6 +1111,64 @@ static u32 d40_chan_has_events(struct d40_chan *d40c) | |||
| 1047 | return val; | 1111 | return val; |
| 1048 | } | 1112 | } |
| 1049 | 1113 | ||
| 1114 | static int | ||
| 1115 | __d40_execute_command_log(struct d40_chan *d40c, enum d40_command command) | ||
| 1116 | { | ||
| 1117 | unsigned long flags; | ||
| 1118 | int ret = 0; | ||
| 1119 | u32 active_status; | ||
| 1120 | void __iomem *active_reg; | ||
| 1121 | |||
| 1122 | if (d40c->phy_chan->num % 2 == 0) | ||
| 1123 | active_reg = d40c->base->virtbase + D40_DREG_ACTIVE; | ||
| 1124 | else | ||
| 1125 | active_reg = d40c->base->virtbase + D40_DREG_ACTIVO; | ||
| 1126 | |||
| 1127 | |||
| 1128 | spin_lock_irqsave(&d40c->phy_chan->lock, flags); | ||
| 1129 | |||
| 1130 | switch (command) { | ||
| 1131 | case D40_DMA_STOP: | ||
| 1132 | case D40_DMA_SUSPEND_REQ: | ||
| 1133 | |||
| 1134 | active_status = (readl(active_reg) & | ||
| 1135 | D40_CHAN_POS_MASK(d40c->phy_chan->num)) >> | ||
| 1136 | D40_CHAN_POS(d40c->phy_chan->num); | ||
| 1137 | |||
| 1138 | if (active_status == D40_DMA_RUN) | ||
| 1139 | d40_config_set_event(d40c, D40_SUSPEND_REQ_EVENTLINE); | ||
| 1140 | else | ||
| 1141 | d40_config_set_event(d40c, D40_DEACTIVATE_EVENTLINE); | ||
| 1142 | |||
| 1143 | if (!d40_chan_has_events(d40c) && (command == D40_DMA_STOP)) | ||
| 1144 | ret = __d40_execute_command_phy(d40c, command); | ||
| 1145 | |||
| 1146 | break; | ||
| 1147 | |||
| 1148 | case D40_DMA_RUN: | ||
| 1149 | |||
| 1150 | d40_config_set_event(d40c, D40_ACTIVATE_EVENTLINE); | ||
| 1151 | ret = __d40_execute_command_phy(d40c, command); | ||
| 1152 | break; | ||
| 1153 | |||
| 1154 | case D40_DMA_SUSPENDED: | ||
| 1155 | BUG(); | ||
| 1156 | break; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | spin_unlock_irqrestore(&d40c->phy_chan->lock, flags); | ||
| 1160 | return ret; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | static int d40_channel_execute_command(struct d40_chan *d40c, | ||
| 1164 | enum d40_command command) | ||
| 1165 | { | ||
| 1166 | if (chan_is_logical(d40c)) | ||
| 1167 | return __d40_execute_command_log(d40c, command); | ||
| 1168 | else | ||
| 1169 | return __d40_execute_command_phy(d40c, command); | ||
| 1170 | } | ||
| 1171 | |||
| 1050 | static u32 d40_get_prmo(struct d40_chan *d40c) | 1172 | static u32 d40_get_prmo(struct d40_chan *d40c) |
| 1051 | { | 1173 | { |
| 1052 | static const unsigned int phy_map[] = { | 1174 | static const unsigned int phy_map[] = { |
| @@ -1149,15 +1271,7 @@ static int d40_pause(struct d40_chan *d40c) | |||
| 1149 | spin_lock_irqsave(&d40c->lock, flags); | 1271 | spin_lock_irqsave(&d40c->lock, flags); |
| 1150 | 1272 | ||
| 1151 | res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); | 1273 | res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); |
| 1152 | if (res == 0) { | 1274 | |
| 1153 | if (chan_is_logical(d40c)) { | ||
| 1154 | d40_config_set_event(d40c, false); | ||
| 1155 | /* Resume the other logical channels if any */ | ||
| 1156 | if (d40_chan_has_events(d40c)) | ||
| 1157 | res = d40_channel_execute_command(d40c, | ||
| 1158 | D40_DMA_RUN); | ||
| 1159 | } | ||
| 1160 | } | ||
| 1161 | pm_runtime_mark_last_busy(d40c->base->dev); | 1275 | pm_runtime_mark_last_busy(d40c->base->dev); |
| 1162 | pm_runtime_put_autosuspend(d40c->base->dev); | 1276 | pm_runtime_put_autosuspend(d40c->base->dev); |
| 1163 | spin_unlock_irqrestore(&d40c->lock, flags); | 1277 | spin_unlock_irqrestore(&d40c->lock, flags); |
| @@ -1174,45 +1288,17 @@ static int d40_resume(struct d40_chan *d40c) | |||
| 1174 | 1288 | ||
| 1175 | spin_lock_irqsave(&d40c->lock, flags); | 1289 | spin_lock_irqsave(&d40c->lock, flags); |
| 1176 | pm_runtime_get_sync(d40c->base->dev); | 1290 | pm_runtime_get_sync(d40c->base->dev); |
| 1177 | if (d40c->base->rev == 0) | ||
| 1178 | if (chan_is_logical(d40c)) { | ||
| 1179 | res = d40_channel_execute_command(d40c, | ||
| 1180 | D40_DMA_SUSPEND_REQ); | ||
| 1181 | goto no_suspend; | ||
| 1182 | } | ||
| 1183 | 1291 | ||
| 1184 | /* If bytes left to transfer or linked tx resume job */ | 1292 | /* If bytes left to transfer or linked tx resume job */ |
| 1185 | if (d40_residue(d40c) || d40_tx_is_linked(d40c)) { | 1293 | if (d40_residue(d40c) || d40_tx_is_linked(d40c)) |
| 1186 | |||
| 1187 | if (chan_is_logical(d40c)) | ||
| 1188 | d40_config_set_event(d40c, true); | ||
| 1189 | |||
| 1190 | res = d40_channel_execute_command(d40c, D40_DMA_RUN); | 1294 | res = d40_channel_execute_command(d40c, D40_DMA_RUN); |
| 1191 | } | ||
| 1192 | 1295 | ||
| 1193 | no_suspend: | ||
| 1194 | pm_runtime_mark_last_busy(d40c->base->dev); | 1296 | pm_runtime_mark_last_busy(d40c->base->dev); |
| 1195 | pm_runtime_put_autosuspend(d40c->base->dev); | 1297 | pm_runtime_put_autosuspend(d40c->base->dev); |
| 1196 | spin_unlock_irqrestore(&d40c->lock, flags); | 1298 | spin_unlock_irqrestore(&d40c->lock, flags); |
| 1197 | return res; | 1299 | return res; |
| 1198 | } | 1300 | } |
| 1199 | 1301 | ||
| 1200 | static int d40_terminate_all(struct d40_chan *chan) | ||
| 1201 | { | ||
| 1202 | unsigned long flags; | ||
| 1203 | int ret = 0; | ||
| 1204 | |||
| 1205 | ret = d40_pause(chan); | ||
| 1206 | if (!ret && chan_is_physical(chan)) | ||
| 1207 | ret = d40_channel_execute_command(chan, D40_DMA_STOP); | ||
| 1208 | |||
| 1209 | spin_lock_irqsave(&chan->lock, flags); | ||
| 1210 | d40_term_all(chan); | ||
| 1211 | spin_unlock_irqrestore(&chan->lock, flags); | ||
| 1212 | |||
| 1213 | return ret; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) | 1302 | static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) |
| 1217 | { | 1303 | { |
| 1218 | struct d40_chan *d40c = container_of(tx->chan, | 1304 | struct d40_chan *d40c = container_of(tx->chan, |
| @@ -1232,20 +1318,6 @@ static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) | |||
| 1232 | 1318 | ||
| 1233 | static int d40_start(struct d40_chan *d40c) | 1319 | static int d40_start(struct d40_chan *d40c) |
| 1234 | { | 1320 | { |
| 1235 | if (d40c->base->rev == 0) { | ||
| 1236 | int err; | ||
| 1237 | |||
| 1238 | if (chan_is_logical(d40c)) { | ||
| 1239 | err = d40_channel_execute_command(d40c, | ||
| 1240 | D40_DMA_SUSPEND_REQ); | ||
| 1241 | if (err) | ||
| 1242 | return err; | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | if (chan_is_logical(d40c)) | ||
| 1247 | d40_config_set_event(d40c, true); | ||
| 1248 | |||
| 1249 | return d40_channel_execute_command(d40c, D40_DMA_RUN); | 1321 | return d40_channel_execute_command(d40c, D40_DMA_RUN); |
| 1250 | } | 1322 | } |
| 1251 | 1323 | ||
| @@ -1258,10 +1330,10 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c) | |||
| 1258 | d40d = d40_first_queued(d40c); | 1330 | d40d = d40_first_queued(d40c); |
| 1259 | 1331 | ||
| 1260 | if (d40d != NULL) { | 1332 | if (d40d != NULL) { |
| 1261 | if (!d40c->busy) | 1333 | if (!d40c->busy) { |
| 1262 | d40c->busy = true; | 1334 | d40c->busy = true; |
| 1263 | 1335 | pm_runtime_get_sync(d40c->base->dev); | |
| 1264 | pm_runtime_get_sync(d40c->base->dev); | 1336 | } |
| 1265 | 1337 | ||
| 1266 | /* Remove from queue */ | 1338 | /* Remove from queue */ |
| 1267 | d40_desc_remove(d40d); | 1339 | d40_desc_remove(d40d); |
| @@ -1388,8 +1460,8 @@ static void dma_tasklet(unsigned long data) | |||
| 1388 | 1460 | ||
| 1389 | return; | 1461 | return; |
| 1390 | 1462 | ||
| 1391 | err: | 1463 | err: |
| 1392 | /* Rescue manoeuvre if receiving double interrupts */ | 1464 | /* Rescue manouver if receiving double interrupts */ |
| 1393 | if (d40c->pending_tx > 0) | 1465 | if (d40c->pending_tx > 0) |
| 1394 | d40c->pending_tx--; | 1466 | d40c->pending_tx--; |
| 1395 | spin_unlock_irqrestore(&d40c->lock, flags); | 1467 | spin_unlock_irqrestore(&d40c->lock, flags); |
| @@ -1770,7 +1842,6 @@ static int d40_config_memcpy(struct d40_chan *d40c) | |||
| 1770 | return 0; | 1842 | return 0; |
| 1771 | } | 1843 | } |
| 1772 | 1844 | ||
| 1773 | |||
| 1774 | static int d40_free_dma(struct d40_chan *d40c) | 1845 | static int d40_free_dma(struct d40_chan *d40c) |
| 1775 | { | 1846 | { |
| 1776 | 1847 | ||
| @@ -1806,43 +1877,18 @@ static int d40_free_dma(struct d40_chan *d40c) | |||
| 1806 | } | 1877 | } |
| 1807 | 1878 | ||
| 1808 | pm_runtime_get_sync(d40c->base->dev); | 1879 | pm_runtime_get_sync(d40c->base->dev); |
| 1809 | res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); | 1880 | res = d40_channel_execute_command(d40c, D40_DMA_STOP); |
| 1810 | if (res) { | 1881 | if (res) { |
| 1811 | chan_err(d40c, "suspend failed\n"); | 1882 | chan_err(d40c, "stop failed\n"); |
| 1812 | goto out; | 1883 | goto out; |
| 1813 | } | 1884 | } |
| 1814 | 1885 | ||
| 1815 | if (chan_is_logical(d40c)) { | 1886 | d40_alloc_mask_free(phy, is_src, chan_is_logical(d40c) ? event : 0); |
| 1816 | /* Release logical channel, deactivate the event line */ | ||
| 1817 | 1887 | ||
| 1818 | d40_config_set_event(d40c, false); | 1888 | if (chan_is_logical(d40c)) |
| 1819 | d40c->base->lookup_log_chans[d40c->log_num] = NULL; | 1889 | d40c->base->lookup_log_chans[d40c->log_num] = NULL; |
| 1820 | 1890 | else | |
| 1821 | /* | 1891 | d40c->base->lookup_phy_chans[phy->num] = NULL; |
| 1822 | * Check if there are more logical allocation | ||
| 1823 | * on this phy channel. | ||
| 1824 | */ | ||
| 1825 | if (!d40_alloc_mask_free(phy, is_src, event)) { | ||
| 1826 | /* Resume the other logical channels if any */ | ||
| 1827 | if (d40_chan_has_events(d40c)) { | ||
| 1828 | res = d40_channel_execute_command(d40c, | ||
| 1829 | D40_DMA_RUN); | ||
| 1830 | if (res) | ||
| 1831 | chan_err(d40c, | ||
| 1832 | "Executing RUN command\n"); | ||
| 1833 | } | ||
| 1834 | goto out; | ||
| 1835 | } | ||
| 1836 | } else { | ||
| 1837 | (void) d40_alloc_mask_free(phy, is_src, 0); | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | /* Release physical channel */ | ||
| 1841 | res = d40_channel_execute_command(d40c, D40_DMA_STOP); | ||
| 1842 | if (res) { | ||
| 1843 | chan_err(d40c, "Failed to stop channel\n"); | ||
| 1844 | goto out; | ||
| 1845 | } | ||
| 1846 | 1892 | ||
| 1847 | if (d40c->busy) { | 1893 | if (d40c->busy) { |
| 1848 | pm_runtime_mark_last_busy(d40c->base->dev); | 1894 | pm_runtime_mark_last_busy(d40c->base->dev); |
| @@ -1852,7 +1898,6 @@ static int d40_free_dma(struct d40_chan *d40c) | |||
| 1852 | d40c->busy = false; | 1898 | d40c->busy = false; |
| 1853 | d40c->phy_chan = NULL; | 1899 | d40c->phy_chan = NULL; |
| 1854 | d40c->configured = false; | 1900 | d40c->configured = false; |
| 1855 | d40c->base->lookup_phy_chans[phy->num] = NULL; | ||
| 1856 | out: | 1901 | out: |
| 1857 | 1902 | ||
| 1858 | pm_runtime_mark_last_busy(d40c->base->dev); | 1903 | pm_runtime_mark_last_busy(d40c->base->dev); |
| @@ -2371,6 +2416,31 @@ static void d40_issue_pending(struct dma_chan *chan) | |||
| 2371 | spin_unlock_irqrestore(&d40c->lock, flags); | 2416 | spin_unlock_irqrestore(&d40c->lock, flags); |
| 2372 | } | 2417 | } |
| 2373 | 2418 | ||
| 2419 | static void d40_terminate_all(struct dma_chan *chan) | ||
| 2420 | { | ||
| 2421 | unsigned long flags; | ||
| 2422 | struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); | ||
| 2423 | int ret; | ||
| 2424 | |||
| 2425 | spin_lock_irqsave(&d40c->lock, flags); | ||
| 2426 | |||
| 2427 | pm_runtime_get_sync(d40c->base->dev); | ||
| 2428 | ret = d40_channel_execute_command(d40c, D40_DMA_STOP); | ||
| 2429 | if (ret) | ||
| 2430 | chan_err(d40c, "Failed to stop channel\n"); | ||
| 2431 | |||
| 2432 | d40_term_all(d40c); | ||
| 2433 | pm_runtime_mark_last_busy(d40c->base->dev); | ||
| 2434 | pm_runtime_put_autosuspend(d40c->base->dev); | ||
| 2435 | if (d40c->busy) { | ||
| 2436 | pm_runtime_mark_last_busy(d40c->base->dev); | ||
| 2437 | pm_runtime_put_autosuspend(d40c->base->dev); | ||
| 2438 | } | ||
| 2439 | d40c->busy = false; | ||
| 2440 | |||
| 2441 | spin_unlock_irqrestore(&d40c->lock, flags); | ||
| 2442 | } | ||
| 2443 | |||
| 2374 | static int | 2444 | static int |
| 2375 | dma40_config_to_halfchannel(struct d40_chan *d40c, | 2445 | dma40_config_to_halfchannel(struct d40_chan *d40c, |
| 2376 | struct stedma40_half_channel_info *info, | 2446 | struct stedma40_half_channel_info *info, |
| @@ -2551,7 +2621,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
| 2551 | 2621 | ||
| 2552 | switch (cmd) { | 2622 | switch (cmd) { |
| 2553 | case DMA_TERMINATE_ALL: | 2623 | case DMA_TERMINATE_ALL: |
| 2554 | return d40_terminate_all(d40c); | 2624 | d40_terminate_all(chan); |
| 2625 | return 0; | ||
| 2555 | case DMA_PAUSE: | 2626 | case DMA_PAUSE: |
| 2556 | return d40_pause(d40c); | 2627 | return d40_pause(d40c); |
| 2557 | case DMA_RESUME: | 2628 | case DMA_RESUME: |
| @@ -2908,6 +2979,12 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) | |||
| 2908 | dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", | 2979 | dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", |
| 2909 | rev, res->start); | 2980 | rev, res->start); |
| 2910 | 2981 | ||
| 2982 | if (rev < 2) { | ||
| 2983 | d40_err(&pdev->dev, "hardware revision: %d is not supported", | ||
| 2984 | rev); | ||
| 2985 | goto failure; | ||
| 2986 | } | ||
| 2987 | |||
| 2911 | plat_data = pdev->dev.platform_data; | 2988 | plat_data = pdev->dev.platform_data; |
| 2912 | 2989 | ||
| 2913 | /* Count the number of logical channels in use */ | 2990 | /* Count the number of logical channels in use */ |
| @@ -2998,6 +3075,7 @@ failure: | |||
| 2998 | 3075 | ||
| 2999 | if (base) { | 3076 | if (base) { |
| 3000 | kfree(base->lcla_pool.alloc_map); | 3077 | kfree(base->lcla_pool.alloc_map); |
| 3078 | kfree(base->reg_val_backup_chan); | ||
| 3001 | kfree(base->lookup_log_chans); | 3079 | kfree(base->lookup_log_chans); |
| 3002 | kfree(base->lookup_phy_chans); | 3080 | kfree(base->lookup_phy_chans); |
| 3003 | kfree(base->phy_res); | 3081 | kfree(base->phy_res); |
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 8d3d490968a3..51e8e5396e9b 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h | |||
| @@ -62,8 +62,6 @@ | |||
| 62 | #define D40_SREG_ELEM_LOG_LIDX_MASK (0xFF << D40_SREG_ELEM_LOG_LIDX_POS) | 62 | #define D40_SREG_ELEM_LOG_LIDX_MASK (0xFF << D40_SREG_ELEM_LOG_LIDX_POS) |
| 63 | 63 | ||
| 64 | /* Link register */ | 64 | /* Link register */ |
| 65 | #define D40_DEACTIVATE_EVENTLINE 0x0 | ||
| 66 | #define D40_ACTIVATE_EVENTLINE 0x1 | ||
| 67 | #define D40_EVENTLINE_POS(i) (2 * i) | 65 | #define D40_EVENTLINE_POS(i) (2 * i) |
| 68 | #define D40_EVENTLINE_MASK(i) (0x3 << D40_EVENTLINE_POS(i)) | 66 | #define D40_EVENTLINE_MASK(i) (0x3 << D40_EVENTLINE_POS(i)) |
| 69 | 67 | ||
