Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
4 : : * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
5 : : * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
6 : : */
7 : :
8 : : #define pr_fmt(fmt) "LOGIC PIO: " fmt
9 : :
10 : : #include <linux/of.h>
11 : : #include <linux/io.h>
12 : : #include <linux/logic_pio.h>
13 : : #include <linux/mm.h>
14 : : #include <linux/rculist.h>
15 : : #include <linux/sizes.h>
16 : : #include <linux/slab.h>
17 : :
18 : : /* The unique hardware address list */
19 : : static LIST_HEAD(io_range_list);
20 : : static DEFINE_MUTEX(io_range_mutex);
21 : :
22 : : /* Consider a kernel general helper for this */
23 : : #define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len))
24 : :
25 : : /**
26 : : * logic_pio_register_range - register logical PIO range for a host
27 : : * @new_range: pointer to the IO range to be registered.
28 : : *
29 : : * Returns 0 on success, the error code in case of failure.
30 : : *
31 : : * Register a new IO range node in the IO range list.
32 : : */
33 : 0 : int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
34 : : {
35 : : struct logic_pio_hwaddr *range;
36 : : resource_size_t start;
37 : : resource_size_t end;
38 : : resource_size_t mmio_end = 0;
39 : : resource_size_t iio_sz = MMIO_UPPER_LIMIT;
40 : : int ret = 0;
41 : :
42 [ # # # # : 0 : if (!new_range || !new_range->fwnode || !new_range->size)
# # ]
43 : : return -EINVAL;
44 : :
45 : 0 : start = new_range->hw_start;
46 : 0 : end = new_range->hw_start + new_range->size;
47 : :
48 : 0 : mutex_lock(&io_range_mutex);
49 [ # # ]: 0 : list_for_each_entry(range, &io_range_list, list) {
50 [ # # ]: 0 : if (range->fwnode == new_range->fwnode) {
51 : : /* range already there */
52 : : goto end_register;
53 : : }
54 [ # # # # ]: 0 : if (range->flags == LOGIC_PIO_CPU_MMIO &&
55 : 0 : new_range->flags == LOGIC_PIO_CPU_MMIO) {
56 : : /* for MMIO ranges we need to check for overlap */
57 [ # # # # ]: 0 : if (start >= range->hw_start + range->size ||
58 : : end < range->hw_start) {
59 : 0 : mmio_end = range->io_start + range->size;
60 : : } else {
61 : : ret = -EFAULT;
62 : : goto end_register;
63 : : }
64 [ # # # # ]: 0 : } else if (range->flags == LOGIC_PIO_INDIRECT &&
65 : 0 : new_range->flags == LOGIC_PIO_INDIRECT) {
66 : 0 : iio_sz += range->size;
67 : : }
68 : : }
69 : :
70 : : /* range not registered yet, check for available space */
71 [ # # ]: 0 : if (new_range->flags == LOGIC_PIO_CPU_MMIO) {
72 [ # # ]: 0 : if (mmio_end + new_range->size - 1 > MMIO_UPPER_LIMIT) {
73 : : /* if it's too big check if 64K space can be reserved */
74 [ # # ]: 0 : if (mmio_end + SZ_64K - 1 > MMIO_UPPER_LIMIT) {
75 : : ret = -E2BIG;
76 : : goto end_register;
77 : : }
78 : 0 : new_range->size = SZ_64K;
79 : 0 : pr_warn("Requested IO range too big, new size set to 64K\n");
80 : : }
81 : 0 : new_range->io_start = mmio_end;
82 [ # # ]: 0 : } else if (new_range->flags == LOGIC_PIO_INDIRECT) {
83 [ # # ]: 0 : if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) {
84 : : ret = -E2BIG;
85 : : goto end_register;
86 : : }
87 : 0 : new_range->io_start = iio_sz;
88 : : } else {
89 : : /* invalid flag */
90 : : ret = -EINVAL;
91 : : goto end_register;
92 : : }
93 : :
94 : 0 : list_add_tail_rcu(&new_range->list, &io_range_list);
95 : :
96 : : end_register:
97 : 0 : mutex_unlock(&io_range_mutex);
98 : 0 : return ret;
99 : : }
100 : :
101 : : /**
102 : : * logic_pio_unregister_range - unregister a logical PIO range for a host
103 : : * @range: pointer to the IO range which has been already registered.
104 : : *
105 : : * Unregister a previously-registered IO range node.
106 : : */
107 : 0 : void logic_pio_unregister_range(struct logic_pio_hwaddr *range)
108 : : {
109 : 0 : mutex_lock(&io_range_mutex);
110 : : list_del_rcu(&range->list);
111 : 0 : mutex_unlock(&io_range_mutex);
112 : 0 : synchronize_rcu();
113 : 0 : }
114 : :
115 : : /**
116 : : * find_io_range_by_fwnode - find logical PIO range for given FW node
117 : : * @fwnode: FW node handle associated with logical PIO range
118 : : *
119 : : * Returns pointer to node on success, NULL otherwise.
120 : : *
121 : : * Traverse the io_range_list to find the registered node for @fwnode.
122 : : */
123 : 9936 : struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
124 : : {
125 : : struct logic_pio_hwaddr *range, *found_range = NULL;
126 : :
127 : : rcu_read_lock();
128 [ # # - + ]: 9936 : list_for_each_entry_rcu(range, &io_range_list, list) {
129 [ # # # # ]: 0 : if (range->fwnode == fwnode) {
130 : 0 : found_range = range;
131 : 0 : break;
132 : : }
133 : : }
134 : : rcu_read_unlock();
135 : :
136 : 9936 : return found_range;
137 : : }
138 : :
139 : : /* Return a registered range given an input PIO token */
140 : 0 : static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
141 : : {
142 : : struct logic_pio_hwaddr *range, *found_range = NULL;
143 : :
144 : : rcu_read_lock();
145 [ # # ]: 0 : list_for_each_entry_rcu(range, &io_range_list, list) {
146 [ # # # # ]: 0 : if (in_range(pio, range->io_start, range->size)) {
147 : 0 : found_range = range;
148 : 0 : break;
149 : : }
150 : : }
151 : : rcu_read_unlock();
152 : :
153 [ # # ]: 0 : if (!found_range)
154 : 0 : pr_err("PIO entry token 0x%lx invalid\n", pio);
155 : :
156 : 0 : return found_range;
157 : : }
158 : :
159 : : /**
160 : : * logic_pio_to_hwaddr - translate logical PIO to HW address
161 : : * @pio: logical PIO value
162 : : *
163 : : * Returns HW address if valid, ~0 otherwise.
164 : : *
165 : : * Translate the input logical PIO to the corresponding hardware address.
166 : : * The input PIO should be unique in the whole logical PIO space.
167 : : */
168 : 0 : resource_size_t logic_pio_to_hwaddr(unsigned long pio)
169 : : {
170 : : struct logic_pio_hwaddr *range;
171 : :
172 : 0 : range = find_io_range(pio);
173 [ # # ]: 0 : if (range)
174 : 0 : return range->hw_start + pio - range->io_start;
175 : :
176 : : return (resource_size_t)~0;
177 : : }
178 : :
179 : : /**
180 : : * logic_pio_trans_hwaddr - translate HW address to logical PIO
181 : : * @fwnode: FW node reference for the host
182 : : * @addr: Host-relative HW address
183 : : * @size: size to translate
184 : : *
185 : : * Returns Logical PIO value if successful, ~0UL otherwise
186 : : */
187 : 0 : unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
188 : : resource_size_t addr, resource_size_t size)
189 : : {
190 : : struct logic_pio_hwaddr *range;
191 : :
192 : : range = find_io_range_by_fwnode(fwnode);
193 [ # # # # ]: 0 : if (!range || range->flags == LOGIC_PIO_CPU_MMIO) {
194 : 0 : pr_err("IO range not found or invalid\n");
195 : 0 : return ~0UL;
196 : : }
197 [ # # ]: 0 : if (range->size < size) {
198 : 0 : pr_err("resource size %pa cannot fit in IO range size %pa\n",
199 : : &size, &range->size);
200 : 0 : return ~0UL;
201 : : }
202 : 0 : return addr - range->hw_start + range->io_start;
203 : : }
204 : :
205 : 0 : unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
206 : : {
207 : : struct logic_pio_hwaddr *range;
208 : :
209 : : rcu_read_lock();
210 [ # # ]: 0 : list_for_each_entry_rcu(range, &io_range_list, list) {
211 [ # # ]: 0 : if (range->flags != LOGIC_PIO_CPU_MMIO)
212 : 0 : continue;
213 [ # # # # ]: 0 : if (in_range(addr, range->hw_start, range->size)) {
214 : : unsigned long cpuaddr;
215 : :
216 : 0 : cpuaddr = addr - range->hw_start + range->io_start;
217 : :
218 : : rcu_read_unlock();
219 : 0 : return cpuaddr;
220 : : }
221 : : }
222 : : rcu_read_unlock();
223 : :
224 : 0 : pr_err("addr %pa not registered in io_range_list\n", &addr);
225 : :
226 : 0 : return ~0UL;
227 : : }
228 : :
229 : : #if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
230 : : #define BUILD_LOGIC_IO(bw, type) \
231 : : type logic_in##bw(unsigned long addr) \
232 : : { \
233 : : type ret = (type)~0; \
234 : : \
235 : : if (addr < MMIO_UPPER_LIMIT) { \
236 : : ret = read##bw(PCI_IOBASE + addr); \
237 : : } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
238 : : struct logic_pio_hwaddr *entry = find_io_range(addr); \
239 : : \
240 : : if (entry && entry->ops) \
241 : : ret = entry->ops->in(entry->hostdata, \
242 : : addr, sizeof(type)); \
243 : : else \
244 : : WARN_ON_ONCE(1); \
245 : : } \
246 : : return ret; \
247 : : } \
248 : : \
249 : : void logic_out##bw(type value, unsigned long addr) \
250 : : { \
251 : : if (addr < MMIO_UPPER_LIMIT) { \
252 : : write##bw(value, PCI_IOBASE + addr); \
253 : : } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
254 : : struct logic_pio_hwaddr *entry = find_io_range(addr); \
255 : : \
256 : : if (entry && entry->ops) \
257 : : entry->ops->out(entry->hostdata, \
258 : : addr, value, sizeof(type)); \
259 : : else \
260 : : WARN_ON_ONCE(1); \
261 : : } \
262 : : } \
263 : : \
264 : : void logic_ins##bw(unsigned long addr, void *buffer, \
265 : : unsigned int count) \
266 : : { \
267 : : if (addr < MMIO_UPPER_LIMIT) { \
268 : : reads##bw(PCI_IOBASE + addr, buffer, count); \
269 : : } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
270 : : struct logic_pio_hwaddr *entry = find_io_range(addr); \
271 : : \
272 : : if (entry && entry->ops) \
273 : : entry->ops->ins(entry->hostdata, \
274 : : addr, buffer, sizeof(type), count); \
275 : : else \
276 : : WARN_ON_ONCE(1); \
277 : : } \
278 : : \
279 : : } \
280 : : \
281 : : void logic_outs##bw(unsigned long addr, const void *buffer, \
282 : : unsigned int count) \
283 : : { \
284 : : if (addr < MMIO_UPPER_LIMIT) { \
285 : : writes##bw(PCI_IOBASE + addr, buffer, count); \
286 : : } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
287 : : struct logic_pio_hwaddr *entry = find_io_range(addr); \
288 : : \
289 : : if (entry && entry->ops) \
290 : : entry->ops->outs(entry->hostdata, \
291 : : addr, buffer, sizeof(type), count); \
292 : : else \
293 : : WARN_ON_ONCE(1); \
294 : : } \
295 : : }
296 : :
297 : : BUILD_LOGIC_IO(b, u8)
298 : : EXPORT_SYMBOL(logic_inb);
299 : : EXPORT_SYMBOL(logic_insb);
300 : : EXPORT_SYMBOL(logic_outb);
301 : : EXPORT_SYMBOL(logic_outsb);
302 : :
303 : : BUILD_LOGIC_IO(w, u16)
304 : : EXPORT_SYMBOL(logic_inw);
305 : : EXPORT_SYMBOL(logic_insw);
306 : : EXPORT_SYMBOL(logic_outw);
307 : : EXPORT_SYMBOL(logic_outsw);
308 : :
309 : : BUILD_LOGIC_IO(l, u32)
310 : : EXPORT_SYMBOL(logic_inl);
311 : : EXPORT_SYMBOL(logic_insl);
312 : : EXPORT_SYMBOL(logic_outl);
313 : : EXPORT_SYMBOL(logic_outsl);
314 : :
315 : : #endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */
|