]> mj.ucw.cz Git - pciutils.git/blob - lib/mmio-ports.c
Merge branch 'amiga'
[pciutils.git] / lib / mmio-ports.c
1 /*
2  *      The PCI Library -- Direct Configuration access via memory mapped ports
3  *
4  *      Copyright (c) 2022 Pali Rohár <pali@kernel.org>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *      SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include "internal.h"
12 #include "physmem.h"
13 #include "physmem-access.h"
14
15 #include <ctype.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 struct mmio_cache {
21   u64 addr_page;
22   u64 data_page;
23   void *addr_map;
24   void *data_map;
25 };
26
27 struct mmio_access {
28   struct mmio_cache *cache;
29   struct physmem *physmem;
30   long pagesize;
31 };
32
33 static void
34 munmap_regs(struct pci_access *a)
35 {
36   struct mmio_access *macc = a->backend_data;
37   struct mmio_cache *cache = macc->cache;
38   struct physmem *physmem = macc->physmem;
39   long pagesize = macc->pagesize;
40
41   if (!cache)
42     return;
43
44   physmem_unmap(physmem, cache->addr_map, pagesize);
45   if (cache->addr_page != cache->data_page)
46     physmem_unmap(physmem, cache->data_map, pagesize);
47
48   pci_mfree(macc->cache);
49   macc->cache = NULL;
50 }
51
52 static int
53 mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data)
54 {
55   struct mmio_access *macc = a->backend_data;
56   struct mmio_cache *cache = macc->cache;
57   struct physmem *physmem = macc->physmem;
58   long pagesize = macc->pagesize;
59   u64 addr_page = addr_reg & ~(pagesize-1);
60   u64 data_page = data_reg & ~(pagesize-1);
61   void *addr_map = (void *)-1;
62   void *data_map = (void *)-1;
63
64   if (cache && cache->addr_page == addr_page)
65     addr_map = cache->addr_map;
66
67   if (cache && cache->data_page == data_page)
68     data_map = cache->data_map;
69
70   if (addr_map == (void *)-1)
71     addr_map = physmem_map(physmem, addr_page, pagesize, 1);
72
73   if (addr_map == (void *)-1)
74     return 0;
75
76   if (data_map == (void *)-1)
77     {
78       if (data_page == addr_page)
79         data_map = addr_map;
80       else
81         data_map = physmem_map(physmem, data_page, pagesize, 1);
82     }
83
84   if (data_map == (void *)-1)
85     {
86       if (!cache || cache->addr_map != addr_map)
87         physmem_unmap(physmem, addr_map, pagesize);
88       return 0;
89     }
90
91   if (cache && cache->addr_page != addr_page)
92     physmem_unmap(physmem, cache->addr_map, pagesize);
93
94   if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page)
95     physmem_unmap(physmem, cache->data_map, pagesize);
96
97   if (!cache)
98     cache = macc->cache = pci_malloc(a, sizeof(*cache));
99
100   cache->addr_page = addr_page;
101   cache->data_page = data_page;
102   cache->addr_map = addr_map;
103   cache->data_map = data_map;
104
105   *addr = (unsigned char *)addr_map + (addr_reg & (pagesize-1));
106   *data = (unsigned char *)data_map + (data_reg & (pagesize-1)) + data_off;
107   return 1;
108 }
109
110 static int
111 validate_addrs(const char *addrs)
112 {
113   const char *sep, *next;
114   u64 num;
115   char *endptr;
116
117   if (!*addrs)
118     return 0;
119
120   while (1)
121     {
122       next = strchr(addrs, ',');
123       if (!next)
124         next = addrs + strlen(addrs);
125
126       sep = strchr(addrs, '/');
127       if (!sep)
128         return 0;
129
130       if (!isxdigit(*addrs) || !isxdigit(*(sep+1)))
131         return 0;
132
133       errno = 0;
134       num = strtoull(addrs, &endptr, 16);
135       if (errno || endptr != sep || (num & 3))
136         return 0;
137
138       errno = 0;
139       num = strtoull(sep+1, &endptr, 16);
140       if (errno || endptr != next || (num & 3))
141         return 0;
142
143       if (!*next)
144         return 1;
145
146       addrs = next + 1;
147     }
148 }
149
150 static int
151 get_domain_count(const char *addrs)
152 {
153   int count = 1;
154   while (addrs = strchr(addrs, ','))
155     {
156       addrs++;
157       count++;
158     }
159   return count;
160 }
161
162 static int
163 get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg)
164 {
165   char *endptr;
166
167   while (domain-- > 0)
168     {
169       addrs = strchr(addrs, ',');
170       if (!addrs)
171         return 0;
172       addrs++;
173     }
174
175   *addr_reg = strtoull(addrs, &endptr, 16);
176   *data_reg = strtoull(endptr+1, NULL, 16);
177
178   return 1;
179 }
180
181 static void
182 conf1_config(struct pci_access *a)
183 {
184   physmem_init_config(a);
185   pci_define_param(a, "mmio-conf1.addrs", "", "Physical addresses of memory mapped Intel conf1 interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
186 }
187
188 static void
189 conf1_ext_config(struct pci_access *a)
190 {
191   physmem_init_config(a);
192   pci_define_param(a, "mmio-conf1-ext.addrs", "", "Physical addresses of memory mapped Intel conf1 extended interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
193 }
194
195 static int
196 detect(struct pci_access *a, char *addrs_param_name)
197 {
198   char *addrs = pci_get_param(a, addrs_param_name);
199
200   if (!*addrs)
201     {
202       a->debug("%s was not specified", addrs_param_name);
203       return 0;
204     }
205
206   if (!validate_addrs(addrs))
207     {
208       a->debug("%s has invalid address format %s", addrs_param_name, addrs);
209       return 0;
210     }
211
212   if (physmem_access(a, 1))
213     {
214       a->debug("cannot access physical memory: %s", strerror(errno));
215       return 0;
216     }
217
218   a->debug("using with %s", addrs);
219   return 1;
220 }
221
222 static int
223 conf1_detect(struct pci_access *a)
224 {
225   return detect(a, "mmio-conf1.addrs");
226 }
227
228 static int
229 conf1_ext_detect(struct pci_access *a)
230 {
231   return detect(a, "mmio-conf1-ext.addrs");
232 }
233
234 static char*
235 get_addrs_param_name(struct pci_access *a)
236 {
237   if (a->methods->config == conf1_ext_config)
238     return "mmio-conf1-ext.addrs";
239   else
240     return "mmio-conf1.addrs";
241 }
242
243 static void
244 conf1_init(struct pci_access *a)
245 {
246   char *addrs_param_name = get_addrs_param_name(a);
247   char *addrs = pci_get_param(a, addrs_param_name);
248   struct mmio_access *macc;
249   struct physmem *physmem;
250   long pagesize;
251
252   if (!*addrs)
253     a->error("Option %s was not specified.", addrs_param_name);
254
255   if (!validate_addrs(addrs))
256     a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs);
257
258   physmem = physmem_open(a, 1);
259   if (!physmem)
260     a->error("Cannot open physcal memory: %s.", strerror(errno));
261
262   pagesize = physmem_get_pagesize(physmem);
263   if (pagesize <= 0)
264     a->error("Cannot get page size: %s.", strerror(errno));
265
266   macc = pci_malloc(a, sizeof(*macc));
267   macc->cache = NULL;
268   macc->physmem = physmem;
269   macc->pagesize = pagesize;
270   a->backend_data = macc;
271 }
272
273 static void
274 conf1_cleanup(struct pci_access *a)
275 {
276   struct mmio_access *macc = a->backend_data;
277
278   munmap_regs(a);
279   physmem_close(macc->physmem);
280   pci_mfree(macc);
281 }
282
283 static void
284 conf1_scan(struct pci_access *a)
285 {
286   char *addrs_param_name = get_addrs_param_name(a);
287   char *addrs = pci_get_param(a, addrs_param_name);
288   int domain_count = get_domain_count(addrs);
289   int domain;
290
291   for (domain = 0; domain < domain_count; domain++)
292     pci_generic_scan_domain(a, domain);
293 }
294
295 static int
296 conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len)
297 {
298   char *addrs_param_name = get_addrs_param_name(d->access);
299   char *addrs = pci_get_param(d->access, addrs_param_name);
300   volatile void *addr, *data;
301   u64 addr_reg, data_reg;
302
303   if (pos >= 4096)
304     return 0;
305
306   if (len != 1 && len != 2 && len != 4)
307     return pci_generic_block_read(d, pos, buf, len);
308
309   if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
310     return 0;
311
312   if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
313     return 0;
314
315   physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
316   physmem_readl(addr); /* write barrier for address */
317
318   switch (len)
319     {
320     case 1:
321       buf[0] = physmem_readb(data);
322       break;
323     case 2:
324       ((u16 *) buf)[0] = physmem_readw(data);
325       break;
326     case 4:
327       ((u32 *) buf)[0] = physmem_readl(data);
328       break;
329     }
330
331   return 1;
332 }
333
334 static int
335 conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
336 {
337   if (pos >= 256)
338     return 0;
339
340   return conf1_ext_read(d, pos, buf, len);
341 }
342
343 static int
344 conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len)
345 {
346   char *addrs_param_name = get_addrs_param_name(d->access);
347   char *addrs = pci_get_param(d->access, addrs_param_name);
348   volatile void *addr, *data;
349   u64 addr_reg, data_reg;
350
351   if (pos >= 4096)
352     return 0;
353
354   if (len != 1 && len != 2 && len != 4)
355     return pci_generic_block_write(d, pos, buf, len);
356
357   if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
358     return 0;
359
360   if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
361     return 0;
362
363   physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
364   physmem_readl(addr); /* write barrier for address */
365
366   switch (len)
367     {
368     case 1:
369       physmem_writeb(buf[0], data);
370       break;
371     case 2:
372       physmem_writew(((u16 *) buf)[0], data);
373       break;
374     case 4:
375       physmem_writel(((u32 *) buf)[0], data);
376       break;
377     }
378
379   /*
380    * write barrier for data
381    * Note that we cannot read from data port because it may have side effect.
382    * Instead we read from address port (which should not have side effect) to
383    * create a barrier between two conf1_write() calls. But this does not have
384    * to be 100% correct as it does not ensure barrier on data port itself.
385    * Correct way is to issue CPU instruction for full hw sync barrier but gcc
386    * does not provide any (builtin) function yet.
387    */
388   physmem_readl(addr);
389
390   return 1;
391 }
392
393 static int
394 conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
395 {
396   if (pos >= 256)
397     return 0;
398
399   return conf1_ext_write(d, pos, buf, len);
400 }
401
402 struct pci_methods pm_mmio_conf1 = {
403   "mmio-conf1",
404   "Raw memory mapped I/O port access using Intel conf1 interface",
405   conf1_config,
406   conf1_detect,
407   conf1_init,
408   conf1_cleanup,
409   conf1_scan,
410   pci_generic_fill_info,
411   conf1_read,
412   conf1_write,
413   NULL,                                 /* read_vpd */
414   NULL,                                 /* init_dev */
415   NULL                                  /* cleanup_dev */
416 };
417
418 struct pci_methods pm_mmio_conf1_ext = {
419   "mmio-conf1-ext",
420   "Raw memory mapped I/O port access using Intel conf1 extended interface",
421   conf1_ext_config,
422   conf1_ext_detect,
423   conf1_init,
424   conf1_cleanup,
425   conf1_scan,
426   pci_generic_fill_info,
427   conf1_ext_read,
428   conf1_ext_write,
429   NULL,                                 /* read_vpd */
430   NULL,                                 /* init_dev */
431   NULL                                  /* cleanup_dev */
432 };