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