Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.8-rc6 253 lines 6.0 kB view raw
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/kernel.h> 21#include <linux/module.h> 22#include <linux/ratelimit.h> 23#include <linux/platform_device.h> 24#include <linux/interrupt.h> 25#include <linux/io.h> 26 27#define DRV_NAME "tegra20-mc" 28 29#define MC_INTSTATUS 0x0 30#define MC_INTMASK 0x4 31 32#define MC_INT_ERR_SHIFT 6 33#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT) 34#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT) 35#define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1) 36#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2) 37#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3) 38 39#define MC_GART_ERROR_REQ 0x30 40#define MC_DECERR_EMEM_OTHERS_STATUS 0x58 41#define MC_SECURITY_VIOLATION_STATUS 0x74 42 43#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */ 44 45#define MC_CLIENT_ID_MASK 0x3f 46 47#define NUM_MC_REG_BANKS 2 48 49struct tegra20_mc { 50 void __iomem *regs[NUM_MC_REG_BANKS]; 51 struct device *dev; 52}; 53 54static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs) 55{ 56 u32 val = 0; 57 58 if (offs < 0x24) 59 val = readl(mc->regs[0] + offs); 60 else if (offs < 0x400) 61 val = readl(mc->regs[1] + offs - 0x3c); 62 63 return val; 64} 65 66static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs) 67{ 68 if (offs < 0x24) 69 writel(val, mc->regs[0] + offs); 70 else if (offs < 0x400) 71 writel(val, mc->regs[1] + offs - 0x3c); 72} 73 74static const char * const tegra20_mc_client[] = { 75 "cbr_display0a", 76 "cbr_display0ab", 77 "cbr_display0b", 78 "cbr_display0bb", 79 "cbr_display0c", 80 "cbr_display0cb", 81 "cbr_display1b", 82 "cbr_display1bb", 83 "cbr_eppup", 84 "cbr_g2pr", 85 "cbr_g2sr", 86 "cbr_mpeunifbr", 87 "cbr_viruv", 88 "csr_avpcarm7r", 89 "csr_displayhc", 90 "csr_displayhcb", 91 "csr_fdcdrd", 92 "csr_g2dr", 93 "csr_host1xdmar", 94 "csr_host1xr", 95 "csr_idxsrd", 96 "csr_mpcorer", 97 "csr_mpe_ipred", 98 "csr_mpeamemrd", 99 "csr_mpecsrd", 100 "csr_ppcsahbdmar", 101 "csr_ppcsahbslvr", 102 "csr_texsrd", 103 "csr_vdebsevr", 104 "csr_vdember", 105 "csr_vdemcer", 106 "csr_vdetper", 107 "cbw_eppu", 108 "cbw_eppv", 109 "cbw_eppy", 110 "cbw_mpeunifbw", 111 "cbw_viwsb", 112 "cbw_viwu", 113 "cbw_viwv", 114 "cbw_viwy", 115 "ccw_g2dw", 116 "csw_avpcarm7w", 117 "csw_fdcdwr", 118 "csw_host1xw", 119 "csw_ispw", 120 "csw_mpcorew", 121 "csw_mpecswr", 122 "csw_ppcsahbdmaw", 123 "csw_ppcsahbslvw", 124 "csw_vdebsevw", 125 "csw_vdembew", 126 "csw_vdetpmw", 127}; 128 129static void tegra20_mc_decode(struct tegra20_mc *mc, int n) 130{ 131 u32 addr, req; 132 const char *client = "Unknown"; 133 int idx, cid; 134 const struct reg_info { 135 u32 offset; 136 u32 write_bit; /* 0=READ, 1=WRITE */ 137 int cid_shift; 138 char *message; 139 } reg[] = { 140 { 141 .offset = MC_DECERR_EMEM_OTHERS_STATUS, 142 .write_bit = 31, 143 .message = "MC_DECERR", 144 }, 145 { 146 .offset = MC_GART_ERROR_REQ, 147 .cid_shift = 1, 148 .message = "MC_GART_ERR", 149 150 }, 151 { 152 .offset = MC_SECURITY_VIOLATION_STATUS, 153 .write_bit = 31, 154 .message = "MC_SECURITY_ERR", 155 }, 156 }; 157 158 idx = n - MC_INT_ERR_SHIFT; 159 if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) { 160 dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", 161 BIT(n)); 162 return; 163 } 164 165 req = mc_readl(mc, reg[idx].offset); 166 cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK; 167 if (cid < ARRAY_SIZE(tegra20_mc_client)) 168 client = tegra20_mc_client[cid]; 169 170 addr = mc_readl(mc, reg[idx].offset + sizeof(u32)); 171 172 dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n", 173 reg[idx].message, req, addr, client, 174 (req & BIT(reg[idx].write_bit)) ? "write" : "read", 175 (reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ? 176 ((req & SECURITY_VIOLATION_TYPE) ? 177 "carveout" : "trustzone") : ""); 178} 179 180static const struct of_device_id tegra20_mc_of_match[] = { 181 { .compatible = "nvidia,tegra20-mc", }, 182 {}, 183}; 184 185static irqreturn_t tegra20_mc_isr(int irq, void *data) 186{ 187 u32 stat, mask, bit; 188 struct tegra20_mc *mc = data; 189 190 stat = mc_readl(mc, MC_INTSTATUS); 191 mask = mc_readl(mc, MC_INTMASK); 192 mask &= stat; 193 if (!mask) 194 return IRQ_NONE; 195 while ((bit = ffs(mask)) != 0) 196 tegra20_mc_decode(mc, bit - 1); 197 mc_writel(mc, stat, MC_INTSTATUS); 198 return IRQ_HANDLED; 199} 200 201static int tegra20_mc_probe(struct platform_device *pdev) 202{ 203 struct resource *irq; 204 struct tegra20_mc *mc; 205 int i, err; 206 u32 intmask; 207 208 mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 209 if (!mc) 210 return -ENOMEM; 211 mc->dev = &pdev->dev; 212 213 for (i = 0; i < ARRAY_SIZE(mc->regs); i++) { 214 struct resource *res; 215 216 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 217 if (!res) 218 return -ENODEV; 219 mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res); 220 if (!mc->regs[i]) 221 return -EBUSY; 222 } 223 224 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 225 if (!irq) 226 return -ENODEV; 227 err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr, 228 IRQF_SHARED, dev_name(&pdev->dev), mc); 229 if (err) 230 return -ENODEV; 231 232 platform_set_drvdata(pdev, mc); 233 234 intmask = MC_INT_INVALID_GART_PAGE | 235 MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION; 236 mc_writel(mc, intmask, MC_INTMASK); 237 return 0; 238} 239 240static struct platform_driver tegra20_mc_driver = { 241 .probe = tegra20_mc_probe, 242 .driver = { 243 .name = DRV_NAME, 244 .owner = THIS_MODULE, 245 .of_match_table = tegra20_mc_of_match, 246 }, 247}; 248module_platform_driver(tegra20_mc_driver); 249 250MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); 251MODULE_DESCRIPTION("Tegra20 MC driver"); 252MODULE_LICENSE("GPL v2"); 253MODULE_ALIAS("platform:" DRV_NAME);