From: Tianfei Zhang Date: Tue, 16 Apr 2019 03:17:46 +0000 (+0800) Subject: raw/ifpga/base: add SPI and MAX10 device driver X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=96ebfcf8;p=dpdk.git raw/ifpga/base: add SPI and MAX10 device driver There is a SPI bus link between A10 FPGA and MAX10 FPGA. MAX10 is in charge of board management, like power management, sensors, flash devices. Signed-off-by: Tianfei Zhang --- diff --git a/drivers/raw/ifpga_rawdev/base/Makefile b/drivers/raw/ifpga_rawdev/base/Makefile index d79da72b82..c6f51a80b9 100644 --- a/drivers/raw/ifpga_rawdev/base/Makefile +++ b/drivers/raw/ifpga_rawdev/base/Makefile @@ -22,5 +22,8 @@ SRCS-y += opae_hw_api.c SRCS-y += opae_ifpga_hw_api.c SRCS-y += opae_debug.c SRCS-y += ifpga_fme_pr.c +SRCS-y += opae_spi.c +SRCS-y += opae_spi_transaction.c +SRCS-y += opae_intel_max10.c SRCS-y += $(wildcard $(SRCDIR)/base/$(OSDEP)/*.c) diff --git a/drivers/raw/ifpga_rawdev/base/ifpga_defines.h b/drivers/raw/ifpga_rawdev/base/ifpga_defines.h index 217d0b1617..f5e22ae2f0 100644 --- a/drivers/raw/ifpga_rawdev/base/ifpga_defines.h +++ b/drivers/raw/ifpga_rawdev/base/ifpga_defines.h @@ -19,6 +19,8 @@ #define FME_FEATURE_HSSI_ETH "fme_hssi" #define FME_FEATURE_GLOBAL_DPERF "fme_dperf" #define FME_FEATURE_QSPI_FLASH "fme_qspi_flash" +#define FME_FEATURE_MAX10_SPI "fme_max10_spi" +#define FME_FEATURE_NIOS_SPI "fme_nios_spi" #define PORT_FEATURE_HEADER "port_hdr" #define PORT_FEATURE_UAFU "port_uafu" @@ -43,6 +45,7 @@ #define FME_HSSI_ETH_REVISION 0 #define FME_GLOBAL_DPERF_REVISION 0 #define FME_QSPI_REVISION 0 +#define FME_MAX10_SPI 0 #define PORT_HEADER_REVISION 0 /* UAFU's header info depends on the downloaded GBS */ @@ -80,6 +83,8 @@ enum fpga_id_type { #define FME_FEATURE_ID_GLOBAL_DPERF 0x7 #define FME_FEATURE_ID_QSPI_FLASH 0x8 #define FME_FEATURE_ID_EMIF_MGMT 0x9 +#define FME_FEATURE_ID_MAX10_SPI 0xe +#define FME_FEATURE_ID_NIOS_SPI 0xd #define PORT_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER #define PORT_FEATURE_ID_ERROR 0x10 diff --git a/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.c b/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.c index d82a8906f3..2a35c0666c 100644 --- a/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.c +++ b/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.c @@ -197,6 +197,10 @@ static struct feature_driver fme_feature_drvs[] = { &fme_hssi_eth_ops),}, {FEATURE_DRV(FME_FEATURE_ID_EMIF_MGMT, FME_FEATURE_EMIF_MGMT, &fme_emif_ops),}, + {FEATURE_DRV(FME_FEATURE_ID_MAX10_SPI, FME_FEATURE_MAX10_SPI, + &fme_spi_master_ops),}, + {FEATURE_DRV(FME_FEATURE_ID_NIOS_SPI, FME_FEATURE_NIOS_SPI, + &fme_nios_spi_master_ops),}, {0, NULL, NULL}, /* end of arrary */ }; diff --git a/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.h b/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.h index 1d80f1d1ef..72352eeb77 100644 --- a/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.h +++ b/drivers/raw/ifpga_rawdev/base/ifpga_feature_dev.h @@ -171,6 +171,8 @@ extern struct feature_ops fme_global_iperf_ops; extern struct feature_ops fme_global_dperf_ops; extern struct feature_ops fme_hssi_eth_ops; extern struct feature_ops fme_emif_ops; +extern struct feature_ops fme_spi_master_ops; +extern struct feature_ops fme_nios_spi_master_ops; int port_get_prop(struct ifpga_port_hw *port, struct feature_prop *prop); int port_set_prop(struct ifpga_port_hw *port, struct feature_prop *prop); diff --git a/drivers/raw/ifpga_rawdev/base/ifpga_fme.c b/drivers/raw/ifpga_rawdev/base/ifpga_fme.c index 9451086aa1..28226f0ce7 100644 --- a/drivers/raw/ifpga_rawdev/base/ifpga_fme.c +++ b/drivers/raw/ifpga_rawdev/base/ifpga_fme.c @@ -3,6 +3,8 @@ */ #include "ifpga_feature_dev.h" +#include "opae_spi.h" +#include "opae_intel_max10.h" #define PWR_THRESHOLD_MAX 0x7F @@ -764,3 +766,197 @@ struct feature_ops fme_emif_ops = { .init = fme_emif_init, .uinit = fme_emif_uinit, }; + +static int spi_self_checking(void) +{ + u32 val; + int ret; + + ret = max10_reg_read(0x30043c, &val); + if (ret) + return -EIO; + + if (val != 0x87654321) { + dev_err(NULL, "Read MAX10 test register fail: 0x%x\n", val); + return -EIO; + } + + dev_info(NULL, "Read MAX10 test register success, SPI self-test done\n"); + + return 0; +} + +static int fme_spi_init(struct feature *feature) +{ + struct ifpga_fme_hw *fme = (struct ifpga_fme_hw *)feature->parent; + struct altera_spi_device *spi_master; + struct intel_max10_device *max10; + int ret = 0; + + dev_info(fme, "FME SPI Master (Max10) Init.\n"); + dev_debug(fme, "FME SPI base addr %p.\n", + feature->addr); + dev_debug(fme, "spi param=0x%llx\n", + (unsigned long long)opae_readq(feature->addr + 0x8)); + + spi_master = altera_spi_alloc(feature->addr, TYPE_SPI); + if (!spi_master) + return -ENODEV; + + altera_spi_init(spi_master); + + max10 = intel_max10_device_probe(spi_master, 0); + if (!max10) { + ret = -ENODEV; + dev_err(fme, "max10 init fail\n"); + goto spi_fail; + } + + fme->max10_dev = max10; + + /* SPI self test */ + if (spi_self_checking()) { + ret = -EIO; + goto max10_fail; + } + + return ret; + +max10_fail: + intel_max10_device_remove(fme->max10_dev); +spi_fail: + altera_spi_release(spi_master); + return ret; +} + +static void fme_spi_uinit(struct feature *feature) +{ + struct ifpga_fme_hw *fme = (struct ifpga_fme_hw *)feature->parent; + + if (fme->max10_dev) + intel_max10_device_remove(fme->max10_dev); +} + +struct feature_ops fme_spi_master_ops = { + .init = fme_spi_init, + .uinit = fme_spi_uinit, +}; + +static int nios_spi_wait_init_done(struct altera_spi_device *dev) +{ + u32 val = 0; + unsigned long timeout = msecs_to_timer_cycles(10000); + unsigned long ticks; + + do { + if (spi_reg_read(dev, NIOS_SPI_INIT_DONE, &val)) + return -EIO; + if (val) + break; + + ticks = rte_get_timer_cycles(); + if (time_after(ticks, timeout)) + return -ETIMEDOUT; + msleep(100); + } while (!val); + + return 0; +} + +static int nios_spi_check_error(struct altera_spi_device *dev) +{ + u32 value = 0; + + if (spi_reg_read(dev, NIOS_SPI_INIT_STS0, &value)) + return -EIO; + + dev_debug(dev, "SPI init status0 0x%x\n", value); + + /* Error code: 0xFFF0 to 0xFFFC */ + if (value >= 0xFFF0 && value <= 0xFFFC) + return -EINVAL; + + value = 0; + if (spi_reg_read(dev, NIOS_SPI_INIT_STS1, &value)) + return -EIO; + + dev_debug(dev, "SPI init status1 0x%x\n", value); + + /* Error code: 0xFFF0 to 0xFFFC */ + if (value >= 0xFFF0 && value <= 0xFFFC) + return -EINVAL; + + return 0; +} + +static int fme_nios_spi_init(struct feature *feature) +{ + struct ifpga_fme_hw *fme = (struct ifpga_fme_hw *)feature->parent; + struct altera_spi_device *spi_master; + struct intel_max10_device *max10; + int ret = 0; + + dev_info(fme, "FME SPI Master (NIOS) Init.\n"); + dev_debug(fme, "FME SPI base addr %p.\n", + feature->addr); + dev_debug(fme, "spi param=0x%llx\n", + (unsigned long long)opae_readq(feature->addr + 0x8)); + + spi_master = altera_spi_alloc(feature->addr, TYPE_NIOS_SPI); + if (!spi_master) + return -ENODEV; + + /** + * 1. wait A10 NIOS initial finished and + * release the SPI master to Host + */ + ret = nios_spi_wait_init_done(spi_master); + if (ret != 0) { + dev_err(fme, "FME NIOS_SPI init fail\n"); + goto release_dev; + } + + dev_info(fme, "FME NIOS_SPI initial done\n"); + + /* 2. check if error occur? */ + if (nios_spi_check_error(spi_master)) + dev_info(fme, "NIOS_SPI INIT done, but found some error\n"); + + /* 3. init the spi master*/ + altera_spi_init(spi_master); + + /* init the max10 device */ + max10 = intel_max10_device_probe(spi_master, 0); + if (!max10) { + ret = -ENODEV; + dev_err(fme, "max10 init fail\n"); + goto release_dev; + } + + fme->max10_dev = max10; + + /* SPI self test */ + if (spi_self_checking()) + goto spi_fail; + + return ret; + +spi_fail: + intel_max10_device_remove(fme->max10_dev); +release_dev: + altera_spi_release(spi_master); + return -ENODEV; +} + +static void fme_nios_spi_uinit(struct feature *feature) +{ + struct ifpga_fme_hw *fme = (struct ifpga_fme_hw *)feature->parent; + + if (fme->max10_dev) + intel_max10_device_remove(fme->max10_dev); +} + +struct feature_ops fme_nios_spi_master_ops = { + .init = fme_nios_spi_init, + .uinit = fme_nios_spi_uinit, +}; diff --git a/drivers/raw/ifpga_rawdev/base/ifpga_hw.h b/drivers/raw/ifpga_rawdev/base/ifpga_hw.h index 8aaa056fdf..a2f4776e43 100644 --- a/drivers/raw/ifpga_rawdev/base/ifpga_hw.h +++ b/drivers/raw/ifpga_rawdev/base/ifpga_hw.h @@ -79,6 +79,8 @@ struct ifpga_fme_hw { u32 cache_size; u32 capability; + + void *max10_dev; /* MAX10 device */ }; enum ifpga_port_state { diff --git a/drivers/raw/ifpga_rawdev/base/meson.build b/drivers/raw/ifpga_rawdev/base/meson.build index 03f5112caa..6bc762f9b9 100644 --- a/drivers/raw/ifpga_rawdev/base/meson.build +++ b/drivers/raw/ifpga_rawdev/base/meson.build @@ -14,7 +14,10 @@ sources = [ 'ifpga_fme_pr.c', 'opae_hw_api.c', 'opae_ifpga_hw_api.c', - 'opae_debug.c' + 'opae_debug.c', + 'opae_spi.c', + 'opae_spi_transaction.c', + 'opae_intel_max10.c', ] error_cflags = ['-Wno-sign-compare', '-Wno-unused-value', diff --git a/drivers/raw/ifpga_rawdev/base/opae_hw_api.h b/drivers/raw/ifpga_rawdev/base/opae_hw_api.h index 332e0f3f62..76224b4d33 100644 --- a/drivers/raw/ifpga_rawdev/base/opae_hw_api.h +++ b/drivers/raw/ifpga_rawdev/base/opae_hw_api.h @@ -11,6 +11,7 @@ #include #include "opae_osdep.h" +#include "opae_intel_max10.h" #ifndef PCI_MAX_RESOURCE #define PCI_MAX_RESOURCE 6 diff --git a/drivers/raw/ifpga_rawdev/base/opae_intel_max10.c b/drivers/raw/ifpga_rawdev/base/opae_intel_max10.c new file mode 100644 index 0000000000..f354ee4b61 --- /dev/null +++ b/drivers/raw/ifpga_rawdev/base/opae_intel_max10.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2019 Intel Corporation + */ + +#include "opae_intel_max10.h" + +static struct intel_max10_device *g_max10; + +int max10_reg_read(unsigned int reg, unsigned int *val) +{ + if (!g_max10) + return -ENODEV; + + return spi_transaction_read(g_max10->spi_tran_dev, + reg, 4, (unsigned char *)val); +} + +int max10_reg_write(unsigned int reg, unsigned int val) +{ + if (!g_max10) + return -ENODEV; + + return spi_transaction_write(g_max10->spi_tran_dev, + reg, 4, (unsigned char *)&val); +} + +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; + + 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; + } + + /* set the max10 device firstly */ + g_max10 = dev; + + /* read FPGA loading information */ + ret = max10_reg_read(FPGA_PAGE_INFO_OFF, &val); + if (ret) { + dev_err(dev, "fail to get FPGA loading info\n"); + goto spi_tran_fail; + } + dev_info(dev, "FPGA loaded from %s Image\n", val ? "User" : "Factory"); + + /* set PKVL Polling manually in BBS */ + ret = max10_reg_write(PKVL_POLLING_CTRL, 0x3); + if (ret) { + dev_err(dev, "%s set PKVL polling fail\n", __func__); + goto spi_tran_fail; + } + + return dev; + +spi_tran_fail: + spi_transaction_remove(dev->spi_tran_dev); +free_dev: + g_max10 = NULL; + opae_free(dev); + + return NULL; +} + +int intel_max10_device_remove(struct intel_max10_device *dev) +{ + if (!dev) + return 0; + + if (dev->spi_tran_dev) + spi_transaction_remove(dev->spi_tran_dev); + + g_max10 = NULL; + opae_free(dev); + + return 0; +} diff --git a/drivers/raw/ifpga_rawdev/base/opae_intel_max10.h b/drivers/raw/ifpga_rawdev/base/opae_intel_max10.h new file mode 100644 index 0000000000..91a188d7d5 --- /dev/null +++ b/drivers/raw/ifpga_rawdev/base/opae_intel_max10.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2019 Intel Corporation + */ + +#ifndef _OPAE_INTEL_MAX10_H_ +#define _OPAE_INTEL_MAX10_H_ + +#include "opae_osdep.h" +#include "opae_spi.h" + +#define INTEL_MAX10_MAX_MDIO_DEVS 2 +#define PKVL_NUMBER_PORTS 4 + +/* max10 capability flags */ +#define MAX10_FLAGS_NO_I2C2 BIT(0) +#define MAX10_FLAGS_NO_BMCIMG_FLASH BIT(1) +#define MAX10_FLAGS_DEVICE_TABLE BIT(2) +#define MAX10_FLAGS_SPI BIT(3) +#define MAX10_FLGAS_NIOS_SPI BIT(4) +#define MAX10_FLAGS_PKVL BIT(5) + +struct intel_max10_device { + unsigned int flags; /*max10 hardware capability*/ + struct altera_spi_device *spi_master; + struct spi_transaction_dev *spi_tran_dev; +}; + +#define FLASH_BASE 0x10000000 +#define FLASH_OPTION_BITS 0x10000 + +#define NIOS2_FW_VERSION_OFF 0x300400 +#define RSU_REG_OFF 0x30042c +#define FPGA_RP_LOAD BIT(3) +#define NIOS2_PRERESET BIT(4) +#define NIOS2_HANG BIT(5) +#define RSU_ENABLE BIT(6) +#define NIOS2_RESET BIT(7) +#define NIOS2_I2C2_POLL_STOP BIT(13) +#define FPGA_RECONF_REG_OFF 0x300430 +#define COUNTDOWN_START BIT(18) +#define MAX10_BUILD_VER_OFF 0x300468 +#define PCB_INFO GENMASK(31, 24) +#define MAX10_BUILD_VERION GENMASK(23, 0) +#define FPGA_PAGE_INFO_OFF 0x30046c +#define DT_AVAIL_REG_OFF 0x300490 +#define DT_AVAIL BIT(0) +#define DT_BASE_ADDR_REG_OFF 0x300494 +#define PKVL_POLLING_CTRL 0x300480 +#define PKVL_LINK_STATUS 0x300564 + +#define DFT_MAX_SIZE 0x7e0000 + +int max10_reg_read(unsigned int reg, unsigned int *val); +int max10_reg_write(unsigned int reg, unsigned int val); +struct intel_max10_device * +intel_max10_device_probe(struct altera_spi_device *spi, + int chipselect); +int intel_max10_device_remove(struct intel_max10_device *dev); + +#endif diff --git a/drivers/raw/ifpga_rawdev/base/opae_osdep.h b/drivers/raw/ifpga_rawdev/base/opae_osdep.h index 78fec50ad3..d710ec0e88 100644 --- a/drivers/raw/ifpga_rawdev/base/opae_osdep.h +++ b/drivers/raw/ifpga_rawdev/base/opae_osdep.h @@ -35,6 +35,7 @@ struct uuid { #ifndef BIT #define BIT(a) (1UL << (a)) #endif /* BIT */ +#define U64_C(x) x ## ULL #ifndef BIT_ULL #define BIT_ULL(a) (1ULL << (a)) #endif /* BIT_ULL */ @@ -76,5 +77,8 @@ struct uuid { #define msleep(x) opae_udelay(1000 * (x)) #define usleep_range(min, max) msleep(DIV_ROUND_UP(min, 1000)) +#define time_after(a, b) ((long)((b) - (a)) < 0) +#define time_before(a, b) time_after(b, a) #define opae_memset(a, b, c) memset((a), (b), (c)) + #endif diff --git a/drivers/raw/ifpga_rawdev/base/opae_spi.c b/drivers/raw/ifpga_rawdev/base/opae_spi.c new file mode 100644 index 0000000000..cc52782d6f --- /dev/null +++ b/drivers/raw/ifpga_rawdev/base/opae_spi.c @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2019 Intel Corporation + */ + +#include "opae_osdep.h" +#include "opae_spi.h" + +static int nios_spi_indirect_read(struct altera_spi_device *dev, u32 reg, + u32 *val) +{ + u64 ctrl = 0; + u64 stat = 0; + int loops = SPI_MAX_RETRY; + + ctrl = NIOS_SPI_RD | ((u64)reg << 32); + opae_writeq(ctrl, dev->regs + NIOS_SPI_CTRL); + + stat = opae_readq(dev->regs + NIOS_SPI_STAT); + while (!(stat & NIOS_SPI_VALID) && --loops) + stat = opae_readq(dev->regs + NIOS_SPI_STAT); + + *val = stat & NIOS_SPI_READ_DATA; + + return loops ? 0 : -ETIMEDOUT; +} + +static int nios_spi_indirect_write(struct altera_spi_device *dev, u32 reg, + u32 value) +{ + + u64 ctrl = 0; + u64 stat = 0; + int loops = SPI_MAX_RETRY; + + ctrl |= NIOS_SPI_WR | (u64)reg << 32; + ctrl |= value & NIOS_SPI_WRITE_DATA; + + opae_writeq(ctrl, dev->regs + NIOS_SPI_CTRL); + + stat = opae_readq(dev->regs + NIOS_SPI_STAT); + while (!(stat & NIOS_SPI_VALID) && --loops) + stat = opae_readq(dev->regs + NIOS_SPI_STAT); + + return loops ? 0 : -ETIMEDOUT; +} + +static int spi_indirect_write(struct altera_spi_device *dev, u32 reg, + u32 value) +{ + u64 ctrl; + + opae_writeq(value & WRITE_DATA_MASK, dev->regs + SPI_WRITE); + + ctrl = CTRL_W | (reg >> 2); + opae_writeq(ctrl, dev->regs + SPI_CTRL); + + return 0; +} + +static int spi_indirect_read(struct altera_spi_device *dev, u32 reg, + u32 *val) +{ + u64 tmp; + u64 ctrl; + + ctrl = CTRL_R | (reg >> 2); + opae_writeq(ctrl, dev->regs + SPI_CTRL); + + /** + * FIXME: Read one more time to avoid HW timing issue. This is + * a short term workaround solution, and must be removed once + * hardware fixing is done. + */ + tmp = opae_readq(dev->regs + SPI_READ); + + *val = (u32)tmp; + + return 0; +} + +int spi_reg_write(struct altera_spi_device *dev, u32 reg, + u32 value) +{ + return dev->reg_write(dev, reg, value); +} + +int spi_reg_read(struct altera_spi_device *dev, u32 reg, + u32 *val) +{ + return dev->reg_read(dev, reg, val); +} + +void spi_cs_activate(struct altera_spi_device *dev, unsigned int chip_select) +{ + spi_reg_write(dev, ALTERA_SPI_SLAVE_SEL, 1 << chip_select); + spi_reg_write(dev, ALTERA_SPI_CONTROL, ALTERA_SPI_CONTROL_SSO_MSK); +} + +void spi_cs_deactivate(struct altera_spi_device *dev) +{ + spi_reg_write(dev, ALTERA_SPI_CONTROL, 0); +} + +static int spi_flush_rx(struct altera_spi_device *dev) +{ + u32 val = 0; + int ret; + + ret = spi_reg_read(dev, ALTERA_SPI_STATUS, &val); + if (ret) + return ret; + + if (val & ALTERA_SPI_STATUS_RRDY_MSK) { + ret = spi_reg_read(dev, ALTERA_SPI_RXDATA, &val); + if (ret) + return ret; + } + + return 0; +} + +static unsigned int spi_write_bytes(struct altera_spi_device *dev, int count) +{ + unsigned int val = 0; + u16 *p16; + u32 *p32; + + if (dev->txbuf) { + switch (dev->data_width) { + case 1: + val = dev->txbuf[count]; + break; + case 2: + p16 = (u16 *)(dev->txbuf + 2*count); + val = *p16; + if (dev->endian == SPI_BIG_ENDIAN) + val = cpu_to_be16(val); + break; + case 4: + p32 = (u32 *)(dev->txbuf + 4*count); + val = *p32; + break; + } + } + + return val; +} + +static void spi_fill_readbuffer(struct altera_spi_device *dev, + unsigned int value, int count) +{ + u16 *p16; + u32 *p32; + + if (dev->rxbuf) { + switch (dev->data_width) { + case 1: + dev->rxbuf[count] = value; + break; + case 2: + p16 = (u16 *)(dev->rxbuf + 2*count); + if (dev->endian == SPI_BIG_ENDIAN) + *p16 = cpu_to_be16((u16)value); + else + *p16 = (u16)value; + break; + case 4: + p32 = (u32 *)(dev->rxbuf + 4*count); + if (dev->endian == SPI_BIG_ENDIAN) + *p32 = cpu_to_be32(value); + else + *p32 = value; + break; + } + } +} + +static int spi_txrx(struct altera_spi_device *dev) +{ + unsigned int count = 0; + u32 rxd; + unsigned int tx_data; + u32 status; + int retry = 0; + int ret; + + while (count < dev->len) { + tx_data = spi_write_bytes(dev, count); + spi_reg_write(dev, ALTERA_SPI_TXDATA, tx_data); + + while (1) { + ret = spi_reg_read(dev, ALTERA_SPI_STATUS, &status); + if (ret) + return -EIO; + if (status & ALTERA_SPI_STATUS_RRDY_MSK) + break; + if (retry++ > SPI_MAX_RETRY) { + dev_err(dev, "%s, read timeout\n", __func__); + return -EBUSY; + } + } + + ret = spi_reg_read(dev, ALTERA_SPI_RXDATA, &rxd); + if (ret) + return -EIO; + + spi_fill_readbuffer(dev, rxd, count); + + count++; + } + + return 0; +} + +int spi_command(struct altera_spi_device *dev, unsigned int chip_select, + unsigned int wlen, void *wdata, + unsigned int rlen, void *rdata) +{ + if (((wlen > 0) && !wdata) || ((rlen > 0) && !rdata)) { + dev_err(dev, "error on spi command checking\n"); + return -EINVAL; + } + + wlen = wlen / dev->data_width; + rlen = rlen / dev->data_width; + + /* flush rx buffer */ + spi_flush_rx(dev); + + spi_cs_activate(dev, chip_select); + if (wlen) { + dev->txbuf = wdata; + dev->rxbuf = rdata; + dev->len = wlen; + spi_txrx(dev); + } + if (rlen) { + dev->rxbuf = rdata; + dev->txbuf = NULL; + dev->len = rlen; + spi_txrx(dev); + } + spi_cs_deactivate(dev); + return 0; +} + +struct altera_spi_device *altera_spi_alloc(void *base, int type) +{ + struct altera_spi_device *spi_dev = + opae_malloc(sizeof(struct altera_spi_device)); + + if (!spi_dev) + return NULL; + + spi_dev->regs = base; + + switch (type) { + case TYPE_SPI: + spi_dev->reg_read = spi_indirect_read; + spi_dev->reg_write = spi_indirect_write; + break; + case TYPE_NIOS_SPI: + spi_dev->reg_read = nios_spi_indirect_read; + spi_dev->reg_write = nios_spi_indirect_write; + break; + default: + dev_err(dev, "%s: invalid SPI type\n", __func__); + goto error; + } + + return spi_dev; + +error: + altera_spi_release(spi_dev); + return NULL; +} + +void altera_spi_init(struct altera_spi_device *spi_dev) +{ + spi_dev->spi_param.info = opae_readq(spi_dev->regs + SPI_CORE_PARAM); + + spi_dev->data_width = spi_dev->spi_param.data_width / 8; + spi_dev->endian = spi_dev->spi_param.endian; + spi_dev->num_chipselect = spi_dev->spi_param.num_chipselect; + dev_info(spi_dev, "spi param: type=%d, data width:%d, endian:%d, clock_polarity=%d, clock=%dMHz, chips=%d, cpha=%d\n", + spi_dev->spi_param.type, + spi_dev->data_width, spi_dev->endian, + spi_dev->spi_param.clock_polarity, + spi_dev->spi_param.clock, + spi_dev->num_chipselect, + spi_dev->spi_param.clock_phase); + + /* clear */ + spi_reg_write(spi_dev, ALTERA_SPI_CONTROL, 0); + spi_reg_write(spi_dev, ALTERA_SPI_STATUS, 0); + /* flush rxdata */ + spi_flush_rx(spi_dev); +} + +void altera_spi_release(struct altera_spi_device *dev) +{ + if (dev) + opae_free(dev); +} diff --git a/drivers/raw/ifpga_rawdev/base/opae_spi.h b/drivers/raw/ifpga_rawdev/base/opae_spi.h new file mode 100644 index 0000000000..ab66e1f322 --- /dev/null +++ b/drivers/raw/ifpga_rawdev/base/opae_spi.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2019 Intel Corporation + */ + +#ifndef _OPAE_SPI_H +#define _OPAE_SPI_H + +#include "opae_osdep.h" + +#define ALTERA_SPI_RXDATA 0 +#define ALTERA_SPI_TXDATA 4 +#define ALTERA_SPI_STATUS 8 +#define ALTERA_SPI_CONTROL 12 +#define ALTERA_SPI_SLAVE_SEL 20 + +#define ALTERA_SPI_STATUS_ROE_MSK 0x8 +#define ALTERA_SPI_STATUS_TOE_MSK 0x10 +#define ALTERA_SPI_STATUS_TMT_MSK 0x20 +#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 +#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 +#define ALTERA_SPI_STATUS_E_MSK 0x100 + +#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 +#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 +#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 +#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 +#define ALTERA_SPI_CONTROL_IE_MSK 0x100 +#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 + +#define SPI_CORE_PARAM 0x8 +#define SPI_CTRL 0x10 +#define CTRL_R BIT_ULL(9) +#define CTRL_W BIT_ULL(8) +#define CTRL_ADDR_MASK GENMASK_ULL(2, 0) +#define SPI_READ 0x18 +#define READ_DATA_VALID BIT_ULL(32) +#define READ_DATA_MASK GENMASK_ULL(31, 0) +#define SPI_WRITE 0x20 +#define WRITE_DATA_MASK GENMASK_ULL(31, 0) + +#define SPI_MAX_RETRY 100000 + +#define TYPE_SPI 0 +#define TYPE_NIOS_SPI 1 + +struct spi_core_param { + union { + u64 info; + struct { + u8 type:1; + u8 endian:1; + u8 data_width:6; + u8 num_chipselect:6; + u8 clock_polarity:1; + u8 clock_phase:1; + u8 stages:2; + u8 resvd:4; + u16 clock:10; + u16 peripheral_id:16; + u8 controller_type:1; + u16 resvd1:15; + }; + }; +}; + +struct altera_spi_device { + u8 *regs; + struct spi_core_param spi_param; + int data_width; /* how many bytes for data width */ + int endian; + #define SPI_BIG_ENDIAN 0 + #define SPI_LITTLE_ENDIAN 1 + int num_chipselect; + unsigned char *rxbuf; + unsigned char *txbuf; + unsigned int len; + int (*reg_read)(struct altera_spi_device *dev, u32 reg, u32 *val); + int (*reg_write)(struct altera_spi_device *dev, u32 reg, + u32 value); +}; + +#define HEADER_LEN 8 +#define RESPONSE_LEN 4 +#define SPI_TRANSACTION_MAX_LEN 1024 +#define TRAN_SEND_MAX_LEN (SPI_TRANSACTION_MAX_LEN + HEADER_LEN) +#define TRAN_RESP_MAX_LEN SPI_TRANSACTION_MAX_LEN +#define PACKET_SEND_MAX_LEN (2*TRAN_SEND_MAX_LEN + 4) +#define PACKET_RESP_MAX_LEN (2*TRAN_RESP_MAX_LEN + 4) +#define BYTES_SEND_MAX_LEN (2*PACKET_SEND_MAX_LEN) +#define BYTES_RESP_MAX_LEN (2*PACKET_RESP_MAX_LEN) + +struct spi_tran_buffer { + unsigned char tran_send[TRAN_SEND_MAX_LEN]; + unsigned char tran_resp[TRAN_RESP_MAX_LEN]; + unsigned char packet_send[PACKET_SEND_MAX_LEN]; + unsigned char packet_resp[PACKET_RESP_MAX_LEN]; + unsigned char bytes_send[BYTES_SEND_MAX_LEN]; + unsigned char bytes_resp[2*BYTES_RESP_MAX_LEN]; +}; + +struct spi_transaction_dev { + struct altera_spi_device *dev; + int chipselect; + struct spi_tran_buffer *buffer; +}; + +struct spi_tran_header { + u8 trans_type; + u8 reserve; + u16 size; + u32 addr; +}; + +int spi_command(struct altera_spi_device *dev, unsigned int chip_select, + unsigned int wlen, void *wdata, unsigned int rlen, void *rdata); +void spi_cs_deactivate(struct altera_spi_device *dev); +void spi_cs_activate(struct altera_spi_device *dev, unsigned int chip_select); +struct altera_spi_device *altera_spi_alloc(void *base, int type); +void altera_spi_init(struct altera_spi_device *dev); +void altera_spi_release(struct altera_spi_device *dev); +int spi_transaction_read(struct spi_transaction_dev *dev, unsigned int addr, + unsigned int size, unsigned char *data); +int spi_transaction_write(struct spi_transaction_dev *dev, unsigned int addr, + unsigned int size, unsigned char *data); +struct spi_transaction_dev *spi_transaction_init(struct altera_spi_device *dev, + int chipselect); +void spi_transaction_remove(struct spi_transaction_dev *dev); +int spi_reg_write(struct altera_spi_device *dev, u32 reg, + u32 value); +int spi_reg_read(struct altera_spi_device *dev, u32 reg, u32 *val); + +#define NIOS_SPI_PARAM 0x8 +#define CONTROL_TYPE BIT_ULL(48) +#define PERI_ID GENMASK_ULL(47, 32) +#define SPI_CLK GENMASK_ULL(31, 22) +#define SYNC_STAGES GENMASK_ULL(17, 16) +#define CLOCK_PHASE BIT_ULL(15) +#define CLOCK_POLARITY BIT_ULL(14) +#define NUM_SELECT GENMASK_ULL(13, 8) +#define DATA_WIDTH GENMASK_ULL(7, 2) +#define SHIFT_DIRECTION BIT_ULL(1) +#define SPI_TYPE BIT_ULL(0) +#define NIOS_SPI_CTRL 0x10 +#define NIOS_SPI_RD (0x1ULL << 62) +#define NIOS_SPI_WR (0x2ULL << 62) +#define NIOS_SPI_COMMAND GENMASK_ULL(63, 62) +#define NIOS_SPI_ADDR GENMASK_ULL(44, 32) +#define NIOS_SPI_WRITE_DATA GENMASK_ULL(31, 0) +#define NIOS_SPI_STAT 0x18 +#define NIOS_SPI_VALID BIT_ULL(32) +#define NIOS_SPI_READ_DATA GENMASK_ULL(31, 0) +#define NIOS_SPI_INIT_DONE 0x1000 + +#define NIOS_SPI_INIT_DONE 0x1000 +#define NIOS_SPI_INIT_STS0 0x1020 +#define NIOS_SPI_INIT_STS1 0x1024 +#define PKVL_STATUS_RESET 0 +#define PKVL_10G_MODE 1 +#define PKVL_25G_MODE 2 +#endif diff --git a/drivers/raw/ifpga_rawdev/base/opae_spi_transaction.c b/drivers/raw/ifpga_rawdev/base/opae_spi_transaction.c new file mode 100644 index 0000000000..17ec3c17db --- /dev/null +++ b/drivers/raw/ifpga_rawdev/base/opae_spi_transaction.c @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2019 Intel Corporation + */ + +#include "opae_spi.h" +#include "ifpga_compat.h" + +/*transaction opcodes*/ +#define SPI_TRAN_SEQ_WRITE 0x04 /* SPI transaction sequential write */ +#define SPI_TRAN_SEQ_READ 0x14 /* SPI transaction sequential read */ +#define SPI_TRAN_NON_SEQ_WRITE 0x00 /* SPI transaction non-sequential write */ +#define SPI_TRAN_NON_SEQ_READ 0x10 /* SPI transaction non-sequential read*/ + +/*specail packet characters*/ +#define SPI_PACKET_SOP 0x7a +#define SPI_PACKET_EOP 0x7b +#define SPI_PACKET_CHANNEL 0x7c +#define SPI_PACKET_ESC 0x7d + +/*special byte characters*/ +#define SPI_BYTE_IDLE 0x4a +#define SPI_BYTE_ESC 0x4d + +#define SPI_REG_BYTES 4 + +#define INIT_SPI_TRAN_HEADER(trans_type, size, address) \ +({ \ + header.trans_type = trans_type; \ + header.reserve = 0; \ + header.size = cpu_to_be16(size); \ + header.addr = cpu_to_be32(addr); \ +}) + +#ifdef OPAE_SPI_DEBUG +static void print_buffer(const char *string, void *buffer, int len) +{ + int i; + unsigned char *p = buffer; + + printf("%s print buffer, len=%d\n", string, len); + + for (i = 0; i < len; i++) + printf("%x ", *(p+i)); + printf("\n"); +} +#else +static void print_buffer(const char *string, void *buffer, int len) +{ + UNUSED(string); + UNUSED(buffer); + UNUSED(len); +} +#endif + +static unsigned char xor_20(unsigned char val) +{ + return val^0x20; +} + +static void reorder_phy_data(u8 bits_per_word, + void *buf, unsigned int len) +{ + unsigned int count = len / (bits_per_word/8); + u32 *p; + + if (bits_per_word == 32) { + p = (u32 *)buf; + while (count--) { + *p = cpu_to_be32(*p); + p++; + } + } +} + +enum { + SPI_FOUND_SOP, + SPI_FOUND_EOP, + SPI_NOT_FOUND, +}; + +static int resp_find_sop_eop(unsigned char *resp, unsigned int len, + int flags) +{ + int ret = SPI_NOT_FOUND; + + unsigned char *b = resp; + + /* find SOP */ + if (flags != SPI_FOUND_SOP) { + while (b < resp + len && *b != SPI_PACKET_SOP) + b++; + + if (*b != SPI_PACKET_SOP) + goto done; + + ret = SPI_FOUND_SOP; + } + + /* find EOP */ + while (b < resp + len && *b != SPI_PACKET_EOP) + b++; + + if (*b != SPI_PACKET_EOP) + goto done; + + ret = SPI_FOUND_EOP; + +done: + return ret; +} + +static int byte_to_core_convert(struct spi_transaction_dev *dev, + unsigned int send_len, unsigned char *send_data, + unsigned int resp_len, unsigned char *resp_data, + unsigned int *valid_resp_len) +{ + unsigned int i; + int ret = 0; + unsigned char *send_packet = dev->buffer->bytes_send; + unsigned char *resp_packet = dev->buffer->bytes_resp; + unsigned char *p; + unsigned char current_byte; + unsigned char *tx_buffer; + unsigned int tx_len = 0; + unsigned char *rx_buffer; + unsigned int rx_len = 0; + int retry = 0; + int spi_flags; + unsigned int resp_max_len = 2 * resp_len; + + print_buffer("before bytes:", send_data, send_len); + + p = send_packet; + + for (i = 0; i < send_len; i++) { + current_byte = send_data[i]; + switch (current_byte) { + case SPI_BYTE_IDLE: + *p++ = SPI_BYTE_IDLE; + *p++ = xor_20(current_byte); + break; + case SPI_BYTE_ESC: + *p++ = SPI_BYTE_ESC; + *p++ = xor_20(current_byte); + break; + default: + *p++ = current_byte; + break; + } + } + + print_buffer("before spi:", send_packet, p-send_packet); + + reorder_phy_data(32, send_packet, p - send_packet); + + print_buffer("after order to spi:", send_packet, p-send_packet); + + /* call spi */ + tx_buffer = send_packet; + tx_len = p - send_packet; + rx_buffer = resp_packet; + rx_len = resp_max_len; + spi_flags = SPI_NOT_FOUND; + +read_again: + ret = spi_command(dev->dev, dev->chipselect, tx_len, tx_buffer, + rx_len, rx_buffer); + if (ret) + return -EBUSY; + + print_buffer("read from spi:", rx_buffer, rx_len); + + /* look for SOP firstly*/ + ret = resp_find_sop_eop(rx_buffer, rx_len - 1, spi_flags); + if (ret != SPI_FOUND_EOP) { + tx_buffer = NULL; + tx_len = 0; + if (retry++ > 10) { + dev_err(NULL, "cannot found valid data from SPI\n"); + return -EBUSY; + } + + if (ret == SPI_FOUND_SOP) { + rx_buffer += rx_len; + resp_max_len += rx_len; + } + + spi_flags = ret; + goto read_again; + } + + print_buffer("found valid data:", resp_packet, resp_max_len); + + /* analyze response packet */ + i = 0; + p = resp_data; + while (i < resp_max_len) { + current_byte = resp_packet[i]; + switch (current_byte) { + case SPI_BYTE_IDLE: + i++; + break; + case SPI_BYTE_ESC: + i++; + current_byte = resp_packet[i]; + *p++ = xor_20(current_byte); + i++; + break; + default: + *p++ = current_byte; + i++; + break; + } + } + + /* receive "4a" means the SPI is idle, not valid data */ + *valid_resp_len = p - resp_data; + if (*valid_resp_len == 0) { + dev_err(NULL, "error: repond package without valid data\n"); + return -EINVAL; + } + + return 0; +} + +static int packet_to_byte_conver(struct spi_transaction_dev *dev, + unsigned int send_len, unsigned char *send_buf, + unsigned int resp_len, unsigned char *resp_buf, + unsigned int *valid) +{ + int ret = 0; + unsigned int i; + unsigned char current_byte; + unsigned int resp_max_len; + unsigned char *send_packet = dev->buffer->packet_send; + unsigned char *resp_packet = dev->buffer->packet_resp; + unsigned char *p; + unsigned int valid_resp_len = 0; + + print_buffer("before packet:", send_buf, send_len); + + resp_max_len = 2 * resp_len + 4; + + p = send_packet; + + /* SOP header */ + *p++ = SPI_PACKET_SOP; + + *p++ = SPI_PACKET_CHANNEL; + *p++ = 0; + + /* append the data into a packet */ + for (i = 0; i < send_len; i++) { + current_byte = send_buf[i]; + + /* EOP for last byte */ + if (i == send_len - 1) + *p++ = SPI_PACKET_EOP; + + switch (current_byte) { + case SPI_PACKET_SOP: + case SPI_PACKET_EOP: + case SPI_PACKET_CHANNEL: + case SPI_PACKET_ESC: + *p++ = SPI_PACKET_ESC; + *p++ = xor_20(current_byte); + break; + default: + *p++ = current_byte; + } + } + + ret = byte_to_core_convert(dev, p - send_packet, + send_packet, resp_max_len, resp_packet, + &valid_resp_len); + if (ret) + return -EBUSY; + + print_buffer("after byte conver:", resp_packet, valid_resp_len); + + /* analyze the response packet */ + p = resp_buf; + + /* look for SOP */ + for (i = 0; i < valid_resp_len; i++) { + if (resp_packet[i] == SPI_PACKET_SOP) + break; + } + + if (i == valid_resp_len) { + dev_err(NULL, "error on analyze response packet 0x%x\n", + resp_packet[i]); + return -EINVAL; + } + + i++; + + /* continue parsing data after SOP */ + while (i < valid_resp_len) { + current_byte = resp_packet[i]; + + switch (current_byte) { + case SPI_PACKET_ESC: + case SPI_PACKET_CHANNEL: + case SPI_PACKET_SOP: + i++; + current_byte = resp_packet[i]; + *p++ = xor_20(current_byte); + i++; + break; + case SPI_PACKET_EOP: + i++; + current_byte = resp_packet[i]; + if (current_byte == SPI_PACKET_ESC || + current_byte == SPI_PACKET_CHANNEL || + current_byte == SPI_PACKET_SOP) { + i++; + current_byte = resp_packet[i]; + *p++ = xor_20(current_byte); + } else + *p++ = current_byte; + i = valid_resp_len; + break; + default: + *p++ = current_byte; + i++; + } + + } + + *valid = p - resp_buf; + + print_buffer("after packet:", resp_buf, *valid); + + return ret; +} + +static int do_transaction(struct spi_transaction_dev *dev, unsigned int addr, + unsigned int size, unsigned char *data, + unsigned int trans_type) +{ + + struct spi_tran_header header; + unsigned char *transaction = dev->buffer->tran_send; + unsigned char *response = dev->buffer->tran_resp; + unsigned char *p; + int ret = 0; + unsigned int i; + unsigned int valid_len = 0; + + /* make transacation header */ + INIT_SPI_TRAN_HEADER(trans_type, size, addr); + + /* fill the header */ + p = transaction; + opae_memcpy(p, &header, sizeof(struct spi_tran_header)); + p = p + sizeof(struct spi_tran_header); + + switch (trans_type) { + case SPI_TRAN_SEQ_WRITE: + case SPI_TRAN_NON_SEQ_WRITE: + for (i = 0; i < size; i++) + *p++ = *data++; + + ret = packet_to_byte_conver(dev, size + HEADER_LEN, + transaction, RESPONSE_LEN, response, + &valid_len); + if (ret) + return -EBUSY; + + /* check the result */ + if (size != ((unsigned int)(response[2] & 0xff) << 8 | + (unsigned int)(response[3] & 0xff))) + ret = -EBUSY; + + break; + case SPI_TRAN_SEQ_READ: + case SPI_TRAN_NON_SEQ_READ: + ret = packet_to_byte_conver(dev, HEADER_LEN, + transaction, size, response, + &valid_len); + if (ret || valid_len != size) + return -EBUSY; + + for (i = 0; i < size; i++) + *data++ = *response++; + + ret = 0; + break; + } + + return ret; +} + +int spi_transaction_read(struct spi_transaction_dev *dev, unsigned int addr, + unsigned int size, unsigned char *data) +{ + return do_transaction(dev, addr, size, data, + (size > SPI_REG_BYTES) ? + SPI_TRAN_SEQ_READ : SPI_TRAN_NON_SEQ_READ); +} + +int spi_transaction_write(struct spi_transaction_dev *dev, unsigned int addr, + unsigned int size, unsigned char *data) +{ + return do_transaction(dev, addr, size, data, + (size > SPI_REG_BYTES) ? + SPI_TRAN_SEQ_WRITE : SPI_TRAN_NON_SEQ_WRITE); +} + +struct spi_transaction_dev *spi_transaction_init(struct altera_spi_device *dev, + int chipselect) +{ + struct spi_transaction_dev *spi_tran_dev; + + spi_tran_dev = opae_malloc(sizeof(struct spi_transaction_dev)); + if (!spi_tran_dev) + return NULL; + + spi_tran_dev->dev = dev; + spi_tran_dev->chipselect = chipselect; + + spi_tran_dev->buffer = opae_malloc(sizeof(struct spi_tran_buffer)); + if (!spi_tran_dev->buffer) { + opae_free(spi_tran_dev); + return NULL; + } + + return spi_tran_dev; +} + +void spi_transaction_remove(struct spi_transaction_dev *dev) +{ + if (dev && dev->buffer) + opae_free(dev->buffer); + if (dev) + opae_free(dev); +} diff --git a/drivers/raw/ifpga_rawdev/base/osdep_rte/osdep_generic.h b/drivers/raw/ifpga_rawdev/base/osdep_rte/osdep_generic.h index 3d9a0cad69..3ff49a8926 100644 --- a/drivers/raw/ifpga_rawdev/base/osdep_rte/osdep_generic.h +++ b/drivers/raw/ifpga_rawdev/base/osdep_rte/osdep_generic.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #define dev_printf(level, fmt, args...) \ @@ -43,5 +44,18 @@ #define spinlock_lock(x) rte_spinlock_lock(x) #define spinlock_unlock(x) rte_spinlock_unlock(x) +#define cpu_to_be16(o) rte_cpu_to_be_16(o) +#define cpu_to_be32(o) rte_cpu_to_be_32(o) +#define cpu_to_be64(o) rte_cpu_to_be_64(o) +#define cpu_to_le16(o) rte_cpu_to_le_16(o) +#define cpu_to_le32(o) rte_cpu_to_le_32(o) +#define cpu_to_le64(o) rte_cpu_to_le_64(o) + #define opae_memcpy(a, b, c) rte_memcpy((a), (b), (c)) + +static inline unsigned long msecs_to_timer_cycles(unsigned int m) +{ + return rte_get_timer_hz() * (m / 1000); +} + #endif