1/*
2 * This file is based on code from OCTEON SDK by Cavium Networks.
3 *
4 * Copyright (c) 2003-2010 Cavium Networks
5 *
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, Version 2, as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/kernel.h>
12#include <linux/netdevice.h>
13#include <linux/slab.h>
14
15#include <asm/octeon/octeon.h>
16
17#include "ethernet-mem.h"
18#include "ethernet-defines.h"
19
20#include <asm/octeon/cvmx-fpa.h>
21
22/**
23 * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs
24 * @pool:     Pool to allocate an skbuff for
25 * @size:     Size of the buffer needed for the pool
26 * @elements: Number of buffers to allocate
27 *
28 * Returns the actual number of buffers allocated.
29 */
30static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
31{
32	int freed = elements;
33
34	while (freed) {
35		struct sk_buff *skb = dev_alloc_skb(size + 256);
36
37		if (unlikely(skb == NULL))
38			break;
39		skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f));
40		*(struct sk_buff **)(skb->data - sizeof(void *)) = skb;
41		cvmx_fpa_free(skb->data, pool, size / 128);
42		freed--;
43	}
44	return elements - freed;
45}
46
47/**
48 * cvm_oct_free_hw_skbuff- free hardware pool skbuffs
49 * @pool:     Pool to allocate an skbuff for
50 * @size:     Size of the buffer needed for the pool
51 * @elements: Number of buffers to allocate
52 */
53static void cvm_oct_free_hw_skbuff(int pool, int size, int elements)
54{
55	char *memory;
56
57	do {
58		memory = cvmx_fpa_alloc(pool);
59		if (memory) {
60			struct sk_buff *skb =
61			    *(struct sk_buff **)(memory - sizeof(void *));
62			elements--;
63			dev_kfree_skb(skb);
64		}
65	} while (memory);
66
67	if (elements < 0)
68		pr_warn("Freeing of pool %u had too many skbuffs (%d)\n",
69			pool, elements);
70	else if (elements > 0)
71		pr_warn("Freeing of pool %u is missing %d skbuffs\n",
72			pool, elements);
73}
74
75/**
76 * cvm_oct_fill_hw_memory - fill a hardware pool with memory.
77 * @pool:     Pool to populate
78 * @size:     Size of each buffer in the pool
79 * @elements: Number of buffers to allocate
80 *
81 * Returns the actual number of buffers allocated.
82 */
83static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
84{
85	char *memory;
86	char *fpa;
87	int freed = elements;
88
89	while (freed) {
90		/*
91		 * FPA memory must be 128 byte aligned.  Since we are
92		 * aligning we need to save the original pointer so we
93		 * can feed it to kfree when the memory is returned to
94		 * the kernel.
95		 *
96		 * We allocate an extra 256 bytes to allow for
97		 * alignment and space for the original pointer saved
98		 * just before the block.
99		 */
100		memory = kmalloc(size + 256, GFP_ATOMIC);
101		if (unlikely(memory == NULL)) {
102			pr_warn("Unable to allocate %u bytes for FPA pool %d\n",
103				elements * size, pool);
104			break;
105		}
106		fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL);
107		*((char **)fpa - 1) = memory;
108		cvmx_fpa_free(fpa, pool, 0);
109		freed--;
110	}
111	return elements - freed;
112}
113
114/**
115 * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory
116 * @pool:     FPA pool to free
117 * @size:     Size of each buffer in the pool
118 * @elements: Number of buffers that should be in the pool
119 */
120static void cvm_oct_free_hw_memory(int pool, int size, int elements)
121{
122	char *memory;
123	char *fpa;
124
125	do {
126		fpa = cvmx_fpa_alloc(pool);
127		if (fpa) {
128			elements--;
129			fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa));
130			memory = *((char **)fpa - 1);
131			kfree(memory);
132		}
133	} while (fpa);
134
135	if (elements < 0)
136		pr_warn("Freeing of pool %u had too many buffers (%d)\n",
137			pool, elements);
138	else if (elements > 0)
139		pr_warn("Warning: Freeing of pool %u is missing %d buffers\n",
140			pool, elements);
141}
142
143int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
144{
145	int freed;
146
147	if (pool == CVMX_FPA_PACKET_POOL)
148		freed = cvm_oct_fill_hw_skbuff(pool, size, elements);
149	else
150		freed = cvm_oct_fill_hw_memory(pool, size, elements);
151	return freed;
152}
153
154void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
155{
156	if (pool == CVMX_FPA_PACKET_POOL)
157		cvm_oct_free_hw_skbuff(pool, size, elements);
158	else
159		cvm_oct_free_hw_memory(pool, size, elements);
160}
161