1/* Copyright (c) 2014 Broadcom Corporation 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 */ 15 16#include <linux/types.h> 17#include <linux/netdevice.h> 18 19#include <brcmu_utils.h> 20#include <brcmu_wifi.h> 21 22#include "core.h" 23#include "commonring.h" 24 25 26/* dma flushing needs implementation for mips and arm platforms. Should 27 * be put in util. Note, this is not real flushing. It is virtual non 28 * cached memory. Only write buffers should have to be drained. Though 29 * this may be different depending on platform...... 30 * SEE ALSO msgbuf.c 31 */ 32#define brcmf_dma_flush(addr, len) 33#define brcmf_dma_invalidate_cache(addr, len) 34 35 36void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, 37 int (*cr_ring_bell)(void *ctx), 38 int (*cr_update_rptr)(void *ctx), 39 int (*cr_update_wptr)(void *ctx), 40 int (*cr_write_rptr)(void *ctx), 41 int (*cr_write_wptr)(void *ctx), void *ctx) 42{ 43 commonring->cr_ring_bell = cr_ring_bell; 44 commonring->cr_update_rptr = cr_update_rptr; 45 commonring->cr_update_wptr = cr_update_wptr; 46 commonring->cr_write_rptr = cr_write_rptr; 47 commonring->cr_write_wptr = cr_write_wptr; 48 commonring->cr_ctx = ctx; 49} 50 51 52void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, 53 u16 item_len, void *buf_addr) 54{ 55 commonring->depth = depth; 56 commonring->item_len = item_len; 57 commonring->buf_addr = buf_addr; 58 if (!commonring->inited) { 59 spin_lock_init(&commonring->lock); 60 commonring->inited = true; 61 } 62 commonring->r_ptr = 0; 63 if (commonring->cr_write_rptr) 64 commonring->cr_write_rptr(commonring->cr_ctx); 65 commonring->w_ptr = 0; 66 if (commonring->cr_write_wptr) 67 commonring->cr_write_wptr(commonring->cr_ctx); 68 commonring->f_ptr = 0; 69} 70 71 72void brcmf_commonring_lock(struct brcmf_commonring *commonring) 73 __acquires(&commonring->lock) 74{ 75 unsigned long flags; 76 77 spin_lock_irqsave(&commonring->lock, flags); 78 commonring->flags = flags; 79} 80 81 82void brcmf_commonring_unlock(struct brcmf_commonring *commonring) 83 __releases(&commonring->lock) 84{ 85 spin_unlock_irqrestore(&commonring->lock, commonring->flags); 86} 87 88 89bool brcmf_commonring_write_available(struct brcmf_commonring *commonring) 90{ 91 u16 available; 92 bool retry = true; 93 94again: 95 if (commonring->r_ptr <= commonring->w_ptr) 96 available = commonring->depth - commonring->w_ptr + 97 commonring->r_ptr; 98 else 99 available = commonring->r_ptr - commonring->w_ptr; 100 101 if (available > 1) { 102 if (!commonring->was_full) 103 return true; 104 if (available > commonring->depth / 8) { 105 commonring->was_full = false; 106 return true; 107 } 108 if (retry) { 109 if (commonring->cr_update_rptr) 110 commonring->cr_update_rptr(commonring->cr_ctx); 111 retry = false; 112 goto again; 113 } 114 return false; 115 } 116 117 if (retry) { 118 if (commonring->cr_update_rptr) 119 commonring->cr_update_rptr(commonring->cr_ctx); 120 retry = false; 121 goto again; 122 } 123 124 commonring->was_full = true; 125 return false; 126} 127 128 129void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) 130{ 131 void *ret_ptr; 132 u16 available; 133 bool retry = true; 134 135again: 136 if (commonring->r_ptr <= commonring->w_ptr) 137 available = commonring->depth - commonring->w_ptr + 138 commonring->r_ptr; 139 else 140 available = commonring->r_ptr - commonring->w_ptr; 141 142 if (available > 1) { 143 ret_ptr = commonring->buf_addr + 144 (commonring->w_ptr * commonring->item_len); 145 commonring->w_ptr++; 146 if (commonring->w_ptr == commonring->depth) 147 commonring->w_ptr = 0; 148 return ret_ptr; 149 } 150 151 if (retry) { 152 if (commonring->cr_update_rptr) 153 commonring->cr_update_rptr(commonring->cr_ctx); 154 retry = false; 155 goto again; 156 } 157 158 commonring->was_full = true; 159 return NULL; 160} 161 162 163void * 164brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, 165 u16 n_items, u16 *alloced) 166{ 167 void *ret_ptr; 168 u16 available; 169 bool retry = true; 170 171again: 172 if (commonring->r_ptr <= commonring->w_ptr) 173 available = commonring->depth - commonring->w_ptr + 174 commonring->r_ptr; 175 else 176 available = commonring->r_ptr - commonring->w_ptr; 177 178 if (available > 1) { 179 ret_ptr = commonring->buf_addr + 180 (commonring->w_ptr * commonring->item_len); 181 *alloced = min_t(u16, n_items, available - 1); 182 if (*alloced + commonring->w_ptr > commonring->depth) 183 *alloced = commonring->depth - commonring->w_ptr; 184 commonring->w_ptr += *alloced; 185 if (commonring->w_ptr == commonring->depth) 186 commonring->w_ptr = 0; 187 return ret_ptr; 188 } 189 190 if (retry) { 191 if (commonring->cr_update_rptr) 192 commonring->cr_update_rptr(commonring->cr_ctx); 193 retry = false; 194 goto again; 195 } 196 197 commonring->was_full = true; 198 return NULL; 199} 200 201 202int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) 203{ 204 void *address; 205 206 address = commonring->buf_addr; 207 address += (commonring->f_ptr * commonring->item_len); 208 if (commonring->f_ptr > commonring->w_ptr) { 209 brcmf_dma_flush(address, 210 (commonring->depth - commonring->f_ptr) * 211 commonring->item_len); 212 address = commonring->buf_addr; 213 commonring->f_ptr = 0; 214 } 215 brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * 216 commonring->item_len); 217 218 commonring->f_ptr = commonring->w_ptr; 219 220 if (commonring->cr_write_wptr) 221 commonring->cr_write_wptr(commonring->cr_ctx); 222 if (commonring->cr_ring_bell) 223 return commonring->cr_ring_bell(commonring->cr_ctx); 224 225 return -EIO; 226} 227 228 229void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, 230 u16 n_items) 231{ 232 if (commonring->w_ptr == 0) 233 commonring->w_ptr = commonring->depth - n_items; 234 else 235 commonring->w_ptr -= n_items; 236} 237 238 239void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, 240 u16 *n_items) 241{ 242 void *ret_addr; 243 244 if (commonring->cr_update_wptr) 245 commonring->cr_update_wptr(commonring->cr_ctx); 246 247 *n_items = (commonring->w_ptr >= commonring->r_ptr) ? 248 (commonring->w_ptr - commonring->r_ptr) : 249 (commonring->depth - commonring->r_ptr); 250 251 if (*n_items == 0) 252 return NULL; 253 254 ret_addr = commonring->buf_addr + 255 (commonring->r_ptr * commonring->item_len); 256 257 commonring->r_ptr += *n_items; 258 if (commonring->r_ptr == commonring->depth) 259 commonring->r_ptr = 0; 260 261 brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); 262 263 return ret_addr; 264} 265 266 267int brcmf_commonring_read_complete(struct brcmf_commonring *commonring) 268{ 269 if (commonring->cr_write_rptr) 270 return commonring->cr_write_rptr(commonring->cr_ctx); 271 272 return -EIO; 273} 274