.. | .. |
---|
7 | 7 | * |
---|
8 | 8 | * Copyright(c) 2015 Intel Mobile Communications GmbH |
---|
9 | 9 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
---|
| 10 | + * Copyright(c) 2019 - 2020 Intel Corporation |
---|
10 | 11 | * |
---|
11 | 12 | * This program is free software; you can redistribute it and/or modify |
---|
12 | 13 | * it under the terms of version 2 of the GNU General Public License as |
---|
.. | .. |
---|
16 | 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
18 | 19 | * General Public License for more details. |
---|
19 | | - * |
---|
20 | | - * You should have received a copy of the GNU General Public License |
---|
21 | | - * along with this program; if not, write to the Free Software |
---|
22 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
---|
23 | | - * USA |
---|
24 | 20 | * |
---|
25 | 21 | * The full GNU General Public License is included in this distribution |
---|
26 | 22 | * in the file called COPYING. |
---|
.. | .. |
---|
33 | 29 | * |
---|
34 | 30 | * Copyright(c) 2015 Intel Mobile Communications GmbH |
---|
35 | 31 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
---|
| 32 | + * Copyright(c) 2019 - 2020 Intel Corporation |
---|
36 | 33 | * All rights reserved. |
---|
37 | 34 | * |
---|
38 | 35 | * Redistribution and use in source and binary forms, with or without |
---|
.. | .. |
---|
65 | 62 | #include <linux/kernel.h> |
---|
66 | 63 | #include <linux/bsearch.h> |
---|
67 | 64 | |
---|
| 65 | +#include "fw/api/tx.h" |
---|
68 | 66 | #include "iwl-trans.h" |
---|
69 | 67 | #include "iwl-drv.h" |
---|
70 | 68 | #include "iwl-fh.h" |
---|
| 69 | +#include "queue/tx.h" |
---|
| 70 | +#include <linux/dmapool.h> |
---|
71 | 71 | |
---|
72 | 72 | struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, |
---|
73 | 73 | struct device *dev, |
---|
74 | | - const struct iwl_cfg *cfg, |
---|
75 | | - const struct iwl_trans_ops *ops) |
---|
| 74 | + const struct iwl_trans_ops *ops, |
---|
| 75 | + const struct iwl_cfg_trans_params *cfg_trans) |
---|
76 | 76 | { |
---|
77 | 77 | struct iwl_trans *trans; |
---|
| 78 | + int txcmd_size, txcmd_align; |
---|
78 | 79 | #ifdef CONFIG_LOCKDEP |
---|
79 | 80 | static struct lock_class_key __key; |
---|
80 | 81 | #endif |
---|
.. | .. |
---|
83 | 84 | if (!trans) |
---|
84 | 85 | return NULL; |
---|
85 | 86 | |
---|
| 87 | + trans->trans_cfg = cfg_trans; |
---|
| 88 | + if (!cfg_trans->gen2) { |
---|
| 89 | + txcmd_size = sizeof(struct iwl_tx_cmd); |
---|
| 90 | + txcmd_align = sizeof(void *); |
---|
| 91 | + } else if (cfg_trans->device_family < IWL_DEVICE_FAMILY_AX210) { |
---|
| 92 | + txcmd_size = sizeof(struct iwl_tx_cmd_gen2); |
---|
| 93 | + txcmd_align = 64; |
---|
| 94 | + } else { |
---|
| 95 | + txcmd_size = sizeof(struct iwl_tx_cmd_gen3); |
---|
| 96 | + txcmd_align = 128; |
---|
| 97 | + } |
---|
| 98 | + |
---|
| 99 | + txcmd_size += sizeof(struct iwl_cmd_header); |
---|
| 100 | + txcmd_size += 36; /* biggest possible 802.11 header */ |
---|
| 101 | + |
---|
| 102 | + /* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */ |
---|
| 103 | + if (WARN_ON(cfg_trans->gen2 && txcmd_size >= txcmd_align)) |
---|
| 104 | + return ERR_PTR(-EINVAL); |
---|
| 105 | + |
---|
86 | 106 | #ifdef CONFIG_LOCKDEP |
---|
87 | 107 | lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", |
---|
88 | 108 | &__key, 0); |
---|
89 | 109 | #endif |
---|
90 | 110 | |
---|
91 | 111 | trans->dev = dev; |
---|
92 | | - trans->cfg = cfg; |
---|
93 | 112 | trans->ops = ops; |
---|
94 | 113 | trans->num_rx_queues = 1; |
---|
| 114 | + |
---|
| 115 | + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) |
---|
| 116 | + trans->txqs.bc_tbl_size = sizeof(struct iwl_gen3_bc_tbl); |
---|
| 117 | + else |
---|
| 118 | + trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl); |
---|
| 119 | + /* |
---|
| 120 | + * For gen2 devices, we use a single allocation for each byte-count |
---|
| 121 | + * table, but they're pretty small (1k) so use a DMA pool that we |
---|
| 122 | + * allocate here. |
---|
| 123 | + */ |
---|
| 124 | + if (trans->trans_cfg->gen2) { |
---|
| 125 | + trans->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", dev, |
---|
| 126 | + trans->txqs.bc_tbl_size, |
---|
| 127 | + 256, 0); |
---|
| 128 | + if (!trans->txqs.bc_pool) |
---|
| 129 | + return NULL; |
---|
| 130 | + } |
---|
| 131 | + |
---|
| 132 | + if (trans->trans_cfg->use_tfh) { |
---|
| 133 | + trans->txqs.tfd.addr_size = 64; |
---|
| 134 | + trans->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS; |
---|
| 135 | + trans->txqs.tfd.size = sizeof(struct iwl_tfh_tfd); |
---|
| 136 | + } else { |
---|
| 137 | + trans->txqs.tfd.addr_size = 36; |
---|
| 138 | + trans->txqs.tfd.max_tbs = IWL_NUM_OF_TBS; |
---|
| 139 | + trans->txqs.tfd.size = sizeof(struct iwl_tfd); |
---|
| 140 | + } |
---|
| 141 | + trans->max_skb_frags = IWL_TRANS_MAX_FRAGS(trans); |
---|
95 | 142 | |
---|
96 | 143 | snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), |
---|
97 | 144 | "iwl_cmd_pool:%s", dev_name(trans->dev)); |
---|
98 | 145 | trans->dev_cmd_pool = |
---|
99 | 146 | kmem_cache_create(trans->dev_cmd_pool_name, |
---|
100 | | - sizeof(struct iwl_device_cmd), |
---|
101 | | - sizeof(void *), |
---|
102 | | - SLAB_HWCACHE_ALIGN, |
---|
103 | | - NULL); |
---|
| 147 | + txcmd_size, txcmd_align, |
---|
| 148 | + SLAB_HWCACHE_ALIGN, NULL); |
---|
104 | 149 | if (!trans->dev_cmd_pool) |
---|
105 | 150 | return NULL; |
---|
106 | 151 | |
---|
107 | 152 | WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty); |
---|
| 153 | + |
---|
| 154 | + trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page); |
---|
| 155 | + if (!trans->txqs.tso_hdr_page) { |
---|
| 156 | + kmem_cache_destroy(trans->dev_cmd_pool); |
---|
| 157 | + return NULL; |
---|
| 158 | + } |
---|
108 | 159 | |
---|
109 | 160 | return trans; |
---|
110 | 161 | } |
---|
111 | 162 | |
---|
112 | 163 | void iwl_trans_free(struct iwl_trans *trans) |
---|
113 | 164 | { |
---|
| 165 | + int i; |
---|
| 166 | + |
---|
| 167 | + for_each_possible_cpu(i) { |
---|
| 168 | + struct iwl_tso_hdr_page *p = |
---|
| 169 | + per_cpu_ptr(trans->txqs.tso_hdr_page, i); |
---|
| 170 | + |
---|
| 171 | + if (p->page) |
---|
| 172 | + __free_page(p->page); |
---|
| 173 | + } |
---|
| 174 | + |
---|
| 175 | + free_percpu(trans->txqs.tso_hdr_page); |
---|
| 176 | + |
---|
114 | 177 | kmem_cache_destroy(trans->dev_cmd_pool); |
---|
115 | 178 | } |
---|
116 | 179 | |
---|
.. | .. |
---|
207 | 270 | return 0; |
---|
208 | 271 | } |
---|
209 | 272 | IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted); |
---|
210 | | - |
---|
211 | | -void iwl_trans_ref(struct iwl_trans *trans) |
---|
212 | | -{ |
---|
213 | | - if (trans->ops->ref) |
---|
214 | | - trans->ops->ref(trans); |
---|
215 | | -} |
---|
216 | | -IWL_EXPORT_SYMBOL(iwl_trans_ref); |
---|
217 | | - |
---|
218 | | -void iwl_trans_unref(struct iwl_trans *trans) |
---|
219 | | -{ |
---|
220 | | - if (trans->ops->unref) |
---|
221 | | - trans->ops->unref(trans); |
---|
222 | | -} |
---|
223 | | -IWL_EXPORT_SYMBOL(iwl_trans_unref); |
---|