1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <linux/io.h> |
4 | #include "ipmi_si.h" |
5 | |
6 | static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset) |
7 | { |
8 | unsigned int addr = io->addr_data; |
9 | |
10 | return inb(port: addr + (offset * io->regspacing)); |
11 | } |
12 | |
13 | static void port_outb(const struct si_sm_io *io, unsigned int offset, |
14 | unsigned char b) |
15 | { |
16 | unsigned int addr = io->addr_data; |
17 | |
18 | outb(value: b, port: addr + (offset * io->regspacing)); |
19 | } |
20 | |
21 | static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset) |
22 | { |
23 | unsigned int addr = io->addr_data; |
24 | |
25 | return (inw(port: addr + (offset * io->regspacing)) >> io->regshift) & 0xff; |
26 | } |
27 | |
28 | static void port_outw(const struct si_sm_io *io, unsigned int offset, |
29 | unsigned char b) |
30 | { |
31 | unsigned int addr = io->addr_data; |
32 | |
33 | outw(value: b << io->regshift, port: addr + (offset * io->regspacing)); |
34 | } |
35 | |
36 | static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset) |
37 | { |
38 | unsigned int addr = io->addr_data; |
39 | |
40 | return (inl(port: addr + (offset * io->regspacing)) >> io->regshift) & 0xff; |
41 | } |
42 | |
43 | static void port_outl(const struct si_sm_io *io, unsigned int offset, |
44 | unsigned char b) |
45 | { |
46 | unsigned int addr = io->addr_data; |
47 | |
48 | outl(value: b << io->regshift, port: addr+(offset * io->regspacing)); |
49 | } |
50 | |
51 | static void port_cleanup(struct si_sm_io *io) |
52 | { |
53 | unsigned int addr = io->addr_data; |
54 | int idx; |
55 | |
56 | if (addr) { |
57 | for (idx = 0; idx < io->io_size; idx++) |
58 | release_region(addr + idx * io->regspacing, |
59 | io->regsize); |
60 | } |
61 | } |
62 | |
63 | int ipmi_si_port_setup(struct si_sm_io *io) |
64 | { |
65 | unsigned int addr = io->addr_data; |
66 | int idx; |
67 | |
68 | if (!addr) |
69 | return -ENODEV; |
70 | |
71 | /* |
72 | * Figure out the actual inb/inw/inl/etc routine to use based |
73 | * upon the register size. |
74 | */ |
75 | switch (io->regsize) { |
76 | case 1: |
77 | io->inputb = port_inb; |
78 | io->outputb = port_outb; |
79 | break; |
80 | case 2: |
81 | io->inputb = port_inw; |
82 | io->outputb = port_outw; |
83 | break; |
84 | case 4: |
85 | io->inputb = port_inl; |
86 | io->outputb = port_outl; |
87 | break; |
88 | default: |
89 | dev_warn(io->dev, "Invalid register size: %d\n" , |
90 | io->regsize); |
91 | return -EINVAL; |
92 | } |
93 | |
94 | /* |
95 | * Some BIOSes reserve disjoint I/O regions in their ACPI |
96 | * tables. This causes problems when trying to register the |
97 | * entire I/O region. Therefore we must register each I/O |
98 | * port separately. |
99 | */ |
100 | for (idx = 0; idx < io->io_size; idx++) { |
101 | if (request_region(addr + idx * io->regspacing, |
102 | io->regsize, SI_DEVICE_NAME) == NULL) { |
103 | /* Undo allocations */ |
104 | while (idx--) |
105 | release_region(addr + idx * io->regspacing, |
106 | io->regsize); |
107 | return -EIO; |
108 | } |
109 | } |
110 | |
111 | io->io_cleanup = port_cleanup; |
112 | |
113 | return 0; |
114 | } |
115 | |