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>
11 #include <linux/of_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_data
{
35 /* numbers of violation index */
44 u32 vio_shift_sta_offset
;
45 u32 vio_shift_sel_offset
;
46 u32 vio_shift_con_offset
;
49 struct mtk_devapc_context
{
51 void __iomem
*infra_base
;
52 struct clk
*infra_clk
;
53 const struct mtk_devapc_data
*data
;
56 static void clear_vio_status(struct mtk_devapc_context
*ctx
)
61 reg
= ctx
->infra_base
+ ctx
->data
->vio_sta_offset
;
63 for (i
= 0; i
< VIO_MOD_TO_REG_IND(ctx
->data
->vio_idx_num
) - 1; i
++)
64 writel(GENMASK(31, 0), reg
+ 4 * i
);
66 writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1, 0),
70 static void mask_module_irq(struct mtk_devapc_context
*ctx
, bool mask
)
76 reg
= ctx
->infra_base
+ ctx
->data
->vio_mask_offset
;
83 for (i
= 0; i
< VIO_MOD_TO_REG_IND(ctx
->data
->vio_idx_num
) - 1; i
++)
84 writel(val
, reg
+ 4 * i
);
86 val
= readl(reg
+ 4 * i
);
88 val
|= GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1,
91 val
&= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx
->data
->vio_idx_num
) - 1,
94 writel(val
, reg
+ 4 * i
);
97 #define PHY_DEVAPC_TIMEOUT 0x10000
100 * devapc_sync_vio_dbg - do "shift" mechansim" to get full violation information.
101 * shift mechanism is depends on devapc hardware design.
102 * Mediatek devapc set multiple slaves as a group.
103 * When violation is triggered, violation info is kept
104 * inside devapc hardware.
105 * Driver should do shift mechansim to sync full violation
106 * info to VIO_DBGs registers.
109 static int devapc_sync_vio_dbg(struct mtk_devapc_context
*ctx
)
111 void __iomem
*pd_vio_shift_sta_reg
;
112 void __iomem
*pd_vio_shift_sel_reg
;
113 void __iomem
*pd_vio_shift_con_reg
;
118 pd_vio_shift_sta_reg
= ctx
->infra_base
+
119 ctx
->data
->vio_shift_sta_offset
;
120 pd_vio_shift_sel_reg
= ctx
->infra_base
+
121 ctx
->data
->vio_shift_sel_offset
;
122 pd_vio_shift_con_reg
= ctx
->infra_base
+
123 ctx
->data
->vio_shift_con_offset
;
125 /* Find the minimum shift group which has violation */
126 val
= readl(pd_vio_shift_sta_reg
);
130 min_shift_group
= __ffs(val
);
132 /* Assign the group to sync */
133 writel(0x1 << min_shift_group
, pd_vio_shift_sel_reg
);
136 writel(0x1, pd_vio_shift_con_reg
);
138 ret
= readl_poll_timeout(pd_vio_shift_con_reg
, val
, val
== 0x3, 0,
141 dev_err(ctx
->dev
, "%s: Shift violation info failed\n", __func__
);
146 writel(0x0, pd_vio_shift_con_reg
);
149 writel(0x1 << min_shift_group
, pd_vio_shift_sta_reg
);
155 * devapc_extract_vio_dbg - extract full violation information after doing
158 static void devapc_extract_vio_dbg(struct mtk_devapc_context
*ctx
)
160 struct mtk_devapc_vio_dbgs vio_dbgs
;
161 void __iomem
*vio_dbg0_reg
;
162 void __iomem
*vio_dbg1_reg
;
164 vio_dbg0_reg
= ctx
->infra_base
+ ctx
->data
->vio_dbg0_offset
;
165 vio_dbg1_reg
= ctx
->infra_base
+ ctx
->data
->vio_dbg1_offset
;
167 vio_dbgs
.vio_dbg0
= readl(vio_dbg0_reg
);
168 vio_dbgs
.vio_dbg1
= readl(vio_dbg1_reg
);
170 /* Print violation information */
171 if (vio_dbgs
.dbg0_bits
.vio_w
)
172 dev_info(ctx
->dev
, "Write Violation\n");
173 else if (vio_dbgs
.dbg0_bits
.vio_r
)
174 dev_info(ctx
->dev
, "Read Violation\n");
176 dev_info(ctx
->dev
, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n",
177 vio_dbgs
.dbg0_bits
.mstid
, vio_dbgs
.dbg0_bits
.dmnid
,
182 * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump
183 * violation information including which master violates
186 static irqreturn_t
devapc_violation_irq(int irq_number
, void *data
)
188 struct mtk_devapc_context
*ctx
= data
;
190 while (devapc_sync_vio_dbg(ctx
))
191 devapc_extract_vio_dbg(ctx
);
193 clear_vio_status(ctx
);
199 * start_devapc - unmask slave's irq to start receiving devapc violation.
201 static void start_devapc(struct mtk_devapc_context
*ctx
)
203 writel(BIT(31), ctx
->infra_base
+ ctx
->data
->apc_con_offset
);
205 mask_module_irq(ctx
, false);
209 * stop_devapc - mask slave's irq to stop service.
211 static void stop_devapc(struct mtk_devapc_context
*ctx
)
213 mask_module_irq(ctx
, true);
215 writel(BIT(2), ctx
->infra_base
+ ctx
->data
->apc_con_offset
);
218 static const struct mtk_devapc_data devapc_mt6779
= {
220 .vio_mask_offset
= 0x0,
221 .vio_sta_offset
= 0x400,
222 .vio_dbg0_offset
= 0x900,
223 .vio_dbg1_offset
= 0x904,
224 .apc_con_offset
= 0xF00,
225 .vio_shift_sta_offset
= 0xF10,
226 .vio_shift_sel_offset
= 0xF14,
227 .vio_shift_con_offset
= 0xF20,
230 static const struct of_device_id mtk_devapc_dt_match
[] = {
232 .compatible
= "mediatek,mt6779-devapc",
233 .data
= &devapc_mt6779
,
238 static int mtk_devapc_probe(struct platform_device
*pdev
)
240 struct device_node
*node
= pdev
->dev
.of_node
;
241 struct mtk_devapc_context
*ctx
;
248 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
252 ctx
->data
= of_device_get_match_data(&pdev
->dev
);
253 ctx
->dev
= &pdev
->dev
;
255 ctx
->infra_base
= of_iomap(node
, 0);
256 if (!ctx
->infra_base
)
259 devapc_irq
= irq_of_parse_and_map(node
, 0);
263 ctx
->infra_clk
= devm_clk_get(&pdev
->dev
, "devapc-infra-clock");
264 if (IS_ERR(ctx
->infra_clk
))
267 if (clk_prepare_enable(ctx
->infra_clk
))
270 ret
= devm_request_irq(&pdev
->dev
, devapc_irq
, devapc_violation_irq
,
271 IRQF_TRIGGER_NONE
, "devapc", ctx
);
273 clk_disable_unprepare(ctx
->infra_clk
);
277 platform_set_drvdata(pdev
, ctx
);
284 static int mtk_devapc_remove(struct platform_device
*pdev
)
286 struct mtk_devapc_context
*ctx
= platform_get_drvdata(pdev
);
290 clk_disable_unprepare(ctx
->infra_clk
);
295 static struct platform_driver mtk_devapc_driver
= {
296 .probe
= mtk_devapc_probe
,
297 .remove
= mtk_devapc_remove
,
299 .name
= "mtk-devapc",
300 .of_match_table
= mtk_devapc_dt_match
,
304 module_platform_driver(mtk_devapc_driver
);
306 MODULE_DESCRIPTION("Mediatek Device APC Driver");
307 MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>");
308 MODULE_LICENSE("GPL");