diff options
author | 2017-08-03 14:05:36 -0700 | |
---|---|---|
committer | 2017-08-04 05:58:23 +0200 | |
commit | 3e5d2bd19138d6683f69de26a34d7eb9ab3823ed (patch) | |
tree | a2083af2752e985c1b66260eebe913811b55838e /drivers/edac/pnd2_edac.c | |
parent | EDAC, sb_edac: Classify memory mirroring modes (diff) | |
download | linux-3e5d2bd19138d6683f69de26a34d7eb9ab3823ed.tar.gz linux-3e5d2bd19138d6683f69de26a34d7eb9ab3823ed.tar.bz2 linux-3e5d2bd19138d6683f69de26a34d7eb9ab3823ed.zip |
EDAC, pnd2: Build in a minimal sideband driver for Apollo Lake
I've been waing a long time for the generic sideband driver to
appear. Patience has run out, so include the minimum here to
just read registers.
Signed-off-by: Tony Luck <tony.luck@intel.com>
Cc: Aristeu Rozanski <arozansk@redhat.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Patrick Geary <patrickg@supermicro.com>
Cc: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: http://lkml.kernel.org/r/20170803210536.5662-1-tony.luck@intel.com
Signed-off-by: Borislav Petkov <bp@suse.de>
Diffstat (limited to 'drivers/edac/pnd2_edac.c')
-rw-r--r-- | drivers/edac/pnd2_edac.c | 88 |
1 files changed, 59 insertions, 29 deletions
diff --git a/drivers/edac/pnd2_edac.c b/drivers/edac/pnd2_edac.c index 8e599490f6de..a3180a87ff96 100644 --- a/drivers/edac/pnd2_edac.c +++ b/drivers/edac/pnd2_edac.c @@ -129,42 +129,66 @@ static struct mem_ctl_info *pnd2_mci; #define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) #define U64_LSHIFT(val, s) ((u64)(val) << (s)) -#ifdef CONFIG_X86_INTEL_SBI_APL -#include "linux/platform_data/sbi_apl.h" -static int sbi_send(int port, int off, int op, u32 *data) +/* + * On Apollo Lake we access memory controller registers via a + * side-band mailbox style interface in a hidden PCI device + * configuration space. + */ +static struct pci_bus *p2sb_bus; +#define P2SB_DEVFN PCI_DEVFN(0xd, 0) +#define P2SB_ADDR_OFF 0xd0 +#define P2SB_DATA_OFF 0xd4 +#define P2SB_STAT_OFF 0xd8 +#define P2SB_ROUT_OFF 0xda +#define P2SB_EADD_OFF 0xdc +#define P2SB_HIDE_OFF 0xe1 + +#define P2SB_BUSY 1 + +#define P2SB_READ(size, off, ptr) \ + pci_bus_read_config_##size(p2sb_bus, P2SB_DEVFN, off, ptr) +#define P2SB_WRITE(size, off, val) \ + pci_bus_write_config_##size(p2sb_bus, P2SB_DEVFN, off, val) + +static bool p2sb_is_busy(u16 *status) { - struct sbi_apl_message sbi_arg; - int ret, read = 0; + P2SB_READ(word, P2SB_STAT_OFF, status); - memset(&sbi_arg, 0, sizeof(sbi_arg)); + return !!(*status & P2SB_BUSY); +} - if (op == 0 || op == 4 || op == 6) - read = 1; - else - sbi_arg.data = *data; +static int _apl_rd_reg(int port, int off, int op, u32 *data) +{ + int retries = 0xff, ret; + u16 status; + + P2SB_WRITE(byte, P2SB_HIDE_OFF, 0); + + if (p2sb_is_busy(&status)) { + ret = -EAGAIN; + goto out; + } - sbi_arg.opcode = op; - sbi_arg.port_address = port; - sbi_arg.register_offset = off; - ret = sbi_apl_commit(&sbi_arg); - if (ret || sbi_arg.status) - edac_dbg(2, "sbi_send status=%d ret=%d data=%x\n", - sbi_arg.status, ret, sbi_arg.data); + P2SB_WRITE(dword, P2SB_ADDR_OFF, (port << 24) | off); + P2SB_WRITE(dword, P2SB_DATA_OFF, 0); + P2SB_WRITE(dword, P2SB_EADD_OFF, 0); + P2SB_WRITE(word, P2SB_ROUT_OFF, 0); + P2SB_WRITE(word, P2SB_STAT_OFF, (op << 8) | P2SB_BUSY); - if (ret == 0) - ret = sbi_arg.status; + while (p2sb_is_busy(&status)) { + if (retries-- == 0) { + ret = -EBUSY; + goto out; + } + } - if (ret == 0 && read) - *data = sbi_arg.data; + P2SB_READ(dword, P2SB_DATA_OFF, data); + ret = (status >> 1) & 0x3; +out: + P2SB_WRITE(byte, P2SB_HIDE_OFF, 1); return ret; } -#else -static int sbi_send(int port, int off, int op, u32 *data) -{ - return -EUNATCH; -} -#endif static int apl_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) { @@ -173,10 +197,10 @@ static int apl_rd_reg(int port, int off, int op, void *data, size_t sz, char *na edac_dbg(2, "Read %s port=%x off=%x op=%x\n", name, port, off, op); switch (sz) { case 8: - ret = sbi_send(port, off + 4, op, (u32 *)(data + 4)); + ret = _apl_rd_reg(port, off + 4, op, (u32 *)(data + 4)); /* fall through */ case 4: - ret |= sbi_send(port, off, op, (u32 *)data); + ret |= _apl_rd_reg(port, off, op, (u32 *)data); pnd2_printk(KERN_DEBUG, "%s=%x%08x ret=%d\n", name, sz == 8 ? *((u32 *)(data + 4)) : 0, *((u32 *)data), ret); break; @@ -1515,6 +1539,12 @@ static int __init pnd2_init(void) ops = (struct dunit_ops *)id->driver_data; + if (ops->type == APL) { + p2sb_bus = pci_find_bus(0, 0); + if (!p2sb_bus) + return -ENODEV; + } + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); |