diff options
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 154 |
1 files changed, 89 insertions, 65 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 7bbdca418314..93b92680d070 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -139,13 +139,18 @@ struct mwl8k_priv { | |||
139 | 139 | ||
140 | struct pci_dev *pdev; | 140 | struct pci_dev *pdev; |
141 | u8 name[16]; | 141 | u8 name[16]; |
142 | /* firmware access lock */ | ||
143 | spinlock_t fw_lock; | ||
144 | 142 | ||
145 | /* firmware files and meta data */ | 143 | /* firmware files and meta data */ |
146 | struct mwl8k_firmware fw; | 144 | struct mwl8k_firmware fw; |
147 | u32 part_num; | 145 | u32 part_num; |
148 | 146 | ||
147 | /* firmware access */ | ||
148 | struct mutex fw_mutex; | ||
149 | struct task_struct *fw_mutex_owner; | ||
150 | int fw_mutex_depth; | ||
151 | struct completion *tx_wait; | ||
152 | struct completion *hostcmd_wait; | ||
153 | |||
149 | /* lock held over TX and TX reap */ | 154 | /* lock held over TX and TX reap */ |
150 | spinlock_t tx_lock; | 155 | spinlock_t tx_lock; |
151 | 156 | ||
@@ -179,9 +184,6 @@ struct mwl8k_priv { | |||
179 | bool radio_short_preamble; | 184 | bool radio_short_preamble; |
180 | bool wmm_enabled; | 185 | bool wmm_enabled; |
181 | 186 | ||
182 | /* Set if PHY config is in progress */ | ||
183 | bool inconfig; | ||
184 | |||
185 | /* XXX need to convert this to handle multiple interfaces */ | 187 | /* XXX need to convert this to handle multiple interfaces */ |
186 | bool capture_beacon; | 188 | bool capture_beacon; |
187 | u8 capture_bssid[ETH_ALEN]; | 189 | u8 capture_bssid[ETH_ALEN]; |
@@ -200,8 +202,6 @@ struct mwl8k_priv { | |||
200 | 202 | ||
201 | /* Work thread to serialize configuration requests */ | 203 | /* Work thread to serialize configuration requests */ |
202 | struct workqueue_struct *config_wq; | 204 | struct workqueue_struct *config_wq; |
203 | struct completion *hostcmd_wait; | ||
204 | struct completion *tx_wait; | ||
205 | }; | 205 | }; |
206 | 206 | ||
207 | /* Per interface specific private data */ | 207 | /* Per interface specific private data */ |
@@ -1113,6 +1113,9 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, | |||
1113 | return ndescs; | 1113 | return ndescs; |
1114 | } | 1114 | } |
1115 | 1115 | ||
1116 | /* | ||
1117 | * Must be called with hw->fw_mutex held and tx queues stopped. | ||
1118 | */ | ||
1116 | static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) | 1119 | static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) |
1117 | { | 1120 | { |
1118 | struct mwl8k_priv *priv = hw->priv; | 1121 | struct mwl8k_priv *priv = hw->priv; |
@@ -1122,9 +1125,6 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) | |||
1122 | 1125 | ||
1123 | might_sleep(); | 1126 | might_sleep(); |
1124 | 1127 | ||
1125 | if (priv->tx_wait != NULL) | ||
1126 | printk(KERN_ERR "WARNING Previous TXWaitEmpty instance\n"); | ||
1127 | |||
1128 | spin_lock_bh(&priv->tx_lock); | 1128 | spin_lock_bh(&priv->tx_lock); |
1129 | count = mwl8k_txq_busy(priv); | 1129 | count = mwl8k_txq_busy(priv); |
1130 | if (count) { | 1130 | if (count) { |
@@ -1140,7 +1140,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) | |||
1140 | int newcount; | 1140 | int newcount; |
1141 | 1141 | ||
1142 | timeout = wait_for_completion_timeout(&cmd_wait, | 1142 | timeout = wait_for_completion_timeout(&cmd_wait, |
1143 | msecs_to_jiffies(1000)); | 1143 | msecs_to_jiffies(5000)); |
1144 | if (timeout) | 1144 | if (timeout) |
1145 | return 0; | 1145 | return 0; |
1146 | 1146 | ||
@@ -1149,7 +1149,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) | |||
1149 | newcount = mwl8k_txq_busy(priv); | 1149 | newcount = mwl8k_txq_busy(priv); |
1150 | spin_unlock_bh(&priv->tx_lock); | 1150 | spin_unlock_bh(&priv->tx_lock); |
1151 | 1151 | ||
1152 | printk(KERN_ERR "%s(%u) TIMEDOUT:1000ms Pend:%u-->%u\n", | 1152 | printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", |
1153 | __func__, __LINE__, count, newcount); | 1153 | __func__, __LINE__, count, newcount); |
1154 | 1154 | ||
1155 | mwl8k_scan_tx_ring(priv, txinfo); | 1155 | mwl8k_scan_tx_ring(priv, txinfo); |
@@ -1228,10 +1228,10 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) | |||
1228 | 1228 | ||
1229 | ieee80211_tx_status_irqsafe(hw, skb); | 1229 | ieee80211_tx_status_irqsafe(hw, skb); |
1230 | 1230 | ||
1231 | wake = !priv->inconfig && priv->radio_on; | 1231 | wake = 1; |
1232 | } | 1232 | } |
1233 | 1233 | ||
1234 | if (wake) | 1234 | if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) |
1235 | ieee80211_wake_queue(hw, index); | 1235 | ieee80211_wake_queue(hw, index); |
1236 | } | 1236 | } |
1237 | 1237 | ||
@@ -1360,6 +1360,60 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1360 | 1360 | ||
1361 | 1361 | ||
1362 | /* | 1362 | /* |
1363 | * Firmware access. | ||
1364 | * | ||
1365 | * We have the following requirements for issuing firmware commands: | ||
1366 | * - Some commands require that the packet transmit path is idle when | ||
1367 | * the command is issued. (For simplicity, we'll just quiesce the | ||
1368 | * transmit path for every command.) | ||
1369 | * - There are certain sequences of commands that need to be issued to | ||
1370 | * the hardware sequentially, with no other intervening commands. | ||
1371 | * | ||
1372 | * This leads to an implementation of a "firmware lock" as a mutex that | ||
1373 | * can be taken recursively, and which is taken by both the low-level | ||
1374 | * command submission function (mwl8k_post_cmd) as well as any users of | ||
1375 | * that function that require issuing of an atomic sequence of commands, | ||
1376 | * and quiesces the transmit path whenever it's taken. | ||
1377 | */ | ||
1378 | static int mwl8k_fw_lock(struct ieee80211_hw *hw) | ||
1379 | { | ||
1380 | struct mwl8k_priv *priv = hw->priv; | ||
1381 | |||
1382 | if (priv->fw_mutex_owner != current) { | ||
1383 | int rc; | ||
1384 | |||
1385 | mutex_lock(&priv->fw_mutex); | ||
1386 | ieee80211_stop_queues(hw); | ||
1387 | |||
1388 | rc = mwl8k_tx_wait_empty(hw); | ||
1389 | if (rc) { | ||
1390 | ieee80211_wake_queues(hw); | ||
1391 | mutex_unlock(&priv->fw_mutex); | ||
1392 | |||
1393 | return rc; | ||
1394 | } | ||
1395 | |||
1396 | priv->fw_mutex_owner = current; | ||
1397 | } | ||
1398 | |||
1399 | priv->fw_mutex_depth++; | ||
1400 | |||
1401 | return 0; | ||
1402 | } | ||
1403 | |||
1404 | static void mwl8k_fw_unlock(struct ieee80211_hw *hw) | ||
1405 | { | ||
1406 | struct mwl8k_priv *priv = hw->priv; | ||
1407 | |||
1408 | if (!--priv->fw_mutex_depth) { | ||
1409 | ieee80211_wake_queues(hw); | ||
1410 | priv->fw_mutex_owner = NULL; | ||
1411 | mutex_unlock(&priv->fw_mutex); | ||
1412 | } | ||
1413 | } | ||
1414 | |||
1415 | |||
1416 | /* | ||
1363 | * Command processing. | 1417 | * Command processing. |
1364 | */ | 1418 | */ |
1365 | 1419 | ||
@@ -1384,28 +1438,28 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) | |||
1384 | if (pci_dma_mapping_error(priv->pdev, dma_addr)) | 1438 | if (pci_dma_mapping_error(priv->pdev, dma_addr)) |
1385 | return -ENOMEM; | 1439 | return -ENOMEM; |
1386 | 1440 | ||
1387 | if (priv->hostcmd_wait != NULL) | 1441 | rc = mwl8k_fw_lock(hw); |
1388 | printk(KERN_ERR "WARNING host command in progress\n"); | 1442 | if (rc) |
1443 | return rc; | ||
1389 | 1444 | ||
1390 | spin_lock_irq(&priv->fw_lock); | ||
1391 | priv->hostcmd_wait = &cmd_wait; | 1445 | priv->hostcmd_wait = &cmd_wait; |
1392 | iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); | 1446 | iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); |
1393 | iowrite32(MWL8K_H2A_INT_DOORBELL, | 1447 | iowrite32(MWL8K_H2A_INT_DOORBELL, |
1394 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); | 1448 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
1395 | iowrite32(MWL8K_H2A_INT_DUMMY, | 1449 | iowrite32(MWL8K_H2A_INT_DUMMY, |
1396 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); | 1450 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
1397 | spin_unlock_irq(&priv->fw_lock); | ||
1398 | 1451 | ||
1399 | timeout = wait_for_completion_timeout(&cmd_wait, | 1452 | timeout = wait_for_completion_timeout(&cmd_wait, |
1400 | msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); | 1453 | msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); |
1401 | 1454 | ||
1455 | priv->hostcmd_wait = NULL; | ||
1456 | |||
1457 | mwl8k_fw_unlock(hw); | ||
1458 | |||
1402 | pci_unmap_single(priv->pdev, dma_addr, dma_size, | 1459 | pci_unmap_single(priv->pdev, dma_addr, dma_size, |
1403 | PCI_DMA_BIDIRECTIONAL); | 1460 | PCI_DMA_BIDIRECTIONAL); |
1404 | 1461 | ||
1405 | if (!timeout) { | 1462 | if (!timeout) { |
1406 | spin_lock_irq(&priv->fw_lock); | ||
1407 | priv->hostcmd_wait = NULL; | ||
1408 | spin_unlock_irq(&priv->fw_lock); | ||
1409 | printk(KERN_ERR "%s: Command %s timeout after %u ms\n", | 1463 | printk(KERN_ERR "%s: Command %s timeout after %u ms\n", |
1410 | priv->name, | 1464 | priv->name, |
1411 | mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), | 1465 | mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), |
@@ -2336,17 +2390,14 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) | |||
2336 | } | 2390 | } |
2337 | 2391 | ||
2338 | if (status & MWL8K_A2H_INT_OPC_DONE) { | 2392 | if (status & MWL8K_A2H_INT_OPC_DONE) { |
2339 | if (priv->hostcmd_wait != NULL) { | 2393 | if (priv->hostcmd_wait != NULL) |
2340 | complete(priv->hostcmd_wait); | 2394 | complete(priv->hostcmd_wait); |
2341 | priv->hostcmd_wait = NULL; | ||
2342 | } | ||
2343 | } | 2395 | } |
2344 | 2396 | ||
2345 | if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { | 2397 | if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { |
2346 | if (!priv->inconfig && | 2398 | if (!mutex_is_locked(&priv->fw_mutex) && |
2347 | priv->radio_on && | 2399 | priv->radio_on && mwl8k_txq_busy(priv)) |
2348 | mwl8k_txq_busy(priv)) | 2400 | mwl8k_tx_start(priv); |
2349 | mwl8k_tx_start(priv); | ||
2350 | } | 2401 | } |
2351 | 2402 | ||
2352 | return IRQ_HANDLED; | 2403 | return IRQ_HANDLED; |
@@ -2395,41 +2446,13 @@ static void mwl8k_config_thread(struct work_struct *wt) | |||
2395 | { | 2446 | { |
2396 | struct mwl8k_work_struct *worker = (struct mwl8k_work_struct *)wt; | 2447 | struct mwl8k_work_struct *worker = (struct mwl8k_work_struct *)wt; |
2397 | struct ieee80211_hw *hw = worker->hw; | 2448 | struct ieee80211_hw *hw = worker->hw; |
2398 | struct mwl8k_priv *priv = hw->priv; | ||
2399 | int rc = 0; | 2449 | int rc = 0; |
2400 | int iter; | ||
2401 | 2450 | ||
2402 | spin_lock_irq(&priv->tx_lock); | 2451 | rc = mwl8k_fw_lock(hw); |
2403 | priv->inconfig = true; | 2452 | if (!rc) { |
2404 | spin_unlock_irq(&priv->tx_lock); | ||
2405 | |||
2406 | ieee80211_stop_queues(hw); | ||
2407 | |||
2408 | /* | ||
2409 | * Wait for host queues to drain before doing PHY | ||
2410 | * reconfiguration. This avoids interrupting any in-flight | ||
2411 | * DMA transfers to the hardware. | ||
2412 | */ | ||
2413 | iter = 4; | ||
2414 | do { | ||
2415 | rc = mwl8k_tx_wait_empty(hw); | ||
2416 | if (rc) | ||
2417 | printk(KERN_ERR "%s() txwait timeout=1000ms " | ||
2418 | "Retry:%u/%u\n", __func__, 4 - iter + 1, 4); | ||
2419 | } while (rc && --iter); | ||
2420 | |||
2421 | rc = iter ? 0 : -ETIMEDOUT; | ||
2422 | |||
2423 | if (!rc) | ||
2424 | rc = worker->wfunc(wt); | 2453 | rc = worker->wfunc(wt); |
2425 | 2454 | mwl8k_fw_unlock(hw); | |
2426 | spin_lock_irq(&priv->tx_lock); | 2455 | } |
2427 | priv->inconfig = false; | ||
2428 | if (priv->pending_tx_pkts && priv->radio_on) | ||
2429 | mwl8k_tx_start(priv); | ||
2430 | spin_unlock_irq(&priv->tx_lock); | ||
2431 | |||
2432 | ieee80211_wake_queues(hw); | ||
2433 | 2456 | ||
2434 | worker->rc = rc; | 2457 | worker->rc = rc; |
2435 | complete(worker->cmd_wait); | 2458 | complete(worker->cmd_wait); |
@@ -3145,15 +3168,10 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, | |||
3145 | priv = hw->priv; | 3168 | priv = hw->priv; |
3146 | priv->hw = hw; | 3169 | priv->hw = hw; |
3147 | priv->pdev = pdev; | 3170 | priv->pdev = pdev; |
3148 | priv->hostcmd_wait = NULL; | ||
3149 | priv->tx_wait = NULL; | ||
3150 | priv->inconfig = false; | ||
3151 | priv->wmm_enabled = false; | 3171 | priv->wmm_enabled = false; |
3152 | priv->pending_tx_pkts = 0; | 3172 | priv->pending_tx_pkts = 0; |
3153 | strncpy(priv->name, MWL8K_NAME, sizeof(priv->name)); | 3173 | strncpy(priv->name, MWL8K_NAME, sizeof(priv->name)); |
3154 | 3174 | ||
3155 | spin_lock_init(&priv->fw_lock); | ||
3156 | |||
3157 | SET_IEEE80211_DEV(hw, &pdev->dev); | 3175 | SET_IEEE80211_DEV(hw, &pdev->dev); |
3158 | pci_set_drvdata(pdev, hw); | 3176 | pci_set_drvdata(pdev, hw); |
3159 | 3177 | ||
@@ -3219,6 +3237,12 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, | |||
3219 | goto err_iounmap; | 3237 | goto err_iounmap; |
3220 | rxq_refill(hw, 0, INT_MAX); | 3238 | rxq_refill(hw, 0, INT_MAX); |
3221 | 3239 | ||
3240 | mutex_init(&priv->fw_mutex); | ||
3241 | priv->fw_mutex_owner = NULL; | ||
3242 | priv->fw_mutex_depth = 0; | ||
3243 | priv->tx_wait = NULL; | ||
3244 | priv->hostcmd_wait = NULL; | ||
3245 | |||
3222 | spin_lock_init(&priv->tx_lock); | 3246 | spin_lock_init(&priv->tx_lock); |
3223 | 3247 | ||
3224 | for (i = 0; i < MWL8K_TX_QUEUES; i++) { | 3248 | for (i = 0; i < MWL8K_TX_QUEUES; i++) { |