/*
* Copyright 2014 IBM Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/pci_regs.h>
#include <linux/pci_ids.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/pci.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <asm/opal.h>
#include <asm/msi_bitmap.h>
#include <asm/pci-bridge.h> /* for struct pci_controller */
#include <asm/pnv-pci.h>
#include <asm/io.h>
#include "cxl.h"
#define CXL_PCI_VSEC_ID 0x1280
#define CXL_VSEC_MIN_SIZE 0x80
#define CXL_READ_VSEC_LENGTH(dev, vsec, dest) \
{ \
pci_read_config_word(dev, vsec + 0x6, dest); \
*dest >>= 4; \
}
#define CXL_READ_VSEC_NAFUS(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0x8, dest)
#define CXL_READ_VSEC_STATUS(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0x9, dest)
#define CXL_STATUS_SECOND_PORT 0x80
#define CXL_STATUS_MSI_X_FULL 0x40
#define CXL_STATUS_MSI_X_SINGLE 0x20
#define CXL_STATUS_FLASH_RW 0x08
#define CXL_STATUS_FLASH_RO 0x04
#define CXL_STATUS_LOADABLE_AFU 0x02
#define CXL_STATUS_LOADABLE_PSL 0x01
/* If we see these features we won't try to use the card */
#define CXL_UNSUPPORTED_FEATURES \
(CXL_STATUS_MSI_X_FULL | CXL_STATUS_MSI_X_SINGLE)
#define CXL_READ_VSEC_MODE_CONTROL(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0xa, dest)
#define CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val) \
pci_write_config_byte(dev, vsec + 0xa, val)
#define CXL_VSEC_PROTOCOL_MASK 0xe0
#define CXL_VSEC_PROTOCOL_1024TB 0x80
#define CXL_VSEC_PROTOCOL_512TB 0x40
#define CXL_VSEC_PROTOCOL_256TB 0x20 /* Power 8 uses this */
#define CXL_VSEC_PROTOCOL_ENABLE 0x01
#define CXL_READ_VSEC_PSL_REVISION(dev, vsec, dest) \
pci_read_config_word(dev, vsec + 0xc, dest)
#define CXL_READ_VSEC_CAIA_MINOR(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0xe, dest)
#define CXL_READ_VSEC_CAIA_MAJOR(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0xf, dest)
#define CXL_READ_VSEC_BASE_IMAGE(dev, vsec, dest) \
pci_read_config_word(dev, vsec + 0x10, dest)
#define CXL_READ_VSEC_IMAGE_STATE(dev, vsec, dest) \
pci_read_config_byte(dev, vsec + 0x13, dest)
#define CXL_WRITE_VSEC_IMAGE_STATE(dev, vsec, val) \
pci_write_config_byte(dev, vsec + 0x13, val)
#define CXL_VSEC_USER_IMAGE_LOADED 0x80 /* RO */
#define CXL_VSEC_PERST_LOADS_IMAGE 0x20 /* RW */
#define CXL_VSEC_PERST_SELECT_USER 0x10 /* RW */
#define CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, dest) \
pci_read_config_dword(dev, vsec + 0x20, dest)
#define CXL_READ_VSEC_AFU_DESC_SIZE(dev, vsec, dest) \
pci_read_config_dword(dev, vsec + 0x24, dest)
#define CXL_READ_VSEC_PS_OFF(dev, vsec, dest) \
pci_read_config_dword(dev, vsec + 0x28, dest)
#define CXL_READ_VSEC_PS_SIZE(dev, vsec, dest) \
pci_read_config_dword(dev, vsec + 0x2c, dest)
/* This works a little different than the p1/p2 register accesses to make it
* easier to pull out individual fields */
#define AFUD_READ(afu, off) in_be64(afu->afu_desc_mmio + off)
#define AFUD_READ_LE(afu, off) in_le64(afu->afu_desc_mmio + off)
#define EXTRACT_PPC_BIT(val, bit) (!!(val & PPC_BIT(bit)))
#define EXTRACT_PPC_BITS(val, bs, be) ((val & PPC_BITMASK(bs, be)) >> PPC_BITLSHIFT(be))
#define AFUD_READ_INFO(afu) AFUD_READ(afu, 0x0)
#define AFUD_NUM_INTS_PER_PROC(val) EXTRACT_PPC_BITS(val, 0, 15)
#define AFUD_NUM_PROCS(val) EXTRACT_PPC_BITS(val, 16, 31)
#define AFUD_NUM_CRS(val) EXTRACT_PPC_BITS(val, 32, 47)
#define AFUD_MULTIMODE(val) EXTRACT_PPC_BIT(val, 48)
#define AFUD_PUSH_BLOCK_TRANSFER(val) EXTRACT_PPC_BIT(val, 55)
#define AFUD_DEDICATED_PROCESS(val) EXTRACT_PPC_BIT(val, 59)
#define AFUD_AFU_DIRECTED(val) EXTRACT_PPC_BIT(val, 61)
#define AFUD_TIME_SLICED(val) EXTRACT_PPC_BIT(val, 63)
#define AFUD_READ_CR(afu) AFUD_READ(afu, 0x20)
#define AFUD_CR_LEN(val) EXTRACT_PPC_BITS(val, 8, 63)
#define AFUD_READ_CR_OFF(afu) AFUD_READ(afu, 0x28)
#define AFUD_READ_PPPSA(afu) AFUD_READ(afu, 0x30)
#define AFUD_PPPSA_PP(val) EXTRACT_PPC_BIT(val, 6)
#define AFUD_PPPSA_PSA(val) EXTRACT_PPC_BIT(val, 7)
#define AFUD_PPPSA_LEN(val) EXTRACT_PPC_BITS(val, 8, 63)
#define AFUD_READ_PPPSA_OFF(afu) AFUD_READ(afu, 0x38)
#define AFUD_READ_EB(afu) AFUD_READ(afu, 0x40)
#define AFUD_EB_LEN(val) EXTRACT_PPC_BITS(val, 8, 63)
#define AFUD_READ_EB_OFF(afu) AFUD_READ(afu, 0x48)
u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off)
{
u64 aligned_off = off & ~0x3L;
u32 val;
val = cxl_afu_cr_read32(afu, cr, aligned_off);
return (val >> ((off & 0x2) * 8)) & 0xffff;
}
u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off)
{
u64 aligned_off = off & ~0x3L;
u32 val;
val = cxl_afu_cr_read32(afu, cr, aligned_off);
return (val >> ((off & 0x3) * 8)) & 0xff;
}
static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), },
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), },
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), },
{ PCI_DEVICE_CLASS(0x120000, ~0), },
{ }
};
MODULE_DEVICE_TABLE(pci, cxl_pci_tbl);
/*
* Mostly using these wrappers to avoid confusion:
* priv 1 is BAR2, while priv 2 is BAR0
*/
static inline resource_size_t p1_base(struct pci_dev *dev)
{
return pci_resource_start(dev, 2);
}
static inline resource_size_t p1_size(struct pci_dev *dev)
{
return pci_resource_len(dev, 2);
}
static