1/* 2 * Tegra20 Memory Controller 3 * 4 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20#include <linux/err.h> 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/ratelimit.h> 24#include <linux/platform_device.h> 25#include <linux/interrupt.h> 26#include <linux/io.h> 27 28#define DRV_NAME "tegra20-mc" 29 30#define MC_INTSTATUS 0x0 31#define MC_INTMASK 0x4 32 33#define MC_INT_ERR_SHIFT 6 34#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT) 35#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT) 36#define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1) 37#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2) 38#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3) 39 40#define MC_GART_ERROR_REQ 0x30 41#define MC_DECERR_EMEM_OTHERS_STATUS 0x58 42#define MC_SECURITY_VIOLATION_STATUS 0x74 43 44#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */ 45 46#define MC_CLIENT_ID_MASK 0x3f 47 48#define NUM_MC_REG_BANKS 2 49 50struct tegra20_mc { 51 void __iomem *regs[NUM_MC_REG_BANKS]; 52 struct device *dev; 53}; 54 55static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs) 56{ 57 u32 val = 0; 58 59 if (offs < 0x24) 60 val = readl(mc->regs[0] + offs); 61 else if (offs < 0x400) 62 val = readl(mc->regs[1] + offs - 0x3c); 63 64 return val; 65} 66 67static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs) 68{ 69 if (offs < 0x24) 70 writel(val, mc->regs[0] + offs); 71 else if (offs < 0x400) 72 writel(val, mc->regs[1] + offs - 0x3c); 73} 74 75static const char * const tegra20_mc_client[] = { 76 "cbr_display0a", 77 "cbr_display0ab", 78 "cbr_display0b", 79 "cbr_display0bb", 80 "cbr_display0c", 81 "cbr_display0cb", 82 "cbr_display1b", 83 "cbr_display1bb", 84 "cbr_eppup", 85 "cbr_g2pr", 86 "cbr_g2sr", 87 "cbr_mpeunifbr", 88 "cbr_viruv", 89 "csr_avpcarm7r", 90 "csr_displayhc", 91 "csr_displayhcb", 92 "csr_fdcdrd", 93 "csr_g2dr", 94 "csr_host1xdmar", 95 "csr_host1xr", 96 "csr_idxsrd", 97 "csr_mpcorer", 98 "csr_mpe_ipred", 99 "csr_mpeamemrd", 100 "csr_mpecsrd", 101 "csr_ppcsahbdmar", 102 "csr_ppcsahbslvr", 103 "csr_texsrd", 104 "csr_vdebsevr", 105 "csr_vdember", 106 "csr_vdemcer", 107 "csr_vdetper", 108 "cbw_eppu", 109 "cbw_eppv", 110 "cbw_eppy", 111 "cbw_mpeunifbw", 112 "cbw_viwsb", 113 "cbw_viwu", 114 "cbw_viwv", 115 "cbw_viwy", 116 "ccw_g2dw", 117 "csw_avpcarm7w", 118 "csw_fdcdwr", 119 "csw_host1xw", 120 "csw_ispw", 121 "csw_mpcorew", 122 "csw_mpecswr", 123 "csw_ppcsahbdmaw", 124 "csw_ppcsahbslvw", 125 "csw_vdebsevw", 126 "csw_vdembew", 127 "csw_vdetpmw", 128}; 129 130static void tegra20_mc_decode(struct tegra20_mc *mc, int n) 131{ 132 u32 addr, req; 133 const char *client = "Unknown"; 134 int idx, cid; 135 const struct reg_info { 136 u32 offset; 137 u32 write_bit; /* 0=READ, 1=WRITE */ 138 int cid_shift; 139 char *message; 140 } reg[] = { 141 { 142 .offset = MC_DECERR_EMEM_OTHERS_STATUS, 143 .write_bit = 31, 144 .message = "MC_DECERR", 145 }, 146 { 147 .offset = MC_GART_ERROR_REQ, 148 .cid_shift = 1, 149 .message = "MC_GART_ERR", 150 151 }, 152 { 153 .offset = MC_SECURITY_VIOLATION_STATUS, 154 .write_bit = 31, 155 .message = "MC_SECURITY_ERR", 156 }, 157 }; 158 159 idx = n - MC_INT_ERR_SHIFT; 160 if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) { 161 dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", 162 BIT(n)); 163 return; 164 } 165 166 req = mc_readl(mc, reg[idx].offset); 167 cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK; 168 if (cid < ARRAY_SIZE(tegra20_mc_client)) 169 client = tegra20_mc_client[cid]; 170 171 addr = mc_readl(mc, reg[idx].offset + sizeof(u32)); 172 173 dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n", 174 reg[idx].message, req, addr, client, 175 (req & BIT(reg[idx].write_bit)) ? "write" : "read", 176 (reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ? 177 ((req & SECURITY_VIOLATION_TYPE) ? 178 "carveout" : "trustzone") : ""); 179} 180 181static const struct of_device_id tegra20_mc_of_match[] = { 182 { .compatible = "nvidia,tegra20-mc", }, 183 {}, 184}; 185 186static irqreturn_t tegra20_mc_isr(int irq, void *data) 187{ 188 u32 stat, mask, bit; 189 struct tegra20_mc *mc = data; 190 191 stat = mc_readl(mc, MC_INTSTATUS); 192 mask = mc_readl(mc, MC_INTMASK); 193 mask &= stat; 194 if (!mask) 195 return IRQ_NONE; 196 while ((bit = ffs(mask)) != 0) { 197 tegra20_mc_decode(mc, bit - 1); 198 mask &= ~BIT(bit - 1); 199 } 200 201 mc_writel(mc, stat, MC_INTSTATUS); 202 return IRQ_HANDLED; 203} 204 205static int tegra20_mc_probe(struct platform_device *pdev) 206{ 207 struct resource *irq; 208 struct tegra20_mc *mc; 209 int i, err; 210 u32 intmask; 211 212 mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 213 if (!mc) 214 return -ENOMEM; 215 mc->dev = &pdev->dev; 216 217 for (i = 0; i < ARRAY_SIZE(mc->regs); i++) { 218 struct resource *res; 219 220 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 221 mc->regs[i] = devm_ioremap_resource(&pdev->dev, res); 222 if (IS_ERR(mc->regs[i])) 223 return PTR_ERR(mc->regs[i]); 224 } 225 226 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 227 if (!irq) 228 return -ENODEV; 229 err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr, 230 IRQF_SHARED, dev_name(&pdev->dev), mc); 231 if (err) 232 return -ENODEV; 233 234 platform_set_drvdata(pdev, mc); 235 236 intmask = MC_INT_INVALID_GART_PAGE | 237 MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION; 238 mc_writel(mc, intmask, MC_INTMASK); 239 return 0; 240} 241 242static struct platform_driver tegra20_mc_driver = { 243 .probe = tegra20_mc_probe, 244 .driver = { 245 .name = DRV_NAME, 246 .of_match_table = tegra20_mc_of_match, 247 }, 248}; 249module_platform_driver(tegra20_mc_driver); 250 251MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); 252MODULE_DESCRIPTION("Tegra20 MC driver"); 253MODULE_LICENSE("GPL v2"); 254MODULE_ALIAS("platform:" DRV_NAME); 255