Linux 4.18.10
[linux/fpc-iii.git] / drivers / char / ipmi / ipmi_si_mem_io.c
blob1b869d530884c47c4763a29019339d91866d3da6
1 // SPDX-License-Identifier: GPL-2.0+
3 #include <linux/io.h>
4 #include "ipmi_si.h"
6 static unsigned char intf_mem_inb(const struct si_sm_io *io,
7 unsigned int offset)
9 return readb((io->addr)+(offset * io->regspacing));
12 static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
13 unsigned char b)
15 writeb(b, (io->addr)+(offset * io->regspacing));
18 static unsigned char intf_mem_inw(const struct si_sm_io *io,
19 unsigned int offset)
21 return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
22 & 0xff;
25 static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
26 unsigned char b)
28 writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
31 static unsigned char intf_mem_inl(const struct si_sm_io *io,
32 unsigned int offset)
34 return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
35 & 0xff;
38 static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
39 unsigned char b)
41 writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
44 #ifdef readq
45 static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
47 return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
48 & 0xff;
51 static void mem_outq(const struct si_sm_io *io, unsigned int offset,
52 unsigned char b)
54 writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));
56 #endif
58 static void mem_region_cleanup(struct si_sm_io *io, int num)
60 unsigned long addr = io->addr_data;
61 int idx;
63 for (idx = 0; idx < num; idx++)
64 release_mem_region(addr + idx * io->regspacing,
65 io->regsize);
68 static void mem_cleanup(struct si_sm_io *io)
70 if (io->addr) {
71 iounmap(io->addr);
72 mem_region_cleanup(io, io->io_size);
76 int ipmi_si_mem_setup(struct si_sm_io *io)
78 unsigned long addr = io->addr_data;
79 int mapsize, idx;
81 if (!addr)
82 return -ENODEV;
84 io->io_cleanup = mem_cleanup;
87 * Figure out the actual readb/readw/readl/etc routine to use based
88 * upon the register size.
90 switch (io->regsize) {
91 case 1:
92 io->inputb = intf_mem_inb;
93 io->outputb = intf_mem_outb;
94 break;
95 case 2:
96 io->inputb = intf_mem_inw;
97 io->outputb = intf_mem_outw;
98 break;
99 case 4:
100 io->inputb = intf_mem_inl;
101 io->outputb = intf_mem_outl;
102 break;
103 #ifdef readq
104 case 8:
105 io->inputb = mem_inq;
106 io->outputb = mem_outq;
107 break;
108 #endif
109 default:
110 dev_warn(io->dev, "Invalid register size: %d\n",
111 io->regsize);
112 return -EINVAL;
116 * Some BIOSes reserve disjoint memory regions in their ACPI
117 * tables. This causes problems when trying to request the
118 * entire region. Therefore we must request each register
119 * separately.
121 for (idx = 0; idx < io->io_size; idx++) {
122 if (request_mem_region(addr + idx * io->regspacing,
123 io->regsize, DEVICE_NAME) == NULL) {
124 /* Undo allocations */
125 mem_region_cleanup(io, idx);
126 return -EIO;
131 * Calculate the total amount of memory to claim. This is an
132 * unusual looking calculation, but it avoids claiming any
133 * more memory than it has to. It will claim everything
134 * between the first address to the end of the last full
135 * register.
137 mapsize = ((io->io_size * io->regspacing)
138 - (io->regspacing - io->regsize));
139 io->addr = ioremap(addr, mapsize);
140 if (io->addr == NULL) {
141 mem_region_cleanup(io, io->io_size);
142 return -EIO;
144 return 0;