diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-15 15:12:28 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-15 15:12:28 -0800 |
commit | 6363b3f3ac5be096d08c8c504128befa0c033529 (patch) | |
tree | 8bef82ead96f39bc09f4d1a1fe17dfa5d0c39d49 /drivers/char/ipmi/ipmi_si_mem_io.c | |
parent | Merge tag 'pci-v4.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci (diff) | |
parent | Merge branch 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux into for-next (diff) | |
download | linux-dev-6363b3f3ac5be096d08c8c504128befa0c033529.tar.xz linux-dev-6363b3f3ac5be096d08c8c504128befa0c033529.zip |
Merge tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi
Pull IPMI updates from Corey Minyard:
"This is a fairly large rework of the IPMI code, along with a bunch of
smaller fixes. The major changes have been in the next tree for a
couple of months, so they should be good to do in.
- Some users had IPMI systems where the GUID of the IPMI controller
could change. So rescanning of the GUID was added. The naming of
some sysfs things was dependent on the GUID, however, so this
resulted in the sysfs interface code in IPMI changing to remove
that dependency and name the IPMI BMCs like other sysfs devices.
- The ipmi_si_intf.c code was fairly bloated with all the different
discovery methods (PCI, ACPI, SMBIOS, OF, platform, module
parameters, hot add). The structure of how the interfaces were
added was redone to make them more modular, then the individual
methods were pulled out into their own files"
* tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi: (48 commits)
ipmi_si: Delete an error message for a failed memory allocation in try_smi_init()
ipmi_si: fix memory leak on new_smi
ipmi: remove redundant initialization of bmc
ipmi: pr_err() strings should end with newlines
ipmi: Clean up some print operations
ipmi: Make the DMI probe into a generic platform probe
ipmi: Make the IPMI proc interface configurable
ipmi_ssif: Add device attrs for the things in proc
ipmi_si: Add device attrs for the things in proc
ipmi_si: remove ipmi_smi_alloc() function
ipmi_si: Move port and mem I/O handling to their own files
ipmi_si: Get rid of unused spacing and port fields
ipmi_si: Move PARISC handling to another file
ipmi_si: Move PCI setup to another file
ipmi_si: Move platform device handling to another file
ipmi_si: Move hardcode handling to a separate file.
ipmi_si: Move the hotmod handling to another file.
ipmi_si: Change ipmi_si_add_smi() to take just I/O info
ipmi_si: Move io setup into io structure
ipmi_si: Move irq setup handling into the io struct
...
Diffstat (limited to 'drivers/char/ipmi/ipmi_si_mem_io.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_mem_io.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c new file mode 100644 index 000000000000..8796396ecd0f --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -0,0 +1,144 @@ + +#include <linux/io.h> +#include "ipmi_si.h" + +static unsigned char intf_mem_inb(const struct si_sm_io *io, + unsigned int offset) +{ + return readb((io->addr)+(offset * io->regspacing)); +} + +static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeb(b, (io->addr)+(offset * io->regspacing)); +} + +static unsigned char intf_mem_inw(const struct si_sm_io *io, + unsigned int offset) +{ + return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeb(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} + +static unsigned char intf_mem_inl(const struct si_sm_io *io, + unsigned int offset) +{ + return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writel(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} + +#ifdef readq +static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset) +{ + return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void mem_outq(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} +#endif + +static void mem_region_cleanup(struct si_sm_io *io, int num) +{ + unsigned long addr = io->addr_data; + int idx; + + for (idx = 0; idx < num; idx++) + release_mem_region(addr + idx * io->regspacing, + io->regsize); +} + +static void mem_cleanup(struct si_sm_io *io) +{ + if (io->addr) { + iounmap(io->addr); + mem_region_cleanup(io, io->io_size); + } +} + +int ipmi_si_mem_setup(struct si_sm_io *io) +{ + unsigned long addr = io->addr_data; + int mapsize, idx; + + if (!addr) + return -ENODEV; + + io->io_cleanup = mem_cleanup; + + /* + * Figure out the actual readb/readw/readl/etc routine to use based + * upon the register size. + */ + switch (io->regsize) { + case 1: + io->inputb = intf_mem_inb; + io->outputb = intf_mem_outb; + break; + case 2: + io->inputb = intf_mem_inw; + io->outputb = intf_mem_outw; + break; + case 4: + io->inputb = intf_mem_inl; + io->outputb = intf_mem_outl; + break; +#ifdef readq + case 8: + io->inputb = mem_inq; + io->outputb = mem_outq; + break; +#endif + default: + dev_warn(io->dev, "Invalid register size: %d\n", + io->regsize); + return -EINVAL; + } + + /* + * Some BIOSes reserve disjoint memory regions in their ACPI + * tables. This causes problems when trying to request the + * entire region. Therefore we must request each register + * separately. + */ + for (idx = 0; idx < io->io_size; idx++) { + if (request_mem_region(addr + idx * io->regspacing, + io->regsize, DEVICE_NAME) == NULL) { + /* Undo allocations */ + mem_region_cleanup(io, idx); + return -EIO; + } + } + + /* + * Calculate the total amount of memory to claim. This is an + * unusual looking calculation, but it avoids claiming any + * more memory than it has to. It will claim everything + * between the first address to the end of the last full + * register. + */ + mapsize = ((io->io_size * io->regspacing) + - (io->regspacing - io->regsize)); + io->addr = ioremap(addr, mapsize); + if (io->addr == NULL) { + mem_region_cleanup(io, io->io_size); + return -EIO; + } + return 0; +} |