File Index
Files and directories
Sign in
FILE

ipmi_si_mem_io.c

File preview with syntax highlighting.

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