[PATCH 2/2] soc: mediatek: Add mt8192 devapc driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Nina Wu <Nina-CM.Wu@xxxxxxxxxxxx>

The hardware architecture of mt8192 devapc is slightly
different from that of the previous IC.
We add necessary extensions to support mt8192 and be
back-compatible with other ICs.

Signed-off-by: Nina Wu <Nina-CM.Wu@xxxxxxxxxxxx>
---
 drivers/soc/mediatek/mtk-devapc.c | 213 ++++++++++++++++++++++++++++----------
 1 file changed, 156 insertions(+), 57 deletions(-)

diff --git a/drivers/soc/mediatek/mtk-devapc.c b/drivers/soc/mediatek/mtk-devapc.c
index f1cea04..1e40a52 100644
--- a/drivers/soc/mediatek/mtk-devapc.c
+++ b/drivers/soc/mediatek/mtk-devapc.c
@@ -15,6 +15,11 @@
 #define VIO_MOD_TO_REG_IND(m)	((m) / 32)
 #define VIO_MOD_TO_REG_OFF(m)	((m) % 32)
 
+#define FOR_EACH_SLAVE_TYPE(ctx, idx) \
+	for ((idx) = 0; (idx) < (ctx)->slave_type_num; (idx)++)
+#define BASE(i)			(ctx->base_list[i])
+#define VIO_IDX_NUM(i)		(ctx->vio_idx_num[i])
+
 struct mtk_devapc_vio_dbgs {
 	union {
 		u32 vio_dbg0;
@@ -26,20 +31,28 @@ struct mtk_devapc_vio_dbgs {
 			u32 addr_h:4;
 			u32 resv:4;
 		} dbg0_bits;
+
+		/* Not used, reference only */
+		struct {
+			u32 dmnid:6;
+			u32 vio_w:1;
+			u32 vio_r:1;
+			u32 addr_h:4;
+			u32 resv:20;
+		} dbg0_bits_ver2;
 	};
 
 	u32 vio_dbg1;
+	u32 vio_dbg2;
 };
 
 struct mtk_devapc_data {
-	/* numbers of violation index */
-	u32 vio_idx_num;
-
 	/* reg offset */
 	u32 vio_mask_offset;
 	u32 vio_sta_offset;
 	u32 vio_dbg0_offset;
 	u32 vio_dbg1_offset;
+	u32 vio_dbg2_offset;
 	u32 apc_con_offset;
 	u32 vio_shift_sta_offset;
 	u32 vio_shift_sel_offset;
@@ -48,7 +61,10 @@ struct mtk_devapc_data {
 
 struct mtk_devapc_context {
 	struct device *dev;
-	void __iomem *infra_base;
+	u32 arch_ver;
+	u32 slave_type_num;
+	void __iomem **base_list;
+	u32 *vio_idx_num;
 	struct clk *infra_clk;
 	const struct mtk_devapc_data *data;
 };
@@ -56,39 +72,39 @@ struct mtk_devapc_context {
 static void clear_vio_status(struct mtk_devapc_context *ctx)
 {
 	void __iomem *reg;
-	int i;
+	int i, j;
 
-	reg = ctx->infra_base + ctx->data->vio_sta_offset;
+	FOR_EACH_SLAVE_TYPE(ctx, i) {
+		reg = BASE(i) + ctx->data->vio_sta_offset;
 
-	for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
-		writel(GENMASK(31, 0), reg + 4 * i);
+		for (j = 0; j < VIO_MOD_TO_REG_IND(VIO_IDX_NUM(i) - 1); j++)
+			writel(GENMASK(31, 0), reg + 4 * j);
+
+		writel(GENMASK(VIO_MOD_TO_REG_OFF(VIO_IDX_NUM(i) - 1), 0),
+		       reg + 4 * j);
+	}
 
-	writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, 0),
-	       reg + 4 * i);
 }
 
-static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
+static void mask_module_irq(void __iomem *reg, int vio_idx_num, bool mask)
 {
-	void __iomem *reg;
 	u32 val;
 	int i;
 
-	reg = ctx->infra_base + ctx->data->vio_mask_offset;
-
 	if (mask)
 		val = GENMASK(31, 0);
 	else
 		val = 0;
 
-	for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
+	for (i = 0; i < VIO_MOD_TO_REG_IND(vio_idx_num - 1); i++)
 		writel(val, reg + 4 * i);
 
 	val = readl(reg + 4 * i);
 	if (mask)
-		val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
+		val |= GENMASK(VIO_MOD_TO_REG_OFF(vio_idx_num - 1),
 			       0);
 	else
-		val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
+		val &= ~GENMASK(VIO_MOD_TO_REG_OFF(vio_idx_num - 1),
 				0);
 
 	writel(val, reg + 4 * i);
@@ -108,6 +124,8 @@ static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
  */
 static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
 {
+	int i;
+	void __iomem *reg_base;
 	void __iomem *pd_vio_shift_sta_reg;
 	void __iomem *pd_vio_shift_sel_reg;
 	void __iomem *pd_vio_shift_con_reg;
@@ -115,57 +133,87 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
 	int ret;
 	u32 val;
 
-	pd_vio_shift_sta_reg = ctx->infra_base +
-			       ctx->data->vio_shift_sta_offset;
-	pd_vio_shift_sel_reg = ctx->infra_base +
-			       ctx->data->vio_shift_sel_offset;
-	pd_vio_shift_con_reg = ctx->infra_base +
-			       ctx->data->vio_shift_con_offset;
+	FOR_EACH_SLAVE_TYPE(ctx, i) {
+		reg_base = BASE(i);
 
-	/* Find the minimum shift group which has violation */
-	val = readl(pd_vio_shift_sta_reg);
-	if (!val)
-		return false;
+		pd_vio_shift_sta_reg = reg_base +
+				       ctx->data->vio_shift_sta_offset;
+		pd_vio_shift_sel_reg = reg_base +
+				       ctx->data->vio_shift_sel_offset;
+		pd_vio_shift_con_reg = reg_base +
+				       ctx->data->vio_shift_con_offset;
 
-	min_shift_group = __ffs(val);
+		/* Find the minimum shift group which has violation */
+		val = readl(pd_vio_shift_sta_reg);
+		if (!val)
+			continue;
 
-	/* Assign the group to sync */
-	writel(0x1 << min_shift_group, pd_vio_shift_sel_reg);
+		min_shift_group = __ffs(val);
 
-	/* Start syncing */
-	writel(0x1, pd_vio_shift_con_reg);
+		/* Assign the group to sync */
+		writel(0x1 << min_shift_group, pd_vio_shift_sel_reg);
 
-	ret = readl_poll_timeout(pd_vio_shift_con_reg, val, val == 0x3, 0,
-				 PHY_DEVAPC_TIMEOUT);
-	if (ret) {
-		dev_err(ctx->dev, "%s: Shift violation info failed\n", __func__);
-		return false;
-	}
+		/* Start syncing */
+		writel(0x1, pd_vio_shift_con_reg);
+
+		ret = readl_poll_timeout(pd_vio_shift_con_reg, val, val == 0x3,
+					 0, PHY_DEVAPC_TIMEOUT);
+		if (ret) {
+			dev_err(ctx->dev, "%s: Shift violation info failed\n",
+				__func__);
+			return -ETIMEDOUT;
+		}
 
-	/* Stop syncing */
-	writel(0x0, pd_vio_shift_con_reg);
+		/* Stop syncing */
+		writel(0x0, pd_vio_shift_con_reg);
 
-	/* Write clear */
-	writel(0x1 << min_shift_group, pd_vio_shift_sta_reg);
+		/* Write clear */
+		writel(0x1 << min_shift_group, pd_vio_shift_sta_reg);
 
-	return true;
+		return i;
+	}
+
+	/* No violation found */
+	return -ENODATA;
 }
 
 /*
  * devapc_extract_vio_dbg - extract full violation information after doing
  *                          shift mechanism.
  */
-static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
+static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx,
+				   int vio_slave_type)
 {
 	struct mtk_devapc_vio_dbgs vio_dbgs;
 	void __iomem *vio_dbg0_reg;
 	void __iomem *vio_dbg1_reg;
+	void __iomem *vio_dbg2_reg;
+	u32 vio_addr, bus_id;
 
-	vio_dbg0_reg = ctx->infra_base + ctx->data->vio_dbg0_offset;
-	vio_dbg1_reg = ctx->infra_base + ctx->data->vio_dbg1_offset;
+	vio_dbg0_reg = BASE(vio_slave_type) + ctx->data->vio_dbg0_offset;
+	vio_dbg1_reg = BASE(vio_slave_type) + ctx->data->vio_dbg1_offset;
+	vio_dbg2_reg = BASE(vio_slave_type) + ctx->data->vio_dbg2_offset;
 
 	vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
 	vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
+	vio_dbgs.vio_dbg2 = readl(vio_dbg2_reg);
+
+	switch (ctx->arch_ver) {
+	case 1:
+		bus_id = vio_dbgs.dbg0_bits.mstid;
+		vio_addr = vio_dbgs.vio_dbg1;
+		break;
+	case 2:
+		bus_id = vio_dbgs.vio_dbg1;
+		vio_addr = vio_dbgs.vio_dbg2;
+
+		/* To align with the bit definition of arch_ver 1 */
+		vio_dbgs.vio_dbg0 = (vio_dbgs.vio_dbg0 << 16);
+		break;
+	default:
+		/* Not Supported */
+		return;
+	}
 
 	/* Print violation information */
 	if (vio_dbgs.dbg0_bits.vio_w)
@@ -174,8 +222,7 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
 		dev_info(ctx->dev, "Read Violation\n");
 
 	dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n",
-		 vio_dbgs.dbg0_bits.mstid, vio_dbgs.dbg0_bits.dmnid,
-		 vio_dbgs.vio_dbg1);
+		 bus_id, vio_dbgs.dbg0_bits.dmnid, vio_addr);
 }
 
 /*
@@ -186,9 +233,10 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
 static irqreturn_t devapc_violation_irq(int irq_number, void *data)
 {
 	struct mtk_devapc_context *ctx = data;
+	int vio_slave_type;
 
-	while (devapc_sync_vio_dbg(ctx))
-		devapc_extract_vio_dbg(ctx);
+	while ((vio_slave_type = devapc_sync_vio_dbg(ctx)) >= 0)
+		devapc_extract_vio_dbg(ctx, vio_slave_type);
 
 	clear_vio_status(ctx);
 
@@ -200,9 +248,15 @@ static irqreturn_t devapc_violation_irq(int irq_number, void *data)
  */
 static void start_devapc(struct mtk_devapc_context *ctx)
 {
-	writel(BIT(31), ctx->infra_base + ctx->data->apc_con_offset);
+	int i;
+	void __iomem *reg_base;
 
-	mask_module_irq(ctx, false);
+	FOR_EACH_SLAVE_TYPE(ctx, i) {
+		writel(BIT(31), BASE(i) + ctx->data->apc_con_offset);
+
+		reg_base = BASE(i) + ctx->data->vio_mask_offset;
+		mask_module_irq(reg_base, VIO_IDX_NUM(i), false);
+	}
 }
 
 /*
@@ -210,13 +264,18 @@ static void start_devapc(struct mtk_devapc_context *ctx)
  */
 static void stop_devapc(struct mtk_devapc_context *ctx)
 {
-	mask_module_irq(ctx, true);
+	int i;
+	void __iomem *reg_base;
+
+	FOR_EACH_SLAVE_TYPE(ctx, i) {
+		reg_base = BASE(i) + ctx->data->vio_mask_offset;
+		mask_module_irq(reg_base, VIO_IDX_NUM(i), true);
 
-	writel(BIT(2), ctx->infra_base + ctx->data->apc_con_offset);
+		writel(BIT(2), BASE(i) + ctx->data->apc_con_offset);
+	}
 }
 
 static const struct mtk_devapc_data devapc_mt6779 = {
-	.vio_idx_num = 511,
 	.vio_mask_offset = 0x0,
 	.vio_sta_offset = 0x400,
 	.vio_dbg0_offset = 0x900,
@@ -227,11 +286,26 @@ static const struct mtk_devapc_data devapc_mt6779 = {
 	.vio_shift_con_offset = 0xF20,
 };
 
+static const struct mtk_devapc_data devapc_mt8192 = {
+	.vio_mask_offset = 0x0,
+	.vio_sta_offset = 0x400,
+	.vio_dbg0_offset = 0x900,
+	.vio_dbg1_offset = 0x904,
+	.vio_dbg2_offset = 0x908,
+	.apc_con_offset = 0xF00,
+	.vio_shift_sta_offset = 0xF20,
+	.vio_shift_sel_offset = 0xF30,
+	.vio_shift_con_offset = 0xF10,
+};
+
 static const struct of_device_id mtk_devapc_dt_match[] = {
 	{
 		.compatible = "mediatek,mt6779-devapc",
 		.data = &devapc_mt6779,
 	}, {
+		.compatible = "mediatek,mt8192-devapc",
+		.data = &devapc_mt8192,
+	}, {
 	},
 };
 
@@ -239,6 +313,7 @@ static int mtk_devapc_probe(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
 	struct mtk_devapc_context *ctx;
+	int i;
 	u32 devapc_irq;
 	int ret;
 
@@ -252,8 +327,32 @@ static int mtk_devapc_probe(struct platform_device *pdev)
 	ctx->data = of_device_get_match_data(&pdev->dev);
 	ctx->dev = &pdev->dev;
 
-	ctx->infra_base = of_iomap(node, 0);
-	if (!ctx->infra_base)
+	if (of_property_read_u32(node, "version", &ctx->arch_ver))
+		return -EINVAL;
+
+	if (of_property_read_u32(node, "slave_type_num", &ctx->slave_type_num))
+		return -EINVAL;
+
+	ctx->base_list = devm_kzalloc(&pdev->dev,
+				      sizeof(void *) * ctx->slave_type_num,
+				      GFP_KERNEL);
+	if (!ctx->base_list)
+		return -ENOMEM;
+
+	FOR_EACH_SLAVE_TYPE(ctx, i) {
+		BASE(i) = of_iomap(node, i);
+		if (!BASE(i))
+			return -EINVAL;
+	}
+
+	ctx->vio_idx_num = devm_kzalloc(&pdev->dev,
+					sizeof(u32) * ctx->slave_type_num,
+					GFP_KERNEL);
+	if (!ctx->vio_idx_num)
+		return -ENOMEM;
+
+	if (of_property_read_u32_array(node, "vio_idx_num",
+				       ctx->vio_idx_num, ctx->slave_type_num))
 		return -EINVAL;
 
 	devapc_irq = irq_of_parse_and_map(node, 0);
-- 
2.6.4




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux