Merge tag 'wireless-drivers-next-for-davem-2019-02-06' of git://git.kernel.org/pub...
[linux-2.6-block.git] / drivers / char / ipmi / ipmi_si_mem_io.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <linux/io.h>
4 #include "ipmi_si.h"
5
6 static unsigned char intf_mem_inb(const struct si_sm_io *io,
7                                   unsigned int offset)
8 {
9         return readb((io->addr)+(offset * io->regspacing));
10 }
11
12 static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
13                           unsigned char b)
14 {
15         writeb(b, (io->addr)+(offset * io->regspacing));
16 }
17
18 static unsigned char intf_mem_inw(const struct si_sm_io *io,
19                                   unsigned int offset)
20 {
21         return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
22                 & 0xff;
23 }
24
25 static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
26                           unsigned char b)
27 {
28         writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
29 }
30
31 static unsigned char intf_mem_inl(const struct si_sm_io *io,
32                                   unsigned int offset)
33 {
34         return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
35                 & 0xff;
36 }
37
38 static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
39                           unsigned char b)
40 {
41         writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
42 }
43
44 #ifdef readq
45 static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
46 {
47         return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
48                 & 0xff;
49 }
50
51 static void mem_outq(const struct si_sm_io *io, unsigned int offset,
52                      unsigned char b)
53 {
54         writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
55 }
56 #endif
57
58 static void mem_region_cleanup(struct si_sm_io *io, int num)
59 {
60         unsigned long addr = io->addr_data;
61         int idx;
62
63         for (idx = 0; idx < num; idx++)
64                 release_mem_region(addr + idx * io->regspacing,
65                                    io->regsize);
66 }
67
68 static void mem_cleanup(struct si_sm_io *io)
69 {
70         if (io->addr) {
71                 iounmap(io->addr);
72                 mem_region_cleanup(io, io->io_size);
73         }
74 }
75
76 int ipmi_si_mem_setup(struct si_sm_io *io)
77 {
78         unsigned long addr = io->addr_data;
79         int           mapsize, idx;
80
81         if (!addr)
82                 return -ENODEV;
83
84         io->io_cleanup = mem_cleanup;
85
86         /*
87          * Figure out the actual readb/readw/readl/etc routine to use based
88          * upon the register size.
89          */
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;
113         }
114
115         /*
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.
120          */
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;
127                 }
128         }
129
130         /*
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.
136          */
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;
143         }
144         return 0;
145 }