| /** |
| * rwnx_utils.c |
| * |
| * IPC utility function definitions |
| * |
| * Copyright (C) RivieraWaves 2012-2021 |
| */ |
| #include "rwnx_utils.h" |
| #include "rwnx_defs.h" |
| #include "rwnx_rx.h" |
| #include "rwnx_tx.h" |
| #include "rwnx_msg_rx.h" |
| #include "rwnx_debugfs.h" |
| #include "rwnx_prof.h" |
| #include "ipc_host.h" |
| |
| #ifdef CONFIG_RWNX_FULLMAC |
| #define FW_STR "fmac" |
| #endif |
| |
| /** |
| * rwnx_ipc_buf_pool_alloc() - Allocate and push to fw a pool of IPC buffer. |
| * |
| * @rwnx_hw: Main driver structure |
| * @pool: Pool to allocate |
| * @nb: Size of the pool to allocate |
| * @buf_size: Size of one pool element |
| * @pool_name: Name of the pool |
| * @push: Function to push one pool buffer to fw |
| * |
| * This function will allocate an array to store the list of IPC buffers, |
| * a dma pool and @nb element in the dma pool. |
| * Each buffer is initialized with '0' and then pushed to fw using the @push function. |
| * |
| * Return: 0 on success and <0 upon error. If error is returned any allocated |
| * memory is NOT freed and rwnx_ipc_buf_pool_dealloc() must be called. |
| */ |
| static int rwnx_ipc_buf_pool_alloc(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf_pool *pool, |
| int nb, size_t buf_size, char *pool_name, |
| int (*push)(struct ipc_host_env_tag *, |
| struct rwnx_ipc_buf *)) |
| { |
| struct rwnx_ipc_buf *buf; |
| int i; |
| |
| pool->nb = 0; |
| |
| /* allocate buf array */ |
| pool->buffers = kmalloc(nb * sizeof(struct rwnx_ipc_buf), GFP_KERNEL); |
| if (!pool->buffers) { |
| dev_err(rwnx_hw->dev, "Allocation of buffer array for %s failed\n", |
| pool_name); |
| return -ENOMEM; |
| } |
| |
| /* allocate dma pool */ |
| pool->pool = dma_pool_create(pool_name, rwnx_hw->dev, buf_size, |
| cache_line_size(), 0); |
| if (!pool->pool) { |
| dev_err(rwnx_hw->dev, "Allocation of dma pool %s failed\n", |
| pool_name); |
| return -ENOMEM; |
| } |
| |
| for (i = 0, buf = pool->buffers; i < nb; buf++, i++) { |
| /* allocate a buffer */ |
| buf->size = buf_size; |
| buf->addr = dma_pool_alloc(pool->pool, GFP_KERNEL, &buf->dma_addr); |
| if (!buf->addr) { |
| dev_err(rwnx_hw->dev, "Allocation of block %d/%d in %s failed\n", |
| (i + 1), nb, pool_name); |
| return -ENOMEM; |
| } |
| pool->nb++; |
| |
| /* reset the buffer */ |
| memset(buf->addr, 0, buf_size); |
| |
| /* push it to FW */ |
| push(rwnx_hw->ipc_env, buf); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_buf_pool_dealloc() - Free all memory allocated for a pool |
| * |
| * @pool: Pool to free |
| * |
| * Must be call once after rwnx_ipc_buf_pool_alloc(), even if it returned |
| * an error |
| */ |
| static void rwnx_ipc_buf_pool_dealloc(struct rwnx_ipc_buf_pool *pool) |
| { |
| struct rwnx_ipc_buf *buf; |
| int i; |
| |
| for (i = 0, buf = pool->buffers; i < pool->nb ; buf++, i++) { |
| dma_pool_free(pool->pool, buf->addr, buf->dma_addr); |
| } |
| pool->nb = 0; |
| |
| if (pool->pool) |
| dma_pool_destroy(pool->pool); |
| pool->pool = NULL; |
| |
| if (pool->buffers) |
| kfree(pool->buffers); |
| pool->buffers = NULL; |
| } |
| |
| /** |
| * rwnx_ipc_buf_alloc - Alloc a single ipc buffer and MAP it for DMA access |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to allocate |
| * @buf_size: Size of the buffer to allocate |
| * @dir: DMA direction |
| * @init: Pointer to initial data to write in buffer before DMA sync. Used |
| * only if direction is DMA_TO_DEVICE and it must be at least @buf_size long |
| * |
| * It allocates a buffer, initializes it if @init is set, and map it for DMA |
| * Use @rwnx_ipc_buf_dealloc when this buffer is no longer needed. |
| * |
| * @return: 0 on success and <0 upon error. If error is returned any allocated |
| * memory has been freed. |
| */ |
| int rwnx_ipc_buf_alloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf, |
| size_t buf_size, enum dma_data_direction dir, const void *init) |
| { |
| buf->addr = kmalloc(buf_size, GFP_KERNEL); |
| if (!buf->addr) |
| return -ENOMEM; |
| |
| buf->size = buf_size; |
| |
| if ((dir == DMA_TO_DEVICE) && init) { |
| memcpy(buf->addr, init, buf_size); |
| } |
| |
| buf->dma_addr = dma_map_single(rwnx_hw->dev, buf->addr, buf_size, dir); |
| if (dma_mapping_error(rwnx_hw->dev, buf->dma_addr)) { |
| kfree(buf->addr); |
| buf->addr = NULL; |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_buf_dealloc() - Free memory allocated for a single ipc buffer |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to free |
| * |
| * IPC buffer must have been allocated by @rwnx_ipc_buf_alloc() or initialized |
| * by @rwnx_ipc_buf_init() and pointing to a buffer allocated by kmalloc. |
| */ |
| void rwnx_ipc_buf_dealloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf) |
| { |
| if (!buf->addr) |
| return; |
| dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, DMA_TO_DEVICE); |
| kfree(buf->addr); |
| buf->addr = NULL; |
| } |
| |
| /** |
| * rwnx_ipc_buf_a2e_init - Initialize an Application to Embedded IPC buffer |
| * with a pre-allocated buffer |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to initialize |
| * @data: Data buffer to use for the IPC buffer. |
| * @buf_size: Size of the buffer the @data buffer |
| * |
| * Initialize the IPC buffer with the provided buffer and map it for DMA transfer. |
| * The mapping direction is always DMA_TO_DEVICE as this an "a2e" buffer. |
| * Use @rwnx_ipc_buf_dealloc() when this buffer is no longer needed. |
| * |
| * @return: 0 on success and <0 upon error. If error is returned the @data buffer |
| * is freed. |
| */ |
| int rwnx_ipc_buf_a2e_init(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf, |
| void *data, size_t buf_size) |
| { |
| buf->addr = data; |
| buf->size = buf_size; |
| buf->dma_addr = dma_map_single(rwnx_hw->dev, buf->addr, buf_size, |
| DMA_TO_DEVICE); |
| if (dma_mapping_error(rwnx_hw->dev, buf->dma_addr)) { |
| buf->addr = NULL; |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_buf_release() - Release DMA mapping for an IPC buffer |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to release |
| * @dir: DMA direction. |
| * |
| * This also "release" the IPC buffer structure (i.e. its addr field is reset) |
| * so that it cannot be re-used except to map another buffer. |
| */ |
| void rwnx_ipc_buf_release(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf, |
| enum dma_data_direction dir) |
| { |
| if (!buf->addr) |
| return; |
| dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, dir); |
| buf->addr = NULL; |
| } |
| |
| /** |
| * rwnx_ipc_buf_e2a_sync() - Synchronize all (or part) of an IPC buffer before |
| * reading content written by the embedded |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to sync |
| * @len: Length to read, 0 means the whole buffer |
| */ |
| void rwnx_ipc_buf_e2a_sync(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf, |
| size_t len) |
| { |
| if (!len) |
| len = buf->size; |
| |
| dma_sync_single_for_cpu(rwnx_hw->dev, buf->dma_addr, len, DMA_FROM_DEVICE); |
| } |
| |
| /** |
| * rwnx_ipc_buf_e2a_sync_back() - Synchronize back all (or part) of an IPC buffer |
| * to allow embedded updating its content. |
| * |
| * @rwnx_hw: Main driver structure |
| * @buf: IPC buffer to sync |
| * @len: Length to sync back, 0 means the whole buffer |
| * |
| * Must be called after each call to rwnx_ipc_buf_e2a_sync() even if host didn't |
| * modified the content of the buffer. |
| */ |
| void rwnx_ipc_buf_e2a_sync_back(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf, |
| size_t len) |
| { |
| if (!len) |
| len = buf->size; |
| |
| dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr, len, DMA_FROM_DEVICE); |
| } |
| |
| /** |
| * rwnx_ipc_rxskb_alloc() - Allocate a skb for RX path |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: rwnx_ipc_buf structure to store skb address |
| * @skb_size: Size of the buffer to allocate |
| * |
| * Allocate a skb for RX path, meaning that the data buffer is written by the firmware |
| * and needs then to be DMA mapped. |
| * |
| * Note that even though the result is stored in a struct rwnx_ipc_buf, in this case the |
| * rwnx_ipc_buf.addr points to skb structure whereas the rwnx_ipc_buf.dma_addr is the |
| * DMA address of the skb data buffer (i.e. skb->data) |
| */ |
| static int rwnx_ipc_rxskb_alloc(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf *buf, size_t skb_size) |
| { |
| struct sk_buff *skb = dev_alloc_skb(skb_size); |
| |
| if (unlikely(!skb)) { |
| dev_err(rwnx_hw->dev, "Allocation of RX skb failed\n"); |
| buf->addr = NULL; |
| return -ENOMEM; |
| } |
| |
| buf->dma_addr = dma_map_single(rwnx_hw->dev, skb->data, skb_size, |
| DMA_FROM_DEVICE); |
| if (unlikely(dma_mapping_error(rwnx_hw->dev, buf->dma_addr))) { |
| dev_err(rwnx_hw->dev, "DMA mapping of RX skb failed\n"); |
| dev_kfree_skb(skb); |
| buf->addr = NULL; |
| return -EIO; |
| } |
| |
| buf->addr = skb; |
| buf->size = skb_size; |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_rxskb_reset_pattern() - Reset pattern in a RX skb or unsupported |
| * RX vector buffer |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: RX skb to reset |
| * @pattern_offset: Pattern location, in byte from the start of the buffer |
| * |
| * Reset the pattern in a RX/unsupported RX vector skb buffer to inform embedded |
| * that it has been processed by the host. |
| * Pattern in a 32bit value. |
| */ |
| static void rwnx_ipc_rxskb_reset_pattern(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf *buf, |
| size_t pattern_offset) |
| { |
| struct sk_buff *skb = buf->addr; |
| u32 *pattern = (u32 *)(skb->data + pattern_offset); |
| |
| *pattern = 0; |
| *(u32 *)(skb->data) = 0; // aic |
| dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr + pattern_offset, |
| sizeof(u32), DMA_FROM_DEVICE); |
| } |
| |
| /** |
| * rwnx_ipc_rxskb_dealloc() - Free a skb allocated for the RX path |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: Rx skb to free |
| * |
| * Free a RX skb allocated by @rwnx_ipc_rxskb_alloc |
| */ |
| static void rwnx_ipc_rxskb_dealloc(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf *buf) |
| { |
| if (!buf->addr) |
| return; |
| |
| dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, DMA_TO_DEVICE); |
| dev_kfree_skb((struct sk_buff *)buf->addr); |
| buf->addr = NULL; |
| } |
| |
| |
| /** |
| * rwnx_ipc_unsup_rx_vec_elem_allocs() - Allocate and push an unsupported |
| * RX vector buffer for the FW |
| * |
| * @rwnx_hw: Main driver data |
| * @elem: Pointer to the skb elem that will contain the address of the buffer |
| */ |
| int rwnx_ipc_unsuprxvec_alloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf) |
| { |
| int err; |
| |
| err = rwnx_ipc_rxskb_alloc(rwnx_hw, buf, rwnx_hw->ipc_env->unsuprxvec_sz); |
| if (err) |
| return err; |
| |
| rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, |
| offsetof(struct rx_vector_desc, pattern)); |
| ipc_host_unsuprxvec_push(rwnx_hw->ipc_env, buf); |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_unsuprxvec_repush() - Reset and repush an already allocated buffer |
| * for unsupported RX vector |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: Buf to repush |
| */ |
| void rwnx_ipc_unsuprxvec_repush(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf) |
| { |
| rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, |
| offsetof(struct rx_vector_desc, pattern)); |
| ipc_host_unsuprxvec_push(rwnx_hw->ipc_env, buf); |
| } |
| |
| /** |
| * rwnx_ipc_unsuprxvecs_alloc() - Allocate and push all unsupported RX |
| * vector buffers for the FW |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| static int rwnx_ipc_unsuprxvecs_alloc(struct rwnx_hw *rwnx_hw) |
| { |
| struct rwnx_ipc_buf *buf; |
| int i; |
| |
| memset(rwnx_hw->unsuprxvecs, 0, sizeof(rwnx_hw->unsuprxvecs)); |
| |
| for (i = 0, buf = rwnx_hw->unsuprxvecs; i < ARRAY_SIZE(rwnx_hw->unsuprxvecs); i++, buf++) |
| { |
| if (rwnx_ipc_unsuprxvec_alloc(rwnx_hw, buf)) { |
| dev_err(rwnx_hw->dev, "Failed to allocate unsuprxvec buf %d\n", i + 1); |
| return -ENOMEM; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_unsuprxvecs_dealloc() - Free all unsupported RX vector buffers |
| * allocated for the FW |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| static void rwnx_ipc_unsuprxvecs_dealloc(struct rwnx_hw *rwnx_hw) |
| { |
| struct rwnx_ipc_buf *buf; |
| int i; |
| |
| for (i = 0, buf = rwnx_hw->unsuprxvecs; i < ARRAY_SIZE(rwnx_hw->unsuprxvecs); i++, buf++) |
| { |
| rwnx_ipc_rxskb_dealloc(rwnx_hw, buf); |
| } |
| } |
| |
| /** |
| * rwnx_ipc_rxbuf_alloc() - Allocate and push a rx buffer for the FW |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: IPC buffer where to store address of the skb. In fullmac this |
| * parameter is not available so look for the first free IPC buffer |
| */ |
| int rwnx_ipc_rxbuf_alloc(struct rwnx_hw *rwnx_hw) |
| { |
| int err; |
| struct rwnx_ipc_buf *buf; |
| int nb = 0, idx; |
| |
| spin_lock_bh(&rwnx_hw->rxbuf_lock); |
| |
| idx = rwnx_hw->rxbuf_idx; |
| while (rwnx_hw->rxbufs[idx].addr && (nb < RWNX_RXBUFF_MAX)) { |
| printk("w %d %p\n", idx, rwnx_hw->rxbufs[idx].addr); |
| idx = ( idx + 1 ) % RWNX_RXBUFF_MAX; |
| nb++; |
| } |
| if (nb == RWNX_RXBUFF_MAX) { |
| dev_err(rwnx_hw->dev, "No more free space for rxbuff"); |
| printk("No more free space for rxbuff %d %d %d\n", rwnx_hw->rxbuf_idx, atomic_read(&rwnx_hw->rxbuf_cnt), rwnx_hw->ipc_env->rxbuf_idx); |
| spin_unlock_bh(&rwnx_hw->rxbuf_lock); |
| return -ENOMEM; |
| } |
| |
| buf = &rwnx_hw->rxbufs[idx]; |
| |
| //printk("alloc %d\n", idx); |
| err = rwnx_ipc_rxskb_alloc(rwnx_hw, buf, rwnx_hw->ipc_env->rxbuf_sz); |
| if (err){ |
| printk("ipc_rxskb_alloc fail %d %d %d %d\n", rwnx_hw->rxbuf_idx, atomic_read(&rwnx_hw->rxbuf_cnt), rwnx_hw->ipc_env->rxbuf_idx, err); |
| spin_unlock_bh(&rwnx_hw->rxbuf_lock); |
| return err; |
| } |
| /* Save idx so that on next push the free slot will be found quicker */ |
| rwnx_hw->rxbuf_idx = ( idx + 1 ) % RWNX_RXBUFF_MAX; |
| atomic_inc(&rwnx_hw->rxbuf_cnt); |
| |
| rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, offsetof(struct hw_rxhdr, pattern)); |
| RWNX_RXBUFF_HOSTID_SET(buf, RWNX_RXBUFF_IDX_TO_HOSTID(idx)); |
| ipc_host_rxbuf_push(rwnx_hw->ipc_env, buf); |
| spin_unlock_bh(&rwnx_hw->rxbuf_lock); |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_rxbuf_dealloc() - Free a RX buffer for the FW |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: IPC buffer associated to the RX buffer to free |
| */ |
| void rwnx_ipc_rxbuf_dealloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf) |
| { |
| rwnx_ipc_rxskb_dealloc(rwnx_hw, buf); |
| } |
| |
| /** |
| * rwnx_ipc_rxbuf_repush() - Reset and repush an already allocated RX buffer |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: Buf to repush |
| * |
| * In case a skb is not forwarded to upper layer it can be re-used. |
| */ |
| void rwnx_ipc_rxbuf_repush(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf) |
| { |
| rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, offsetof(struct hw_rxhdr, pattern)); |
| ipc_host_rxbuf_push(rwnx_hw->ipc_env, buf); |
| } |
| |
| /** |
| * rwnx_ipc_rxbufs_alloc() - Allocate and push all RX buffer for the FW |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| static int rwnx_ipc_rxbufs_alloc(struct rwnx_hw *rwnx_hw) |
| { |
| int i, nb = rwnx_hw->ipc_env->rxbuf_nb; |
| |
| memset(rwnx_hw->rxbufs, 0, sizeof(rwnx_hw->rxbufs)); |
| |
| for (i = 0; i < nb; i++) { |
| if (rwnx_ipc_rxbuf_alloc(rwnx_hw)) { |
| dev_err(rwnx_hw->dev, "Failed to allocate rx buf %d/%d\n", |
| i + 1, nb); |
| return -ENOMEM; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rwnx_ipc_rxbufs_dealloc() - Free all RX buffer allocated for the FW |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| static void rwnx_ipc_rxbufs_dealloc(struct rwnx_hw *rwnx_hw) |
| { |
| struct rwnx_ipc_buf *buf; |
| int i; |
| |
| for (i = 0, buf = rwnx_hw->rxbufs; i < ARRAY_SIZE(rwnx_hw->rxbufs); i++, buf++) { |
| rwnx_ipc_rxskb_dealloc(rwnx_hw, buf); |
| } |
| } |
| |
| /** |
| * rwnx_ipc_rxdesc_repush() - Repush a RX descriptor to FW |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: RX descriptor to repush |
| * |
| * Once RX buffer has been received, the RX descriptor used by FW to upload this |
| * buffer can be re-used for another RX buffer. |
| */ |
| void rwnx_ipc_rxdesc_repush(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf *buf) |
| { |
| struct rxdesc_tag *rxdesc = buf->addr; |
| rxdesc->status = 0; |
| dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr, |
| sizeof(struct rxdesc_tag), DMA_BIDIRECTIONAL); |
| ipc_host_rxdesc_push(rwnx_hw->ipc_env, buf); |
| } |
| |
| /** |
| * rwnx_ipc_rxbuf_from_hostid() - Return IPC buffer of a RX buffer from a hostid |
| * |
| * @rwnx_hw: Main driver data |
| * @hostid: Hostid of the RX buffer |
| * @return: Pointer to the RX buffer with the provided hostid and NULL if the |
| * hostid is invalid or no buffer is associated. |
| */ |
| struct rwnx_ipc_buf *rwnx_ipc_rxbuf_from_hostid(struct rwnx_hw *rwnx_hw, u32 hostid) |
| { |
| int rxbuf_idx = RWNX_RXBUFF_HOSTID_TO_IDX(hostid); |
| |
| if (RWNX_RXBUFF_VALID_IDX(rxbuf_idx)) { |
| struct rwnx_ipc_buf *buf = &rwnx_hw->rxbufs[rxbuf_idx]; |
| if (buf->addr && (RWNX_RXBUFF_HOSTID_GET(buf) == hostid)) |
| return buf; |
| |
| dev_err(rwnx_hw->dev, "Invalid Rx buff: hostid=%d addr=%p hostid_in_buff=%d\n", |
| hostid, buf->addr, (buf->addr) ? RWNX_RXBUFF_HOSTID_GET(buf): -1); |
| |
| if (buf->addr) |
| rwnx_ipc_rxbuf_dealloc(rwnx_hw, buf); |
| } |
| |
| dev_err(rwnx_hw->dev, "RX Buff invalid hostid [%d]\n", hostid); |
| return NULL; |
| } |
| |
| /** |
| * rwnx_elems_deallocs() - Deallocate IPC storage elements. |
| * @rwnx_hw: Main driver data |
| * |
| * This function deallocates all the elements required for communications with |
| * LMAC, such as Rx Data elements, MSGs elements, ... |
| * This function should be called in correspondence with the allocation function. |
| */ |
| static void rwnx_elems_deallocs(struct rwnx_hw *rwnx_hw) |
| { |
| printk("rwnx_elems_deallocs 1\n"); |
| rwnx_ipc_rxbufs_dealloc(rwnx_hw); |
| rwnx_ipc_unsuprxvecs_dealloc(rwnx_hw); |
| #ifdef CONFIG_RWNX_FULLMAC |
| rwnx_ipc_buf_pool_dealloc(&rwnx_hw->rxdesc_pool); |
| #endif |
| rwnx_ipc_buf_pool_dealloc(&rwnx_hw->msgbuf_pool); |
| rwnx_ipc_buf_pool_dealloc(&rwnx_hw->dbgbuf_pool); |
| rwnx_ipc_buf_pool_dealloc(&rwnx_hw->radar_pool); |
| rwnx_ipc_buf_pool_dealloc(&rwnx_hw->txcfm_pool); |
| rwnx_ipc_buf_dealloc(rwnx_hw, &rwnx_hw->tx_pattern); |
| rwnx_ipc_buf_dealloc(rwnx_hw, &rwnx_hw->dbgdump.buf); |
| |
| printk("rwnx_elems_deallocs end\n"); |
| } |
| |
| /** |
| * rwnx_elems_allocs() - Allocate IPC storage elements. |
| * @rwnx_hw: Main driver data |
| * |
| * This function allocates all the elements required for communications with |
| * LMAC, such as Rx Data elements, MSGs elements, ... |
| * This function should be called in correspondence with the deallocation function. |
| */ |
| static int rwnx_elems_allocs(struct rwnx_hw *rwnx_hw) |
| { |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| |
| if (dma_set_coherent_mask(rwnx_hw->dev, DMA_BIT_MASK(32))) |
| goto err_alloc; |
| |
| if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->msgbuf_pool, |
| IPC_MSGE2A_BUF_CNT, |
| sizeof(struct ipc_e2a_msg), |
| "rwnx_ipc_msgbuf_pool", |
| ipc_host_msgbuf_push)) |
| goto err_alloc; |
| |
| if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->dbgbuf_pool, |
| IPC_DBGBUF_CNT, |
| sizeof(struct ipc_dbg_msg), |
| "rwnx_ipc_dbgbuf_pool", |
| ipc_host_dbgbuf_push)) |
| goto err_alloc; |
| |
| if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->radar_pool, |
| IPC_RADARBUF_CNT, |
| sizeof(struct radar_pulse_array_desc), |
| "rwnx_ipc_radar_pool", |
| ipc_host_radar_push)) |
| goto err_alloc; |
| |
| if (rwnx_ipc_unsuprxvecs_alloc(rwnx_hw)) |
| goto err_alloc; |
| |
| |
| if (rwnx_ipc_buf_a2e_alloc(rwnx_hw, &rwnx_hw->tx_pattern, sizeof(u32), |
| &rwnx_tx_pattern)) |
| goto err_alloc; |
| |
| ipc_host_pattern_push(rwnx_hw->ipc_env, &rwnx_hw->tx_pattern); |
| |
| #if 0 |
| printk("%s: 8\n", __func__); |
| |
| if (rwnx_ipc_buf_e2a_alloc(rwnx_hw, &rwnx_hw->dbgdump.buf, |
| sizeof(struct dbg_debug_dump_tag))) |
| goto err_alloc; |
| |
| |
| printk("%s: 9\n", __func__); |
| ipc_host_dbginfo_push(rwnx_hw->ipc_env, &rwnx_hw->dbgdump.buf); |
| |
| /* |
| * Note that the RX buffers are no longer allocated here as their size depends on the |
| * FW configuration, which is not available at that time. |
| * They will be allocated when checking the parameter compatibility between the driver |
| * and the underlying components (i.e. during the rwnx_handle_dynparams() execution) |
| */ |
| printk("%s: 10\n", __func__); |
| |
| #endif |
| #ifdef CONFIG_RWNX_FULLMAC |
| if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->rxdesc_pool, |
| rwnx_hw->ipc_env->rxdesc_nb, |
| sizeof(struct rxdesc_tag), |
| "rwnx_ipc_rxdesc_pool", |
| ipc_host_rxdesc_push)) |
| goto err_alloc; |
| |
| #endif /* CONFIG_RWNX_FULLMAC */ |
| |
| return 0; |
| |
| err_alloc: |
| dev_err(rwnx_hw->dev, "Error while allocating IPC buffers\n"); |
| rwnx_elems_deallocs(rwnx_hw); |
| return -ENOMEM; |
| } |
| |
| /** |
| * rwnx_ipc_msg_push() - Push a msg to IPC queue |
| * |
| * @rwnx_hw: Main driver data |
| * @msg_buf: Pointer to message |
| * @len: Size, in bytes, of message |
| */ |
| void rwnx_ipc_msg_push(struct rwnx_hw *rwnx_hw, void *msg_buf, uint16_t len) |
| { |
| ipc_host_msg_push(rwnx_hw->ipc_env, msg_buf, len); |
| } |
| |
| /** |
| * rwnx_ipc_txdesc_push() - Push a txdesc to FW |
| * |
| * @rwnx_hw: Main driver data |
| * @sw_txhdr: Pointer to the SW TX header associated to the descriptor to push |
| * @skb: TX Buffer associated. Pointer saved in ipc env to retrieve it upon confirmation. |
| * @hw_queue: Hw queue to push txdesc to |
| */ |
| #if 0 |
| void rwnx_ipc_txdesc_push(struct rwnx_hw *rwnx_hw, struct rwnx_sw_txhdr *sw_txhdr, |
| struct sk_buff *skb, int hw_queue) |
| { |
| struct txdesc_host *txdesc_host = &sw_txhdr->desc; |
| struct rwnx_ipc_buf *ipc_desc = &sw_txhdr->ipc_desc; |
| |
| txdesc_host->ctrl.hwq = hw_queue; |
| txdesc_host->api.host.hostid = ipc_host_tx_host_ptr_to_id(rwnx_hw->ipc_env, skb); |
| txdesc_host->ready = 0xFFFFFFFF; |
| if (!txdesc_host->api.host.hostid) { |
| dev_err(rwnx_hw->dev, "No more tx_hostid available \n"); |
| return; |
| } |
| |
| if (rwnx_ipc_buf_a2e_init(rwnx_hw, ipc_desc, txdesc_host, sizeof(*txdesc_host))) |
| return ; |
| |
| ipc_host_txdesc_push(rwnx_hw->ipc_env, ipc_desc); |
| } |
| #endif |
| |
| /** |
| * rwnx_ipc_get_skb_from_cfm() - Retrieve the TX buffer associated to a confirmation buffer |
| * |
| * @rwnx_hw: Main driver data |
| * @buf: IPC buffer for the confirmation buffer |
| * @return: Pointer to TX buffer associated to this confirmation and NULL if confirmation |
| * has not yet been updated by firmware |
| * |
| * To ensure that a confirmation has been processed by firmware check if the hostid field |
| * has been updated. If this is the case retrieve TX buffer from it and reset it, otherwise |
| * simply return NULL. |
| */ |
| struct sk_buff *rwnx_ipc_get_skb_from_cfm(struct rwnx_hw *rwnx_hw, |
| struct rwnx_ipc_buf *buf) |
| { |
| struct sk_buff *skb = NULL; |
| struct tx_cfm_tag *cfm = buf->addr; |
| |
| /* get ownership of confirmation */ |
| rwnx_ipc_buf_e2a_sync(rwnx_hw, buf, 0); |
| |
| /* Check host id in the confirmation. */ |
| /* If 0 it means that this confirmation has not yet been updated by firmware */ |
| if (cfm->hostid) { |
| skb = ipc_host_tx_host_id_to_ptr(rwnx_hw->ipc_env, cfm->hostid); |
| if (unlikely(!skb)) { |
| dev_err(rwnx_hw->dev, "Cannot retrieve skb from cfm=%p/0x%llx, hostid %d in confirmation\n", |
| buf->addr, buf->dma_addr, cfm->hostid); |
| } else { |
| /* Unmap TX descriptor */ |
| struct rwnx_ipc_buf *ipc_desc = &((struct rwnx_txhdr *)skb->data)->sw_hdr->ipc_desc; |
| rwnx_ipc_buf_a2e_release(rwnx_hw, ipc_desc); |
| } |
| |
| cfm->hostid = 0; |
| } |
| |
| /* always re-give ownership to firmware. */ |
| rwnx_ipc_buf_e2a_sync_back(rwnx_hw, buf, 0); |
| |
| return skb; |
| } |
| |
| /** |
| * rwnx_ipc_sta_buffer_init - Initialize counter of buffered data for a given sta |
| * |
| * @rwnx_hw: Main driver data |
| * @sta_idx: Index of the station to initialize |
| */ |
| void rwnx_ipc_sta_buffer_init(struct rwnx_hw *rwnx_hw, int sta_idx) |
| { |
| int i; |
| volatile u32_l *buffered; |
| |
| if (sta_idx >= NX_REMOTE_STA_MAX) |
| return; |
| |
| buffered = rwnx_hw->ipc_env->shared->buffered[sta_idx]; |
| |
| for (i = 0; i < TID_MAX; i++) { |
| *buffered++ = 0; |
| } |
| } |
| |
| /** |
| * rwnx_ipc_sta_buffer - Update counter of buffered data for a given sta |
| * |
| * @rwnx_hw: Main driver data |
| * @sta: Managed station |
| * @tid: TID on which data has been added or removed |
| * @size: Size of data to add (or remove if < 0) to STA buffer. |
| */ |
| void rwnx_ipc_sta_buffer(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta, int tid, int size) |
| { |
| u32_l *buffered; |
| |
| if (!sta) |
| return; |
| |
| if ((sta->sta_idx >= NX_REMOTE_STA_MAX) || (tid >= TID_MAX)) |
| return; |
| |
| buffered = &rwnx_hw->ipc_env->shared->buffered[sta->sta_idx][tid]; |
| |
| if (size < 0) { |
| size = -size; |
| if (*buffered < size) |
| *buffered = 0; |
| else |
| *buffered -= size; |
| } else { |
| // no test on overflow |
| *buffered += size; |
| } |
| } |
| |
| /** |
| * rwnx_msgind() - IRQ handler callback for %IPC_IRQ_E2A_MSG |
| * |
| * @pthis: Pointer to main driver data |
| * @arg: Pointer to IPC buffer from msgbuf_pool |
| */ |
| static u8 rwnx_msgind(void *pthis, void *arg) |
| { |
| struct rwnx_hw *rwnx_hw = pthis; |
| struct rwnx_ipc_buf *buf = arg; |
| struct ipc_e2a_msg *msg = buf->addr; |
| u8 ret = 0; |
| |
| REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_MSGIND); |
| |
| /* Look for pattern which means that this hostbuf has been used for a MSG */ |
| if (msg->pattern != IPC_MSGE2A_VALID_PATTERN) { |
| ret = -1; |
| goto msg_no_push; |
| } |
| /* Relay further actions to the msg parser */ |
| rwnx_rx_handle_msg(rwnx_hw, msg); |
| |
| /* Reset the msg buffer and re-use it */ |
| msg->pattern = 0; |
| wmb(); |
| |
| /* Push back the buffer to the LMAC */ |
| ipc_host_msgbuf_push(rwnx_hw->ipc_env, buf); |
| |
| msg_no_push: |
| REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_MSGIND); |
| return ret; |
| } |
| |
| /** |
| * rwnx_msgackind() - IRQ handler callback for %IPC_IRQ_E2A_MSG_ACK |
| * |
| * @pthis: Pointer to main driver data |
| * @hostid: Pointer to command acknowledged |
| */ |
| static u8 rwnx_msgackind(void *pthis, void *hostid) |
| { |
| struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis; |
| rwnx_hw->cmd_mgr->llind(rwnx_hw->cmd_mgr, (struct rwnx_cmd *)hostid); |
| return -1; |
| } |
| |
| /** |
| * rwnx_radarind() - IRQ handler callback for %IPC_IRQ_E2A_RADAR |
| * |
| * @pthis: Pointer to main driver data |
| * @arg: Pointer to IPC buffer from radar_pool |
| */ |
| static u8 rwnx_radarind(void *pthis, void *arg) |
| { |
| #ifdef CONFIG_RWNX_RADAR |
| struct rwnx_hw *rwnx_hw = pthis; |
| struct rwnx_ipc_buf *buf = arg; |
| struct radar_pulse_array_desc *pulses = buf->addr; |
| u8 ret = 0; |
| int i; |
| |
| /* Look for pulse count meaning that this hostbuf contains RADAR pulses */ |
| if (pulses->cnt == 0) { |
| ret = -1; |
| goto radar_no_push; |
| } |
| |
| if (rwnx_radar_detection_is_enable(&rwnx_hw->radar, pulses->idx)) { |
| /* Save the received pulses only if radar detection is enabled */ |
| for (i = 0; i < pulses->cnt; i++) { |
| struct rwnx_radar_pulses *p = &rwnx_hw->radar.pulses[pulses->idx]; |
| |
| p->buffer[p->index] = pulses->pulse[i]; |
| p->index = (p->index + 1) % RWNX_RADAR_PULSE_MAX; |
| if (p->count < RWNX_RADAR_PULSE_MAX) |
| p->count++; |
| } |
| |
| /* Defer pulse processing in separate work */ |
| if (! work_pending(&rwnx_hw->radar.detection_work)) |
| schedule_work(&rwnx_hw->radar.detection_work); |
| } |
| |
| /* Reset the radar bufent and re-use it */ |
| pulses->cnt = 0; |
| wmb(); |
| |
| /* Push back the buffer to the LMAC */ |
| ipc_host_radar_push(rwnx_hw->ipc_env, buf); |
| |
| radar_no_push: |
| return ret; |
| #else |
| return -1; |
| #endif |
| } |
| |
| /** |
| * rwnx_prim_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_PRIM |
| * |
| * @pthis: Pointer to main driver data |
| */ |
| #if 0 |
| static void rwnx_prim_tbtt_ind(void *pthis) |
| { |
| #if 0 |
| struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis; |
| rwnx_tx_bcns(rwnx_hw); |
| #endif |
| } |
| #endif |
| /** |
| * rwnx_sec_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_SEC |
| * |
| * @pthis: Pointer to main driver data |
| */ |
| #if 0 |
| static void rwnx_sec_tbtt_ind(void *pthis) |
| { |
| } |
| #endif |
| /** |
| * rwnx_dbgind() - IRQ handler callback for %IPC_IRQ_E2A_DBG |
| * |
| * @pthis: Pointer to main driver data |
| * @hostid: Pointer to IPC buffer from dbgbuf_pool |
| */ |
| static u8 rwnx_dbgind(void *pthis, void *arg) |
| { |
| struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis; |
| struct rwnx_ipc_buf *buf = arg; |
| struct ipc_dbg_msg *dbg_msg = buf->addr; |
| u8 ret = 0; |
| |
| REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_DBGIND); |
| |
| /* Look for pattern which means that this hostbuf has been used for a MSG */ |
| if (dbg_msg->pattern != IPC_DBG_VALID_PATTERN) { |
| ret = -1; |
| goto dbg_no_push; |
| } |
| |
| /* Display the string */ |
| printk("%s %s", (char *)FW_STR, (char *)dbg_msg->string); |
| |
| /* Reset the msg buffer and re-use it */ |
| dbg_msg->pattern = 0; |
| wmb(); |
| |
| /* Push back the buffer to the LMAC */ |
| ipc_host_dbgbuf_push(rwnx_hw->ipc_env, buf); |
| |
| dbg_no_push: |
| REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_DBGIND); |
| |
| return ret; |
| } |
| |
| /** |
| * rwnx_ipc_rxbuf_init() - Allocate and initialize RX buffers. |
| * |
| * @rwnx_hw: Main driver data |
| * @rxbuf_sz: Size of the buffer to be allocated |
| * |
| * This function updates the RX buffer size according to the parameter and allocates the |
| * RX buffers |
| */ |
| int rwnx_ipc_rxbuf_init(struct rwnx_hw *rwnx_hw, uint32_t rxbuf_sz) |
| { |
| rwnx_hw->ipc_env->rxbuf_sz = rxbuf_sz; |
| return rwnx_ipc_rxbufs_alloc(rwnx_hw); |
| } |
| |
| /** |
| * rwnx_ipc_init() - Initialize IPC interface. |
| * |
| * @rwnx_hw: Main driver data |
| * @shared_ram: Pointer to shared memory that contains IPC shared struct |
| * |
| * This function initializes IPC interface by registering callbacks, setting |
| * shared memory area and calling IPC Init function. |
| * It should be called only once during driver's lifetime. |
| */ |
| int rwnx_ipc_init(struct rwnx_hw *rwnx_hw, u8 *shared_ram) |
| { |
| struct ipc_host_cb_tag cb; |
| int res; |
| |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| |
| /* initialize the API interface */ |
| cb.recv_data_ind = rwnx_rxdataind; |
| cb.recv_radar_ind = rwnx_radarind; |
| cb.recv_msg_ind = rwnx_msgind; |
| cb.recv_msgack_ind = rwnx_msgackind; |
| cb.recv_dbg_ind = rwnx_dbgind; |
| cb.send_data_cfm = rwnx_txdatacfm; |
| cb.recv_unsup_rx_vec_ind = rwnx_unsup_rx_vec_ind; |
| |
| /* set the IPC environment */ |
| rwnx_hw->ipc_env = (struct ipc_host_env_tag *) |
| kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL); |
| |
| if (!rwnx_hw->ipc_env) |
| return -ENOMEM; |
| |
| /* call the initialization of the IPC */ |
| ipc_host_init(rwnx_hw->ipc_env, &cb, |
| (struct ipc_shared_env_tag *)shared_ram, rwnx_hw); |
| |
| rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr); |
| |
| res = rwnx_elems_allocs(rwnx_hw); |
| if (res) { |
| kfree(rwnx_hw->ipc_env); |
| rwnx_hw->ipc_env = NULL; |
| } |
| |
| return res; |
| } |
| |
| /** |
| * rwnx_ipc_deinit() - Release IPC interface |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| void rwnx_ipc_deinit(struct rwnx_hw *rwnx_hw) |
| { |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| |
| rwnx_ipc_tx_drain(rwnx_hw); |
| rwnx_cmd_mgr_deinit(rwnx_hw->cmd_mgr); |
| rwnx_elems_deallocs(rwnx_hw); |
| if (rwnx_hw->ipc_env) { |
| kfree(rwnx_hw->ipc_env); |
| rwnx_hw->ipc_env = NULL; |
| } |
| } |
| |
| /** |
| * rwnx_ipc_start() - Start IPC interface |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| void rwnx_ipc_start(struct rwnx_hw *rwnx_hw) |
| { |
| ipc_host_enable_irq(rwnx_hw->ipc_env, IPC_IRQ_E2A_ALL); |
| } |
| |
| /** |
| * rwnx_ipc_stop() - Stop IPC interface |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| void rwnx_ipc_stop(struct rwnx_hw *rwnx_hw) |
| { |
| ipc_host_disable_irq(rwnx_hw->ipc_env, IPC_IRQ_E2A_ALL); |
| } |
| |
| /** |
| * rwnx_ipc_tx_drain() - Flush IPC TX buffers |
| * |
| * @rwnx_hw: Main driver data |
| * |
| * This assumes LMAC is still (tx wise) and there's no TX race until LMAC is up |
| * tx wise. |
| * This also lets both IPC sides remain in sync before resetting the LMAC, |
| * e.g with rwnx_send_reset. |
| */ |
| void rwnx_ipc_tx_drain(struct rwnx_hw *rwnx_hw) |
| { |
| struct sk_buff *skb; |
| |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| |
| if (!rwnx_hw->ipc_env) { |
| printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__); |
| return; |
| } |
| |
| while ((skb = ipc_host_tx_flush(rwnx_hw->ipc_env))) { |
| struct rwnx_sw_txhdr *sw_txhdr = ((struct rwnx_txhdr *)skb->data)->sw_hdr; |
| |
| #ifdef CONFIG_RWNX_AMSDUS_TX |
| if (sw_txhdr->desc.api.host.packet_cnt > 1) { |
| struct rwnx_amsdu_txhdr *amsdu_txhdr; |
| list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) { |
| rwnx_ipc_buf_a2e_release(rwnx_hw, &amsdu_txhdr->ipc_data); |
| dev_kfree_skb_any(amsdu_txhdr->skb); |
| } |
| } |
| #endif |
| rwnx_ipc_buf_a2e_release(rwnx_hw, &sw_txhdr->ipc_data); |
| kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr); |
| skb_pull(skb, RWNX_TX_HEADROOM); |
| dev_kfree_skb_any(skb); |
| } |
| } |
| |
| /** |
| * rwnx_ipc_tx_pending() - Check if TX frames are pending at FW level |
| * |
| * @rwnx_hw: Main driver data |
| */ |
| bool rwnx_ipc_tx_pending(struct rwnx_hw *rwnx_hw) |
| { |
| return ipc_host_tx_frames_pending(rwnx_hw->ipc_env); |
| } |
| |
| /** |
| * rwnx_error_ind() - %DBG_ERROR_IND message callback |
| * |
| * @rwnx_hw: Main driver data |
| * |
| * This function triggers the UMH script call that will indicate to the user |
| * space the error that occurred and stored the debug dump. Once the UMH script |
| * is executed, the rwnx_umh_done() function has to be called. |
| */ |
| void rwnx_error_ind(struct rwnx_hw *rwnx_hw) |
| { |
| struct rwnx_ipc_buf *buf = &rwnx_hw->dbgdump.buf; |
| struct dbg_debug_dump_tag *dump = buf->addr; |
| |
| rwnx_ipc_buf_e2a_sync(rwnx_hw, buf, 0); |
| dev_err(rwnx_hw->dev, "(type %d): dump received\n", |
| dump->dbg_info.error_type); |
| #ifdef CONFIG_RWNX_DEBUGFS |
| rwnx_hw->debugfs.trace_prst = true; |
| #endif |
| //rwnx_trigger_um_helper(&rwnx_hw->debugfs); |
| } |
| |
| /** |
| * rwnx_umh_done() - Indicate User Mode helper finished |
| * |
| * @rwnx_hw: Main driver data |
| * |
| */ |
| |
| extern void aicwf_pcie_host_init(struct ipc_host_env_tag *env, void *cb, struct ipc_shared_env_tag *shared_env_ptr, void *pthis); |
| void rwnx_umh_done(struct rwnx_hw *rwnx_hw) |
| { |
| if (!test_bit(RWNX_DEV_STARTED, &rwnx_hw->flags)) |
| return; |
| |
| /* this assumes error_ind won't trigger before ipc_host_dbginfo_push |
| is called and so does not irq protect (TODO) against error_ind */ |
| #ifdef CONFIG_RWNX_DEBUGFS |
| rwnx_hw->debugfs.trace_prst = false; |
| #endif |
| ipc_host_dbginfo_push(rwnx_hw->ipc_env, &rwnx_hw->dbgdump.buf); |
| } |
| |
| int rwnx_init_aic(struct rwnx_hw *rwnx_hw) |
| { |
| int res = 0; |
| struct ipc_shared_env_tag *shared_env = NULL; |
| |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| #ifdef AICWF_SDIO_SUPPORT |
| aicwf_sdio_host_init(&(rwnx_hw->sdio_env), NULL, NULL, rwnx_hw); |
| #endif |
| |
| #ifdef AICWF_USB_SUPPORT |
| aicwf_usb_host_init(&(rwnx_hw->usb_env), NULL, NULL, rwnx_hw); |
| #endif |
| |
| #ifdef AICWF_PCIE_SUPPORT |
| rwnx_hw->ipc_env = (struct ipc_host_env_tag *) kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL); |
| |
| if (!rwnx_hw->ipc_env){ |
| return -ENOMEM; |
| } |
| |
| if(rwnx_hw->pcidev->chip_id == PRODUCT_ID_AIC8800D80) { |
| if (rwnx_hw->pcidev->bar_count == 1) { |
| aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm), rwnx_hw); |
| shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm); |
| } else { |
| aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->pci_bar0_vaddr + 0x1DC000), rwnx_hw); |
| shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->pci_bar0_vaddr + 0x1DC000); |
| } |
| } else { |
| aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm), rwnx_hw); |
| shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm); |
| } |
| |
| res = rwnx_elems_allocs(rwnx_hw); |
| if (res) { |
| kfree(rwnx_hw->ipc_env); |
| rwnx_hw->ipc_env = NULL; |
| } |
| printk("sizeof struct ipc_shared_env_tag is %ld byte, offset=%ld, %ld, %ld , txdesc %lx\n", sizeof(struct ipc_shared_env_tag), |
| (u8 *)&shared_env->host_rxdesc - (u8 *)shared_env, |
| (u8 *)&shared_env->host_rxbuf - (u8 *)shared_env, |
| (u8 *)&shared_env->buffered - (u8 *)shared_env, |
| (u8 *)&rwnx_hw->ipc_env->shared->txdesc - (u8 *)shared_env); |
| printk("txdesc size %ld\n", sizeof(rwnx_hw->ipc_env->shared->txdesc)); |
| |
| #endif |
| rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr); |
| |
| return res; |
| } |
| |
| void rwnx_aic_deinit(struct rwnx_hw *rwnx_hw) |
| { |
| RWNX_DBG(RWNX_FN_ENTRY_STR); |
| |
| //rwnx_ipc_tx_drain(rwnx_hw); |
| rwnx_elems_deallocs(rwnx_hw); |
| if (rwnx_hw->ipc_env) { |
| kfree(rwnx_hw->ipc_env); |
| rwnx_hw->ipc_env = NULL; |
| } |
| } |
| |
| |