4a19f89104
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>
651 lines
13 KiB
C
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;
|
|
}
|