ipmi_si_mem_io.c (3401B)
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}