1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020 MediaTek Inc.
7 #include <linux/interrupt.h>
8 #include <linux/iopoll.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
12 #include <linux/of_irq.h>
13 #include <linux/of_address.h>
15 #define VIO_MOD_TO_REG_IND(m) ((m) / 32)
16 #define VIO_MOD_TO_REG_OFF(m) ((m) % 32)
18 struct mtk_devapc_vio_dbgs
{
34 struct mtk_devapc_regs_ofs
{
41 u32 vio_shift_sta_offset
;
42 u32 vio_shift_sel_offset
;
43 u32 vio_shift_con_offset
;
46 struct mtk_devapc_data
{
47 /* numbers of violation index */
49 const struct mtk_devapc_regs_ofs
*regs_ofs
;
52 struct mtk_devapc_context
{
54 void __iomem
*infra_base
;
55 struct clk
*infra_clk
;
56 const struct mtk_devapc_data
*data
;
59 static void clear_vio_status(struct mtk_devapc_context
*ctx
)
64 reg
= ctx
->infra_base
+ ctx
->data
->regs_ofs
->vio_sta_offset
;
66 for (i
= 0; i
< VIO_MOD_TO_REG_IND(ctx
->data
->vio_idx_num
) - 1; i
++)
67 writel(GENMASK(31, 0), reg
+ 4 * i
);
69 writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1, 0),
73 static void mask_module_irq(struct mtk_devapc_context
*ctx
, bool mask
)
79 reg
= ctx
->infra_base
+ ctx
->data
->regs_ofs
->vio_mask_offset
;
86 for (i
= 0; i
< VIO_MOD_TO_REG_IND(ctx
->data
->vio_idx_num
) - 1; i
++)
87 writel(val
, reg
+ 4 * i
);
89 val
= readl(reg
+ 4 * i
);
91 val
|= GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1,
94 val
&= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1,
97 writel(val
, reg
+ 4 * i
);
100 #define PHY_DEVAPC_TIMEOUT 0x10000
103 * devapc_sync_vio_dbg - do "shift" mechansim" to get full violation information.
104 * shift mechanism is depends on devapc hardware design.
105 * Mediatek devapc set multiple slaves as a group.
106 * When violation is triggered, violation info is kept
107 * inside devapc hardware.
108 * Driver should do shift mechansim to sync full violation
109 * info to VIO_DBGs registers.
112 static int devapc_sync_vio_dbg(struct mtk_devapc_context
*ctx
)
114 void __iomem
*pd_vio_shift_sta_reg
;
115 void __iomem
*pd_vio_shift_sel_reg
;
116 void __iomem
*pd_vio_shift_con_reg
;
121 pd_vio_shift_sta_reg
= ctx
->infra_base
+
122 ctx
->data
->regs_ofs
->vio_shift_sta_offset
;
123 pd_vio_shift_sel_reg
= ctx
->infra_base
+
124 ctx
->data
->regs_ofs
->vio_shift_sel_offset
;
125 pd_vio_shift_con_reg
= ctx
->infra_base
+
126 ctx
->data
->regs_ofs
->vio_shift_con_offset
;
128 /* Find the minimum shift group which has violation */
129 val
= readl(pd_vio_shift_sta_reg
);
133 min_shift_group
= __ffs(val
);
135 /* Assign the group to sync */
136 writel(0x1 << min_shift_group
, pd_vio_shift_sel_reg
);
139 writel(0x1, pd_vio_shift_con_reg
);
141 ret
= readl_poll_timeout(pd_vio_shift_con_reg
, val
, val
== 0x3, 0,
144 dev_err(ctx
->dev
, "%s: Shift violation info failed\n", __func__
);
149 writel(0x0, pd_vio_shift_con_reg
);
152 writel(0x1 << min_shift_group
, pd_vio_shift_sta_reg
);
158 * devapc_extract_vio_dbg - extract full violation information after doing
161 static void devapc_extract_vio_dbg(struct mtk_devapc_context
*ctx
)
163 struct mtk_devapc_vio_dbgs vio_dbgs
;
164 void __iomem
*vio_dbg0_reg
;
165 void __iomem
*vio_dbg1_reg
;
167 vio_dbg0_reg
= ctx
->infra_base
+ ctx
->data
->regs_ofs
->vio_dbg0_offset
;
168 vio_dbg1_reg
= ctx
->infra_base
+ ctx
->data
->regs_ofs
->vio_dbg1_offset
;
170 vio_dbgs
.vio_dbg0
= readl(vio_dbg0_reg
);
171 vio_dbgs
.vio_dbg1
= readl(vio_dbg1_reg
);
173 /* Print violation information */
174 if (vio_dbgs
.dbg0_bits
.vio_w
)
175 dev_info(ctx
->dev
, "Write Violation\n");
176 else if (vio_dbgs
.dbg0_bits
.vio_r
)
177 dev_info(ctx
->dev
, "Read Violation\n");
179 dev_info(ctx
->dev
, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n",
180 vio_dbgs
.dbg0_bits
.mstid
, vio_dbgs
.dbg0_bits
.dmnid
,
185 * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump
186 * violation information including which master violates
189 static irqreturn_t
devapc_violation_irq(int irq_number
, void *data
)
191 struct mtk_devapc_context
*ctx
= data
;
193 while (devapc_sync_vio_dbg(ctx
))
194 devapc_extract_vio_dbg(ctx
);
196 clear_vio_status(ctx
);
202 * start_devapc - unmask slave's irq to start receiving devapc violation.
204 static void start_devapc(struct mtk_devapc_context
*ctx
)
206 writel(BIT(31), ctx
->infra_base
+ ctx
->data
->regs_ofs
->apc_con_offset
);
208 mask_module_irq(ctx
, false);
212 * stop_devapc - mask slave's irq to stop service.
214 static void stop_devapc(struct mtk_devapc_context
*ctx
)
216 mask_module_irq(ctx
, true);
218 writel(BIT(2), ctx
->infra_base
+ ctx
->data
->regs_ofs
->apc_con_offset
);
221 static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779
= {
222 .vio_mask_offset
= 0x0,
223 .vio_sta_offset
= 0x400,
224 .vio_dbg0_offset
= 0x900,
225 .vio_dbg1_offset
= 0x904,
226 .apc_con_offset
= 0xF00,
227 .vio_shift_sta_offset
= 0xF10,
228 .vio_shift_sel_offset
= 0xF14,
229 .vio_shift_con_offset
= 0xF20,
232 static const struct mtk_devapc_data devapc_mt6779
= {
234 .regs_ofs
= &devapc_regs_ofs_mt6779
,
237 static const struct mtk_devapc_data devapc_mt8186
= {
239 .regs_ofs
= &devapc_regs_ofs_mt6779
,
242 static const struct of_device_id mtk_devapc_dt_match
[] = {
244 .compatible
= "mediatek,mt6779-devapc",
245 .data
= &devapc_mt6779
,
247 .compatible
= "mediatek,mt8186-devapc",
248 .data
= &devapc_mt8186
,
252 MODULE_DEVICE_TABLE(of
, mtk_devapc_dt_match
);
254 static int mtk_devapc_probe(struct platform_device
*pdev
)
256 struct device_node
*node
= pdev
->dev
.of_node
;
257 struct mtk_devapc_context
*ctx
;
264 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
268 ctx
->data
= of_device_get_match_data(&pdev
->dev
);
269 ctx
->dev
= &pdev
->dev
;
271 ctx
->infra_base
= of_iomap(node
, 0);
272 if (!ctx
->infra_base
)
275 devapc_irq
= irq_of_parse_and_map(node
, 0);
279 ctx
->infra_clk
= devm_clk_get_enabled(&pdev
->dev
, "devapc-infra-clock");
280 if (IS_ERR(ctx
->infra_clk
))
283 ret
= devm_request_irq(&pdev
->dev
, devapc_irq
, devapc_violation_irq
,
284 IRQF_TRIGGER_NONE
, "devapc", ctx
);
288 platform_set_drvdata(pdev
, ctx
);
295 static void mtk_devapc_remove(struct platform_device
*pdev
)
297 struct mtk_devapc_context
*ctx
= platform_get_drvdata(pdev
);
302 static struct platform_driver mtk_devapc_driver
= {
303 .probe
= mtk_devapc_probe
,
304 .remove
= mtk_devapc_remove
,
306 .name
= "mtk-devapc",
307 .of_match_table
= mtk_devapc_dt_match
,
311 module_platform_driver(mtk_devapc_driver
);
313 MODULE_DESCRIPTION("Mediatek Device APC Driver");
314 MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>");
315 MODULE_LICENSE("GPL");