| .. | .. |
|---|
| 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); |
|---|