162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/io.h>
462306a36Sopenharmony_ci#include "ipmi_si.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistatic unsigned char intf_mem_inb(const struct si_sm_io *io,
762306a36Sopenharmony_ci				  unsigned int offset)
862306a36Sopenharmony_ci{
962306a36Sopenharmony_ci	return readb((io->addr)+(offset * io->regspacing));
1062306a36Sopenharmony_ci}
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
1362306a36Sopenharmony_ci			  unsigned char b)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	writeb(b, (io->addr)+(offset * io->regspacing));
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic unsigned char intf_mem_inw(const struct si_sm_io *io,
1962306a36Sopenharmony_ci				  unsigned int offset)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
2262306a36Sopenharmony_ci		& 0xff;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
2662306a36Sopenharmony_ci			  unsigned char b)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic unsigned char intf_mem_inl(const struct si_sm_io *io,
3262306a36Sopenharmony_ci				  unsigned int offset)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
3562306a36Sopenharmony_ci		& 0xff;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
3962306a36Sopenharmony_ci			  unsigned char b)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#ifdef readq
4562306a36Sopenharmony_cistatic unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
4862306a36Sopenharmony_ci		& 0xff;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void mem_outq(const struct si_sm_io *io, unsigned int offset,
5262306a36Sopenharmony_ci		     unsigned char b)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void mem_region_cleanup(struct si_sm_io *io, int num)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	unsigned long addr = io->addr_data;
6162306a36Sopenharmony_ci	int idx;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	for (idx = 0; idx < num; idx++)
6462306a36Sopenharmony_ci		release_mem_region(addr + idx * io->regspacing,
6562306a36Sopenharmony_ci				   io->regsize);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void mem_cleanup(struct si_sm_io *io)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	if (io->addr) {
7162306a36Sopenharmony_ci		iounmap(io->addr);
7262306a36Sopenharmony_ci		mem_region_cleanup(io, io->io_size);
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint ipmi_si_mem_setup(struct si_sm_io *io)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	unsigned long addr = io->addr_data;
7962306a36Sopenharmony_ci	int           mapsize, idx;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (!addr)
8262306a36Sopenharmony_ci		return -ENODEV;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * Figure out the actual readb/readw/readl/etc routine to use based
8662306a36Sopenharmony_ci	 * upon the register size.
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	switch (io->regsize) {
8962306a36Sopenharmony_ci	case 1:
9062306a36Sopenharmony_ci		io->inputb = intf_mem_inb;
9162306a36Sopenharmony_ci		io->outputb = intf_mem_outb;
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci	case 2:
9462306a36Sopenharmony_ci		io->inputb = intf_mem_inw;
9562306a36Sopenharmony_ci		io->outputb = intf_mem_outw;
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	case 4:
9862306a36Sopenharmony_ci		io->inputb = intf_mem_inl;
9962306a36Sopenharmony_ci		io->outputb = intf_mem_outl;
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci#ifdef readq
10262306a36Sopenharmony_ci	case 8:
10362306a36Sopenharmony_ci		io->inputb = mem_inq;
10462306a36Sopenharmony_ci		io->outputb = mem_outq;
10562306a36Sopenharmony_ci		break;
10662306a36Sopenharmony_ci#endif
10762306a36Sopenharmony_ci	default:
10862306a36Sopenharmony_ci		dev_warn(io->dev, "Invalid register size: %d\n",
10962306a36Sopenharmony_ci			 io->regsize);
11062306a36Sopenharmony_ci		return -EINVAL;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/*
11462306a36Sopenharmony_ci	 * Some BIOSes reserve disjoint memory regions in their ACPI
11562306a36Sopenharmony_ci	 * tables.  This causes problems when trying to request the
11662306a36Sopenharmony_ci	 * entire region.  Therefore we must request each register
11762306a36Sopenharmony_ci	 * separately.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	for (idx = 0; idx < io->io_size; idx++) {
12062306a36Sopenharmony_ci		if (request_mem_region(addr + idx * io->regspacing,
12162306a36Sopenharmony_ci				       io->regsize, SI_DEVICE_NAME) == NULL) {
12262306a36Sopenharmony_ci			/* Undo allocations */
12362306a36Sopenharmony_ci			mem_region_cleanup(io, idx);
12462306a36Sopenharmony_ci			return -EIO;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * Calculate the total amount of memory to claim.  This is an
13062306a36Sopenharmony_ci	 * unusual looking calculation, but it avoids claiming any
13162306a36Sopenharmony_ci	 * more memory than it has to.  It will claim everything
13262306a36Sopenharmony_ci	 * between the first address to the end of the last full
13362306a36Sopenharmony_ci	 * register.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	mapsize = ((io->io_size * io->regspacing)
13662306a36Sopenharmony_ci		   - (io->regspacing - io->regsize));
13762306a36Sopenharmony_ci	io->addr = ioremap(addr, mapsize);
13862306a36Sopenharmony_ci	if (io->addr == NULL) {
13962306a36Sopenharmony_ci		mem_region_cleanup(io, io->io_size);
14062306a36Sopenharmony_ci		return -EIO;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	io->io_cleanup = mem_cleanup;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
147