1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <linux/io.h> 4 #include "ipmi_si.h" 5 6 static 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 12 static 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 18 static 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 25 static 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 31 static 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 38 static 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 45 static 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 51 static void mem_outq(const struct si_sm_io *io, unsigned int offset, 52 unsigned char b) 53 { 54 writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); 55 } 56 #endif 57 58 static 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 68 static 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 76 int 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 io->io_cleanup = mem_cleanup; 85 86 /* 87 * Figure out the actual readb/readw/readl/etc routine to use based 88 * upon the register size. 89 */ 90 switch (io->regsize) { 91 case 1: 92 io->inputb = intf_mem_inb; 93 io->outputb = intf_mem_outb; 94 break; 95 case 2: 96 io->inputb = intf_mem_inw; 97 io->outputb = intf_mem_outw; 98 break; 99 case 4: 100 io->inputb = intf_mem_inl; 101 io->outputb = intf_mem_outl; 102 break; 103 #ifdef readq 104 case 8: 105 io->inputb = mem_inq; 106 io->outputb = mem_outq; 107 break; 108 #endif 109 default: 110 dev_warn(io->dev, "Invalid register size: %d\n", 111 io->regsize); 112 return -EINVAL; 113 } 114 115 /* 116 * Some BIOSes reserve disjoint memory regions in their ACPI 117 * tables. This causes problems when trying to request the 118 * entire region. Therefore we must request each register 119 * separately. 120 */ 121 for (idx = 0; idx < io->io_size; idx++) { 122 if (request_mem_region(addr + idx * io->regspacing, 123 io->regsize, DEVICE_NAME) == NULL) { 124 /* Undo allocations */ 125 mem_region_cleanup(io, idx); 126 return -EIO; 127 } 128 } 129 130 /* 131 * Calculate the total amount of memory to claim. This is an 132 * unusual looking calculation, but it avoids claiming any 133 * more memory than it has to. It will claim everything 134 * between the first address to the end of the last full 135 * register. 136 */ 137 mapsize = ((io->io_size * io->regspacing) 138 - (io->regspacing - io->regsize)); 139 io->addr = ioremap(addr, mapsize); 140 if (io->addr == NULL) { 141 mem_region_cleanup(io, io->io_size); 142 return -EIO; 143 } 144 return 0; 145 } 146