xref: /openbmc/linux/drivers/char/ipmi/ipmi_si_mem_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 
intf_mem_inb(const struct si_sm_io * io,unsigned int offset)658e27635SCorey Minyard static unsigned char intf_mem_inb(const struct si_sm_io *io,
758e27635SCorey Minyard 				  unsigned int offset)
858e27635SCorey Minyard {
958e27635SCorey Minyard 	return readb((io->addr)+(offset * io->regspacing));
1058e27635SCorey Minyard }
1158e27635SCorey Minyard 
intf_mem_outb(const struct si_sm_io * io,unsigned int offset,unsigned char b)1258e27635SCorey Minyard static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
1358e27635SCorey Minyard 			  unsigned char b)
1458e27635SCorey Minyard {
1558e27635SCorey Minyard 	writeb(b, (io->addr)+(offset * io->regspacing));
1658e27635SCorey Minyard }
1758e27635SCorey Minyard 
intf_mem_inw(const struct si_sm_io * io,unsigned int offset)1858e27635SCorey Minyard static unsigned char intf_mem_inw(const struct si_sm_io *io,
1958e27635SCorey Minyard 				  unsigned int offset)
2058e27635SCorey Minyard {
2158e27635SCorey Minyard 	return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
2258e27635SCorey Minyard 		& 0xff;
2358e27635SCorey Minyard }
2458e27635SCorey Minyard 
intf_mem_outw(const struct si_sm_io * io,unsigned int offset,unsigned char b)2558e27635SCorey Minyard static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
2658e27635SCorey Minyard 			  unsigned char b)
2758e27635SCorey Minyard {
2858e27635SCorey Minyard 	writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
2958e27635SCorey Minyard }
3058e27635SCorey Minyard 
intf_mem_inl(const struct si_sm_io * io,unsigned int offset)3158e27635SCorey Minyard static unsigned char intf_mem_inl(const struct si_sm_io *io,
3258e27635SCorey Minyard 				  unsigned int offset)
3358e27635SCorey Minyard {
3458e27635SCorey Minyard 	return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
3558e27635SCorey Minyard 		& 0xff;
3658e27635SCorey Minyard }
3758e27635SCorey Minyard 
intf_mem_outl(const struct si_sm_io * io,unsigned int offset,unsigned char b)3858e27635SCorey Minyard static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
3958e27635SCorey Minyard 			  unsigned char b)
4058e27635SCorey Minyard {
4158e27635SCorey Minyard 	writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
4258e27635SCorey Minyard }
4358e27635SCorey Minyard 
4458e27635SCorey Minyard #ifdef readq
mem_inq(const struct si_sm_io * io,unsigned int offset)4558e27635SCorey Minyard static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
4658e27635SCorey Minyard {
4758e27635SCorey Minyard 	return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
4858e27635SCorey Minyard 		& 0xff;
4958e27635SCorey Minyard }
5058e27635SCorey Minyard 
mem_outq(const struct si_sm_io * io,unsigned int offset,unsigned char b)5158e27635SCorey Minyard static void mem_outq(const struct si_sm_io *io, unsigned int offset,
5258e27635SCorey Minyard 		     unsigned char b)
5358e27635SCorey Minyard {
5497a103e6SColin Ian King 	writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
5558e27635SCorey Minyard }
5658e27635SCorey Minyard #endif
5758e27635SCorey Minyard 
mem_region_cleanup(struct si_sm_io * io,int num)5858e27635SCorey Minyard static void mem_region_cleanup(struct si_sm_io *io, int num)
5958e27635SCorey Minyard {
6058e27635SCorey Minyard 	unsigned long addr = io->addr_data;
6158e27635SCorey Minyard 	int idx;
6258e27635SCorey Minyard 
6358e27635SCorey Minyard 	for (idx = 0; idx < num; idx++)
6458e27635SCorey Minyard 		release_mem_region(addr + idx * io->regspacing,
6558e27635SCorey Minyard 				   io->regsize);
6658e27635SCorey Minyard }
6758e27635SCorey Minyard 
mem_cleanup(struct si_sm_io * io)6858e27635SCorey Minyard static void mem_cleanup(struct si_sm_io *io)
6958e27635SCorey Minyard {
7058e27635SCorey Minyard 	if (io->addr) {
7158e27635SCorey Minyard 		iounmap(io->addr);
7258e27635SCorey Minyard 		mem_region_cleanup(io, io->io_size);
7358e27635SCorey Minyard 	}
7458e27635SCorey Minyard }
7558e27635SCorey Minyard 
ipmi_si_mem_setup(struct si_sm_io * io)7658e27635SCorey Minyard int ipmi_si_mem_setup(struct si_sm_io *io)
7758e27635SCorey Minyard {
7858e27635SCorey Minyard 	unsigned long addr = io->addr_data;
7958e27635SCorey Minyard 	int           mapsize, idx;
8058e27635SCorey Minyard 
8158e27635SCorey Minyard 	if (!addr)
8258e27635SCorey Minyard 		return -ENODEV;
8358e27635SCorey Minyard 
8458e27635SCorey Minyard 	/*
8558e27635SCorey Minyard 	 * Figure out the actual readb/readw/readl/etc routine to use based
8658e27635SCorey Minyard 	 * upon the register size.
8758e27635SCorey Minyard 	 */
8858e27635SCorey Minyard 	switch (io->regsize) {
8958e27635SCorey Minyard 	case 1:
9058e27635SCorey Minyard 		io->inputb = intf_mem_inb;
9158e27635SCorey Minyard 		io->outputb = intf_mem_outb;
9258e27635SCorey Minyard 		break;
9358e27635SCorey Minyard 	case 2:
9458e27635SCorey Minyard 		io->inputb = intf_mem_inw;
9558e27635SCorey Minyard 		io->outputb = intf_mem_outw;
9658e27635SCorey Minyard 		break;
9758e27635SCorey Minyard 	case 4:
9858e27635SCorey Minyard 		io->inputb = intf_mem_inl;
9958e27635SCorey Minyard 		io->outputb = intf_mem_outl;
10058e27635SCorey Minyard 		break;
10158e27635SCorey Minyard #ifdef readq
10258e27635SCorey Minyard 	case 8:
10358e27635SCorey Minyard 		io->inputb = mem_inq;
10458e27635SCorey Minyard 		io->outputb = mem_outq;
10558e27635SCorey Minyard 		break;
10658e27635SCorey Minyard #endif
10758e27635SCorey Minyard 	default:
10858e27635SCorey Minyard 		dev_warn(io->dev, "Invalid register size: %d\n",
10958e27635SCorey Minyard 			 io->regsize);
11058e27635SCorey Minyard 		return -EINVAL;
11158e27635SCorey Minyard 	}
11258e27635SCorey Minyard 
11358e27635SCorey Minyard 	/*
11458e27635SCorey Minyard 	 * Some BIOSes reserve disjoint memory regions in their ACPI
11558e27635SCorey Minyard 	 * tables.  This causes problems when trying to request the
11658e27635SCorey Minyard 	 * entire region.  Therefore we must request each register
11758e27635SCorey Minyard 	 * separately.
11858e27635SCorey Minyard 	 */
11958e27635SCorey Minyard 	for (idx = 0; idx < io->io_size; idx++) {
12058e27635SCorey Minyard 		if (request_mem_region(addr + idx * io->regspacing,
121*104fb25fSCorey Minyard 				       io->regsize, SI_DEVICE_NAME) == NULL) {
12258e27635SCorey Minyard 			/* Undo allocations */
12358e27635SCorey Minyard 			mem_region_cleanup(io, idx);
12458e27635SCorey Minyard 			return -EIO;
12558e27635SCorey Minyard 		}
12658e27635SCorey Minyard 	}
12758e27635SCorey Minyard 
12858e27635SCorey Minyard 	/*
12958e27635SCorey Minyard 	 * Calculate the total amount of memory to claim.  This is an
13058e27635SCorey Minyard 	 * unusual looking calculation, but it avoids claiming any
13158e27635SCorey Minyard 	 * more memory than it has to.  It will claim everything
13258e27635SCorey Minyard 	 * between the first address to the end of the last full
13358e27635SCorey Minyard 	 * register.
13458e27635SCorey Minyard 	 */
13558e27635SCorey Minyard 	mapsize = ((io->io_size * io->regspacing)
13658e27635SCorey Minyard 		   - (io->regspacing - io->regsize));
13758e27635SCorey Minyard 	io->addr = ioremap(addr, mapsize);
13858e27635SCorey Minyard 	if (io->addr == NULL) {
13958e27635SCorey Minyard 		mem_region_cleanup(io, io->io_size);
14058e27635SCorey Minyard 		return -EIO;
14158e27635SCorey Minyard 	}
142401e7e88SYang Yingliang 
143401e7e88SYang Yingliang 	io->io_cleanup = mem_cleanup;
144401e7e88SYang Yingliang 
14558e27635SCorey Minyard 	return 0;
14658e27635SCorey Minyard }
147