Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6-microblaze.git] / drivers / char / ipmi / ipmi_si_port_io.c
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 }