Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/ipmi/ipmi_si_mem_io.c
26282 views
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
/*
85
* Figure out the actual readb/readw/readl/etc routine to use based
86
* upon the register size.
87
*/
88
switch (io->regsize) {
89
case 1:
90
io->inputb = intf_mem_inb;
91
io->outputb = intf_mem_outb;
92
break;
93
case 2:
94
io->inputb = intf_mem_inw;
95
io->outputb = intf_mem_outw;
96
break;
97
case 4:
98
io->inputb = intf_mem_inl;
99
io->outputb = intf_mem_outl;
100
break;
101
#ifdef readq
102
case 8:
103
io->inputb = mem_inq;
104
io->outputb = mem_outq;
105
break;
106
#endif
107
default:
108
dev_warn(io->dev, "Invalid register size: %d\n",
109
io->regsize);
110
return -EINVAL;
111
}
112
113
/*
114
* Some BIOSes reserve disjoint memory regions in their ACPI
115
* tables. This causes problems when trying to request the
116
* entire region. Therefore we must request each register
117
* separately.
118
*/
119
for (idx = 0; idx < io->io_size; idx++) {
120
if (request_mem_region(addr + idx * io->regspacing,
121
io->regsize, SI_DEVICE_NAME) == NULL) {
122
/* Undo allocations */
123
mem_region_cleanup(io, idx);
124
return -EIO;
125
}
126
}
127
128
/*
129
* Calculate the total amount of memory to claim. This is an
130
* unusual looking calculation, but it avoids claiming any
131
* more memory than it has to. It will claim everything
132
* between the first address to the end of the last full
133
* register.
134
*/
135
mapsize = ((io->io_size * io->regspacing)
136
- (io->regspacing - io->regsize));
137
io->addr = ioremap(addr, mapsize);
138
if (io->addr == NULL) {
139
mem_region_cleanup(io, io->io_size);
140
return -EIO;
141
}
142
143
io->io_cleanup = mem_cleanup;
144
145
return 0;
146
}
147
148