18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Linux device driver for PCI based Prism54 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 78c2ecf20Sopenharmony_ci * Copyright (c) 2008, Christian Lamparter <chunkeey@web.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on the islsm (softmac prism54) driver, which is: 108c2ecf20Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jean-baptiste.note@m4x.org>, et al. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/completion.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <net/mac80211.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "p54.h" 238c2ecf20Sopenharmony_ci#include "lmac.h" 248c2ecf20Sopenharmony_ci#include "p54pci.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Prism54 PCI wireless driver"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ciMODULE_ALIAS("prism54pci"); 308c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isl3886pci"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct pci_device_id p54p_table[] = { 338c2ecf20Sopenharmony_ci /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ 348c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1260, 0x3890) }, 358c2ecf20Sopenharmony_ci /* 3COM 3CRWE154G72 Wireless LAN adapter */ 368c2ecf20Sopenharmony_ci { PCI_DEVICE(0x10b7, 0x6001) }, 378c2ecf20Sopenharmony_ci /* Intersil PRISM Indigo Wireless LAN adapter */ 388c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1260, 0x3877) }, 398c2ecf20Sopenharmony_ci /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ 408c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1260, 0x3886) }, 418c2ecf20Sopenharmony_ci /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */ 428c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1260, 0xffff) }, 438c2ecf20Sopenharmony_ci { }, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, p54p_table); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int p54p_upload_firmware(struct ieee80211_hw *dev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 518c2ecf20Sopenharmony_ci __le32 reg; 528c2ecf20Sopenharmony_ci int err; 538c2ecf20Sopenharmony_ci __le32 *data; 548c2ecf20Sopenharmony_ci u32 remains, left, device_addr; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci P54P_WRITE(int_enable, cpu_to_le32(0)); 578c2ecf20Sopenharmony_ci P54P_READ(int_enable); 588c2ecf20Sopenharmony_ci udelay(10); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci reg = P54P_READ(ctrl_stat); 618c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 628c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); 638c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 648c2ecf20Sopenharmony_ci P54P_READ(ctrl_stat); 658c2ecf20Sopenharmony_ci udelay(10); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 688c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 698c2ecf20Sopenharmony_ci wmb(); 708c2ecf20Sopenharmony_ci udelay(10); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 738c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 748c2ecf20Sopenharmony_ci wmb(); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* wait for the firmware to reset properly */ 778c2ecf20Sopenharmony_ci mdelay(10); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci err = p54_parse_firmware(dev, priv->firmware); 808c2ecf20Sopenharmony_ci if (err) 818c2ecf20Sopenharmony_ci return err; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (priv->common.fw_interface != FW_LM86) { 848c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "wrong firmware, " 858c2ecf20Sopenharmony_ci "please get a LM86(PCI) firmware a try again.\n"); 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci data = (__le32 *) priv->firmware->data; 908c2ecf20Sopenharmony_ci remains = priv->firmware->size; 918c2ecf20Sopenharmony_ci device_addr = ISL38XX_DEV_FIRMWARE_ADDR; 928c2ecf20Sopenharmony_ci while (remains) { 938c2ecf20Sopenharmony_ci u32 i = 0; 948c2ecf20Sopenharmony_ci left = min((u32)0x1000, remains); 958c2ecf20Sopenharmony_ci P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr)); 968c2ecf20Sopenharmony_ci P54P_READ(int_enable); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci device_addr += 0x1000; 998c2ecf20Sopenharmony_ci while (i < left) { 1008c2ecf20Sopenharmony_ci P54P_WRITE(direct_mem_win[i], *data++); 1018c2ecf20Sopenharmony_ci i += sizeof(u32); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci remains -= left; 1058c2ecf20Sopenharmony_ci P54P_READ(int_enable); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci reg = P54P_READ(ctrl_stat); 1098c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); 1108c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 1118c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); 1128c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 1138c2ecf20Sopenharmony_ci P54P_READ(ctrl_stat); 1148c2ecf20Sopenharmony_ci udelay(10); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 1178c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 1188c2ecf20Sopenharmony_ci wmb(); 1198c2ecf20Sopenharmony_ci udelay(10); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 1228c2ecf20Sopenharmony_ci P54P_WRITE(ctrl_stat, reg); 1238c2ecf20Sopenharmony_ci wmb(); 1248c2ecf20Sopenharmony_ci udelay(10); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* wait for the firmware to boot properly */ 1278c2ecf20Sopenharmony_ci mdelay(100); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void p54p_refill_rx_ring(struct ieee80211_hw *dev, 1338c2ecf20Sopenharmony_ci int ring_index, struct p54p_desc *ring, u32 ring_limit, 1348c2ecf20Sopenharmony_ci struct sk_buff **rx_buf, u32 index) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 1378c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 1388c2ecf20Sopenharmony_ci u32 limit, idx, i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci idx = le32_to_cpu(ring_control->host_idx[ring_index]); 1418c2ecf20Sopenharmony_ci limit = idx; 1428c2ecf20Sopenharmony_ci limit -= index; 1438c2ecf20Sopenharmony_ci limit = ring_limit - limit; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci i = idx % ring_limit; 1468c2ecf20Sopenharmony_ci while (limit-- > 1) { 1478c2ecf20Sopenharmony_ci struct p54p_desc *desc = &ring[i]; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!desc->host_addr) { 1508c2ecf20Sopenharmony_ci struct sk_buff *skb; 1518c2ecf20Sopenharmony_ci dma_addr_t mapping; 1528c2ecf20Sopenharmony_ci skb = dev_alloc_skb(priv->common.rx_mtu + 32); 1538c2ecf20Sopenharmony_ci if (!skb) 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mapping = dma_map_single(&priv->pdev->dev, 1578c2ecf20Sopenharmony_ci skb_tail_pointer(skb), 1588c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 1598c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, mapping)) { 1628c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1638c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, 1648c2ecf20Sopenharmony_ci "RX DMA Mapping error\n"); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci desc->host_addr = cpu_to_le32(mapping); 1698c2ecf20Sopenharmony_ci desc->device_addr = 0; // FIXME: necessary? 1708c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(priv->common.rx_mtu + 32); 1718c2ecf20Sopenharmony_ci desc->flags = 0; 1728c2ecf20Sopenharmony_ci rx_buf[i] = skb; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci i++; 1768c2ecf20Sopenharmony_ci idx++; 1778c2ecf20Sopenharmony_ci i %= ring_limit; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci wmb(); 1818c2ecf20Sopenharmony_ci ring_control->host_idx[ring_index] = cpu_to_le32(idx); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, 1858c2ecf20Sopenharmony_ci int ring_index, struct p54p_desc *ring, u32 ring_limit, 1868c2ecf20Sopenharmony_ci struct sk_buff **rx_buf) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 1898c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 1908c2ecf20Sopenharmony_ci struct p54p_desc *desc; 1918c2ecf20Sopenharmony_ci u32 idx, i; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci i = (*index) % ring_limit; 1948c2ecf20Sopenharmony_ci (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); 1958c2ecf20Sopenharmony_ci idx %= ring_limit; 1968c2ecf20Sopenharmony_ci while (i != idx) { 1978c2ecf20Sopenharmony_ci u16 len; 1988c2ecf20Sopenharmony_ci struct sk_buff *skb; 1998c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2008c2ecf20Sopenharmony_ci desc = &ring[i]; 2018c2ecf20Sopenharmony_ci len = le16_to_cpu(desc->len); 2028c2ecf20Sopenharmony_ci skb = rx_buf[i]; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!skb) { 2058c2ecf20Sopenharmony_ci i++; 2068c2ecf20Sopenharmony_ci i %= ring_limit; 2078c2ecf20Sopenharmony_ci continue; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (unlikely(len > priv->common.rx_mtu)) { 2118c2ecf20Sopenharmony_ci if (net_ratelimit()) 2128c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "rx'd frame size " 2138c2ecf20Sopenharmony_ci "exceeds length threshold.\n"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci len = priv->common.rx_mtu; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci dma_addr = le32_to_cpu(desc->host_addr); 2188c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&priv->pdev->dev, dma_addr, 2198c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 2208c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2218c2ecf20Sopenharmony_ci skb_put(skb, len); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (p54_rx(dev, skb)) { 2248c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, dma_addr, 2258c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 2268c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2278c2ecf20Sopenharmony_ci rx_buf[i] = NULL; 2288c2ecf20Sopenharmony_ci desc->host_addr = cpu_to_le32(0); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci skb_trim(skb, 0); 2318c2ecf20Sopenharmony_ci dma_sync_single_for_device(&priv->pdev->dev, dma_addr, 2328c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 2338c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2348c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(priv->common.rx_mtu + 32); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci i++; 2388c2ecf20Sopenharmony_ci i %= ring_limit; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, 2458c2ecf20Sopenharmony_ci int ring_index, struct p54p_desc *ring, u32 ring_limit, 2468c2ecf20Sopenharmony_ci struct sk_buff **tx_buf) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 2498c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 2508c2ecf20Sopenharmony_ci struct p54p_desc *desc; 2518c2ecf20Sopenharmony_ci struct sk_buff *skb; 2528c2ecf20Sopenharmony_ci u32 idx, i; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci i = (*index) % ring_limit; 2558c2ecf20Sopenharmony_ci (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); 2568c2ecf20Sopenharmony_ci idx %= ring_limit; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci while (i != idx) { 2598c2ecf20Sopenharmony_ci desc = &ring[i]; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci skb = tx_buf[i]; 2628c2ecf20Sopenharmony_ci tx_buf[i] = NULL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 2658c2ecf20Sopenharmony_ci le32_to_cpu(desc->host_addr), 2668c2ecf20Sopenharmony_ci le16_to_cpu(desc->len), DMA_TO_DEVICE); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci desc->host_addr = 0; 2698c2ecf20Sopenharmony_ci desc->device_addr = 0; 2708c2ecf20Sopenharmony_ci desc->len = 0; 2718c2ecf20Sopenharmony_ci desc->flags = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (skb && FREE_AFTER_TX(skb)) 2748c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci i++; 2778c2ecf20Sopenharmony_ci i %= ring_limit; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void p54p_tasklet(struct tasklet_struct *t) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct p54p_priv *priv = from_tasklet(priv, t, tasklet); 2848c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = pci_get_drvdata(priv->pdev); 2858c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt, 2888c2ecf20Sopenharmony_ci ARRAY_SIZE(ring_control->tx_mgmt), 2898c2ecf20Sopenharmony_ci priv->tx_buf_mgmt); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data, 2928c2ecf20Sopenharmony_ci ARRAY_SIZE(ring_control->tx_data), 2938c2ecf20Sopenharmony_ci priv->tx_buf_data); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt, 2968c2ecf20Sopenharmony_ci ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data, 2998c2ecf20Sopenharmony_ci ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci wmb(); 3028c2ecf20Sopenharmony_ci P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic irqreturn_t p54p_interrupt(int irq, void *dev_id) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = dev_id; 3088c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 3098c2ecf20Sopenharmony_ci __le32 reg; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci reg = P54P_READ(int_ident); 3128c2ecf20Sopenharmony_ci if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) { 3138c2ecf20Sopenharmony_ci goto out; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci P54P_WRITE(int_ack, reg); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci reg &= P54P_READ(int_enable); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) 3208c2ecf20Sopenharmony_ci tasklet_schedule(&priv->tasklet); 3218c2ecf20Sopenharmony_ci else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) 3228c2ecf20Sopenharmony_ci complete(&priv->boot_comp); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciout: 3258c2ecf20Sopenharmony_ci return reg ? IRQ_HANDLED : IRQ_NONE; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci unsigned long flags; 3318c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 3328c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 3338c2ecf20Sopenharmony_ci struct p54p_desc *desc; 3348c2ecf20Sopenharmony_ci dma_addr_t mapping; 3358c2ecf20Sopenharmony_ci u32 idx, i; 3368c2ecf20Sopenharmony_ci __le32 device_addr; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 3398c2ecf20Sopenharmony_ci idx = le32_to_cpu(ring_control->host_idx[1]); 3408c2ecf20Sopenharmony_ci i = idx % ARRAY_SIZE(ring_control->tx_data); 3418c2ecf20Sopenharmony_ci device_addr = ((struct p54_hdr *)skb->data)->req_id; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci mapping = dma_map_single(&priv->pdev->dev, skb->data, skb->len, 3448c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 3458c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, mapping)) { 3468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3478c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 3488c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); 3498c2ecf20Sopenharmony_ci return ; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci priv->tx_buf_data[i] = skb; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci desc = &ring_control->tx_data[i]; 3548c2ecf20Sopenharmony_ci desc->host_addr = cpu_to_le32(mapping); 3558c2ecf20Sopenharmony_ci desc->device_addr = device_addr; 3568c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(skb->len); 3578c2ecf20Sopenharmony_ci desc->flags = 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci wmb(); 3608c2ecf20Sopenharmony_ci ring_control->host_idx[1] = cpu_to_le32(idx + 1); 3618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); 3648c2ecf20Sopenharmony_ci P54P_READ(dev_int); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void p54p_stop(struct ieee80211_hw *dev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 3708c2ecf20Sopenharmony_ci struct p54p_ring_control *ring_control = priv->ring_control; 3718c2ecf20Sopenharmony_ci unsigned int i; 3728c2ecf20Sopenharmony_ci struct p54p_desc *desc; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci P54P_WRITE(int_enable, cpu_to_le32(0)); 3758c2ecf20Sopenharmony_ci P54P_READ(int_enable); 3768c2ecf20Sopenharmony_ci udelay(10); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci free_irq(priv->pdev->irq, dev); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci tasklet_kill(&priv->tasklet); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) { 3858c2ecf20Sopenharmony_ci desc = &ring_control->rx_data[i]; 3868c2ecf20Sopenharmony_ci if (desc->host_addr) 3878c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 3888c2ecf20Sopenharmony_ci le32_to_cpu(desc->host_addr), 3898c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 3908c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3918c2ecf20Sopenharmony_ci kfree_skb(priv->rx_buf_data[i]); 3928c2ecf20Sopenharmony_ci priv->rx_buf_data[i] = NULL; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) { 3968c2ecf20Sopenharmony_ci desc = &ring_control->rx_mgmt[i]; 3978c2ecf20Sopenharmony_ci if (desc->host_addr) 3988c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 3998c2ecf20Sopenharmony_ci le32_to_cpu(desc->host_addr), 4008c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, 4018c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4028c2ecf20Sopenharmony_ci kfree_skb(priv->rx_buf_mgmt[i]); 4038c2ecf20Sopenharmony_ci priv->rx_buf_mgmt[i] = NULL; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) { 4078c2ecf20Sopenharmony_ci desc = &ring_control->tx_data[i]; 4088c2ecf20Sopenharmony_ci if (desc->host_addr) 4098c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 4108c2ecf20Sopenharmony_ci le32_to_cpu(desc->host_addr), 4118c2ecf20Sopenharmony_ci le16_to_cpu(desc->len), 4128c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci p54_free_skb(dev, priv->tx_buf_data[i]); 4158c2ecf20Sopenharmony_ci priv->tx_buf_data[i] = NULL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) { 4198c2ecf20Sopenharmony_ci desc = &ring_control->tx_mgmt[i]; 4208c2ecf20Sopenharmony_ci if (desc->host_addr) 4218c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 4228c2ecf20Sopenharmony_ci le32_to_cpu(desc->host_addr), 4238c2ecf20Sopenharmony_ci le16_to_cpu(desc->len), 4248c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci p54_free_skb(dev, priv->tx_buf_mgmt[i]); 4278c2ecf20Sopenharmony_ci priv->tx_buf_mgmt[i] = NULL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci memset(ring_control, 0, sizeof(*ring_control)); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int p54p_open(struct ieee80211_hw *dev) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct p54p_priv *priv = dev->priv; 4368c2ecf20Sopenharmony_ci int err; 4378c2ecf20Sopenharmony_ci long timeout; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci init_completion(&priv->boot_comp); 4408c2ecf20Sopenharmony_ci err = request_irq(priv->pdev->irq, p54p_interrupt, 4418c2ecf20Sopenharmony_ci IRQF_SHARED, "p54pci", dev); 4428c2ecf20Sopenharmony_ci if (err) { 4438c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "failed to register IRQ handler\n"); 4448c2ecf20Sopenharmony_ci return err; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci memset(priv->ring_control, 0, sizeof(*priv->ring_control)); 4488c2ecf20Sopenharmony_ci err = p54p_upload_firmware(dev); 4498c2ecf20Sopenharmony_ci if (err) { 4508c2ecf20Sopenharmony_ci free_irq(priv->pdev->irq, dev); 4518c2ecf20Sopenharmony_ci return err; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci priv->rx_idx_data = priv->tx_idx_data = 0; 4548c2ecf20Sopenharmony_ci priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data, 4578c2ecf20Sopenharmony_ci ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt, 4608c2ecf20Sopenharmony_ci ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma)); 4638c2ecf20Sopenharmony_ci P54P_READ(ring_control_base); 4648c2ecf20Sopenharmony_ci wmb(); 4658c2ecf20Sopenharmony_ci udelay(10); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); 4688c2ecf20Sopenharmony_ci P54P_READ(int_enable); 4698c2ecf20Sopenharmony_ci wmb(); 4708c2ecf20Sopenharmony_ci udelay(10); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); 4738c2ecf20Sopenharmony_ci P54P_READ(dev_int); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci timeout = wait_for_completion_interruptible_timeout( 4768c2ecf20Sopenharmony_ci &priv->boot_comp, HZ); 4778c2ecf20Sopenharmony_ci if (timeout <= 0) { 4788c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "Cannot boot firmware!\n"); 4798c2ecf20Sopenharmony_ci p54p_stop(dev); 4808c2ecf20Sopenharmony_ci return timeout ? -ERESTARTSYS : -ETIMEDOUT; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); 4848c2ecf20Sopenharmony_ci P54P_READ(int_enable); 4858c2ecf20Sopenharmony_ci wmb(); 4868c2ecf20Sopenharmony_ci udelay(10); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); 4898c2ecf20Sopenharmony_ci P54P_READ(dev_int); 4908c2ecf20Sopenharmony_ci wmb(); 4918c2ecf20Sopenharmony_ci udelay(10); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void p54p_firmware_step2(const struct firmware *fw, 4978c2ecf20Sopenharmony_ci void *context) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct p54p_priv *priv = context; 5008c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = priv->common.hw; 5018c2ecf20Sopenharmony_ci struct pci_dev *pdev = priv->pdev; 5028c2ecf20Sopenharmony_ci int err; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!fw) { 5058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); 5068c2ecf20Sopenharmony_ci err = -ENOENT; 5078c2ecf20Sopenharmony_ci goto out; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci priv->firmware = fw; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci err = p54p_open(dev); 5138c2ecf20Sopenharmony_ci if (err) 5148c2ecf20Sopenharmony_ci goto out; 5158c2ecf20Sopenharmony_ci err = p54_read_eeprom(dev); 5168c2ecf20Sopenharmony_ci p54p_stop(dev); 5178c2ecf20Sopenharmony_ci if (err) 5188c2ecf20Sopenharmony_ci goto out; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci err = p54_register_common(dev, &pdev->dev); 5218c2ecf20Sopenharmony_ci if (err) 5228c2ecf20Sopenharmony_ci goto out; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ciout: 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci complete(&priv->fw_loaded); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (err) { 5298c2ecf20Sopenharmony_ci struct device *parent = pdev->dev.parent; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (parent) 5328c2ecf20Sopenharmony_ci device_lock(parent); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * This will indirectly result in a call to p54p_remove. 5368c2ecf20Sopenharmony_ci * Hence, we don't need to bother with freeing any 5378c2ecf20Sopenharmony_ci * allocated ressources at all. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci device_release_driver(&pdev->dev); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (parent) 5428c2ecf20Sopenharmony_ci device_unlock(parent); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci pci_dev_put(pdev); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic int p54p_probe(struct pci_dev *pdev, 5498c2ecf20Sopenharmony_ci const struct pci_device_id *id) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct p54p_priv *priv; 5528c2ecf20Sopenharmony_ci struct ieee80211_hw *dev; 5538c2ecf20Sopenharmony_ci unsigned long mem_addr, mem_len; 5548c2ecf20Sopenharmony_ci int err; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci pci_dev_get(pdev); 5578c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 5588c2ecf20Sopenharmony_ci if (err) { 5598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable new PCI device\n"); 5608c2ecf20Sopenharmony_ci goto err_put; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci mem_addr = pci_resource_start(pdev, 0); 5648c2ecf20Sopenharmony_ci mem_len = pci_resource_len(pdev, 0); 5658c2ecf20Sopenharmony_ci if (mem_len < sizeof(struct p54p_csr)) { 5668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Too short PCI resources\n"); 5678c2ecf20Sopenharmony_ci err = -ENODEV; 5688c2ecf20Sopenharmony_ci goto err_disable_dev; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, "p54pci"); 5728c2ecf20Sopenharmony_ci if (err) { 5738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); 5748c2ecf20Sopenharmony_ci goto err_disable_dev; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 5788c2ecf20Sopenharmony_ci if (!err) 5798c2ecf20Sopenharmony_ci err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 5808c2ecf20Sopenharmony_ci if (err) { 5818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available\n"); 5828c2ecf20Sopenharmony_ci goto err_free_reg; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci pci_set_master(pdev); 5868c2ecf20Sopenharmony_ci pci_try_set_mwi(pdev); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x40, 0); 5898c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x41, 0); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci dev = p54_init_common(sizeof(*priv)); 5928c2ecf20Sopenharmony_ci if (!dev) { 5938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ieee80211 alloc failed\n"); 5948c2ecf20Sopenharmony_ci err = -ENOMEM; 5958c2ecf20Sopenharmony_ci goto err_free_reg; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci priv = dev->priv; 5998c2ecf20Sopenharmony_ci priv->pdev = pdev; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci init_completion(&priv->fw_loaded); 6028c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(dev, &pdev->dev); 6038c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci priv->map = ioremap(mem_addr, mem_len); 6068c2ecf20Sopenharmony_ci if (!priv->map) { 6078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map device memory\n"); 6088c2ecf20Sopenharmony_ci err = -ENOMEM; 6098c2ecf20Sopenharmony_ci goto err_free_dev; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci priv->ring_control = dma_alloc_coherent(&pdev->dev, 6138c2ecf20Sopenharmony_ci sizeof(*priv->ring_control), 6148c2ecf20Sopenharmony_ci &priv->ring_control_dma, GFP_KERNEL); 6158c2ecf20Sopenharmony_ci if (!priv->ring_control) { 6168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate rings\n"); 6178c2ecf20Sopenharmony_ci err = -ENOMEM; 6188c2ecf20Sopenharmony_ci goto err_iounmap; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci priv->common.open = p54p_open; 6218c2ecf20Sopenharmony_ci priv->common.stop = p54p_stop; 6228c2ecf20Sopenharmony_ci priv->common.tx = p54p_tx; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 6258c2ecf20Sopenharmony_ci tasklet_setup(&priv->tasklet, p54p_tasklet); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci", 6288c2ecf20Sopenharmony_ci &priv->pdev->dev, GFP_KERNEL, 6298c2ecf20Sopenharmony_ci priv, p54p_firmware_step2); 6308c2ecf20Sopenharmony_ci if (!err) 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(*priv->ring_control), 6348c2ecf20Sopenharmony_ci priv->ring_control, priv->ring_control_dma); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci err_iounmap: 6378c2ecf20Sopenharmony_ci iounmap(priv->map); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci err_free_dev: 6408c2ecf20Sopenharmony_ci p54_free_common(dev); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci err_free_reg: 6438c2ecf20Sopenharmony_ci pci_release_regions(pdev); 6448c2ecf20Sopenharmony_ci err_disable_dev: 6458c2ecf20Sopenharmony_ci pci_disable_device(pdev); 6468c2ecf20Sopenharmony_cierr_put: 6478c2ecf20Sopenharmony_ci pci_dev_put(pdev); 6488c2ecf20Sopenharmony_ci return err; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void p54p_remove(struct pci_dev *pdev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = pci_get_drvdata(pdev); 6548c2ecf20Sopenharmony_ci struct p54p_priv *priv; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (!dev) 6578c2ecf20Sopenharmony_ci return; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci priv = dev->priv; 6608c2ecf20Sopenharmony_ci wait_for_completion(&priv->fw_loaded); 6618c2ecf20Sopenharmony_ci p54_unregister_common(dev); 6628c2ecf20Sopenharmony_ci release_firmware(priv->firmware); 6638c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(*priv->ring_control), 6648c2ecf20Sopenharmony_ci priv->ring_control, priv->ring_control_dma); 6658c2ecf20Sopenharmony_ci iounmap(priv->map); 6668c2ecf20Sopenharmony_ci pci_release_regions(pdev); 6678c2ecf20Sopenharmony_ci pci_disable_device(pdev); 6688c2ecf20Sopenharmony_ci p54_free_common(dev); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6728c2ecf20Sopenharmony_cistatic int p54p_suspend(struct device *device) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci pci_save_state(pdev); 6778c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 6788c2ecf20Sopenharmony_ci pci_disable_device(pdev); 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int p54p_resume(struct device *device) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 6858c2ecf20Sopenharmony_ci int err; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci err = pci_reenable_device(pdev); 6888c2ecf20Sopenharmony_ci if (err) 6898c2ecf20Sopenharmony_ci return err; 6908c2ecf20Sopenharmony_ci return pci_set_power_state(pdev, PCI_D0); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci#define P54P_PM_OPS (&p54pci_pm_ops) 6968c2ecf20Sopenharmony_ci#else 6978c2ecf20Sopenharmony_ci#define P54P_PM_OPS (NULL) 6988c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic struct pci_driver p54p_driver = { 7018c2ecf20Sopenharmony_ci .name = "p54pci", 7028c2ecf20Sopenharmony_ci .id_table = p54p_table, 7038c2ecf20Sopenharmony_ci .probe = p54p_probe, 7048c2ecf20Sopenharmony_ci .remove = p54p_remove, 7058c2ecf20Sopenharmony_ci .driver.pm = P54P_PM_OPS, 7068c2ecf20Sopenharmony_ci}; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cimodule_pci_driver(p54p_driver); 709