2 * The PCI Library -- Direct Configuration access via memory mapped ports
4 * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "physmem-access.h"
28 struct mmio_cache *cache;
29 struct physmem *physmem;
34 munmap_regs(struct pci_access *a)
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;
44 physmem_unmap(physmem, cache->addr_map, pagesize);
45 if (cache->addr_page != cache->data_page)
46 physmem_unmap(physmem, cache->data_map, pagesize);
48 pci_mfree(macc->cache);
53 mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data)
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;
64 if (cache && cache->addr_page == addr_page)
65 addr_map = cache->addr_map;
67 if (cache && cache->data_page == data_page)
68 data_map = cache->data_map;
70 if (addr_map == (void *)-1)
71 addr_map = physmem_map(physmem, addr_page, pagesize, 1);
73 if (addr_map == (void *)-1)
76 if (data_map == (void *)-1)
78 if (data_page == addr_page)
81 data_map = physmem_map(physmem, data_page, pagesize, 1);
84 if (data_map == (void *)-1)
86 if (!cache || cache->addr_map != addr_map)
87 physmem_unmap(physmem, addr_map, pagesize);
91 if (cache && cache->addr_page != addr_page)
92 physmem_unmap(physmem, cache->addr_map, pagesize);
94 if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page)
95 physmem_unmap(physmem, cache->data_map, pagesize);
98 cache = macc->cache = pci_malloc(a, sizeof(*cache));
100 cache->addr_page = addr_page;
101 cache->data_page = data_page;
102 cache->addr_map = addr_map;
103 cache->data_map = data_map;
105 *addr = (unsigned char *)addr_map + (addr_reg & (pagesize-1));
106 *data = (unsigned char *)data_map + (data_reg & (pagesize-1)) + data_off;
111 validate_addrs(const char *addrs)
113 const char *sep, *next;
122 next = strchr(addrs, ',');
124 next = addrs + strlen(addrs);
126 sep = strchr(addrs, '/');
130 if (!isxdigit(*addrs) || !isxdigit(*(sep+1)))
134 num = strtoull(addrs, &endptr, 16);
135 if (errno || endptr != sep || (num & 3))
139 num = strtoull(sep+1, &endptr, 16);
140 if (errno || endptr != next || (num & 3))
151 get_domain_count(const char *addrs)
154 while (addrs = strchr(addrs, ','))
163 get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg)
169 addrs = strchr(addrs, ',');
175 *addr_reg = strtoull(addrs, &endptr, 16);
176 *data_reg = strtoull(endptr+1, NULL, 16);
182 conf1_config(struct pci_access *a)
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,... */
189 conf1_ext_config(struct pci_access *a)
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,... */
196 detect(struct pci_access *a, char *addrs_param_name)
198 char *addrs = pci_get_param(a, addrs_param_name);
202 a->debug("%s was not specified", addrs_param_name);
206 if (!validate_addrs(addrs))
208 a->debug("%s has invalid address format %s", addrs_param_name, addrs);
212 if (physmem_access(a, 1))
214 a->debug("cannot access physical memory: %s", strerror(errno));
218 a->debug("using with %s", addrs);
223 conf1_detect(struct pci_access *a)
225 return detect(a, "mmio-conf1.addrs");
229 conf1_ext_detect(struct pci_access *a)
231 return detect(a, "mmio-conf1-ext.addrs");
235 get_addrs_param_name(struct pci_access *a)
237 if (a->methods->config == conf1_ext_config)
238 return "mmio-conf1-ext.addrs";
240 return "mmio-conf1.addrs";
244 conf1_init(struct pci_access *a)
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;
253 a->error("Option %s was not specified.", addrs_param_name);
255 if (!validate_addrs(addrs))
256 a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs);
258 physmem = physmem_open(a, 1);
260 a->error("Cannot open physcal memory: %s.", strerror(errno));
262 pagesize = physmem_get_pagesize(physmem);
264 a->error("Cannot get page size: %s.", strerror(errno));
266 macc = pci_malloc(a, sizeof(*macc));
268 macc->physmem = physmem;
269 macc->pagesize = pagesize;
270 a->backend_data = macc;
274 conf1_cleanup(struct pci_access *a)
276 struct mmio_access *macc = a->backend_data;
279 physmem_close(macc->physmem);
284 conf1_scan(struct pci_access *a)
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);
291 for (domain = 0; domain < domain_count; domain++)
292 pci_generic_scan_domain(a, domain);
296 conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len)
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;
306 if (len != 1 && len != 2 && len != 4)
307 return pci_generic_block_read(d, pos, buf, len);
309 if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
312 if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
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 */
321 buf[0] = physmem_readb(data);
324 ((u16 *) buf)[0] = physmem_readw(data);
327 ((u32 *) buf)[0] = physmem_readl(data);
335 conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
340 return conf1_ext_read(d, pos, buf, len);
344 conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len)
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;
354 if (len != 1 && len != 2 && len != 4)
355 return pci_generic_block_write(d, pos, buf, len);
357 if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
360 if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
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 */
369 physmem_writeb(buf[0], data);
372 physmem_writew(((u16 *) buf)[0], data);
375 physmem_writel(((u32 *) buf)[0], data);
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.
394 conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
399 return conf1_ext_write(d, pos, buf, len);
402 struct pci_methods pm_mmio_conf1 = {
403 .name = "mmio-conf1",
404 .help = "Raw memory mapped I/O port access using Intel conf1 interface",
405 .config = conf1_config,
406 .detect = conf1_detect,
408 .cleanup = conf1_cleanup,
410 .fill_info = pci_generic_fill_info,
412 .write = conf1_write,
415 struct pci_methods pm_mmio_conf1_ext = {
416 .name = "mmio-conf1-ext",
417 .help = "Raw memory mapped I/O port access using Intel conf1 extended interface",
418 .config = conf1_ext_config,
419 .detect = conf1_ext_detect,
421 .cleanup = conf1_cleanup,
423 .fill_info = pci_generic_fill_info,
424 .read = conf1_ext_read,
425 .write = conf1_ext_write,