Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 #include <linux/io.h>
0004 #include "ipmi_si.h"
0005 
0006 static unsigned char intf_mem_inb(const struct si_sm_io *io,
0007                   unsigned int offset)
0008 {
0009     return readb((io->addr)+(offset * io->regspacing));
0010 }
0011 
0012 static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
0013               unsigned char b)
0014 {
0015     writeb(b, (io->addr)+(offset * io->regspacing));
0016 }
0017 
0018 static unsigned char intf_mem_inw(const struct si_sm_io *io,
0019                   unsigned int offset)
0020 {
0021     return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
0022         & 0xff;
0023 }
0024 
0025 static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
0026               unsigned char b)
0027 {
0028     writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
0029 }
0030 
0031 static unsigned char intf_mem_inl(const struct si_sm_io *io,
0032                   unsigned int offset)
0033 {
0034     return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
0035         & 0xff;
0036 }
0037 
0038 static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
0039               unsigned char b)
0040 {
0041     writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
0042 }
0043 
0044 #ifdef readq
0045 static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
0046 {
0047     return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
0048         & 0xff;
0049 }
0050 
0051 static void mem_outq(const struct si_sm_io *io, unsigned int offset,
0052              unsigned char b)
0053 {
0054     writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
0055 }
0056 #endif
0057 
0058 static void mem_region_cleanup(struct si_sm_io *io, int num)
0059 {
0060     unsigned long addr = io->addr_data;
0061     int idx;
0062 
0063     for (idx = 0; idx < num; idx++)
0064         release_mem_region(addr + idx * io->regspacing,
0065                    io->regsize);
0066 }
0067 
0068 static void mem_cleanup(struct si_sm_io *io)
0069 {
0070     if (io->addr) {
0071         iounmap(io->addr);
0072         mem_region_cleanup(io, io->io_size);
0073     }
0074 }
0075 
0076 int ipmi_si_mem_setup(struct si_sm_io *io)
0077 {
0078     unsigned long addr = io->addr_data;
0079     int           mapsize, idx;
0080 
0081     if (!addr)
0082         return -ENODEV;
0083 
0084     /*
0085      * Figure out the actual readb/readw/readl/etc routine to use based
0086      * upon the register size.
0087      */
0088     switch (io->regsize) {
0089     case 1:
0090         io->inputb = intf_mem_inb;
0091         io->outputb = intf_mem_outb;
0092         break;
0093     case 2:
0094         io->inputb = intf_mem_inw;
0095         io->outputb = intf_mem_outw;
0096         break;
0097     case 4:
0098         io->inputb = intf_mem_inl;
0099         io->outputb = intf_mem_outl;
0100         break;
0101 #ifdef readq
0102     case 8:
0103         io->inputb = mem_inq;
0104         io->outputb = mem_outq;
0105         break;
0106 #endif
0107     default:
0108         dev_warn(io->dev, "Invalid register size: %d\n",
0109              io->regsize);
0110         return -EINVAL;
0111     }
0112 
0113     /*
0114      * Some BIOSes reserve disjoint memory regions in their ACPI
0115      * tables.  This causes problems when trying to request the
0116      * entire region.  Therefore we must request each register
0117      * separately.
0118      */
0119     for (idx = 0; idx < io->io_size; idx++) {
0120         if (request_mem_region(addr + idx * io->regspacing,
0121                        io->regsize, SI_DEVICE_NAME) == NULL) {
0122             /* Undo allocations */
0123             mem_region_cleanup(io, idx);
0124             return -EIO;
0125         }
0126     }
0127 
0128     /*
0129      * Calculate the total amount of memory to claim.  This is an
0130      * unusual looking calculation, but it avoids claiming any
0131      * more memory than it has to.  It will claim everything
0132      * between the first address to the end of the last full
0133      * register.
0134      */
0135     mapsize = ((io->io_size * io->regspacing)
0136            - (io->regspacing - io->regsize));
0137     io->addr = ioremap(addr, mapsize);
0138     if (io->addr == NULL) {
0139         mem_region_cleanup(io, io->io_size);
0140         return -EIO;
0141     }
0142 
0143     io->io_cleanup = mem_cleanup;
0144 
0145     return 0;
0146 }