diff options
Diffstat (limited to 'drivers/fpga')
-rw-r--r-- | drivers/fpga/Kconfig | 2 | ||||
-rw-r--r-- | drivers/fpga/dfl-afu-region.c | 1 | ||||
-rw-r--r-- | drivers/fpga/dfl-afu.h | 2 | ||||
-rw-r--r-- | drivers/fpga/dfl-fme-perf.c | 2 | ||||
-rw-r--r-- | drivers/fpga/dfl-fme-pr.c | 4 | ||||
-rw-r--r-- | drivers/fpga/dfl-fme-pr.h | 2 | ||||
-rw-r--r-- | drivers/fpga/dfl.c | 253 | ||||
-rw-r--r-- | drivers/fpga/dfl.h | 43 | ||||
-rw-r--r-- | drivers/fpga/fpga-bridge.c | 11 | ||||
-rw-r--r-- | drivers/fpga/intel-m10-bmc-sec-update.c | 432 | ||||
-rw-r--r-- | drivers/fpga/microchip-spi.c | 145 | ||||
-rw-r--r-- | drivers/fpga/stratix10-soc.c | 4 |
12 files changed, 621 insertions, 280 deletions
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 6ce143dafd04..0a00763b9f28 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -246,7 +246,7 @@ config FPGA_MGR_VERSAL_FPGA config FPGA_M10_BMC_SEC_UPDATE tristate "Intel MAX10 BMC Secure Update driver" - depends on MFD_INTEL_M10_BMC + depends on MFD_INTEL_M10_BMC_CORE select FW_LOADER select FW_UPLOAD help diff --git a/drivers/fpga/dfl-afu-region.c b/drivers/fpga/dfl-afu-region.c index 0804b7a0c298..2e7b41629406 100644 --- a/drivers/fpga/dfl-afu-region.c +++ b/drivers/fpga/dfl-afu-region.c @@ -39,6 +39,7 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu, /** * afu_mmio_region_add - add a mmio region to given feature dev. * + * @pdata: afu platform device's pdata. * @region_index: region index. * @region_size: region size. * @phys: region's physical address of this region. diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h index e5020e2b1f3d..674e9772f0ea 100644 --- a/drivers/fpga/dfl-afu.h +++ b/drivers/fpga/dfl-afu.h @@ -41,7 +41,7 @@ struct dfl_afu_mmio_region { }; /** - * struct fpga_afu_dma_region - afu DMA region data structure + * struct dfl_afu_dma_region - afu DMA region data structure * * @user_addr: region userspace virtual address. * @length: region length. diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 587c82be12f7..7422d2bc6f37 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -141,7 +141,7 @@ * @fab_port_id: used to indicate current working mode of fabric counters. * @fab_lock: lock to protect fabric counters working mode. * @cpu: active CPU to which the PMU is bound for accesses. - * @cpuhp_node: node for CPU hotplug notifier link. + * @node: node for CPU hotplug notifier link. * @cpuhp_state: state for CPU hotplug notification; */ struct fme_perf_priv { diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index d61ce9a18879..cdcf6dea4cc9 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -164,7 +164,7 @@ free_exit: /** * dfl_fme_create_mgr - create fpga mgr platform device as child device - * + * @feature: sub feature info * @pdata: fme platform_device's pdata * * Return: mgr platform device if successful, and error code otherwise. @@ -273,7 +273,7 @@ static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) } /** - * dfl_fme_destroy_bridge - destroy all fpga bridge platform device + * dfl_fme_destroy_bridges - destroy all fpga bridge platform device * @pdata: fme platform device's pdata */ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h index 096a699089d3..761f80f63312 100644 --- a/drivers/fpga/dfl-fme-pr.h +++ b/drivers/fpga/dfl-fme-pr.h @@ -58,7 +58,7 @@ struct dfl_fme_bridge { }; /** - * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device. + * struct dfl_fme_br_pdata - platform data for FME bridge platform device. * * @cdev: container device. * @port_id: port id. diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index b9aae85ba930..dd7a783d53b5 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -13,6 +13,7 @@ #include <linux/dfl.h> #include <linux/fpga-dfl.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/uaccess.h> #include "dfl.h" @@ -45,7 +46,7 @@ static const char *dfl_pdata_key_strings[DFL_ID_MAX] = { }; /** - * dfl_dev_info - dfl feature device information. + * struct dfl_dev_info - dfl feature device information. * @name: name string of the feature platform device. * @dfh_id: id value in Device Feature Header (DFH) register by DFL spec. * @id: idr id of the feature dev. @@ -67,7 +68,7 @@ static struct dfl_dev_info dfl_devs[] = { }; /** - * dfl_chardev_info - chardev information of dfl feature device + * struct dfl_chardev_info - chardev information of dfl feature device * @name: nmae string of the char device. * @devt: devt of the char device. */ @@ -293,9 +294,9 @@ static void dfl_bus_remove(struct device *dev) ddrv->remove(ddev); } -static int dfl_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +static int dfl_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) { - struct dfl_device *ddev = to_dfl_dev(dev); + const struct dfl_device *ddev = to_dfl_dev(dev); return add_uevent_var(env, "MODALIAS=dfl:t%04Xf%04X", ddev->type, ddev->feature_id); @@ -342,6 +343,8 @@ static void release_dfl_dev(struct device *dev) if (ddev->mmio_res.parent) release_resource(&ddev->mmio_res); + kfree(ddev->params); + ida_free(&dfl_device_ida, ddev->id); kfree(ddev->irqs); kfree(ddev); @@ -380,7 +383,16 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata, ddev->type = feature_dev_id_type(pdev); ddev->feature_id = feature->id; ddev->revision = feature->revision; + ddev->dfh_version = feature->dfh_version; ddev->cdev = pdata->dfl_cdev; + if (feature->param_size) { + ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL); + if (!ddev->params) { + ret = -ENOMEM; + goto put_dev; + } + ddev->param_size = feature->param_size; + } /* add mmio resource */ parent_res = &pdev->resource[feature->resource_index]; @@ -708,20 +720,27 @@ struct build_feature_devs_info { * struct dfl_feature_info - sub feature info collected during feature dev build * * @fid: id of this sub feature. + * @revision: revision of this sub feature + * @dfh_version: version of Device Feature Header (DFH) * @mmio_res: mmio resource of this sub feature. * @ioaddr: mapped base address of mmio resource. * @node: node in sub_features linked list. * @irq_base: start of irq index in this sub feature. * @nr_irqs: number of irqs of this sub feature. + * @param_size: size DFH parameters. + * @params: DFH parameter data. */ struct dfl_feature_info { u16 fid; u8 revision; + u8 dfh_version; struct resource mmio_res; void __iomem *ioaddr; struct list_head node; unsigned int irq_base; unsigned int nr_irqs; + unsigned int param_size; + u64 params[]; }; static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, @@ -797,7 +816,17 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) feature->dev = fdev; feature->id = finfo->fid; feature->revision = finfo->revision; + feature->dfh_version = finfo->dfh_version; + + if (finfo->param_size) { + feature->params = devm_kmemdup(binfo->dev, + finfo->params, finfo->param_size, + GFP_KERNEL); + if (!feature->params) + return -ENOMEM; + feature->param_size = finfo->param_size; + } /* * the FIU header feature has some fundamental functions (sriov * set, port enable/disable) needed for the dfl bus device and @@ -934,56 +963,115 @@ static u16 feature_id(u64 value) return 0; } +static u64 *find_param(u64 *params, resource_size_t max, int param_id) +{ + u64 *end = params + max / sizeof(u64); + u64 v, next; + + while (params < end) { + v = *params; + if (param_id == FIELD_GET(DFHv1_PARAM_HDR_ID, v)) + return params; + + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) + break; + + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); + params += next; + } + + return NULL; +} + +/** + * dfh_find_param() - find parameter block for the given parameter id + * @dfl_dev: dfl device + * @param_id: id of dfl parameter + * @psize: destination to store size of parameter data in bytes + * + * Return: pointer to start of parameter data, PTR_ERR otherwise. + */ +void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *psize) +{ + u64 *phdr = find_param(dfl_dev->params, dfl_dev->param_size, param_id); + + if (!phdr) + return ERR_PTR(-ENOENT); + + if (psize) + *psize = (FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, *phdr) - 1) * sizeof(u64); + + return phdr + 1; +} +EXPORT_SYMBOL_GPL(dfh_find_param); + static int parse_feature_irqs(struct build_feature_devs_info *binfo, - resource_size_t ofst, u16 fid, - unsigned int *irq_base, unsigned int *nr_irqs) + resource_size_t ofst, struct dfl_feature_info *finfo) { void __iomem *base = binfo->ioaddr + ofst; unsigned int i, ibase, inr = 0; + void *params = finfo->params; enum dfl_id_type type; + u16 fid = finfo->fid; int virq; + u64 *p; u64 v; - type = feature_dev_id_type(binfo->feature_dev); + switch (finfo->dfh_version) { + case 0: + /* + * DFHv0 only provides MMIO resource information for each feature + * in the DFL header. There is no generic interrupt information. + * Instead, features with interrupt functionality provide + * the information in feature specific registers. + */ + type = feature_dev_id_type(binfo->feature_dev); + if (type == PORT_ID) { + switch (fid) { + case PORT_FEATURE_ID_UINT: + v = readq(base + PORT_UINT_CAP); + ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); + inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); + break; + case PORT_FEATURE_ID_ERROR: + v = readq(base + PORT_ERROR_CAP); + ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); + break; + } + } else if (type == FME_ID) { + switch (fid) { + case FME_FEATURE_ID_GLOBAL_ERR: + v = readq(base + FME_ERROR_CAP); + ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); + break; + } + } + break; - /* - * Ideally DFL framework should only read info from DFL header, but - * current version DFL only provides mmio resources information for - * each feature in DFL Header, no field for interrupt resources. - * Interrupt resource information is provided by specific mmio - * registers of each private feature which supports interrupt. So in - * order to parse and assign irq resources, DFL framework has to look - * into specific capability registers of these private features. - * - * Once future DFL version supports generic interrupt resource - * information in common DFL headers, the generic interrupt parsing - * code will be added. But in order to be compatible to old version - * DFL, the driver may still fall back to these quirks. - */ - if (type == PORT_ID) { - switch (fid) { - case PORT_FEATURE_ID_UINT: - v = readq(base + PORT_UINT_CAP); - ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); - inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); - break; - case PORT_FEATURE_ID_ERROR: - v = readq(base + PORT_ERROR_CAP); - ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); - inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); + case 1: + /* + * DFHv1 provides interrupt resource information in DFHv1 + * parameter blocks. + */ + p = find_param(params, finfo->param_size, DFHv1_PARAM_ID_MSI_X); + if (!p) break; - } - } else if (type == FME_ID) { - if (fid == FME_FEATURE_ID_GLOBAL_ERR) { - v = readq(base + FME_ERROR_CAP); - ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); - inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); - } + + p++; + ibase = FIELD_GET(DFHv1_PARAM_MSI_X_STARTV, *p); + inr = FIELD_GET(DFHv1_PARAM_MSI_X_NUMV, *p); + break; + + default: + dev_warn(binfo->dev, "unexpected DFH version %d\n", finfo->dfh_version); + break; } if (!inr) { - *irq_base = 0; - *nr_irqs = 0; + finfo->irq_base = 0; + finfo->nr_irqs = 0; return 0; } @@ -1006,12 +1094,37 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo, } } - *irq_base = ibase; - *nr_irqs = inr; + finfo->irq_base = ibase; + finfo->nr_irqs = inr; return 0; } +static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) +{ + int size = 0; + u64 v, next; + + if (!FIELD_GET(DFHv1_CSR_SIZE_GRP_HAS_PARAMS, + readq(dfh_base + DFHv1_CSR_SIZE_GRP))) + return 0; + + while (size + DFHv1_PARAM_HDR < max) { + v = readq(dfh_base + DFHv1_PARAM_HDR + size); + + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); + if (!next) + return -EINVAL; + + size += next * sizeof(u64); + + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) + return size; + } + + return -ENOENT; +} + /* * when create sub feature instances, for private features, it doesn't need * to provide resource size and feature id as they could be read from DFH @@ -1023,39 +1136,69 @@ static int create_feature_instance(struct build_feature_devs_info *binfo, resource_size_t ofst, resource_size_t size, u16 fid) { - unsigned int irq_base, nr_irqs; struct dfl_feature_info *finfo; + resource_size_t start, end; + int dfh_psize = 0; u8 revision = 0; + u64 v, addr_off; + u8 dfh_ver = 0; int ret; - u64 v; if (fid != FEATURE_ID_AFU) { v = readq(binfo->ioaddr + ofst); revision = FIELD_GET(DFH_REVISION, v); - + dfh_ver = FIELD_GET(DFH_VERSION, v); /* read feature size and id if inputs are invalid */ size = size ? size : feature_size(v); fid = fid ? fid : feature_id(v); + if (dfh_ver == 1) { + dfh_psize = dfh_get_param_size(binfo->ioaddr + ofst, size); + if (dfh_psize < 0) { + dev_err(binfo->dev, + "failed to read size of DFHv1 parameters %d\n", + dfh_psize); + return dfh_psize; + } + dev_dbg(binfo->dev, "dfhv1_psize %d\n", dfh_psize); + } } if (binfo->len - ofst < size) return -EINVAL; - ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs); - if (ret) - return ret; - - finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); + finfo = kzalloc(struct_size(finfo, params, dfh_psize / sizeof(u64)), GFP_KERNEL); if (!finfo) return -ENOMEM; + memcpy_fromio(finfo->params, binfo->ioaddr + ofst + DFHv1_PARAM_HDR, dfh_psize); + finfo->param_size = dfh_psize; + finfo->fid = fid; finfo->revision = revision; - finfo->mmio_res.start = binfo->start + ofst; - finfo->mmio_res.end = finfo->mmio_res.start + size - 1; + finfo->dfh_version = dfh_ver; + if (dfh_ver == 1) { + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_ADDR); + addr_off = FIELD_GET(DFHv1_CSR_ADDR_MASK, v); + if (FIELD_GET(DFHv1_CSR_ADDR_REL, v)) + start = addr_off << 1; + else + start = binfo->start + ofst + addr_off; + + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_SIZE_GRP); + end = start + FIELD_GET(DFHv1_CSR_SIZE_GRP_SIZE, v) - 1; + } else { + start = binfo->start + ofst; + end = start + size - 1; + } finfo->mmio_res.flags = IORESOURCE_MEM; - finfo->irq_base = irq_base; - finfo->nr_irqs = nr_irqs; + finfo->mmio_res.start = start; + finfo->mmio_res.end = end; + + ret = parse_feature_irqs(binfo, ofst, finfo); + if (ret) { + kfree(finfo); + return ret; + } list_add_tail(&finfo->node, &binfo->sub_features); binfo->feature_num++; diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 06cfcd5e84bb..1d724a28f00a 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -74,11 +74,47 @@ #define DFH_REVISION GENMASK_ULL(15, 12) /* Feature revision */ #define DFH_NEXT_HDR_OFST GENMASK_ULL(39, 16) /* Offset to next DFH */ #define DFH_EOL BIT_ULL(40) /* End of list */ +#define DFH_VERSION GENMASK_ULL(59, 52) /* DFH version */ #define DFH_TYPE GENMASK_ULL(63, 60) /* Feature type */ #define DFH_TYPE_AFU 1 #define DFH_TYPE_PRIVATE 3 #define DFH_TYPE_FIU 4 +/* + * DFHv1 Register Offset definitons + * In DHFv1, DFH + GUID + CSR_START + CSR_SIZE_GROUP + PARAM_HDR + PARAM_DATA + * as common header registers + */ +#define DFHv1_CSR_ADDR 0x18 /* CSR Register start address */ +#define DFHv1_CSR_SIZE_GRP 0x20 /* Size of Reg Block and Group/tag */ +#define DFHv1_PARAM_HDR 0x28 /* Optional First Param header */ + +/* + * CSR Rel Bit, 1'b0 = relative (offset from feature DFH start), + * 1'b1 = absolute (ARM or other non-PCIe use) + */ +#define DFHv1_CSR_ADDR_REL BIT_ULL(0) + +/* CSR Header Register Bit Definitions */ +#define DFHv1_CSR_ADDR_MASK GENMASK_ULL(63, 1) /* 63:1 of CSR address */ + +/* CSR SIZE Goup Register Bit Definitions */ +#define DFHv1_CSR_SIZE_GRP_INSTANCE_ID GENMASK_ULL(15, 0) /* Enumeration instantiated IP */ +#define DFHv1_CSR_SIZE_GRP_GROUPING_ID GENMASK_ULL(30, 16) /* Group Features/interfaces */ +#define DFHv1_CSR_SIZE_GRP_HAS_PARAMS BIT_ULL(31) /* Presence of Parameters */ +#define DFHv1_CSR_SIZE_GRP_SIZE GENMASK_ULL(63, 32) /* Size of CSR Block in bytes */ + +/* PARAM Header Register Bit Definitions */ +#define DFHv1_PARAM_HDR_ID GENMASK_ULL(15, 0) /* Id of this Param */ +#define DFHv1_PARAM_HDR_VER GENMASK_ULL(31, 16) /* Version Param */ +#define DFHv1_PARAM_HDR_NEXT_OFFSET GENMASK_ULL(63, 35) /* Offset of next Param */ +#define DFHv1_PARAM_HDR_NEXT_EOP BIT_ULL(32) +#define DFHv1_PARAM_DATA 0x08 /* Offset of Param data from Param header */ + +#define DFHv1_PARAM_ID_MSI_X 0x1 +#define DFHv1_PARAM_MSI_X_NUMV GENMASK_ULL(63, 32) +#define DFHv1_PARAM_MSI_X_STARTV GENMASK_ULL(31, 0) + /* Next AFU Register Bitfield */ #define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */ @@ -231,6 +267,7 @@ struct dfl_feature_irq_ctx { * * @dev: ptr to pdev of the feature device which has the sub feature. * @id: sub feature id. + * @revision: revision of this sub feature. * @resource_index: each sub feature has one mmio resource for its registers. * this index is used to find its mmio resource from the * feature dev (platform device)'s resources. @@ -240,6 +277,9 @@ struct dfl_feature_irq_ctx { * @ops: ops of this sub feature. * @ddev: ptr to the dfl device of this sub feature. * @priv: priv data of this feature. + * @dfh_version: version of the DFH + * @param_size: size of dfh parameters + * @params: point to memory copy of dfh parameters */ struct dfl_feature { struct platform_device *dev; @@ -252,6 +292,9 @@ struct dfl_feature { const struct dfl_feature_ops *ops; struct dfl_device *ddev; void *priv; + u8 dfh_version; + unsigned int param_size; + void *params; }; #define FEATURE_DEV_ID_UNUSED (-1) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 727704431f61..5cd40acab5bf 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -293,12 +293,15 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpga_bridge *bridge = to_fpga_bridge(dev); - int enable = 1; + int state = 1; - if (bridge->br_ops && bridge->br_ops->enable_show) - enable = bridge->br_ops->enable_show(bridge); + if (bridge->br_ops && bridge->br_ops->enable_show) { + state = bridge->br_ops->enable_show(bridge); + if (state < 0) + return state; + } - return sprintf(buf, "%s\n", enable ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); } static DEVICE_ATTR_RO(name); diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 79d48852825e..f0acedc80182 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -14,6 +14,12 @@ #include <linux/platform_device.h> #include <linux/slab.h> +struct m10bmc_sec; + +struct m10bmc_sec_ops { + int (*rsu_status)(struct m10bmc_sec *sec); +}; + struct m10bmc_sec { struct device *dev; struct intel_m10bmc *m10bmc; @@ -21,6 +27,7 @@ struct m10bmc_sec { char *fw_name; u32 fw_name_id; bool cancel_request; + const struct m10bmc_sec_ops *ops; }; static DEFINE_XARRAY_ALLOC(fw_upload_xa); @@ -31,6 +38,71 @@ static DEFINE_XARRAY_ALLOC(fw_upload_xa); #define REH_MAGIC GENMASK(15, 0) #define REH_SHA_NUM_BYTES GENMASK(31, 16) +static int m10bmc_sec_write(struct m10bmc_sec *sec, const u8 *buf, u32 offset, u32 size) +{ + struct intel_m10bmc *m10bmc = sec->m10bmc; + unsigned int stride = regmap_get_reg_stride(m10bmc->regmap); + u32 write_count = size / stride; + u32 leftover_offset = write_count * stride; + u32 leftover_size = size - leftover_offset; + u32 leftover_tmp = 0; + int ret; + + if (sec->m10bmc->flash_bulk_ops) + return sec->m10bmc->flash_bulk_ops->write(m10bmc, buf, offset, size); + + if (WARN_ON_ONCE(stride > sizeof(leftover_tmp))) + return -EINVAL; + + ret = regmap_bulk_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset, + buf + offset, write_count); + if (ret) + return ret; + + /* If size is not aligned to stride, handle the remainder bytes with regmap_write() */ + if (leftover_size) { + memcpy(&leftover_tmp, buf + leftover_offset, leftover_size); + ret = regmap_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset + leftover_offset, + leftover_tmp); + if (ret) + return ret; + } + + return 0; +} + +static int m10bmc_sec_read(struct m10bmc_sec *sec, u8 *buf, u32 addr, u32 size) +{ + struct intel_m10bmc *m10bmc = sec->m10bmc; + unsigned int stride = regmap_get_reg_stride(m10bmc->regmap); + u32 read_count = size / stride; + u32 leftover_offset = read_count * stride; + u32 leftover_size = size - leftover_offset; + u32 leftover_tmp; + int ret; + + if (sec->m10bmc->flash_bulk_ops) + return sec->m10bmc->flash_bulk_ops->read(m10bmc, buf, addr, size); + + if (WARN_ON_ONCE(stride > sizeof(leftover_tmp))) + return -EINVAL; + + ret = regmap_bulk_read(m10bmc->regmap, addr, buf, read_count); + if (ret) + return ret; + + /* If size is not aligned to stride, handle the remainder bytes with regmap_read() */ + if (leftover_size) { + ret = regmap_read(m10bmc->regmap, addr + leftover_offset, &leftover_tmp); + if (ret) + return ret; + memcpy(buf + leftover_offset, &leftover_tmp, leftover_size); + } + + return 0; +} + + static ssize_t show_root_entry_hash(struct device *dev, u32 exp_magic, u32 prog_addr, u32 reh_addr, char *buf) @@ -38,11 +110,9 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, struct m10bmc_sec *sec = dev_get_drvdata(dev); int sha_num_bytes, i, ret, cnt = 0; u8 hash[REH_SHA384_SIZE]; - unsigned int stride; u32 magic; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); - ret = m10bmc_raw_read(sec->m10bmc, prog_addr, &magic); + ret = m10bmc_sec_read(sec, (u8 *)&magic, prog_addr, sizeof(magic)); if (ret) return ret; @@ -50,19 +120,16 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, return sysfs_emit(buf, "hash not programmed\n"); sha_num_bytes = FIELD_GET(REH_SHA_NUM_BYTES, magic) / 8; - if ((sha_num_bytes % stride) || - (sha_num_bytes != REH_SHA256_SIZE && - sha_num_bytes != REH_SHA384_SIZE)) { + if (sha_num_bytes != REH_SHA256_SIZE && + sha_num_bytes != REH_SHA384_SIZE) { dev_err(sec->dev, "%s bad sha num bytes %d\n", __func__, sha_num_bytes); return -EINVAL; } - ret = regmap_bulk_read(sec->m10bmc->regmap, reh_addr, - hash, sha_num_bytes / stride); + ret = m10bmc_sec_read(sec, hash, reh_addr, sha_num_bytes); if (ret) { - dev_err(dev, "failed to read root entry hash: %x cnt %x: %d\n", - reh_addr, sha_num_bytes / stride, ret); + dev_err(dev, "failed to read root entry hash\n"); return ret; } @@ -73,16 +140,24 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, return cnt; } -#define DEVICE_ATTR_SEC_REH_RO(_name, _magic, _prog_addr, _reh_addr) \ +#define DEVICE_ATTR_SEC_REH_RO(_name) \ static ssize_t _name##_root_entry_hash_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ -{ return show_root_entry_hash(dev, _magic, _prog_addr, _reh_addr, buf); } \ +{ \ + struct m10bmc_sec *sec = dev_get_drvdata(dev); \ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \ + \ + return show_root_entry_hash(dev, csr_map->_name##_magic, \ + csr_map->_name##_prog_addr, \ + csr_map->_name##_reh_addr, \ + buf); \ +} \ static DEVICE_ATTR_RO(_name##_root_entry_hash) -DEVICE_ATTR_SEC_REH_RO(bmc, BMC_PROG_MAGIC, BMC_PROG_ADDR, BMC_REH_ADDR); -DEVICE_ATTR_SEC_REH_RO(sr, SR_PROG_MAGIC, SR_PROG_ADDR, SR_REH_ADDR); -DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR); +DEVICE_ATTR_SEC_REH_RO(bmc); +DEVICE_ATTR_SEC_REH_RO(sr); +DEVICE_ATTR_SEC_REH_RO(pr); #define CSK_BIT_LEN 128U #define CSK_32ARRAY_SIZE DIV_ROUND_UP(CSK_BIT_LEN, 32) @@ -90,27 +165,16 @@ DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR); static ssize_t show_canceled_csk(struct device *dev, u32 addr, char *buf) { - unsigned int i, stride, size = CSK_32ARRAY_SIZE * sizeof(u32); + unsigned int i, size = CSK_32ARRAY_SIZE * sizeof(u32); struct m10bmc_sec *sec = dev_get_drvdata(dev); DECLARE_BITMAP(csk_map, CSK_BIT_LEN); __le32 csk_le32[CSK_32ARRAY_SIZE]; u32 csk32[CSK_32ARRAY_SIZE]; int ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); - if (size % stride) { - dev_err(sec->dev, - "CSK vector size (0x%x) not aligned to stride (0x%x)\n", - size, stride); - WARN_ON_ONCE(1); - return -EINVAL; - } - - ret = regmap_bulk_read(sec->m10bmc->regmap, addr, csk_le32, - size / stride); + ret = m10bmc_sec_read(sec, (u8 *)&csk_le32, addr, size); if (ret) { - dev_err(sec->dev, "failed to read CSK vector: %x cnt %x: %d\n", - addr, size / stride, ret); + dev_err(sec->dev, "failed to read CSK vector\n"); return ret; } @@ -122,18 +186,25 @@ show_canceled_csk(struct device *dev, u32 addr, char *buf) return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN); } -#define DEVICE_ATTR_SEC_CSK_RO(_name, _addr) \ +#define DEVICE_ATTR_SEC_CSK_RO(_name) \ static ssize_t _name##_canceled_csks_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ -{ return show_canceled_csk(dev, _addr, buf); } \ +{ \ + struct m10bmc_sec *sec = dev_get_drvdata(dev); \ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \ + \ + return show_canceled_csk(dev, \ + csr_map->_name##_prog_addr + CSK_VEC_OFFSET, \ + buf); \ +} \ static DEVICE_ATTR_RO(_name##_canceled_csks) #define CSK_VEC_OFFSET 0x34 -DEVICE_ATTR_SEC_CSK_RO(bmc, BMC_PROG_ADDR + CSK_VEC_OFFSET); -DEVICE_ATTR_SEC_CSK_RO(sr, SR_PROG_ADDR + CSK_VEC_OFFSET); -DEVICE_ATTR_SEC_CSK_RO(pr, PR_PROG_ADDR + CSK_VEC_OFFSET); +DEVICE_ATTR_SEC_CSK_RO(bmc); +DEVICE_ATTR_SEC_CSK_RO(sr); +DEVICE_ATTR_SEC_CSK_RO(pr); #define FLASH_COUNT_SIZE 4096 /* count stored as inverted bit vector */ @@ -141,31 +212,21 @@ static ssize_t flash_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct m10bmc_sec *sec = dev_get_drvdata(dev); - unsigned int stride, num_bits; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + unsigned int num_bits; u8 *flash_buf; int cnt, ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); num_bits = FLASH_COUNT_SIZE * 8; - if (FLASH_COUNT_SIZE % stride) { - dev_err(sec->dev, - "FLASH_COUNT_SIZE (0x%x) not aligned to stride (0x%x)\n", - FLASH_COUNT_SIZE, stride); - WARN_ON_ONCE(1); - return -EINVAL; - } - flash_buf = kmalloc(FLASH_COUNT_SIZE, GFP_KERNEL); if (!flash_buf) return -ENOMEM; - ret = regmap_bulk_read(sec->m10bmc->regmap, STAGING_FLASH_COUNT, - flash_buf, FLASH_COUNT_SIZE / stride); + ret = m10bmc_sec_read(sec, flash_buf, csr_map->rsu_update_counter, + FLASH_COUNT_SIZE); if (ret) { - dev_err(sec->dev, - "failed to read flash count: %x cnt %x: %d\n", - STAGING_FLASH_COUNT, FLASH_COUNT_SIZE / stride, ret); + dev_err(sec->dev, "failed to read flash count\n"); goto exit_free; } cnt = num_bits - bitmap_weight((unsigned long *)flash_buf, num_bits); @@ -200,25 +261,94 @@ static const struct attribute_group *m10bmc_sec_attr_groups[] = { static void log_error_regs(struct m10bmc_sec *sec, u32 doorbell) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 auth_result; - dev_err(sec->dev, "RSU error status: 0x%08x\n", doorbell); + dev_err(sec->dev, "Doorbell: 0x%08x\n", doorbell); - if (!m10bmc_sys_read(sec->m10bmc, M10BMC_AUTH_RESULT, &auth_result)) + if (!m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result)) dev_err(sec->dev, "RSU auth result: 0x%08x\n", auth_result); } +static int m10bmc_sec_n3000_rsu_status(struct m10bmc_sec *sec) +{ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell; + int ret; + + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); + if (ret) + return ret; + + return FIELD_GET(DRBL_RSU_STATUS, doorbell); +} + +static int m10bmc_sec_n6000_rsu_status(struct m10bmc_sec *sec) +{ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 auth_result; + int ret; + + ret = m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result); + if (ret) + return ret; + + return FIELD_GET(AUTH_RESULT_RSU_STATUS, auth_result); +} + +static bool rsu_status_ok(u32 status) +{ + return (status == RSU_STAT_NORMAL || + status == RSU_STAT_NIOS_OK || + status == RSU_STAT_USER_OK || + status == RSU_STAT_FACTORY_OK); +} + +static bool rsu_progress_done(u32 progress) +{ + return (progress == RSU_PROG_IDLE || + progress == RSU_PROG_RSU_DONE); +} + +static bool rsu_progress_busy(u32 progress) +{ + return (progress == RSU_PROG_AUTHENTICATING || + progress == RSU_PROG_COPYING || + progress == RSU_PROG_UPDATE_CANCEL || + progress == RSU_PROG_PROGRAM_KEY_HASH); +} + +static int m10bmc_sec_progress_status(struct m10bmc_sec *sec, u32 *doorbell_reg, + u32 *progress, u32 *status) +{ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + int ret; + + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, doorbell_reg); + if (ret) + return ret; + + ret = sec->ops->rsu_status(sec); + if (ret < 0) + return ret; + + *status = ret; + *progress = rsu_prog(*doorbell_reg); + + return 0; +} + static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 doorbell; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; - if (rsu_prog(doorbell) != RSU_PROG_IDLE && - rsu_prog(doorbell) != RSU_PROG_RSU_DONE) { + if (!rsu_progress_done(rsu_prog(doorbell))) { log_error_regs(sec, doorbell); return FW_UPLOAD_ERR_BUSY; } @@ -226,19 +356,15 @@ static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_NONE; } -static inline bool rsu_start_done(u32 doorbell) +static inline bool rsu_start_done(u32 doorbell_reg, u32 progress, u32 status) { - u32 status, progress; - - if (doorbell & DRBL_RSU_REQUEST) + if (doorbell_reg & DRBL_RSU_REQUEST) return false; - status = rsu_stat(doorbell); if (status == RSU_STAT_ERASE_FAIL || status == RSU_STAT_WEAROUT) return true; - progress = rsu_prog(doorbell); - if (progress != RSU_PROG_IDLE && progress != RSU_PROG_RSU_DONE) + if (!rsu_progress_done(progress)) return true; return false; @@ -246,11 +372,12 @@ static inline bool rsu_start_done(u32 doorbell) static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) { - u32 doorbell, status; - int ret; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell_reg, progress, status; + int ret, err; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_RSU_REQUEST | DRBL_HOST_STATUS, DRBL_RSU_REQUEST | FIELD_PREP(DRBL_HOST_STATUS, @@ -258,26 +385,25 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) if (ret) return FW_UPLOAD_ERR_RW_ERROR; - ret = regmap_read_poll_timeout(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, - doorbell, - rsu_start_done(doorbell), - NIOS_HANDSHAKE_INTERVAL_US, - NIOS_HANDSHAKE_TIMEOUT_US); + ret = read_poll_timeout(m10bmc_sec_progress_status, err, + err < 0 || rsu_start_done(doorbell_reg, progress, status), + NIOS_HANDSHAKE_INTERVAL_US, + NIOS_HANDSHAKE_TIMEOUT_US, + false, + sec, &doorbell_reg, &progress, &status); if (ret == -ETIMEDOUT) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_TIMEOUT; - } else if (ret) { + } else if (err) { return FW_UPLOAD_ERR_RW_ERROR; } - status = rsu_stat(doorbell); if (status == RSU_STAT_WEAROUT) { dev_warn(sec->dev, "Excessive flash update count detected\n"); return FW_UPLOAD_ERR_WEAROUT; } else if (status == RSU_STAT_ERASE_FAIL) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_HW_ERROR; } @@ -286,11 +412,12 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; unsigned long poll_timeout; u32 doorbell, progress; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -300,7 +427,7 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) if (time_after(jiffies, poll_timeout)) break; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; } @@ -319,11 +446,12 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec) { - u32 doorbell; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell_reg, status; int ret; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_HOST_STATUS, FIELD_PREP(DRBL_HOST_STATUS, HOST_STATUS_WRITE_DONE)); @@ -331,68 +459,58 @@ static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_RW_ERROR; ret = regmap_read_poll_timeout(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, - doorbell, - rsu_prog(doorbell) != RSU_PROG_READY, + csr_map->base + csr_map->doorbell, + doorbell_reg, + rsu_prog(doorbell_reg) != RSU_PROG_READY, NIOS_HANDSHAKE_INTERVAL_US, NIOS_HANDSHAKE_TIMEOUT_US); if (ret == -ETIMEDOUT) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_TIMEOUT; } else if (ret) { return FW_UPLOAD_ERR_RW_ERROR; } - switch (rsu_stat(doorbell)) { - case RSU_STAT_NORMAL: - case RSU_STAT_NIOS_OK: - case RSU_STAT_USER_OK: - case RSU_STAT_FACTORY_OK: - break; - default: - log_error_regs(sec, doorbell); + ret = sec->ops->rsu_status(sec); + if (ret < 0) + return ret; + status = ret; + + if (!rsu_status_ok(status)) { + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_HW_ERROR; } return FW_UPLOAD_ERR_NONE; } -static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell) +static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell_reg) { - if (m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, doorbell)) + u32 progress, status; + + if (m10bmc_sec_progress_status(sec, doorbell_reg, &progress, &status)) return -EIO; - switch (rsu_stat(*doorbell)) { - case RSU_STAT_NORMAL: - case RSU_STAT_NIOS_OK: - case RSU_STAT_USER_OK: - case RSU_STAT_FACTORY_OK: - break; - default: + if (!rsu_status_ok(status)) return -EINVAL; - } - switch (rsu_prog(*doorbell)) { - case RSU_PROG_IDLE: - case RSU_PROG_RSU_DONE: + if (rsu_progress_done(progress)) return 0; - case RSU_PROG_AUTHENTICATING: - case RSU_PROG_COPYING: - case RSU_PROG_UPDATE_CANCEL: - case RSU_PROG_PROGRAM_KEY_HASH: + + if (rsu_progress_busy(progress)) return -EAGAIN; - default: - return -EINVAL; - } + + return -EINVAL; } static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 doorbell; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -400,7 +518,7 @@ static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_BUSY; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_HOST_STATUS, FIELD_PREP(DRBL_HOST_STATUS, HOST_STATUS_ABORT_RSU)); @@ -421,39 +539,50 @@ static enum fw_upload_err m10bmc_sec_prepare(struct fw_upload *fwl, if (!size || size > M10BMC_STAGING_SIZE) return FW_UPLOAD_ERR_INVALID_SIZE; + if (sec->m10bmc->flash_bulk_ops) + if (sec->m10bmc->flash_bulk_ops->lock_write(sec->m10bmc)) + return FW_UPLOAD_ERR_BUSY; + ret = rsu_check_idle(sec); if (ret != FW_UPLOAD_ERR_NONE) - return ret; + goto unlock_flash; ret = rsu_update_init(sec); if (ret != FW_UPLOAD_ERR_NONE) - return ret; + goto unlock_flash; ret = rsu_prog_ready(sec); if (ret != FW_UPLOAD_ERR_NONE) - return ret; + goto unlock_flash; - if (sec->cancel_request) - return rsu_cancel(sec); + if (sec->cancel_request) { + ret = rsu_cancel(sec); + goto unlock_flash; + } return FW_UPLOAD_ERR_NONE; + +unlock_flash: + if (sec->m10bmc->flash_bulk_ops) + sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc); + return ret; } #define WRITE_BLOCK_SIZE 0x4000 /* Default write-block size is 0x4000 bytes */ -static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data, - u32 offset, u32 size, u32 *written) +static enum fw_upload_err m10bmc_sec_fw_write(struct fw_upload *fwl, const u8 *data, + u32 offset, u32 size, u32 *written) { struct m10bmc_sec *sec = fwl->dd_handle; - u32 blk_size, doorbell, extra_offset; - unsigned int stride, extra = 0; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + struct intel_m10bmc *m10bmc = sec->m10bmc; + u32 blk_size, doorbell; int ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); if (sec->cancel_request) return rsu_cancel(sec); - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(m10bmc, csr_map->doorbell, &doorbell); if (ret) { return FW_UPLOAD_ERR_RW_ERROR; } else if (rsu_prog(doorbell) != RSU_PROG_READY) { @@ -461,28 +590,12 @@ static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data return FW_UPLOAD_ERR_HW_ERROR; } - WARN_ON_ONCE(WRITE_BLOCK_SIZE % stride); + WARN_ON_ONCE(WRITE_BLOCK_SIZE % regmap_get_reg_stride(m10bmc->regmap)); blk_size = min_t(u32, WRITE_BLOCK_SIZE, size); - ret = regmap_bulk_write(sec->m10bmc->regmap, - M10BMC_STAGING_BASE + offset, - (void *)data + offset, - blk_size / stride); + ret = m10bmc_sec_write(sec, data, offset, blk_size); if (ret) return FW_UPLOAD_ERR_RW_ERROR; - /* - * If blk_size is not aligned to stride, then handle the extra - * bytes with regmap_write. - */ - if (blk_size % stride) { - extra_offset = offset + ALIGN_DOWN(blk_size, stride); - memcpy(&extra, (u8 *)(data + extra_offset), blk_size % stride); - ret = regmap_write(sec->m10bmc->regmap, - M10BMC_STAGING_BASE + extra_offset, extra); - if (ret) - return FW_UPLOAD_ERR_RW_ERROR; - } - *written = blk_size; return FW_UPLOAD_ERR_NONE; } @@ -539,16 +652,27 @@ static void m10bmc_sec_cleanup(struct fw_upload *fwl) struct m10bmc_sec *sec = fwl->dd_handle; (void)rsu_cancel(sec); + + if (sec->m10bmc->flash_bulk_ops) + sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc); } static const struct fw_upload_ops m10bmc_ops = { .prepare = m10bmc_sec_prepare, - .write = m10bmc_sec_write, + .write = m10bmc_sec_fw_write, .poll_complete = m10bmc_sec_poll_complete, .cancel = m10bmc_sec_cancel, .cleanup = m10bmc_sec_cleanup, }; +static const struct m10bmc_sec_ops m10sec_n3000_ops = { + .rsu_status = m10bmc_sec_n3000_rsu_status, +}; + +static const struct m10bmc_sec_ops m10sec_n6000_ops = { + .rsu_status = m10bmc_sec_n6000_rsu_status, +}; + #define SEC_UPDATE_LEN_MAX 32 static int m10bmc_sec_probe(struct platform_device *pdev) { @@ -564,6 +688,7 @@ static int m10bmc_sec_probe(struct platform_device *pdev) sec->dev = &pdev->dev; sec->m10bmc = dev_get_drvdata(pdev->dev.parent); + sec->ops = (struct m10bmc_sec_ops *)platform_get_device_id(pdev)->driver_data; dev_set_drvdata(&pdev->dev, sec); ret = xa_alloc(&fw_upload_xa, &sec->fw_name_id, sec, @@ -574,20 +699,27 @@ static int m10bmc_sec_probe(struct platform_device *pdev) len = scnprintf(buf, SEC_UPDATE_LEN_MAX, "secure-update%d", sec->fw_name_id); sec->fw_name = kmemdup_nul(buf, len, GFP_KERNEL); - if (!sec->fw_name) - return -ENOMEM; + if (!sec->fw_name) { + ret = -ENOMEM; + goto fw_name_fail; + } fwl = firmware_upload_register(THIS_MODULE, sec->dev, sec->fw_name, &m10bmc_ops, sec); if (IS_ERR(fwl)) { dev_err(sec->dev, "Firmware Upload driver failed to start\n"); - kfree(sec->fw_name); - xa_erase(&fw_upload_xa, sec->fw_name_id); - return PTR_ERR(fwl); + ret = PTR_ERR(fwl); + goto fw_uploader_fail; } sec->fwl = fwl; return 0; + +fw_uploader_fail: + kfree(sec->fw_name); +fw_name_fail: + xa_erase(&fw_upload_xa, sec->fw_name_id); + return ret; } static int m10bmc_sec_remove(struct platform_device *pdev) @@ -604,9 +736,15 @@ static int m10bmc_sec_remove(struct platform_device *pdev) static const struct platform_device_id intel_m10bmc_sec_ids[] = { { .name = "n3000bmc-sec-update", + .driver_data = (kernel_ulong_t)&m10sec_n3000_ops, }, { .name = "d5005bmc-sec-update", + .driver_data = (kernel_ulong_t)&m10sec_n3000_ops, + }, + { + .name = "n6000bmc-sec-update", + .driver_data = (kernel_ulong_t)&m10sec_n6000_ops, }, { } }; diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 7436976ea904..d6070e7f5205 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -6,6 +6,7 @@ #include <asm/unaligned.h> #include <linux/delay.h> #include <linux/fpga/fpga-mgr.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/spi/spi.h> @@ -33,7 +34,7 @@ #define MPF_BITS_PER_COMPONENT_SIZE 22 -#define MPF_STATUS_POLL_RETRIES 10000 +#define MPF_STATUS_POLL_TIMEOUT (2 * USEC_PER_SEC) #define MPF_STATUS_BUSY BIT(0) #define MPF_STATUS_READY BIT(1) #define MPF_STATUS_SPI_VIOLATION BIT(2) @@ -42,46 +43,55 @@ struct mpf_priv { struct spi_device *spi; bool program_mode; + u8 tx __aligned(ARCH_KMALLOC_MINALIGN); + u8 rx; }; -static int mpf_read_status(struct spi_device *spi) +static int mpf_read_status(struct mpf_priv *priv) { - u8 status = 0, status_command = MPF_SPI_READ_STATUS; - struct spi_transfer xfers[2] = { 0 }; - int ret; - /* * HW status is returned on MISO in the first byte after CS went * active. However, first reading can be inadequate, so we submit * two identical SPI transfers and use result of the later one. */ - xfers[0].tx_buf = &status_command; - xfers[1].tx_buf = &status_command; - xfers[0].rx_buf = &status; - xfers[1].rx_buf = &status; - xfers[0].len = 1; - xfers[1].len = 1; - xfers[0].cs_change = 1; + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + .cs_change = 1, + }, { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + }, + }; + u8 status; + int ret; - ret = spi_sync_transfer(spi, xfers, 2); + priv->tx = MPF_SPI_READ_STATUS; + + ret = spi_sync_transfer(priv->spi, xfers, 2); + if (ret) + return ret; + + status = priv->rx; if ((status & MPF_STATUS_SPI_VIOLATION) || (status & MPF_STATUS_SPI_ERROR)) - ret = -EIO; + return -EIO; - return ret ? : status; + return status; } static enum fpga_mgr_states mpf_ops_state(struct fpga_manager *mgr) { struct mpf_priv *priv = mgr->priv; - struct spi_device *spi; bool program_mode; int status; - spi = priv->spi; program_mode = priv->program_mode; - status = mpf_read_status(spi); + status = mpf_read_status(priv); if (!program_mode && !status) return FPGA_MGR_STATE_OPERATING; @@ -185,52 +195,53 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, return 0; } -/* Poll HW status until busy bit is cleared and mask bits are set. */ -static int mpf_poll_status(struct spi_device *spi, u8 mask) +static int mpf_poll_status(struct mpf_priv *priv, u8 mask) { - int status, retries = MPF_STATUS_POLL_RETRIES; - - while (retries--) { - status = mpf_read_status(spi); - if (status < 0) - return status; + int ret, status; - if (status & MPF_STATUS_BUSY) - continue; - - if (!mask || (status & mask)) - return status; - } + /* + * Busy poll HW status. Polling stops if any of the following + * conditions are met: + * - timeout is reached + * - mpf_read_status() returns an error + * - busy bit is cleared AND mask bits are set + */ + ret = read_poll_timeout(mpf_read_status, status, + (status < 0) || + ((status & (MPF_STATUS_BUSY | mask)) == mask), + 0, MPF_STATUS_POLL_TIMEOUT, false, priv); + if (ret < 0) + return ret; - return -EBUSY; + return status; } -static int mpf_spi_write(struct spi_device *spi, const void *buf, size_t buf_size) +static int mpf_spi_write(struct mpf_priv *priv, const void *buf, size_t buf_size) { - int status = mpf_poll_status(spi, 0); + int status = mpf_poll_status(priv, 0); if (status < 0) return status; - return spi_write(spi, buf, buf_size); + return spi_write_then_read(priv->spi, buf, buf_size, NULL, 0); } -static int mpf_spi_write_then_read(struct spi_device *spi, +static int mpf_spi_write_then_read(struct mpf_priv *priv, const void *txbuf, size_t txbuf_size, void *rxbuf, size_t rxbuf_size) { const u8 read_command[] = { MPF_SPI_READ_DATA }; int ret; - ret = mpf_spi_write(spi, txbuf, txbuf_size); + ret = mpf_spi_write(priv, txbuf, txbuf_size); if (ret) return ret; - ret = mpf_poll_status(spi, MPF_STATUS_READY); + ret = mpf_poll_status(priv, MPF_STATUS_READY); if (ret < 0) return ret; - return spi_write_then_read(spi, read_command, sizeof(read_command), + return spi_write_then_read(priv->spi, read_command, sizeof(read_command), rxbuf, rxbuf_size); } @@ -242,7 +253,6 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, const u8 isc_en_command[] = { MPF_SPI_ISC_ENABLE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; u32 isc_ret = 0; int ret; @@ -251,9 +261,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EOPNOTSUPP; } - spi = priv->spi; - - ret = mpf_spi_write_then_read(spi, isc_en_command, sizeof(isc_en_command), + ret = mpf_spi_write_then_read(priv, isc_en_command, sizeof(isc_en_command), &isc_ret, sizeof(isc_ret)); if (ret || isc_ret) { dev_err(dev, "Failed to enable ISC: spi_ret %d, isc_ret %u\n", @@ -261,7 +269,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EFAULT; } - ret = mpf_spi_write(spi, program_mode, sizeof(program_mode)); + ret = mpf_spi_write(priv, program_mode, sizeof(program_mode)); if (ret) { dev_err(dev, "Failed to enter program mode: %d\n", ret); return ret; @@ -272,13 +280,32 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return 0; } +static int mpf_spi_frame_write(struct mpf_priv *priv, const char *buf) +{ + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .len = 1, + }, { + .tx_buf = buf, + .len = MPF_SPI_FRAME_SIZE, + }, + }; + int ret; + + ret = mpf_poll_status(priv, 0); + if (ret < 0) + return ret; + + priv->tx = MPF_SPI_FRAME; + + return spi_sync_transfer(priv->spi, xfers, ARRAY_SIZE(xfers)); +} + static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count) { - u8 spi_frame_command[] = { MPF_SPI_FRAME }; - struct spi_transfer xfers[2] = { 0 }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret, i; if (count % MPF_SPI_FRAME_SIZE) { @@ -287,19 +314,8 @@ static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count return -EINVAL; } - spi = priv->spi; - - xfers[0].tx_buf = spi_frame_command; - xfers[0].len = sizeof(spi_frame_command); - for (i = 0; i < count / MPF_SPI_FRAME_SIZE; i++) { - xfers[1].tx_buf = buf + i * MPF_SPI_FRAME_SIZE; - xfers[1].len = MPF_SPI_FRAME_SIZE; - - ret = mpf_poll_status(spi, 0); - if (ret >= 0) - ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); - + ret = mpf_spi_frame_write(priv, buf + i * MPF_SPI_FRAME_SIZE); if (ret) { dev_err(dev, "Failed to write bitstream frame %d/%zu\n", i, count / MPF_SPI_FRAME_SIZE); @@ -317,12 +333,9 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, const u8 release_command[] = { MPF_SPI_RELEASE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret; - spi = priv->spi; - - ret = mpf_spi_write(spi, isc_dis_command, sizeof(isc_dis_command)); + ret = mpf_spi_write(priv, isc_dis_command, sizeof(isc_dis_command)); if (ret) { dev_err(dev, "Failed to disable ISC: %d\n", ret); return ret; @@ -330,7 +343,7 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, usleep_range(1000, 2000); - ret = mpf_spi_write(spi, release_command, sizeof(release_command)); + ret = mpf_spi_write(priv, release_command, sizeof(release_command)); if (ret) { dev_err(dev, "Failed to exit program mode: %d\n", ret); return ret; diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 357cea58ec98..f7f01982a512 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -213,9 +213,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr, /* Allocate buffers from the service layer's pool. */ for (i = 0; i < NUM_SVC_BUFS; i++) { kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE); - if (!kbuf) { + if (IS_ERR(kbuf)) { s10_free_buffers(mgr); - ret = -ENOMEM; + ret = PTR_ERR(kbuf); goto init_done; } |