linux/drivers/char/ipmi/ipmi_si_mem_io.c
<<
>>
Prefs
   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}
 147