Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2020 MediaTek Inc.
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/interrupt.h>
0008 #include <linux/iopoll.h>
0009 #include <linux/module.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/of_device.h>
0012 #include <linux/of_irq.h>
0013 #include <linux/of_address.h>
0014 
0015 #define VIO_MOD_TO_REG_IND(m)   ((m) / 32)
0016 #define VIO_MOD_TO_REG_OFF(m)   ((m) % 32)
0017 
0018 struct mtk_devapc_vio_dbgs {
0019     union {
0020         u32 vio_dbg0;
0021         struct {
0022             u32 mstid:16;
0023             u32 dmnid:6;
0024             u32 vio_w:1;
0025             u32 vio_r:1;
0026             u32 addr_h:4;
0027             u32 resv:4;
0028         } dbg0_bits;
0029     };
0030 
0031     u32 vio_dbg1;
0032 };
0033 
0034 struct mtk_devapc_regs_ofs {
0035     /* reg offset */
0036     u32 vio_mask_offset;
0037     u32 vio_sta_offset;
0038     u32 vio_dbg0_offset;
0039     u32 vio_dbg1_offset;
0040     u32 apc_con_offset;
0041     u32 vio_shift_sta_offset;
0042     u32 vio_shift_sel_offset;
0043     u32 vio_shift_con_offset;
0044 };
0045 
0046 struct mtk_devapc_data {
0047     /* numbers of violation index */
0048     u32 vio_idx_num;
0049     const struct mtk_devapc_regs_ofs *regs_ofs;
0050 };
0051 
0052 struct mtk_devapc_context {
0053     struct device *dev;
0054     void __iomem *infra_base;
0055     struct clk *infra_clk;
0056     const struct mtk_devapc_data *data;
0057 };
0058 
0059 static void clear_vio_status(struct mtk_devapc_context *ctx)
0060 {
0061     void __iomem *reg;
0062     int i;
0063 
0064     reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset;
0065 
0066     for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
0067         writel(GENMASK(31, 0), reg + 4 * i);
0068 
0069     writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, 0),
0070            reg + 4 * i);
0071 }
0072 
0073 static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
0074 {
0075     void __iomem *reg;
0076     u32 val;
0077     int i;
0078 
0079     reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset;
0080 
0081     if (mask)
0082         val = GENMASK(31, 0);
0083     else
0084         val = 0;
0085 
0086     for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
0087         writel(val, reg + 4 * i);
0088 
0089     val = readl(reg + 4 * i);
0090     if (mask)
0091         val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
0092                    0);
0093     else
0094         val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
0095                 0);
0096 
0097     writel(val, reg + 4 * i);
0098 }
0099 
0100 #define PHY_DEVAPC_TIMEOUT  0x10000
0101 
0102 /*
0103  * devapc_sync_vio_dbg - do "shift" mechansim" to get full violation information.
0104  *                       shift mechanism is depends on devapc hardware design.
0105  *                       Mediatek devapc set multiple slaves as a group.
0106  *                       When violation is triggered, violation info is kept
0107  *                       inside devapc hardware.
0108  *                       Driver should do shift mechansim to sync full violation
0109  *                       info to VIO_DBGs registers.
0110  *
0111  */
0112 static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
0113 {
0114     void __iomem *pd_vio_shift_sta_reg;
0115     void __iomem *pd_vio_shift_sel_reg;
0116     void __iomem *pd_vio_shift_con_reg;
0117     int min_shift_group;
0118     int ret;
0119     u32 val;
0120 
0121     pd_vio_shift_sta_reg = ctx->infra_base +
0122                    ctx->data->regs_ofs->vio_shift_sta_offset;
0123     pd_vio_shift_sel_reg = ctx->infra_base +
0124                    ctx->data->regs_ofs->vio_shift_sel_offset;
0125     pd_vio_shift_con_reg = ctx->infra_base +
0126                    ctx->data->regs_ofs->vio_shift_con_offset;
0127 
0128     /* Find the minimum shift group which has violation */
0129     val = readl(pd_vio_shift_sta_reg);
0130     if (!val)
0131         return false;
0132 
0133     min_shift_group = __ffs(val);
0134 
0135     /* Assign the group to sync */
0136     writel(0x1 << min_shift_group, pd_vio_shift_sel_reg);
0137 
0138     /* Start syncing */
0139     writel(0x1, pd_vio_shift_con_reg);
0140 
0141     ret = readl_poll_timeout(pd_vio_shift_con_reg, val, val == 0x3, 0,
0142                  PHY_DEVAPC_TIMEOUT);
0143     if (ret) {
0144         dev_err(ctx->dev, "%s: Shift violation info failed\n", __func__);
0145         return false;
0146     }
0147 
0148     /* Stop syncing */
0149     writel(0x0, pd_vio_shift_con_reg);
0150 
0151     /* Write clear */
0152     writel(0x1 << min_shift_group, pd_vio_shift_sta_reg);
0153 
0154     return true;
0155 }
0156 
0157 /*
0158  * devapc_extract_vio_dbg - extract full violation information after doing
0159  *                          shift mechanism.
0160  */
0161 static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
0162 {
0163     struct mtk_devapc_vio_dbgs vio_dbgs;
0164     void __iomem *vio_dbg0_reg;
0165     void __iomem *vio_dbg1_reg;
0166 
0167     vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset;
0168     vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset;
0169 
0170     vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
0171     vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
0172 
0173     /* Print violation information */
0174     if (vio_dbgs.dbg0_bits.vio_w)
0175         dev_info(ctx->dev, "Write Violation\n");
0176     else if (vio_dbgs.dbg0_bits.vio_r)
0177         dev_info(ctx->dev, "Read Violation\n");
0178 
0179     dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n",
0180          vio_dbgs.dbg0_bits.mstid, vio_dbgs.dbg0_bits.dmnid,
0181          vio_dbgs.vio_dbg1);
0182 }
0183 
0184 /*
0185  * devapc_violation_irq - the devapc Interrupt Service Routine (ISR) will dump
0186  *                        violation information including which master violates
0187  *                        access slave.
0188  */
0189 static irqreturn_t devapc_violation_irq(int irq_number, void *data)
0190 {
0191     struct mtk_devapc_context *ctx = data;
0192 
0193     while (devapc_sync_vio_dbg(ctx))
0194         devapc_extract_vio_dbg(ctx);
0195 
0196     clear_vio_status(ctx);
0197 
0198     return IRQ_HANDLED;
0199 }
0200 
0201 /*
0202  * start_devapc - unmask slave's irq to start receiving devapc violation.
0203  */
0204 static void start_devapc(struct mtk_devapc_context *ctx)
0205 {
0206     writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
0207 
0208     mask_module_irq(ctx, false);
0209 }
0210 
0211 /*
0212  * stop_devapc - mask slave's irq to stop service.
0213  */
0214 static void stop_devapc(struct mtk_devapc_context *ctx)
0215 {
0216     mask_module_irq(ctx, true);
0217 
0218     writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
0219 }
0220 
0221 static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = {
0222     .vio_mask_offset = 0x0,
0223     .vio_sta_offset = 0x400,
0224     .vio_dbg0_offset = 0x900,
0225     .vio_dbg1_offset = 0x904,
0226     .apc_con_offset = 0xF00,
0227     .vio_shift_sta_offset = 0xF10,
0228     .vio_shift_sel_offset = 0xF14,
0229     .vio_shift_con_offset = 0xF20,
0230 };
0231 
0232 static const struct mtk_devapc_data devapc_mt6779 = {
0233     .vio_idx_num = 511,
0234     .regs_ofs = &devapc_regs_ofs_mt6779,
0235 };
0236 
0237 static const struct mtk_devapc_data devapc_mt8186 = {
0238     .vio_idx_num = 519,
0239     .regs_ofs = &devapc_regs_ofs_mt6779,
0240 };
0241 
0242 static const struct of_device_id mtk_devapc_dt_match[] = {
0243     {
0244         .compatible = "mediatek,mt6779-devapc",
0245         .data = &devapc_mt6779,
0246     }, {
0247         .compatible = "mediatek,mt8186-devapc",
0248         .data = &devapc_mt8186,
0249     }, {
0250     },
0251 };
0252 MODULE_DEVICE_TABLE(of, mtk_devapc_dt_match);
0253 
0254 static int mtk_devapc_probe(struct platform_device *pdev)
0255 {
0256     struct device_node *node = pdev->dev.of_node;
0257     struct mtk_devapc_context *ctx;
0258     u32 devapc_irq;
0259     int ret;
0260 
0261     if (IS_ERR(node))
0262         return -ENODEV;
0263 
0264     ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0265     if (!ctx)
0266         return -ENOMEM;
0267 
0268     ctx->data = of_device_get_match_data(&pdev->dev);
0269     ctx->dev = &pdev->dev;
0270 
0271     ctx->infra_base = of_iomap(node, 0);
0272     if (!ctx->infra_base)
0273         return -EINVAL;
0274 
0275     devapc_irq = irq_of_parse_and_map(node, 0);
0276     if (!devapc_irq)
0277         return -EINVAL;
0278 
0279     ctx->infra_clk = devm_clk_get(&pdev->dev, "devapc-infra-clock");
0280     if (IS_ERR(ctx->infra_clk))
0281         return -EINVAL;
0282 
0283     if (clk_prepare_enable(ctx->infra_clk))
0284         return -EINVAL;
0285 
0286     ret = devm_request_irq(&pdev->dev, devapc_irq, devapc_violation_irq,
0287                    IRQF_TRIGGER_NONE, "devapc", ctx);
0288     if (ret) {
0289         clk_disable_unprepare(ctx->infra_clk);
0290         return ret;
0291     }
0292 
0293     platform_set_drvdata(pdev, ctx);
0294 
0295     start_devapc(ctx);
0296 
0297     return 0;
0298 }
0299 
0300 static int mtk_devapc_remove(struct platform_device *pdev)
0301 {
0302     struct mtk_devapc_context *ctx = platform_get_drvdata(pdev);
0303 
0304     stop_devapc(ctx);
0305 
0306     clk_disable_unprepare(ctx->infra_clk);
0307 
0308     return 0;
0309 }
0310 
0311 static struct platform_driver mtk_devapc_driver = {
0312     .probe = mtk_devapc_probe,
0313     .remove = mtk_devapc_remove,
0314     .driver = {
0315         .name = "mtk-devapc",
0316         .of_match_table = mtk_devapc_dt_match,
0317     },
0318 };
0319 
0320 module_platform_driver(mtk_devapc_driver);
0321 
0322 MODULE_DESCRIPTION("Mediatek Device APC Driver");
0323 MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>");
0324 MODULE_LICENSE("GPL");