blob: 0b4a0232a51d7c23ec884cbc0cca55d413884e86 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30/*
31 * bcmos_system.c
32 *
33 * This file implements a subset of OS Abstraction services
34 * for linux kernel
35 */
36
37#include <bcmos_system.h>
38
39#include <linux/delay.h>
40#include <linux/pci.h>
41#include <asm-generic/pci-dma-compat.h>
42#include <linux/fs.h>
43#include <linux/fdtable.h>
44
45#include <bcmolt_llpcie.h>
46
47/* task control blocks */
48extern STAILQ_HEAD(task_list, bcmos_task) task_list;
49
50/* global OS lock */
51extern bcmos_mutex bcmos_res_lock;
52
53/* Initialize system library */
54bcmos_errno bcmos_sys_init(void)
55{
56 return BCM_ERR_OK;
57}
58/* Clean-up system library */
59void bcmos_sys_exit(void)
60{
61}
62
63/*
64 * Thread handler
65 */
66static int bcmos_task_handler(void *data)
67{
68 bcmos_task *task = (bcmos_task *)data;
69 F_bcmos_task_handler handler;
70 long handler_data;
71
72 task->sys_task.t = current;
73
74 if (task->parm.handler)
75 {
76 /* "traditional task */
77 handler = task->parm.handler;
78 handler_data = task->parm.data;
79 }
80 else
81 {
82 /* "integrated" task */
83 handler = bcmos_dft_task_handler;
84 handler_data = (long)task;
85 }
86 handler(handler_data);
87
88 return 0;
89}
90
91void _bcmos_backtrace(void)
92{
93 /*todo implement this*/
94}
95
96/* Create a new task.
97 * Attention! Priority is ignored.
98 */
99bcmos_errno bcmos_task_create(bcmos_task *task, const bcmos_task_parm *parm)
100{
101 struct task_struct *t;
102 bcmos_errno rc;
103
104 if (!task || !parm)
105 return BCM_ERR_PARM;
106
107 memset(task, 0, sizeof(*task));
108 task->parm = *parm;
109 if (!parm->handler)
110 {
111 /* Initialize and lock mutex to wait on */
112 rc = bcmos_sem_create(&task->active_sem, 0, task->parm.flags, parm->name);
113 if (rc)
114 {
115 BCMOS_TRACE_ERR("Task %s: can't create active_sem. Error %s (%d)\n",
116 task->parm.name, bcmos_strerror(rc), rc);
117 return rc;
118 }
119 }
120 /* Copy name to make sure that it is not released - in case it was on the stack */
121 if (task->parm.name)
122 {
123 strncpy(task->name, task->parm.name, sizeof(task->name) - 1);
124 task->parm.name = task->name;
125 }
126 bcmos_fastlock_init(&task->active_lock, 0);
127 task->magic = BCMOS_TASK_MAGIC;
128 bcmos_mutex_lock(&bcmos_res_lock);
129 STAILQ_INSERT_TAIL(&task_list, task, list);
130 bcmos_mutex_unlock(&bcmos_res_lock);
131
132 t = kthread_run(bcmos_task_handler, task, parm->name);
133 if (t == ERR_PTR(-ENOMEM))
134 {
135 bcmos_mutex_lock(&bcmos_res_lock);
136 STAILQ_REMOVE(&task_list, task, bcmos_task, list);
137 bcmos_mutex_unlock(&bcmos_res_lock);
138 task->magic = 0;
139 return BCM_ERR_NOMEM;
140 }
141
142 return BCM_ERR_OK;
143}
144
145/* Destroy task */
146bcmos_errno bcmos_task_destroy(bcmos_task *task)
147{
148 if (task->magic != BCMOS_TASK_MAGIC)
149 {
150 return BCM_ERR_PARM;
151 }
152 if (!task->sys_task.t)
153 {
154 return BCM_ERR_NOENT;
155 }
156 task->destroy_request = BCMOS_TRUE;
157 task->magic = BCMOS_TASK_MAGIC_DESTROYED;
158 bcmos_mutex_lock(&bcmos_res_lock);
159 STAILQ_REMOVE(&task_list, task, bcmos_task, list);
160 bcmos_mutex_unlock(&bcmos_res_lock);
161 /* The task may be waiting on semaphore. Kick it */
162 if (!task->parm.handler)
163 {
164 bcmos_sem_post(&task->active_sem);
165 }
166
167 /* Sometimes by the time we get here the task has already been disposed of.
168 * TODO: investigate why */
169 if (task->sys_task.t->cred != NULL)
170 {
171 kthread_stop(task->sys_task.t);
172 }
173 return BCM_ERR_OK;
174}
175
176/** Get current task
177 * \returns task handle or NULL if not in task context
178 */
179bcmos_task *bcmos_task_current(void)
180{
181 struct task_struct *kt = current;
182 bcmos_task *t, *tmp;
183
184 STAILQ_FOREACH_SAFE(t, &task_list, list, tmp)
185 {
186 if (t->sys_task.t == kt)
187 break;
188 }
189 return t;
190}
191
192/* timer signal handler */
193static void sys_timer_handler(unsigned long data)
194{
195 bcmos_sys_timer *timer = (bcmos_sys_timer *)data;
196 timer->handler(timer->data);
197}
198
199/* Create timer */
200bcmos_errno bcmos_sys_timer_create(bcmos_sys_timer *timer, bcmos_sys_timer_handler handler, void *data)
201{
202 if (!timer || !handler)
203 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "timer %p, handler %p\n", timer, handler);
204
205 timer->handler = handler;
206 timer->data = data;
207 init_timer(&timer->t);
208 timer->t.function = sys_timer_handler;
209 timer->t.data = (unsigned long)timer;
210
211 return BCM_ERR_OK;
212}
213
214/* Destroy timer */
215void bcmos_sys_timer_destroy(bcmos_sys_timer *timer)
216{
217 del_timer_sync(&timer->t);
218}
219
220/* (Re)start timer */
221void bcmos_sys_timer_start(bcmos_sys_timer *timer, uint32_t delay)
222{
223 /* Convert delay us --> ticks */
224 uint32_t ticks = usecs_to_jiffies(delay);
225 mod_timer(&timer->t, jiffies + ticks);
226}
227
228/* Stop timer if running */
229void bcmos_sys_timer_stop(bcmos_sys_timer *timer)
230{
231 /* No need to do anything. System timer isn't running */
232}
233
234void bcmos_usleep(uint32_t u)
235{
236 if (u >= 10000)
237 msleep((u + 999) / 1000);
238 else
239 udelay(u);
240}
241
242/*
243 * Semaphore
244 */
245
246/* Decrement semaphore counter. Wait if the counter is 0.
247 */
248bcmos_errno bcmos_sem_wait(bcmos_sem *sem, uint32_t timeout)
249{
250 if (timeout)
251 {
252 if (timeout != BCMOS_WAIT_FOREVER)
253 {
254 if (down_timeout(&sem->s, usecs_to_jiffies(timeout)))
255 return BCM_ERR_TIMEOUT;
256 }
257 else
258 {
259 if (down_interruptible(&sem->s))
260 return BCM_ERR_INTERNAL;
261 }
262 return BCM_ERR_OK;
263 }
264
265 /* 0 timeout */
266 if (down_trylock(&sem->s))
267 {
268 return BCM_ERR_NOENT;
269 }
270 return BCM_ERR_OK;
271}
272
273
274/*
275 * Byte memory pool
276 */
277
278/* Memory block header */
279typedef struct bcmos_byte_memblk
280{
281 bcmos_byte_pool *pool; /** pool that owns the block */
282 uint32_t size; /** block size (bytes) including bcmos_byte_memblk header */
283#ifdef BCMOS_MEM_DEBUG
284 uint32_t magic; /** magic number */
285#define BCMOS_MEM_MAGIC_ALLOC (('m'<<24) | ('b' << 16) | ('l' << 8) | 'k')
286#define BCMOS_MEM_MAGIC_FREE (('m'<<24) | ('b' << 16) | ('l' << 8) | '~')
287#endif
288} bcmos_byte_memblk;
289
290/* Create byte memory pool */
291bcmos_errno bcmos_byte_pool_create(bcmos_byte_pool *pool, const bcmos_byte_pool_parm *parm)
292{
293 if (!pool || !parm)
294 {
295 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "pool %p, parm %p\n", pool, parm);
296 }
297
298 BCM_MEMZERO_STRUCT(pool);
299 pool->parm = *parm;
300 if (!pool->parm.size)
301 {
302 BCMOS_TRACE_RETURN(BCM_ERR_PARM, "size %u\n", parm->size);
303 }
304#ifdef BCMOS_MEM_DEBUG
305 pool->magic = BCMOS_BYTE_POOL_VALID;
306#endif
307 return BCM_ERR_OK;
308}
309
310/* Destroy memory pool */
311bcmos_errno bcmos_byte_pool_destroy(bcmos_byte_pool *pool)
312{
313 if (pool->allocated)
314 {
315 BCMOS_TRACE_RETURN(BCM_ERR_STATE, "%u bytes of memory are still allocated from the pool %s\n",
316 pool->allocated, pool->parm.name);
317 }
318#ifdef BCMOS_MEM_DEBUG
319 pool->magic = BCMOS_BYTE_POOL_DELETED;
320#endif
321 return BCM_ERR_OK;
322}
323
324/* Allocate memory from memory pool */
325void *bcmos_byte_pool_alloc(bcmos_byte_pool *pool, uint32_t size)
326{
327 bcmos_byte_memblk *blk;
328 uint32_t byte_size;
329 void *ptr;
330
331#ifdef BCMOS_MEM_DEBUG
332 BUG_ON(pool->magic != BCMOS_BYTE_POOL_VALID);
333#endif
334
335 if (size + pool->allocated > pool->parm.size)
336 return NULL;
337
338 byte_size = size + sizeof(bcmos_byte_memblk);
339#ifdef BCMOS_MEM_DEBUG
340 byte_size += sizeof(uint32_t); /* block suffix */
341#endif
342 /* ToDo: Maintain LL of allocated blocks */
343 blk = (bcmos_byte_memblk *)bcmos_alloc(byte_size);
344 if (!blk)
345 return NULL;
346 ptr = (void *)(blk + 1);
347 blk->size = byte_size;
348 pool->allocated += byte_size;
349 blk->pool = pool;
350#ifdef BCMOS_MEM_DEBUG
351 blk->magic = BCMOS_MEM_MAGIC_ALLOC;
352 *(uint32_t *)((long)blk + byte_size - sizeof(uint32_t)) = BCMOS_MEM_MAGIC_ALLOC;
353#endif
354
355 return ptr;
356}
357
358/* Release memory allocated using bcmos_byte_pool_alloc() */
359void bcmos_byte_pool_free(void *ptr)
360{
361 bcmos_byte_memblk *blk;
362 bcmos_byte_pool *pool;
363
364 BUG_ON(!ptr);
365 blk = (bcmos_byte_memblk *)((long)ptr - sizeof(bcmos_byte_memblk));
366 pool = blk->pool;
367#ifdef BCMOS_MEM_DEBUG
368 BUG_ON(pool->magic != BCMOS_BYTE_POOL_VALID);
369 BUG_ON(blk->magic != BCMOS_MEM_MAGIC_ALLOC);
370 BUG_ON(*(uint32_t *)((long)blk + blk->size - sizeof(uint32_t)) != BCMOS_MEM_MAGIC_ALLOC);
371 blk->magic = BCMOS_MEM_MAGIC_FREE;
372#endif
373 pool->allocated -= blk->size;
374 bcmos_free(blk);
375}
376
377/*
378 * DMA-able memory management
379 */
380
381/* Dma-able block header */
382typedef struct
383{
384 struct pci_dev *pdev;
385 dma_addr_t dma_handle;
386 uint32_t size;
387} bcmos_dma_mem_hdr;
388
389/* Allocate DMA-able memory */
390void *bcmos_dma_alloc(uint8_t device, uint32_t size)
391{
392 bcmos_dma_mem_hdr hdr =
393 {
394 .pdev = (struct pci_dev *)bcm_ll_pcie_dev_get(device),
395 .size = size
396 };
397 void *ptr;
398
399 if (!hdr.pdev)
400 {
401 return NULL;
402 }
403
404 ptr = pci_alloc_consistent(hdr.pdev, size + sizeof(bcmos_dma_mem_hdr), &hdr.dma_handle);
405 if (ptr)
406 {
407 memcpy(ptr, &hdr, sizeof(hdr));
408 }
409 return (void *)((long)ptr + sizeof(hdr));
410}
411
412/* Release DMA-able memory */
413void bcmos_dma_free(uint8_t device, void *ptr)
414{
415 bcmos_dma_mem_hdr hdr;
416
417 /* Step back to prefix area */
418 ptr = (void *)((long)ptr - sizeof(hdr));
419 memcpy(&hdr, ptr, sizeof(hdr));
420
421 /* Sanity check */
422 if (!hdr.pdev || hdr.pdev != bcm_ll_pcie_dev_get(device))
423 {
424 BCMOS_TRACE_ERR("!!!Attempt to release insane DMA-able pointer. ptr=%p pdev=%p/%p\n",
425 ptr, hdr.pdev, bcm_ll_pcie_dev_get(device));
426 return;
427 }
428 pci_free_consistent(hdr.pdev, hdr.size, ptr, hdr.dma_handle);
429}
430
431/*
432 * Print to the current process' stdout
433 */
434
435#ifndef STDOUT_FILENO
436#define STDOUT_FILENO 1
437#endif
438
439static int _bcmos_write(struct file *fd, const unsigned char *buf, int len)
440{
441 mm_segment_t oldfs;
442 int len0 = len;
443 oldfs = get_fs();
444 set_fs(KERNEL_DS);
445 while(len)
446 {
447 int lw;
448 lw = vfs_write(fd, (char *)buf, len, &fd->f_pos);
449 if (lw < 0)
450 break;
451 len -= lw;
452 if (len)
453 {
454 if (msleep_interruptible(1))
455 break;
456 buf += lw;
457 }
458 }
459 set_fs(oldfs);
460 return (len0 - len);
461}
462
463int bcmos_sys_vprintf(const char *format, va_list args)
464{
465 struct file *f;
466 int rc=0;
467
468 /* Get stdout file handle if any */
469 if (in_interrupt() || !current || !current->files)
470 f = 0;
471 else
472 f = fcheck(STDOUT_FILENO);
473 if (!f)
474 {
475 rc = vprintk(format, args);
476 }
477 else
478 {
479 char printbuf[1024];
480 char *p1=printbuf, *p2;
481 vscnprintf(printbuf, sizeof(printbuf)-1, format, args);
482 printbuf[sizeof(printbuf)-1]=0;
483 do
484 {
485 p2 = strchr(p1, '\n');
486 if (p2)
487 {
488 rc += _bcmos_write(f, (unsigned char*) p1, p2-p1+1);
489 rc += _bcmos_write(f, (unsigned char*)"\r", 1);
490 }
491 else
492 {
493 rc += _bcmos_write(f, (unsigned char*)p1, strlen(p1));
494 }
495 p1 = p2 + 1;
496 } while(p2);
497 }
498 return rc;
499}
500
501static int os_linux_module_init(void)
502{
503 bcmos_errno rc;
504
505 rc = bcmos_init();
506
507 return rc ? -EINVAL : 0;
508}
509
510static void os_linux_module_exit(void)
511{
512 bcmos_exit();
513}
514
515module_init(os_linux_module_init);
516module_exit(os_linux_module_exit);
517
518MODULE_DESCRIPTION("OS Abstraction");
519MODULE_LICENSE("Dual BSD/GPL");
520
521EXPORT_SYMBOL(bcmos_task_create);
522EXPORT_SYMBOL(bcmos_task_destroy);
523EXPORT_SYMBOL(bcmos_task_current);
524EXPORT_SYMBOL(bcmos_sem_wait);
525EXPORT_SYMBOL(bcmos_sys_trace_level);
526EXPORT_SYMBOL(bcmos_usleep);
527EXPORT_SYMBOL(bcmos_byte_pool_create);
528EXPORT_SYMBOL(bcmos_byte_pool_destroy);
529EXPORT_SYMBOL(bcmos_byte_pool_alloc);
530EXPORT_SYMBOL(bcmos_byte_pool_free);
531EXPORT_SYMBOL(bcmos_sys_vprintf);
532EXPORT_SYMBOL(bcmos_dma_alloc);
533EXPORT_SYMBOL(bcmos_dma_free);
534EXPORT_SYMBOL(_bcmos_backtrace);
535EXPORT_SYMBOL(sw_error_handler);
536EXPORT_SYMBOL(bcmos_exit);