1 
2 #include <linux/io.h>
3 #include "ipmi_si.h"
4 
5 static unsigned char intf_mem_inb(const struct si_sm_io *io,
6 				  unsigned int offset)
7 {
8 	return readb((io->addr)+(offset * io->regspacing));
9 }
10 
11 static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
12 			  unsigned char b)
13 {
14 	writeb(b, (io->addr)+(offset * io->regspacing));
15 }
16 
17 static unsigned char intf_mem_inw(const struct si_sm_io *io,
18 				  unsigned int offset)
19 {
20 	return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
21 		& 0xff;
22 }
23 
24 static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
25 			  unsigned char b)
26 {
27 	writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
28 }
29 
30 static unsigned char intf_mem_inl(const struct si_sm_io *io,
31 				  unsigned int offset)
32 {
33 	return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
34 		& 0xff;
35 }
36 
37 static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
38 			  unsigned char b)
39 {
40 	writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
41 }
42 
43 #ifdef readq
44 static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
45 {
46 	return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
47 		& 0xff;
48 }
49 
50 static void mem_outq(const struct si_sm_io *io, unsigned int offset,
51 		     unsigned char b)
52 {
53 	writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));
54 }
55 #endif
56 
57 static void mem_region_cleanup(struct si_sm_io *io, int num)
58 {
59 	unsigned long addr = io->addr_data;
60 	int idx;
61 
62 	for (idx = 0; idx < num; idx++)
63 		release_mem_region(addr + idx * io->regspacing,
64 				   io->regsize);
65 }
66 
67 static void mem_cleanup(struct si_sm_io *io)
68 {
69 	if (io->addr) {
70 		iounmap(io->addr);
71 		mem_region_cleanup(io, io->io_size);
72 	}
73 }
74 
75 int ipmi_si_mem_setup(struct si_sm_io *io)
76 {
77 	unsigned long addr = io->addr_data;
78 	int           mapsize, idx;
79 
80 	if (!addr)
81 		return -ENODEV;
82 
83 	io->io_cleanup = mem_cleanup;
84 
85 	/*
86 	 * Figure out the actual readb/readw/readl/etc routine to use based
87 	 * upon the register size.
88 	 */
89 	switch (io->regsize) {
90 	case 1:
91 		io->inputb = intf_mem_inb;
92 		io->outputb = intf_mem_outb;
93 		break;
94 	case 2:
95 		io->inputb = intf_mem_inw;
96 		io->outputb = intf_mem_outw;
97 		break;
98 	case 4:
99 		io->inputb = intf_mem_inl;
100 		io->outputb = intf_mem_outl;
101 		break;
102 #ifdef readq
103 	case 8:
104 		io->inputb = mem_inq;
105 		io->outputb = mem_outq;
106 		break;
107 #endif
108 	default:
109 		dev_warn(io->dev, "Invalid register size: %d\n",
110 			 io->regsize);
111 		return -EINVAL;
112 	}
113 
114 	/*
115 	 * Some BIOSes reserve disjoint memory regions in their ACPI
116 	 * tables.  This causes problems when trying to request the
117 	 * entire region.  Therefore we must request each register
118 	 * separately.
119 	 */
120 	for (idx = 0; idx < io->io_size; idx++) {
121 		if (request_mem_region(addr + idx * io->regspacing,
122 				       io->regsize, DEVICE_NAME) == NULL) {
123 			/* Undo allocations */
124 			mem_region_cleanup(io, idx);
125 			return -EIO;
126 		}
127 	}
128 
129 	/*
130 	 * Calculate the total amount of memory to claim.  This is an
131 	 * unusual looking calculation, but it avoids claiming any
132 	 * more memory than it has to.  It will claim everything
133 	 * between the first address to the end of the last full
134 	 * register.
135 	 */
136 	mapsize = ((io->io_size * io->regspacing)
137 		   - (io->regspacing - io->regsize));
138 	io->addr = ioremap(addr, mapsize);
139 	if (io->addr == NULL) {
140 		mem_region_cleanup(io, io->io_size);
141 		return -EIO;
142 	}
143 	return 0;
144 }
145