numam-dpdk/drivers/raw/ifpga/base/opae_intel_max10.c
Tianfei Zhang 4a19f89104 raw/ifpga/base: support multiple cards
In PAC N3000 card, there is one MAX10 chip in each card, and
all of the sensors are connected to MAX10 chip. To support multiple
cards in one server, we introducing a sensor device list under
intel_max10_device instead of a global list. On the other hand, we
using separate intel_max10_device instance for each opae_adatper.

Add mutex lock on do_transaction() function for SPI driver to avoid
race condition.

Signed-off-by: Tianfei Zhang <tianfei.zhang@intel.com>
Signed-off-by: Andy Pei <andy.pei@intel.com>
2019-11-20 17:36:05 +01:00

651 lines
13 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2019 Intel Corporation
*/
#include "opae_intel_max10.h"
#include <libfdt.h>
int max10_reg_read(struct intel_max10_device *dev,
unsigned int reg, unsigned int *val)
{
if (!dev)
return -ENODEV;
dev_debug(dev, "%s: bus:0x%x, reg:0x%x\n", __func__, dev->bus, reg);
return spi_transaction_read(dev->spi_tran_dev,
reg, 4, (unsigned char *)val);
}
int max10_reg_write(struct intel_max10_device *dev,
unsigned int reg, unsigned int val)
{
unsigned int tmp = val;
if (!dev)
return -ENODEV;
dev_debug(dev, "%s: bus:0x%x, reg:0x%x, val:0x%x\n", __func__,
dev->bus, reg, val);
return spi_transaction_write(dev->spi_tran_dev,
reg, 4, (unsigned char *)&tmp);
}
int max10_sys_read(struct intel_max10_device *dev,
unsigned int offset, unsigned int *val)
{
if (!dev)
return -ENODEV;
return max10_reg_read(dev, dev->base + offset, val);
}
int max10_sys_write(struct intel_max10_device *dev,
unsigned int offset, unsigned int val)
{
if (!dev)
return -ENODEV;
return max10_reg_write(dev, dev->base + offset, val);
}
static struct max10_compatible_id max10_id_table[] = {
{.compatible = MAX10_PAC,},
{.compatible = MAX10_PAC_N3000,},
{.compatible = MAX10_PAC_END,}
};
static struct max10_compatible_id *max10_match_compatible(const char *fdt_root)
{
struct max10_compatible_id *id = max10_id_table;
for (; strcmp(id->compatible, MAX10_PAC_END); id++) {
if (fdt_node_check_compatible(fdt_root, 0, id->compatible))
continue;
return id;
}
return NULL;
}
static inline bool
is_max10_pac_n3000(struct intel_max10_device *max10)
{
return max10->id && !strcmp(max10->id->compatible,
MAX10_PAC_N3000);
}
static void max10_check_capability(struct intel_max10_device *max10)
{
if (!max10->fdt_root)
return;
if (is_max10_pac_n3000(max10)) {
max10->flags |= MAX10_FLAGS_NO_I2C2 |
MAX10_FLAGS_NO_BMCIMG_FLASH;
dev_info(max10, "found %s card\n", max10->id->compatible);
} else
max10->flags |= MAX10_FLAGS_MAC_CACHE;
}
static int altera_nor_flash_read(struct intel_max10_device *dev,
u32 offset, void *buffer, u32 len)
{
int word_len;
int i;
unsigned int *buf = (unsigned int *)buffer;
unsigned int value;
int ret;
if (!dev || !buffer || len <= 0)
return -ENODEV;
word_len = len/4;
for (i = 0; i < word_len; i++) {
ret = max10_reg_read(dev, offset + i*4,
&value);
if (ret)
return -EBUSY;
*buf++ = value;
}
return 0;
}
static int enable_nor_flash(struct intel_max10_device *dev, bool on)
{
unsigned int val = 0;
int ret;
ret = max10_sys_read(dev, RSU_REG, &val);
if (ret) {
dev_err(NULL "enabling flash error\n");
return ret;
}
if (on)
val |= RSU_ENABLE;
else
val &= ~RSU_ENABLE;
return max10_sys_write(dev, RSU_REG, val);
}
static int init_max10_device_table(struct intel_max10_device *max10)
{
struct max10_compatible_id *id;
struct fdt_header hdr;
char *fdt_root = NULL;
u32 dt_size, dt_addr, val;
int ret;
ret = max10_sys_read(max10, DT_AVAIL_REG, &val);
if (ret) {
dev_err(max10 "cannot read DT_AVAIL_REG\n");
return ret;
}
if (!(val & DT_AVAIL)) {
dev_err(max10 "DT not available\n");
return -EINVAL;
}
ret = max10_sys_read(max10, DT_BASE_ADDR_REG, &dt_addr);
if (ret) {
dev_info(max10 "cannot get base addr of device table\n");
return ret;
}
ret = enable_nor_flash(max10, true);
if (ret) {
dev_err(max10 "fail to enable flash\n");
return ret;
}
ret = altera_nor_flash_read(max10, dt_addr, &hdr, sizeof(hdr));
if (ret) {
dev_err(max10 "read fdt header fail\n");
goto done;
}
ret = fdt_check_header(&hdr);
if (ret) {
dev_err(max10 "check fdt header fail\n");
goto done;
}
dt_size = fdt_totalsize(&hdr);
if (dt_size > DFT_MAX_SIZE) {
dev_err(max10 "invalid device table size\n");
ret = -EINVAL;
goto done;
}
fdt_root = opae_malloc(dt_size);
if (!fdt_root) {
ret = -ENOMEM;
goto done;
}
ret = altera_nor_flash_read(max10, dt_addr, fdt_root, dt_size);
if (ret) {
dev_err(max10 "cannot read device table\n");
goto done;
}
id = max10_match_compatible(fdt_root);
if (!id) {
dev_err(max10 "max10 compatible not found\n");
ret = -ENODEV;
goto done;
}
max10->flags |= MAX10_FLAGS_DEVICE_TABLE;
max10->id = id;
max10->fdt_root = fdt_root;
done:
ret = enable_nor_flash(max10, false);
if (ret && fdt_root)
opae_free(fdt_root);
return ret;
}
static u64 fdt_get_number(const fdt32_t *cell, int size)
{
u64 r = 0;
while (size--)
r = (r << 32) | fdt32_to_cpu(*cell++);
return r;
}
static int fdt_get_reg(const void *fdt, int node, unsigned int idx,
u64 *start, u64 *size)
{
const fdt32_t *prop, *end;
int na = 0, ns = 0, len = 0, parent;
parent = fdt_parent_offset(fdt, node);
if (parent < 0)
return parent;
prop = fdt_getprop(fdt, parent, "#address-cells", NULL);
na = prop ? fdt32_to_cpu(*prop) : 2;
prop = fdt_getprop(fdt, parent, "#size-cells", NULL);
ns = prop ? fdt32_to_cpu(*prop) : 2;
prop = fdt_getprop(fdt, node, "reg", &len);
if (!prop)
return -FDT_ERR_NOTFOUND;
end = prop + len/sizeof(*prop);
prop = prop + (na + ns) * idx;
if (prop + na + ns > end)
return -FDT_ERR_NOTFOUND;
*start = fdt_get_number(prop, na);
*size = fdt_get_number(prop + na, ns);
return 0;
}
static int __fdt_stringlist_search(const void *fdt, int offset,
const char *prop, const char *string)
{
int length, len, index = 0;
const char *list, *end;
list = fdt_getprop(fdt, offset, prop, &length);
if (!list)
return length;
len = strlen(string) + 1;
end = list + length;
while (list < end) {
length = strnlen(list, end - list) + 1;
if (list + length > end)
return -FDT_ERR_BADVALUE;
if (length == len && memcmp(list, string, length) == 0)
return index;
list += length;
index++;
}
return -FDT_ERR_NOTFOUND;
}
static int fdt_get_named_reg(const void *fdt, int node, const char *name,
u64 *start, u64 *size)
{
int idx;
idx = __fdt_stringlist_search(fdt, node, "reg-names", name);
if (idx < 0)
return idx;
return fdt_get_reg(fdt, node, idx, start, size);
}
static void max10_sensor_uinit(struct intel_max10_device *dev)
{
struct opae_sensor_info *info;
TAILQ_FOREACH(info, &dev->opae_sensor_list, node) {
TAILQ_REMOVE(&dev->opae_sensor_list, info, node);
opae_free(info);
}
}
static bool sensor_reg_valid(struct sensor_reg *reg)
{
return !!reg->size;
}
static int max10_add_sensor(struct intel_max10_device *dev,
struct raw_sensor_info *info, struct opae_sensor_info *sensor)
{
int i;
int ret = 0;
unsigned int val;
if (!info || !sensor)
return -ENODEV;
sensor->id = info->id;
sensor->name = info->name;
sensor->type = info->type;
sensor->multiplier = info->multiplier;
for (i = SENSOR_REG_VALUE; i < SENSOR_REG_MAX; i++) {
if (!sensor_reg_valid(&info->regs[i]))
continue;
ret = max10_sys_read(dev, info->regs[i].regoff, &val);
if (ret)
break;
if (val == 0xdeadbeef) {
dev_debug(dev, "%s: sensor:%s invalid 0x%x at:%d\n",
__func__, sensor->name, val, i);
continue;
}
val *= info->multiplier;
switch (i) {
case SENSOR_REG_VALUE:
sensor->value_reg = info->regs[i].regoff;
sensor->flags |= OPAE_SENSOR_VALID;
break;
case SENSOR_REG_HIGH_WARN:
sensor->high_warn = val;
sensor->flags |= OPAE_SENSOR_HIGH_WARN_VALID;
break;
case SENSOR_REG_HIGH_FATAL:
sensor->high_fatal = val;
sensor->flags |= OPAE_SENSOR_HIGH_FATAL_VALID;
break;
case SENSOR_REG_LOW_WARN:
sensor->low_warn = val;
sensor->flags |= OPAE_SENSOR_LOW_WARN_VALID;
break;
case SENSOR_REG_LOW_FATAL:
sensor->low_fatal = val;
sensor->flags |= OPAE_SENSOR_LOW_FATAL_VALID;
break;
case SENSOR_REG_HYSTERESIS:
sensor->hysteresis = val;
sensor->flags |= OPAE_SENSOR_HYSTERESIS_VALID;
break;
}
}
return ret;
}
static int
max10_sensor_init(struct intel_max10_device *dev, int parent)
{
int i, ret = 0, offset = 0;
const fdt32_t *num;
const char *ptr;
u64 start, size;
struct raw_sensor_info *raw;
struct opae_sensor_info *sensor;
char *fdt_root = dev->fdt_root;
if (!fdt_root) {
dev_debug(dev, "skip sensor init as not find Device Tree\n");
return 0;
}
fdt_for_each_subnode(offset, fdt_root, parent) {
ptr = fdt_get_name(fdt_root, offset, NULL);
if (!ptr) {
dev_err(dev, "failed to fdt get name\n");
continue;
}
if (!strstr(ptr, "sensor")) {
dev_debug(dev, "%s is not a sensor node\n", ptr);
continue;
}
dev_debug(dev, "found sensor node %s\n", ptr);
raw = (struct raw_sensor_info *)opae_zmalloc(sizeof(*raw));
if (!raw) {
ret = -ENOMEM;
goto free_sensor;
}
raw->name = fdt_getprop(fdt_root, offset, "sensor_name", NULL);
if (!raw->name) {
ret = -EINVAL;
goto free_sensor;
}
raw->type = fdt_getprop(fdt_root, offset, "type", NULL);
if (!raw->type) {
ret = -EINVAL;
goto free_sensor;
}
for (i = SENSOR_REG_VALUE; i < SENSOR_REG_MAX; i++) {
ret = fdt_get_named_reg(fdt_root, offset,
sensor_reg_name[i], &start,
&size);
if (ret) {
dev_debug(dev, "no found %d: sensor node %s, %s\n",
ret, ptr, sensor_reg_name[i]);
if (i == SENSOR_REG_VALUE) {
ret = -EINVAL;
goto free_sensor;
}
continue;
}
/* This is a hack to compatible with non-secure
* solution. If sensors are included in root node,
* then it's non-secure dtb, which use absolute addr
* of non-secure solution.
*/
if (parent)
raw->regs[i].regoff = start;
else
raw->regs[i].regoff = start -
MAX10_BASE_ADDR;
raw->regs[i].size = size;
}
num = fdt_getprop(fdt_root, offset, "id", NULL);
if (!num) {
ret = -EINVAL;
goto free_sensor;
}
raw->id = fdt32_to_cpu(*num);
num = fdt_getprop(fdt_root, offset, "multiplier", NULL);
raw->multiplier = num ? fdt32_to_cpu(*num) : 1;
dev_debug(dev, "found sensor from DTB: %s: %s: %u: %u\n",
raw->name, raw->type,
raw->id, raw->multiplier);
for (i = SENSOR_REG_VALUE; i < SENSOR_REG_MAX; i++)
dev_debug(dev, "sensor reg[%d]: %x: %zu\n",
i, raw->regs[i].regoff,
raw->regs[i].size);
sensor = opae_zmalloc(sizeof(*sensor));
if (!sensor) {
ret = -EINVAL;
goto free_sensor;
}
if (max10_add_sensor(dev, raw, sensor)) {
ret = -EINVAL;
opae_free(sensor);
goto free_sensor;
}
if (sensor->flags & OPAE_SENSOR_VALID) {
TAILQ_INSERT_TAIL(&dev->opae_sensor_list, sensor, node);
dev_info(dev, "found valid sensor: %s\n", sensor->name);
} else
opae_free(sensor);
opae_free(raw);
}
return 0;
free_sensor:
if (raw)
opae_free(raw);
max10_sensor_uinit(dev);
return ret;
}
static int check_max10_version(struct intel_max10_device *dev)
{
unsigned int v;
if (!max10_reg_read(dev, MAX10_SEC_BASE_ADDR + MAX10_BUILD_VER,
&v)) {
if (v != 0xffffffff) {
dev_info(dev, "secure MAX10 detected\n");
dev->base = MAX10_SEC_BASE_ADDR;
dev->flags |= MAX10_FLAGS_SECURE;
} else {
dev_info(dev, "non-secure MAX10 detected\n");
dev->base = MAX10_BASE_ADDR;
}
return 0;
}
return -ENODEV;
}
static int
max10_secure_hw_init(struct intel_max10_device *dev)
{
int offset, sysmgr_offset = 0;
char *fdt_root;
fdt_root = dev->fdt_root;
if (!fdt_root) {
dev_debug(dev, "skip init as not find Device Tree\n");
return 0;
}
fdt_for_each_subnode(offset, fdt_root, 0) {
if (!fdt_node_check_compatible(fdt_root, offset,
"intel-max10,system-manager")) {
sysmgr_offset = offset;
break;
}
}
max10_check_capability(dev);
max10_sensor_init(dev, sysmgr_offset);
return 0;
}
static int
max10_non_secure_hw_init(struct intel_max10_device *dev)
{
max10_check_capability(dev);
max10_sensor_init(dev, 0);
return 0;
}
struct intel_max10_device *
intel_max10_device_probe(struct altera_spi_device *spi,
int chipselect)
{
struct intel_max10_device *dev;
int ret;
unsigned int val;
dev = opae_malloc(sizeof(*dev));
if (!dev)
return NULL;
TAILQ_INIT(&dev->opae_sensor_list);
dev->spi_master = spi;
dev->spi_tran_dev = spi_transaction_init(spi, chipselect);
if (!dev->spi_tran_dev) {
dev_err(dev, "%s spi tran init fail\n", __func__);
goto free_dev;
}
/* check the max10 version */
ret = check_max10_version(dev);
if (ret) {
dev_err(dev, "Failed to find max10 hardware!\n");
goto free_dev;
}
/* load the MAX10 device table */
ret = init_max10_device_table(dev);
if (ret) {
dev_err(dev, "Init max10 device table fail\n");
goto free_dev;
}
/* init max10 devices, like sensor*/
if (dev->flags & MAX10_FLAGS_SECURE)
ret = max10_secure_hw_init(dev);
else
ret = max10_non_secure_hw_init(dev);
if (ret) {
dev_err(dev, "Failed to init max10 hardware!\n");
goto free_dtb;
}
/* read FPGA loading information */
ret = max10_sys_read(dev, FPGA_PAGE_INFO, &val);
if (ret) {
dev_err(dev, "fail to get FPGA loading info\n");
goto release_max10_hw;
}
dev_info(dev, "FPGA loaded from %s Image\n", val ? "User" : "Factory");
return dev;
release_max10_hw:
max10_sensor_uinit(dev);
free_dtb:
if (dev->fdt_root)
opae_free(dev->fdt_root);
if (dev->spi_tran_dev)
spi_transaction_remove(dev->spi_tran_dev);
free_dev:
opae_free(dev);
return NULL;
}
int intel_max10_device_remove(struct intel_max10_device *dev)
{
if (!dev)
return 0;
max10_sensor_uinit(dev);
if (dev->spi_tran_dev)
spi_transaction_remove(dev->spi_tran_dev);
if (dev->fdt_root)
opae_free(dev->fdt_root);
opae_free(dev);
return 0;
}