147 lines · 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 readq45static 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#endif57 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 based86 * 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 readq102 case 8:103 io->inputb = mem_inq;104 io->outputb = mem_outq;105 break;106#endif107 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 ACPI115 * tables. This causes problems when trying to request the116 * entire region. Therefore we must request each register117 * 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 an130 * unusual looking calculation, but it avoids claiming any131 * more memory than it has to. It will claim everything132 * between the first address to the end of the last full133 * 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}147