xref: /openbmc/linux/drivers/char/ipmi/ipmi_si_port_io.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+
258e27635SCorey Minyard 
358e27635SCorey Minyard #include <linux/io.h>
458e27635SCorey Minyard #include "ipmi_si.h"
558e27635SCorey Minyard 
port_inb(const struct si_sm_io * io,unsigned int offset)658e27635SCorey Minyard static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset)
758e27635SCorey Minyard {
858e27635SCorey Minyard 	unsigned int addr = io->addr_data;
958e27635SCorey Minyard 
1058e27635SCorey Minyard 	return inb(addr + (offset * io->regspacing));
1158e27635SCorey Minyard }
1258e27635SCorey Minyard 
port_outb(const struct si_sm_io * io,unsigned int offset,unsigned char b)1358e27635SCorey Minyard static void port_outb(const struct si_sm_io *io, unsigned int offset,
1458e27635SCorey Minyard 		      unsigned char b)
1558e27635SCorey Minyard {
1658e27635SCorey Minyard 	unsigned int addr = io->addr_data;
1758e27635SCorey Minyard 
1858e27635SCorey Minyard 	outb(b, addr + (offset * io->regspacing));
1958e27635SCorey Minyard }
2058e27635SCorey Minyard 
port_inw(const struct si_sm_io * io,unsigned int offset)2158e27635SCorey Minyard static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset)
2258e27635SCorey Minyard {
2358e27635SCorey Minyard 	unsigned int addr = io->addr_data;
2458e27635SCorey Minyard 
2558e27635SCorey Minyard 	return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
2658e27635SCorey Minyard }
2758e27635SCorey Minyard 
port_outw(const struct si_sm_io * io,unsigned int offset,unsigned char b)2858e27635SCorey Minyard static void port_outw(const struct si_sm_io *io, unsigned int offset,
2958e27635SCorey Minyard 		      unsigned char b)
3058e27635SCorey Minyard {
3158e27635SCorey Minyard 	unsigned int addr = io->addr_data;
3258e27635SCorey Minyard 
3358e27635SCorey Minyard 	outw(b << io->regshift, addr + (offset * io->regspacing));
3458e27635SCorey Minyard }
3558e27635SCorey Minyard 
port_inl(const struct si_sm_io * io,unsigned int offset)3658e27635SCorey Minyard static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset)
3758e27635SCorey Minyard {
3858e27635SCorey Minyard 	unsigned int addr = io->addr_data;
3958e27635SCorey Minyard 
4058e27635SCorey Minyard 	return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
4158e27635SCorey Minyard }
4258e27635SCorey Minyard 
port_outl(const struct si_sm_io * io,unsigned int offset,unsigned char b)4358e27635SCorey Minyard static void port_outl(const struct si_sm_io *io, unsigned int offset,
4458e27635SCorey Minyard 		      unsigned char b)
4558e27635SCorey Minyard {
4658e27635SCorey Minyard 	unsigned int addr = io->addr_data;
4758e27635SCorey Minyard 
4858e27635SCorey Minyard 	outl(b << io->regshift, addr+(offset * io->regspacing));
4958e27635SCorey Minyard }
5058e27635SCorey Minyard 
port_cleanup(struct si_sm_io * io)5158e27635SCorey Minyard static void port_cleanup(struct si_sm_io *io)
5258e27635SCorey Minyard {
5358e27635SCorey Minyard 	unsigned int addr = io->addr_data;
5458e27635SCorey Minyard 	int          idx;
5558e27635SCorey Minyard 
5658e27635SCorey Minyard 	if (addr) {
5758e27635SCorey Minyard 		for (idx = 0; idx < io->io_size; idx++)
5858e27635SCorey Minyard 			release_region(addr + idx * io->regspacing,
5958e27635SCorey Minyard 				       io->regsize);
6058e27635SCorey Minyard 	}
6158e27635SCorey Minyard }
6258e27635SCorey Minyard 
ipmi_si_port_setup(struct si_sm_io * io)6358e27635SCorey Minyard int ipmi_si_port_setup(struct si_sm_io *io)
6458e27635SCorey Minyard {
6558e27635SCorey Minyard 	unsigned int addr = io->addr_data;
6658e27635SCorey Minyard 	int          idx;
6758e27635SCorey Minyard 
6858e27635SCorey Minyard 	if (!addr)
6958e27635SCorey Minyard 		return -ENODEV;
7058e27635SCorey Minyard 
7158e27635SCorey Minyard 	/*
7258e27635SCorey Minyard 	 * Figure out the actual inb/inw/inl/etc routine to use based
7358e27635SCorey Minyard 	 * upon the register size.
7458e27635SCorey Minyard 	 */
7558e27635SCorey Minyard 	switch (io->regsize) {
7658e27635SCorey Minyard 	case 1:
7758e27635SCorey Minyard 		io->inputb = port_inb;
7858e27635SCorey Minyard 		io->outputb = port_outb;
7958e27635SCorey Minyard 		break;
8058e27635SCorey Minyard 	case 2:
8158e27635SCorey Minyard 		io->inputb = port_inw;
8258e27635SCorey Minyard 		io->outputb = port_outw;
8358e27635SCorey Minyard 		break;
8458e27635SCorey Minyard 	case 4:
8558e27635SCorey Minyard 		io->inputb = port_inl;
8658e27635SCorey Minyard 		io->outputb = port_outl;
8758e27635SCorey Minyard 		break;
8858e27635SCorey Minyard 	default:
8958e27635SCorey Minyard 		dev_warn(io->dev, "Invalid register size: %d\n",
9058e27635SCorey Minyard 			 io->regsize);
9158e27635SCorey Minyard 		return -EINVAL;
9258e27635SCorey Minyard 	}
9358e27635SCorey Minyard 
9458e27635SCorey Minyard 	/*
9558e27635SCorey Minyard 	 * Some BIOSes reserve disjoint I/O regions in their ACPI
9658e27635SCorey Minyard 	 * tables.  This causes problems when trying to register the
9758e27635SCorey Minyard 	 * entire I/O region.  Therefore we must register each I/O
9858e27635SCorey Minyard 	 * port separately.
9958e27635SCorey Minyard 	 */
10058e27635SCorey Minyard 	for (idx = 0; idx < io->io_size; idx++) {
10158e27635SCorey Minyard 		if (request_region(addr + idx * io->regspacing,
102*104fb25fSCorey Minyard 				   io->regsize, SI_DEVICE_NAME) == NULL) {
10358e27635SCorey Minyard 			/* Undo allocations */
10458e27635SCorey Minyard 			while (idx--)
10558e27635SCorey Minyard 				release_region(addr + idx * io->regspacing,
10658e27635SCorey Minyard 					       io->regsize);
10758e27635SCorey Minyard 			return -EIO;
10858e27635SCorey Minyard 		}
10958e27635SCorey Minyard 	}
110401e7e88SYang Yingliang 
111401e7e88SYang Yingliang 	io->io_cleanup = port_cleanup;
112401e7e88SYang Yingliang 
11358e27635SCorey Minyard 	return 0;
11458e27635SCorey Minyard }
115