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