linux/arch/powerpc/platforms/powernv/opal-lpc.c
<<
>>
Prefs
   1/*
   2 * PowerNV LPC bus handling.
   3 *
   4 * Copyright 2013 IBM Corp.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/of.h>
  14#include <linux/bug.h>
  15
  16#include <asm/machdep.h>
  17#include <asm/firmware.h>
  18#include <asm/xics.h>
  19#include <asm/opal.h>
  20
  21static int opal_lpc_chip_id = -1;
  22
  23static u8 opal_lpc_inb(unsigned long port)
  24{
  25        int64_t rc;
  26        uint32_t data;
  27
  28        if (opal_lpc_chip_id < 0 || port > 0xffff)
  29                return 0xff;
  30        rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 1);
  31        return rc ? 0xff : data;
  32}
  33
  34static __le16 __opal_lpc_inw(unsigned long port)
  35{
  36        int64_t rc;
  37        uint32_t data;
  38
  39        if (opal_lpc_chip_id < 0 || port > 0xfffe)
  40                return 0xffff;
  41        if (port & 1)
  42                return (__le16)opal_lpc_inb(port) << 8 | opal_lpc_inb(port + 1);
  43        rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 2);
  44        return rc ? 0xffff : data;
  45}
  46static u16 opal_lpc_inw(unsigned long port)
  47{
  48        return le16_to_cpu(__opal_lpc_inw(port));
  49}
  50
  51static __le32 __opal_lpc_inl(unsigned long port)
  52{
  53        int64_t rc;
  54        uint32_t data;
  55
  56        if (opal_lpc_chip_id < 0 || port > 0xfffc)
  57                return 0xffffffff;
  58        if (port & 3)
  59                return (__le32)opal_lpc_inb(port    ) << 24 |
  60                       (__le32)opal_lpc_inb(port + 1) << 16 |
  61                       (__le32)opal_lpc_inb(port + 2) <<  8 |
  62                               opal_lpc_inb(port + 3);
  63        rc = opal_lpc_read(opal_lpc_chip_id, OPAL_LPC_IO, port, &data, 4);
  64        return rc ? 0xffffffff : data;
  65}
  66
  67static u32 opal_lpc_inl(unsigned long port)
  68{
  69        return le32_to_cpu(__opal_lpc_inl(port));
  70}
  71
  72static void opal_lpc_outb(u8 val, unsigned long port)
  73{
  74        if (opal_lpc_chip_id < 0 || port > 0xffff)
  75                return;
  76        opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 1);
  77}
  78
  79static void __opal_lpc_outw(__le16 val, unsigned long port)
  80{
  81        if (opal_lpc_chip_id < 0 || port > 0xfffe)
  82                return;
  83        if (port & 1) {
  84                opal_lpc_outb(val >> 8, port);
  85                opal_lpc_outb(val     , port + 1);
  86                return;
  87        }
  88        opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 2);
  89}
  90
  91static void opal_lpc_outw(u16 val, unsigned long port)
  92{
  93        __opal_lpc_outw(cpu_to_le16(val), port);
  94}
  95
  96static void __opal_lpc_outl(__le32 val, unsigned long port)
  97{
  98        if (opal_lpc_chip_id < 0 || port > 0xfffc)
  99                return;
 100        if (port & 3) {
 101                opal_lpc_outb(val >> 24, port);
 102                opal_lpc_outb(val >> 16, port + 1);
 103                opal_lpc_outb(val >>  8, port + 2);
 104                opal_lpc_outb(val      , port + 3);
 105                return;
 106        }
 107        opal_lpc_write(opal_lpc_chip_id, OPAL_LPC_IO, port, val, 4);
 108}
 109
 110static void opal_lpc_outl(u32 val, unsigned long port)
 111{
 112        __opal_lpc_outl(cpu_to_le32(val), port);
 113}
 114
 115static void opal_lpc_insb(unsigned long p, void *b, unsigned long c)
 116{
 117        u8 *ptr = b;
 118
 119        while(c--)
 120                *(ptr++) = opal_lpc_inb(p);
 121}
 122
 123static void opal_lpc_insw(unsigned long p, void *b, unsigned long c)
 124{
 125        __le16 *ptr = b;
 126
 127        while(c--)
 128                *(ptr++) = __opal_lpc_inw(p);
 129}
 130
 131static void opal_lpc_insl(unsigned long p, void *b, unsigned long c)
 132{
 133        __le32 *ptr = b;
 134
 135        while(c--)
 136                *(ptr++) = __opal_lpc_inl(p);
 137}
 138
 139static void opal_lpc_outsb(unsigned long p, const void *b, unsigned long c)
 140{
 141        const u8 *ptr = b;
 142
 143        while(c--)
 144                opal_lpc_outb(*(ptr++), p);
 145}
 146
 147static void opal_lpc_outsw(unsigned long p, const void *b, unsigned long c)
 148{
 149        const __le16 *ptr = b;
 150
 151        while(c--)
 152                __opal_lpc_outw(*(ptr++), p);
 153}
 154
 155static void opal_lpc_outsl(unsigned long p, const void *b, unsigned long c)
 156{
 157        const __le32 *ptr = b;
 158
 159        while(c--)
 160                __opal_lpc_outl(*(ptr++), p);
 161}
 162
 163static const struct ppc_pci_io opal_lpc_io = {
 164        .inb    = opal_lpc_inb,
 165        .inw    = opal_lpc_inw,
 166        .inl    = opal_lpc_inl,
 167        .outb   = opal_lpc_outb,
 168        .outw   = opal_lpc_outw,
 169        .outl   = opal_lpc_outl,
 170        .insb   = opal_lpc_insb,
 171        .insw   = opal_lpc_insw,
 172        .insl   = opal_lpc_insl,
 173        .outsb  = opal_lpc_outsb,
 174        .outsw  = opal_lpc_outsw,
 175        .outsl  = opal_lpc_outsl,
 176};
 177
 178void opal_lpc_init(void)
 179{
 180        struct device_node *np;
 181
 182        /*
 183         * Look for a Power8 LPC bus tagged as "primary",
 184         * we currently support only one though the OPAL APIs
 185         * support any number.
 186         */
 187        for_each_compatible_node(np, NULL, "ibm,power8-lpc") {
 188                if (!of_device_is_available(np))
 189                        continue;
 190                if (!of_get_property(np, "primary", NULL))
 191                        continue;
 192                opal_lpc_chip_id = of_get_ibm_chip_id(np);
 193                break;
 194        }
 195        if (opal_lpc_chip_id < 0)
 196                return;
 197
 198        /* Setup special IO ops */
 199        ppc_pci_io = opal_lpc_io;
 200        isa_io_special = true;
 201
 202        pr_info("OPAL: Power8 LPC bus found, chip ID %d\n", opal_lpc_chip_id);
 203}
 204