18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/io.h>
48c2ecf20Sopenharmony_ci#include "ipmi_si.h"
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_cistatic unsigned char intf_mem_inb(const struct si_sm_io *io,
78c2ecf20Sopenharmony_ci				  unsigned int offset)
88c2ecf20Sopenharmony_ci{
98c2ecf20Sopenharmony_ci	return readb((io->addr)+(offset * io->regspacing));
108c2ecf20Sopenharmony_ci}
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
138c2ecf20Sopenharmony_ci			  unsigned char b)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	writeb(b, (io->addr)+(offset * io->regspacing));
168c2ecf20Sopenharmony_ci}
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic unsigned char intf_mem_inw(const struct si_sm_io *io,
198c2ecf20Sopenharmony_ci				  unsigned int offset)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
228c2ecf20Sopenharmony_ci		& 0xff;
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
268c2ecf20Sopenharmony_ci			  unsigned char b)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic unsigned char intf_mem_inl(const struct si_sm_io *io,
328c2ecf20Sopenharmony_ci				  unsigned int offset)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
358c2ecf20Sopenharmony_ci		& 0xff;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
398c2ecf20Sopenharmony_ci			  unsigned char b)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#ifdef readq
458c2ecf20Sopenharmony_cistatic unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
488c2ecf20Sopenharmony_ci		& 0xff;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void mem_outq(const struct si_sm_io *io, unsigned int offset,
528c2ecf20Sopenharmony_ci		     unsigned char b)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci#endif
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void mem_region_cleanup(struct si_sm_io *io, int num)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned long addr = io->addr_data;
618c2ecf20Sopenharmony_ci	int idx;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	for (idx = 0; idx < num; idx++)
648c2ecf20Sopenharmony_ci		release_mem_region(addr + idx * io->regspacing,
658c2ecf20Sopenharmony_ci				   io->regsize);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void mem_cleanup(struct si_sm_io *io)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (io->addr) {
718c2ecf20Sopenharmony_ci		iounmap(io->addr);
728c2ecf20Sopenharmony_ci		mem_region_cleanup(io, io->io_size);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciint ipmi_si_mem_setup(struct si_sm_io *io)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	unsigned long addr = io->addr_data;
798c2ecf20Sopenharmony_ci	int           mapsize, idx;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (!addr)
828c2ecf20Sopenharmony_ci		return -ENODEV;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/*
858c2ecf20Sopenharmony_ci	 * Figure out the actual readb/readw/readl/etc routine to use based
868c2ecf20Sopenharmony_ci	 * upon the register size.
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	switch (io->regsize) {
898c2ecf20Sopenharmony_ci	case 1:
908c2ecf20Sopenharmony_ci		io->inputb = intf_mem_inb;
918c2ecf20Sopenharmony_ci		io->outputb = intf_mem_outb;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	case 2:
948c2ecf20Sopenharmony_ci		io->inputb = intf_mem_inw;
958c2ecf20Sopenharmony_ci		io->outputb = intf_mem_outw;
968c2ecf20Sopenharmony_ci		break;
978c2ecf20Sopenharmony_ci	case 4:
988c2ecf20Sopenharmony_ci		io->inputb = intf_mem_inl;
998c2ecf20Sopenharmony_ci		io->outputb = intf_mem_outl;
1008c2ecf20Sopenharmony_ci		break;
1018c2ecf20Sopenharmony_ci#ifdef readq
1028c2ecf20Sopenharmony_ci	case 8:
1038c2ecf20Sopenharmony_ci		io->inputb = mem_inq;
1048c2ecf20Sopenharmony_ci		io->outputb = mem_outq;
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci#endif
1078c2ecf20Sopenharmony_ci	default:
1088c2ecf20Sopenharmony_ci		dev_warn(io->dev, "Invalid register size: %d\n",
1098c2ecf20Sopenharmony_ci			 io->regsize);
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * Some BIOSes reserve disjoint memory regions in their ACPI
1158c2ecf20Sopenharmony_ci	 * tables.  This causes problems when trying to request the
1168c2ecf20Sopenharmony_ci	 * entire region.  Therefore we must request each register
1178c2ecf20Sopenharmony_ci	 * separately.
1188c2ecf20Sopenharmony_ci	 */
1198c2ecf20Sopenharmony_ci	for (idx = 0; idx < io->io_size; idx++) {
1208c2ecf20Sopenharmony_ci		if (request_mem_region(addr + idx * io->regspacing,
1218c2ecf20Sopenharmony_ci				       io->regsize, SI_DEVICE_NAME) == NULL) {
1228c2ecf20Sopenharmony_ci			/* Undo allocations */
1238c2ecf20Sopenharmony_ci			mem_region_cleanup(io, idx);
1248c2ecf20Sopenharmony_ci			return -EIO;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * Calculate the total amount of memory to claim.  This is an
1308c2ecf20Sopenharmony_ci	 * unusual looking calculation, but it avoids claiming any
1318c2ecf20Sopenharmony_ci	 * more memory than it has to.  It will claim everything
1328c2ecf20Sopenharmony_ci	 * between the first address to the end of the last full
1338c2ecf20Sopenharmony_ci	 * register.
1348c2ecf20Sopenharmony_ci	 */
1358c2ecf20Sopenharmony_ci	mapsize = ((io->io_size * io->regspacing)
1368c2ecf20Sopenharmony_ci		   - (io->regspacing - io->regsize));
1378c2ecf20Sopenharmony_ci	io->addr = ioremap(addr, mapsize);
1388c2ecf20Sopenharmony_ci	if (io->addr == NULL) {
1398c2ecf20Sopenharmony_ci		mem_region_cleanup(io, io->io_size);
1408c2ecf20Sopenharmony_ci		return -EIO;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	io->io_cleanup = mem_cleanup;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
147