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((u64)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 /* 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 } 147