@@ -8,4 +8,5 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o
xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \
ai-engine-dev.o \
ai-engine-part.o \
- ai-engine-res.o
+ ai-engine-res.o \
+ ai-engine-reset.o
@@ -5,6 +5,9 @@
* Copyright (C) 2020 Xilinx, Inc.
*/
+#include <linux/bitfield.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/io.h>
#include <linux/slab.h>
#include "ai-engine-internal.h"
@@ -24,9 +27,25 @@
#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF 0x00035000U
#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U
#define AIE_SHIMPL_CLKCNTR_REGOFF 0x00036040U
+#define AIE_SHIMPL_COLRESET_REGOFF 0x00036048U
#define AIE_SHIMPL_RESET_REGOFF 0x0003604cU
#define AIE_TILE_CORE_CLKCNTR_REGOFF 0x00036040U
+/*
+ * Register masks
+ */
+#define AIE_SHIMPL_SHIMRST_MASK 0x1U
+#define AIE_SHIMPL_COLRST_MASK 0x1U
+#define AIE_SHIMPL_CLKCNTR_COLBUF_MASK 0x1U
+
+/*
+ * AI engine SHIM reset ID.
+ * TODO: it should follow the Linux reset framework. The ID should be in the
+ * device tree. However, as versal resets is not ready, we hardcode it in the
+ * driver.
+ */
+#define VERSAL_PM_RST_AIE_SHIM_ID 0xc10405fU
+
static const struct aie_tile_regs aie_kernel_regs[] = {
/* SHIM AXI MM Config */
{.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT,
@@ -49,6 +68,12 @@ static const struct aie_tile_regs aie_kernel_regs[] = {
.soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF,
.eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF,
},
+ /* SHIM column reset */
+ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
+ AIE_REGS_ATTR_TILE_TYPE_SHIFT,
+ .soff = AIE_SHIMPL_COLRESET_REGOFF,
+ .eoff = AIE_SHIMPL_COLRESET_REGOFF,
+ },
/* SHIM reset Enable */
{.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) <<
AIE_REGS_ATTR_TILE_TYPE_SHIFT,
@@ -68,6 +93,16 @@ static const struct aie_tile_regs aie_kernel_regs[] = {
},
};
+static const struct aie_single_reg_field aie_col_rst = {
+ .mask = AIE_SHIMPL_COLRST_MASK,
+ .regoff = AIE_SHIMPL_COLRESET_REGOFF,
+};
+
+static const struct aie_single_reg_field aie_col_clkbuf = {
+ .mask = AIE_SHIMPL_CLKCNTR_COLBUF_MASK,
+ .regoff = AIE_SHIMPL_CLKCNTR_REGOFF,
+};
+
static u32 aie_get_tile_type(struct aie_location *loc)
{
if (loc->row)
@@ -79,8 +114,63 @@ static u32 aie_get_tile_type(struct aie_location *loc)
return AIE_TILE_TYPE_SHIMNOC;
}
+/**
+ * aie_set_shim_reset() - Set AI engine SHIM reset
+ * @adev: AI engine device
+ * @range: range of AI engine tiles
+ * @assert: true to set reset, false to unset reset
+ */
+static void aie_set_shim_reset(struct aie_device *adev,
+ struct aie_range *range, bool assert)
+{
+ u32 c;
+ u32 val;
+ struct aie_location loc;
+
+ val = FIELD_PREP(AIE_SHIMPL_SHIMRST_MASK, (assert ? 1 : 0));
+ loc.row = 0;
+ for (c = range->start.col; c < range->start.col + range->size.col;
+ c++) {
+ u32 regoff;
+
+ loc.col = c;
+ regoff = aie_cal_regoff(adev, loc, AIE_SHIMPL_RESET_REGOFF);
+ iowrite32(val, adev->base + regoff);
+ }
+}
+
+static int aie_reset_shim(struct aie_device *adev, struct aie_range *range)
+{
+ int ret;
+
+ /* Enable shim reset of each column */
+ aie_set_shim_reset(adev, range, true);
+
+ /* Assert shim reset of AI engine array */
+ ret = zynqmp_pm_reset_assert(VERSAL_PM_RST_AIE_SHIM_ID,
+ PM_RESET_ACTION_ASSERT);
+ if (ret < 0) {
+ dev_err(&adev->dev, "failed to assert SHIM reset.\n");
+ return ret;
+ }
+
+ /* Release shim reset of AI engine array */
+ ret = zynqmp_pm_reset_assert(VERSAL_PM_RST_AIE_SHIM_ID,
+ PM_RESET_ACTION_RELEASE);
+ if (ret < 0) {
+ dev_err(&adev->dev, "failed to release SHIM reset.\n");
+ return ret;
+ }
+
+ /* Disable shim reset of each column */
+ aie_set_shim_reset(adev, range, false);
+
+ return 0;
+}
+
static const struct aie_tile_operations aie_ops = {
.get_tile_type = aie_get_tile_type,
+ .reset_shim = aie_reset_shim,
};
/**
@@ -104,6 +194,8 @@ int aie_device_init(struct aie_device *adev)
adev->ops = &aie_ops;
adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs);
adev->kernel_regs = aie_kernel_regs;
+ adev->col_rst = &aie_col_rst;
+ adev->col_clkbuf = &aie_col_clkbuf;
/* Get the columns resource */
/* Get number of columns from AI engine memory resource */
@@ -208,6 +208,8 @@ struct aie_partition *aie_request_partition(struct aie_device *adev,
* exported for user to access.
*/
apart->status = XAIE_PART_STATUS_INUSE;
+ apart->cntrflag = req->flag;
+
mutex_unlock(&apart->mlock);
}
mutex_unlock(&adev->mlock);
@@ -53,18 +53,30 @@ struct aie_tile_regs {
u32 attribute;
};
+/**
+ * struct aie_single_reg_field - AI engine single field register attribute
+ * @mask: field mask
+ * @regoff: register offset of the field
+ */
+struct aie_single_reg_field {
+ u32 mask;
+ u32 regoff;
+};
+
struct aie_device;
struct aie_partition;
/**
* struct aie_tile_operations - AI engine device operations
* @get_tile_type: get type of tile based on tile operation
+ * @reset_shim: reset shim, it will assert and then release SHIM reset
*
* Different AI engine device version has its own device
* operation.
*/
struct aie_tile_operations {
u32 (*get_tile_type)(struct aie_location *loc);
+ int (*reset_shim)(struct aie_device *adev, struct aie_range *range);
};
/**
@@ -87,6 +99,8 @@ struct aie_resource {
* @res: memory resource of AI engine device
* @kernel_regs: array of kernel only registers
* @ops: tile operations
+ * @col_rst: column reset attribute
+ * @col_clkbuf: column clock buffer attribute
* @size: size of the AI engine address space
* @array_shift: array address shift
* @col_shift: column address shift
@@ -105,6 +119,8 @@ struct aie_device {
struct resource *res;
const struct aie_tile_regs *kernel_regs;
const struct aie_tile_operations *ops;
+ const struct aie_single_reg_field *col_rst;
+ const struct aie_single_reg_field *col_clkbuf;
size_t size;
struct aie_resource cols_res;
u32 array_shift;
@@ -124,6 +140,8 @@ struct aie_device {
* @partition_id: partition id. Partition ID is the identifier
* of the AI engine partition in the system.
* @status: indicate if the partition is in use
+ * @cntrflag: partition control flag. e.g. whether to reset columns when
+ * the partition is released
*/
struct aie_partition {
struct list_head node;
@@ -133,6 +151,7 @@ struct aie_partition {
struct device dev;
u32 partition_id;
u32 status;
+ u32 cntrflag;
};
extern struct class *aie_class;
@@ -168,6 +187,20 @@ extern const struct file_operations aie_part_fops;
aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff))
/**
+ * aie_get_field_val() - calculate value of an AI engine register field
+ * @field: a field in a register
+ * @val: value of the field
+ * @return: value of a register field
+ */
+static inline u32 aie_get_field_val(const struct aie_single_reg_field *field,
+ u32 val)
+{
+ long long mask = (long long)field->mask & 0x00000000ffffffff;
+
+ return (val << __bf_shf(mask)) & field->mask;
+}
+
+/**
* aie_cal_regoff() - calculate register offset to the whole AI engine
* device start address
* @adev: AI engine device
@@ -221,6 +254,7 @@ struct aie_partition *aie_request_partition(struct aie_device *adev,
struct aie_partition *of_aie_part_probe(struct aie_device *adev,
struct device_node *nc);
void aie_part_remove(struct aie_partition *apart);
+int aie_part_clean(struct aie_partition *apart);
int aie_device_init(struct aie_device *adev);
#endif /* AIE_INTERNAL_H */
@@ -217,14 +217,12 @@ static int aie_part_release(struct inode *inode, struct file *filp)
struct aie_partition *apart = filp->private_data;
int ret;
- /*
- * TODO: It will need to reset the SHIM columns and gate the
- * tiles of the partition.
- */
ret = mutex_lock_interruptible(&apart->mlock);
if (ret)
return ret;
+ aie_part_clean(apart);
+
apart->status = 0;
mutex_unlock(&apart->mlock);
@@ -413,7 +411,6 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev,
put_device(dev);
return ERR_PTR(ret);
}
-
list_add_tail(&apart->node, &adev->partitions);
mutex_unlock(&adev->mlock);
get_device(&adev->dev);
new file mode 100644
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AI Engine device driver resets implementation
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+
+#include "ai-engine-internal.h"
+
+/**
+ * aie_part_set_col_reset() - set AI engine column reset
+ * @apart: AI engine partition
+ * @col: column to reset
+ * @reset: true to assert reset, false to release reset
+ */
+static void aie_part_set_col_reset(struct aie_partition *apart, u32 col,
+ bool reset)
+{
+ struct aie_device *adev = apart->adev;
+ const struct aie_single_reg_field *col_rst = adev->col_rst;
+ struct aie_location loc;
+ u32 regoff, val;
+
+ loc.row = 0;
+ loc.col = col;
+
+ val = aie_get_field_val(col_rst, (reset ? 1 : 0));
+ regoff = aie_cal_regoff(adev, loc, col_rst->regoff);
+ iowrite32(val, adev->base + regoff);
+}
+
+/**
+ * aie_part_set_col_clkbuf() - set AI engine column clock buffer
+ * @apart: AI engine partition
+ * @col: column to reset
+ * @enable: true to enable, false to disable
+ */
+static void aie_part_set_col_clkbuf(struct aie_partition *apart, u32 col,
+ bool enable)
+{
+ struct aie_device *adev = apart->adev;
+ const struct aie_single_reg_field *col_clkbuf = adev->col_clkbuf;
+ struct aie_location loc;
+ u32 regoff, val;
+
+ loc.row = 0;
+ loc.col = col;
+
+ val = aie_get_field_val(col_clkbuf, (enable ? 1 : 0));
+ regoff = aie_cal_regoff(adev, loc, col_clkbuf->regoff);
+ iowrite32(val, adev->base + regoff);
+}
+
+/**
+ * aie_part_set_cols_reset() - set column reset of every column in a partition
+ * @apart: AI engine partition
+ * @reset: bool to assert reset, false to release reset
+ */
+static void aie_part_set_cols_reset(struct aie_partition *apart, bool reset)
+{
+ struct aie_range *range = &apart->range;
+ u32 c;
+
+ for (c = range->start.col; c < range->start.col + range->size.col;
+ c++)
+ aie_part_set_col_reset(apart, c, reset);
+}
+
+/**
+ * aie_part_set_cols_clkbuf() - set column clock buffer of every column in a
+ * partition
+ * @apart: AI engine partition
+ * @enable: true to enable, false to disable
+ */
+static void aie_part_set_cols_clkbuf(struct aie_partition *apart, bool enable)
+{
+ struct aie_range *range = &apart->range;
+ u32 c;
+
+ for (c = range->start.col; c < range->start.col + range->size.col;
+ c++)
+ aie_part_set_col_clkbuf(apart, c, enable);
+}
+
+/**
+ * aie_part_clean() - reset and clear AI engine partition
+ * @apart: AI engine partition
+ * @return: 0 for success and negative value for failure
+ *
+ * This function will:
+ * * gate all the columns
+ * * reset AI engine partition columns
+ * * reset AI engine shims
+ * * clear the memories
+ * * gate all the tiles in a partition.
+ *
+ * This function will not validate the partition, the caller will need to
+ * provide a valid AI engine partition.
+ */
+int aie_part_clean(struct aie_partition *apart)
+{
+ struct aie_device *adev = apart->adev;
+ int ret;
+
+ if (apart->cntrflag & XAIE_PART_NOT_RST_ON_RELEASE)
+ return 0;
+
+ aie_part_set_cols_clkbuf(apart, false);
+ aie_part_set_cols_reset(apart, true);
+
+ ret = apart->adev->ops->reset_shim(adev, &apart->range);
+ if (ret < 0)
+ return ret;
+
+ aie_part_set_cols_clkbuf(apart, false);
+
+ return 0;
+}
@@ -16,6 +16,12 @@ enum aie_reg_op {
/* AI engine partition is in use */
#define XAIE_PART_STATUS_INUSE (1U << 0)
+/*
+ * AI engine partition control flags
+ */
+/* Not reset when release AI engine partition */
+#define XAIE_PART_NOT_RST_ON_RELEASE 0x00000001U
+
/**
* struct aie_location - AIE location information
* @col: column id