cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mtk-devapc.c (7308B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2020 MediaTek Inc.
      4 */
      5
      6#include <linux/clk.h>
      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>
     14
     15#define VIO_MOD_TO_REG_IND(m)	((m) / 32)
     16#define VIO_MOD_TO_REG_OFF(m)	((m) % 32)
     17
     18struct mtk_devapc_vio_dbgs {
     19	union {
     20		u32 vio_dbg0;
     21		struct {
     22			u32 mstid:16;
     23			u32 dmnid:6;
     24			u32 vio_w:1;
     25			u32 vio_r:1;
     26			u32 addr_h:4;
     27			u32 resv:4;
     28		} dbg0_bits;
     29	};
     30
     31	u32 vio_dbg1;
     32};
     33
     34struct mtk_devapc_data {
     35	/* numbers of violation index */
     36	u32 vio_idx_num;
     37
     38	/* reg offset */
     39	u32 vio_mask_offset;
     40	u32 vio_sta_offset;
     41	u32 vio_dbg0_offset;
     42	u32 vio_dbg1_offset;
     43	u32 apc_con_offset;
     44	u32 vio_shift_sta_offset;
     45	u32 vio_shift_sel_offset;
     46	u32 vio_shift_con_offset;
     47};
     48
     49struct mtk_devapc_context {
     50	struct device *dev;
     51	void __iomem *infra_base;
     52	struct clk *infra_clk;
     53	const struct mtk_devapc_data *data;
     54};
     55
     56static void clear_vio_status(struct mtk_devapc_context *ctx)
     57{
     58	void __iomem *reg;
     59	int i;
     60
     61	reg = ctx->infra_base + ctx->data->vio_sta_offset;
     62
     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);
     65
     66	writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, 0),
     67	       reg + 4 * i);
     68}
     69
     70static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
     71{
     72	void __iomem *reg;
     73	u32 val;
     74	int i;
     75
     76	reg = ctx->infra_base + ctx->data->vio_mask_offset;
     77
     78	if (mask)
     79		val = GENMASK(31, 0);
     80	else
     81		val = 0;
     82
     83	for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
     84		writel(val, reg + 4 * i);
     85
     86	val = readl(reg + 4 * i);
     87	if (mask)
     88		val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
     89			       0);
     90	else
     91		val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
     92				0);
     93
     94	writel(val, reg + 4 * i);
     95}
     96
     97#define PHY_DEVAPC_TIMEOUT	0x10000
     98
     99/*
    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.
    107 *
    108 */
    109static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
    110{
    111	void __iomem *pd_vio_shift_sta_reg;
    112	void __iomem *pd_vio_shift_sel_reg;
    113	void __iomem *pd_vio_shift_con_reg;
    114	int min_shift_group;
    115	int ret;
    116	u32 val;
    117
    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;
    124
    125	/* Find the minimum shift group which has violation */
    126	val = readl(pd_vio_shift_sta_reg);
    127	if (!val)
    128		return false;
    129
    130	min_shift_group = __ffs(val);
    131
    132	/* Assign the group to sync */
    133	writel(0x1 << min_shift_group, pd_vio_shift_sel_reg);
    134
    135	/* Start syncing */
    136	writel(0x1, pd_vio_shift_con_reg);
    137
    138	ret = readl_poll_timeout(pd_vio_shift_con_reg, val, val == 0x3, 0,
    139				 PHY_DEVAPC_TIMEOUT);
    140	if (ret) {
    141		dev_err(ctx->dev, "%s: Shift violation info failed\n", __func__);
    142		return false;
    143	}
    144
    145	/* Stop syncing */
    146	writel(0x0, pd_vio_shift_con_reg);
    147
    148	/* Write clear */
    149	writel(0x1 << min_shift_group, pd_vio_shift_sta_reg);
    150
    151	return true;
    152}
    153
    154/*
    155 * devapc_extract_vio_dbg - extract full violation information after doing
    156 *                          shift mechanism.
    157 */
    158static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
    159{
    160	struct mtk_devapc_vio_dbgs vio_dbgs;
    161	void __iomem *vio_dbg0_reg;
    162	void __iomem *vio_dbg1_reg;
    163
    164	vio_dbg0_reg = ctx->infra_base + ctx->data->vio_dbg0_offset;
    165	vio_dbg1_reg = ctx->infra_base + ctx->data->vio_dbg1_offset;
    166
    167	vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
    168	vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
    169
    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");
    175
    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,
    178		 vio_dbgs.vio_dbg1);
    179}
    180
    181/*
    182 * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump
    183 *                        violation information including which master violates
    184 *                        access slave.
    185 */
    186static irqreturn_t devapc_violation_irq(int irq_number, void *data)
    187{
    188	struct mtk_devapc_context *ctx = data;
    189
    190	while (devapc_sync_vio_dbg(ctx))
    191		devapc_extract_vio_dbg(ctx);
    192
    193	clear_vio_status(ctx);
    194
    195	return IRQ_HANDLED;
    196}
    197
    198/*
    199 * start_devapc - unmask slave's irq to start receiving devapc violation.
    200 */
    201static void start_devapc(struct mtk_devapc_context *ctx)
    202{
    203	writel(BIT(31), ctx->infra_base + ctx->data->apc_con_offset);
    204
    205	mask_module_irq(ctx, false);
    206}
    207
    208/*
    209 * stop_devapc - mask slave's irq to stop service.
    210 */
    211static void stop_devapc(struct mtk_devapc_context *ctx)
    212{
    213	mask_module_irq(ctx, true);
    214
    215	writel(BIT(2), ctx->infra_base + ctx->data->apc_con_offset);
    216}
    217
    218static const struct mtk_devapc_data devapc_mt6779 = {
    219	.vio_idx_num = 511,
    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,
    228};
    229
    230static const struct of_device_id mtk_devapc_dt_match[] = {
    231	{
    232		.compatible = "mediatek,mt6779-devapc",
    233		.data = &devapc_mt6779,
    234	}, {
    235	},
    236};
    237MODULE_DEVICE_TABLE(of, mtk_devapc_dt_match);
    238
    239static int mtk_devapc_probe(struct platform_device *pdev)
    240{
    241	struct device_node *node = pdev->dev.of_node;
    242	struct mtk_devapc_context *ctx;
    243	u32 devapc_irq;
    244	int ret;
    245
    246	if (IS_ERR(node))
    247		return -ENODEV;
    248
    249	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
    250	if (!ctx)
    251		return -ENOMEM;
    252
    253	ctx->data = of_device_get_match_data(&pdev->dev);
    254	ctx->dev = &pdev->dev;
    255
    256	ctx->infra_base = of_iomap(node, 0);
    257	if (!ctx->infra_base)
    258		return -EINVAL;
    259
    260	devapc_irq = irq_of_parse_and_map(node, 0);
    261	if (!devapc_irq)
    262		return -EINVAL;
    263
    264	ctx->infra_clk = devm_clk_get(&pdev->dev, "devapc-infra-clock");
    265	if (IS_ERR(ctx->infra_clk))
    266		return -EINVAL;
    267
    268	if (clk_prepare_enable(ctx->infra_clk))
    269		return -EINVAL;
    270
    271	ret = devm_request_irq(&pdev->dev, devapc_irq, devapc_violation_irq,
    272			       IRQF_TRIGGER_NONE, "devapc", ctx);
    273	if (ret) {
    274		clk_disable_unprepare(ctx->infra_clk);
    275		return ret;
    276	}
    277
    278	platform_set_drvdata(pdev, ctx);
    279
    280	start_devapc(ctx);
    281
    282	return 0;
    283}
    284
    285static int mtk_devapc_remove(struct platform_device *pdev)
    286{
    287	struct mtk_devapc_context *ctx = platform_get_drvdata(pdev);
    288
    289	stop_devapc(ctx);
    290
    291	clk_disable_unprepare(ctx->infra_clk);
    292
    293	return 0;
    294}
    295
    296static struct platform_driver mtk_devapc_driver = {
    297	.probe = mtk_devapc_probe,
    298	.remove = mtk_devapc_remove,
    299	.driver = {
    300		.name = "mtk-devapc",
    301		.of_match_table = mtk_devapc_dt_match,
    302	},
    303};
    304
    305module_platform_driver(mtk_devapc_driver);
    306
    307MODULE_DESCRIPTION("Mediatek Device APC Driver");
    308MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>");
    309MODULE_LICENSE("GPL");