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