1 /*
2 * The PCI Library -- NetBSD libpci access
3 * (based on FreeBSD /dev/pci access)
4 *
5 * Copyright (c) 1999 Jari Kirma <[email protected]>
6 * Copyright (c) 2002 Quentin Garnier <[email protected]>
7 * Copyright (c) 2002 Martin Mares <[email protected]>
8 *
9 * Can be freely distributed and used under the terms of the GNU GPL v2+.
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14 /*
15 * Read functionality of this driver is briefly tested, and seems
16 * to supply basic information correctly, but I promise no more.
17 */
18
19 #include <fcntl.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <pci.h>
24
25 #include "internal.h"
26
27 static void
nbsd_config(struct pci_access * a)28 nbsd_config(struct pci_access *a)
29 {
30 pci_define_param(a, "nbsd.path", PCI_PATH_NBSD_DEVICE, "Path to the NetBSD PCI device");
31 }
32
33 static int
nbsd_detect(struct pci_access * a)34 nbsd_detect(struct pci_access *a)
35 {
36 char *name = pci_get_param(a, "nbsd.path");
37
38 if (access(name, R_OK))
39 {
40 a->warning("Cannot open %s", name);
41 return 0;
42 }
43
44 if (!access(name, W_OK))
45 a->writeable = O_RDWR;
46 a->debug("...using %s", name);
47 return 1;
48 }
49
50 static void
nbsd_init(struct pci_access * a)51 nbsd_init(struct pci_access *a)
52 {
53 char *name = pci_get_param(a, "nbsd.path");
54 int mode = a->writeable ? O_RDWR : O_RDONLY;
55
56 a->fd = open(name, mode, 0);
57 if (a->fd < 0)
58 a->error("nbsd_init: %s open failed", name);
59 }
60
61 static void
nbsd_cleanup(struct pci_access * a)62 nbsd_cleanup(struct pci_access *a)
63 {
64 close(a->fd);
65 }
66
67 static int
nbsd_read(struct pci_dev * d,int pos,byte * buf,int len)68 nbsd_read(struct pci_dev *d, int pos, byte *buf, int len)
69 {
70 pcireg_t val;
71 int shift;
72
73 if (!(len == 1 || len == 2 || len == 4))
74 return pci_generic_block_read(d, pos, buf, len);
75
76 if (d->domain || pos >= 4096)
77 return 0;
78
79 shift = 8*(pos % 4);
80 pos &= ~3;
81
82 if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0)
83 d->access->error("nbsd_read: pci_bus_conf_read() failed");
84
85 switch (len)
86 {
87 case 1:
88 *buf = val >> shift;
89 break;
90 case 2:
91 *(u16*)buf = cpu_to_le16(val >> shift);
92 break;
93 case 4:
94 *(u32*)buf = cpu_to_le32(val);
95 break;
96 }
97 return 1;
98 }
99
100 static int
nbsd_write(struct pci_dev * d,int pos,byte * buf,int len)101 nbsd_write(struct pci_dev *d, int pos, byte *buf, int len)
102 {
103 pcireg_t val = 0;
104 int shift;
105
106 if (!(len == 1 || len == 2 || len == 4))
107 return pci_generic_block_write(d, pos, buf, len);
108
109 if (d->domain || pos >= 256)
110 return 0;
111
112 /*
113 * BEWARE: NetBSD seems to support only 32-bit access, so we have
114 * to emulate byte and word writes by read-modify-write, possibly
115 * causing troubles.
116 */
117
118 shift = 8*(pos % 4);
119 pos &= ~3;
120 if (len != 4)
121 {
122 if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0)
123 d->access->error("nbsd_write: pci_bus_conf_read() failed");
124 }
125
126 switch (len)
127 {
128 case 1:
129 val = (val & ~(0xff << shift)) | (buf[0] << shift);
130 break;
131 case 2:
132 val = (val & ~(0xffff << shift)) | (le16_to_cpu(*(u16*)buf) << shift);
133 break;
134 case 4:
135 val = le32_to_cpu(*(u32*)buf);
136 break;
137 }
138
139 if (pcibus_conf_write(d->access->fd, d->bus, d->dev, d->func, pos, val) < 0)
140 d->access->error("nbsd_write: pci_bus_conf_write() failed");
141
142 return 1;
143 }
144
145 struct pci_methods pm_nbsd_libpci = {
146 .name = "nbsd-libpci",
147 .help = "NetBSD libpci",
148 .config = nbsd_config,
149 .detect = nbsd_detect,
150 .init = nbsd_init,
151 .cleanup = nbsd_cleanup,
152 .scan = pci_generic_scan,
153 .fill_info = pci_generic_fill_info,
154 .read = nbsd_read,
155 .write = nbsd_write,
156 };
157