162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP L3 Interconnect error handling driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ 662306a36Sopenharmony_ci * Santosh Shilimkar <santosh.shilimkar@ti.com> 762306a36Sopenharmony_ci * Sricharan <r.sricharan@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "omap_l3_noc.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * l3_handle_target() - Handle Target specific parse and reporting 2362306a36Sopenharmony_ci * @l3: pointer to l3 struct 2462306a36Sopenharmony_ci * @base: base address of clkdm 2562306a36Sopenharmony_ci * @flag_mux: flagmux corresponding to the event 2662306a36Sopenharmony_ci * @err_src: error source index of the slave (target) 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * This does the second part of the error interrupt handling: 2962306a36Sopenharmony_ci * 3) Parse in the slave information 3062306a36Sopenharmony_ci * 4) Print the logged information. 3162306a36Sopenharmony_ci * 5) Add dump stack to provide kernel trace. 3262306a36Sopenharmony_ci * 6) Clear the source if known. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * This handles two types of errors: 3562306a36Sopenharmony_ci * 1) Custom errors in L3 : 3662306a36Sopenharmony_ci * Target like DMM/FW/EMIF generates SRESP=ERR error 3762306a36Sopenharmony_ci * 2) Standard L3 error: 3862306a36Sopenharmony_ci * - Unsupported CMD. 3962306a36Sopenharmony_ci * L3 tries to access target while it is idle 4062306a36Sopenharmony_ci * - OCP disconnect. 4162306a36Sopenharmony_ci * - Address hole error: 4262306a36Sopenharmony_ci * If DSS/ISS/FDIF/USBHOSTFS access a target where they 4362306a36Sopenharmony_ci * do not have connectivity, the error is logged in 4462306a36Sopenharmony_ci * their default target which is DMM2. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * On High Secure devices, firewall errors are possible and those 4762306a36Sopenharmony_ci * can be trapped as well. But the trapping is implemented as part 4862306a36Sopenharmony_ci * secure software and hence need not be implemented here. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic int l3_handle_target(struct omap_l3 *l3, void __iomem *base, 5162306a36Sopenharmony_ci struct l3_flagmux_data *flag_mux, int err_src) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci int k; 5462306a36Sopenharmony_ci u32 std_err_main, clear, masterid; 5562306a36Sopenharmony_ci u8 op_code, m_req_info; 5662306a36Sopenharmony_ci void __iomem *l3_targ_base; 5762306a36Sopenharmony_ci void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; 5862306a36Sopenharmony_ci void __iomem *l3_targ_hdr, *l3_targ_info; 5962306a36Sopenharmony_ci struct l3_target_data *l3_targ_inst; 6062306a36Sopenharmony_ci struct l3_masters_data *master; 6162306a36Sopenharmony_ci char *target_name, *master_name = "UN IDENTIFIED"; 6262306a36Sopenharmony_ci char *err_description; 6362306a36Sopenharmony_ci char err_string[30] = { 0 }; 6462306a36Sopenharmony_ci char info_string[60] = { 0 }; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* We DONOT expect err_src to go out of bounds */ 6762306a36Sopenharmony_ci BUG_ON(err_src > MAX_CLKDM_TARGETS); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (err_src < flag_mux->num_targ_data) { 7062306a36Sopenharmony_ci l3_targ_inst = &flag_mux->l3_targ[err_src]; 7162306a36Sopenharmony_ci target_name = l3_targ_inst->name; 7262306a36Sopenharmony_ci l3_targ_base = base + l3_targ_inst->offset; 7362306a36Sopenharmony_ci } else { 7462306a36Sopenharmony_ci target_name = L3_TARGET_NOT_SUPPORTED; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (target_name == L3_TARGET_NOT_SUPPORTED) 7862306a36Sopenharmony_ci return -ENODEV; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Read the stderrlog_main_source from clk domain */ 8162306a36Sopenharmony_ci l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; 8262306a36Sopenharmony_ci l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci std_err_main = readl_relaxed(l3_targ_stderr); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (std_err_main & CUSTOM_ERROR) { 8762306a36Sopenharmony_ci case STANDARD_ERROR: 8862306a36Sopenharmony_ci err_description = "Standard"; 8962306a36Sopenharmony_ci snprintf(err_string, sizeof(err_string), 9062306a36Sopenharmony_ci ": At Address: 0x%08X ", 9162306a36Sopenharmony_ci readl_relaxed(l3_targ_slvofslsb)); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR; 9462306a36Sopenharmony_ci l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_HDR; 9562306a36Sopenharmony_ci l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_INFO; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci case CUSTOM_ERROR: 9962306a36Sopenharmony_ci err_description = "Custom"; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci l3_targ_mstaddr = l3_targ_base + 10262306a36Sopenharmony_ci L3_TARG_STDERRLOG_CINFO_MSTADDR; 10362306a36Sopenharmony_ci l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_OPCODE; 10462306a36Sopenharmony_ci l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_CINFO_INFO; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci /* Nothing to be handled here as of now */ 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* STDERRLOG_MSTADDR Stores the NTTP master address. */ 11362306a36Sopenharmony_ci masterid = (readl_relaxed(l3_targ_mstaddr) & 11462306a36Sopenharmony_ci l3->mst_addr_mask) >> __ffs(l3->mst_addr_mask); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (k = 0, master = l3->l3_masters; k < l3->num_masters; 11762306a36Sopenharmony_ci k++, master++) { 11862306a36Sopenharmony_ci if (masterid == master->id) { 11962306a36Sopenharmony_ci master_name = master->name; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci op_code = readl_relaxed(l3_targ_hdr) & 0x7; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci m_req_info = readl_relaxed(l3_targ_info) & 0xF; 12762306a36Sopenharmony_ci snprintf(info_string, sizeof(info_string), 12862306a36Sopenharmony_ci ": %s in %s mode during %s access", 12962306a36Sopenharmony_ci (m_req_info & BIT(0)) ? "Opcode Fetch" : "Data Access", 13062306a36Sopenharmony_ci (m_req_info & BIT(1)) ? "Supervisor" : "User", 13162306a36Sopenharmony_ci (m_req_info & BIT(3)) ? "Debug" : "Functional"); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci WARN(true, 13462306a36Sopenharmony_ci "%s:L3 %s Error: MASTER %s TARGET %s (%s)%s%s\n", 13562306a36Sopenharmony_ci dev_name(l3->dev), 13662306a36Sopenharmony_ci err_description, 13762306a36Sopenharmony_ci master_name, target_name, 13862306a36Sopenharmony_ci l3_transaction_type[op_code], 13962306a36Sopenharmony_ci err_string, info_string); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* clear the std error log*/ 14262306a36Sopenharmony_ci clear = std_err_main | CLEAR_STDERR_LOG; 14362306a36Sopenharmony_ci writel_relaxed(clear, l3_targ_stderr); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * l3_interrupt_handler() - interrupt handler for l3 events 15062306a36Sopenharmony_ci * @irq: irq number 15162306a36Sopenharmony_ci * @_l3: pointer to l3 structure 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Interrupt Handler for L3 error detection. 15462306a36Sopenharmony_ci * 1) Identify the L3 clockdomain partition to which the error belongs to. 15562306a36Sopenharmony_ci * 2) Identify the slave where the error information is logged 15662306a36Sopenharmony_ci * ... handle the slave event.. 15762306a36Sopenharmony_ci * 7) if the slave is unknown, mask out the slave. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic irqreturn_t l3_interrupt_handler(int irq, void *_l3) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct omap_l3 *l3 = _l3; 16262306a36Sopenharmony_ci int inttype, i, ret; 16362306a36Sopenharmony_ci int err_src = 0; 16462306a36Sopenharmony_ci u32 err_reg, mask_val; 16562306a36Sopenharmony_ci void __iomem *base, *mask_reg; 16662306a36Sopenharmony_ci struct l3_flagmux_data *flag_mux; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Get the Type of interrupt */ 16962306a36Sopenharmony_ci inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < l3->num_modules; i++) { 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * Read the regerr register of the clock domain 17462306a36Sopenharmony_ci * to determine the source 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci base = l3->l3_base[i]; 17762306a36Sopenharmony_ci flag_mux = l3->l3_flagmux[i]; 17862306a36Sopenharmony_ci err_reg = readl_relaxed(base + flag_mux->offset + 17962306a36Sopenharmony_ci L3_FLAGMUX_REGERR0 + (inttype << 3)); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci err_reg &= ~(inttype ? flag_mux->mask_app_bits : 18262306a36Sopenharmony_ci flag_mux->mask_dbg_bits); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Get the corresponding error and analyse */ 18562306a36Sopenharmony_ci if (err_reg) { 18662306a36Sopenharmony_ci /* Identify the source from control status register */ 18762306a36Sopenharmony_ci err_src = __ffs(err_reg); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ret = l3_handle_target(l3, base, flag_mux, err_src); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Certain plaforms may have "undocumented" status 19362306a36Sopenharmony_ci * pending on boot. So dont generate a severe warning 19462306a36Sopenharmony_ci * here. Just mask it off to prevent the error from 19562306a36Sopenharmony_ci * reoccuring and locking up the system. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci if (ret) { 19862306a36Sopenharmony_ci dev_err(l3->dev, 19962306a36Sopenharmony_ci "L3 %s error: target %d mod:%d %s\n", 20062306a36Sopenharmony_ci inttype ? "debug" : "application", 20162306a36Sopenharmony_ci err_src, i, "(unclearable)"); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mask_reg = base + flag_mux->offset + 20462306a36Sopenharmony_ci L3_FLAGMUX_MASK0 + (inttype << 3); 20562306a36Sopenharmony_ci mask_val = readl_relaxed(mask_reg); 20662306a36Sopenharmony_ci mask_val &= ~(1 << err_src); 20762306a36Sopenharmony_ci writel_relaxed(mask_val, mask_reg); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Mark these bits as to be ignored */ 21062306a36Sopenharmony_ci if (inttype) 21162306a36Sopenharmony_ci flag_mux->mask_app_bits |= 1 << err_src; 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci flag_mux->mask_dbg_bits |= 1 << err_src; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Error found so break the for loop */ 21762306a36Sopenharmony_ci return IRQ_HANDLED; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci dev_err(l3->dev, "L3 %s IRQ not handled!!\n", 22262306a36Sopenharmony_ci inttype ? "debug" : "application"); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return IRQ_NONE; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const struct of_device_id l3_noc_match[] = { 22862306a36Sopenharmony_ci {.compatible = "ti,omap4-l3-noc", .data = &omap4_l3_data}, 22962306a36Sopenharmony_ci {.compatible = "ti,omap5-l3-noc", .data = &omap5_l3_data}, 23062306a36Sopenharmony_ci {.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data}, 23162306a36Sopenharmony_ci {.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data}, 23262306a36Sopenharmony_ci {}, 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, l3_noc_match); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int omap_l3_probe(struct platform_device *pdev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci const struct of_device_id *of_id; 23962306a36Sopenharmony_ci static struct omap_l3 *l3; 24062306a36Sopenharmony_ci int ret, i, res_idx; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci of_id = of_match_device(l3_noc_match, &pdev->dev); 24362306a36Sopenharmony_ci if (!of_id) { 24462306a36Sopenharmony_ci dev_err(&pdev->dev, "OF data missing\n"); 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!l3) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci memcpy(l3, of_id->data, sizeof(*l3)); 25362306a36Sopenharmony_ci l3->dev = &pdev->dev; 25462306a36Sopenharmony_ci platform_set_drvdata(pdev, l3); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Get mem resources */ 25762306a36Sopenharmony_ci for (i = 0, res_idx = 0; i < l3->num_modules; i++) { 25862306a36Sopenharmony_ci struct resource *res; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (l3->l3_base[i] == L3_BASE_IS_SUBMODULE) { 26162306a36Sopenharmony_ci /* First entry cannot be submodule */ 26262306a36Sopenharmony_ci BUG_ON(i == 0); 26362306a36Sopenharmony_ci l3->l3_base[i] = l3->l3_base[i - 1]; 26462306a36Sopenharmony_ci continue; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, res_idx); 26762306a36Sopenharmony_ci l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res); 26862306a36Sopenharmony_ci if (IS_ERR(l3->l3_base[i])) { 26962306a36Sopenharmony_ci dev_err(l3->dev, "ioremap %d failed\n", i); 27062306a36Sopenharmony_ci return PTR_ERR(l3->l3_base[i]); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci res_idx++; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * Setup interrupt Handlers 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci l3->debug_irq = platform_get_irq(pdev, 0); 27962306a36Sopenharmony_ci ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler, 28062306a36Sopenharmony_ci IRQF_NO_THREAD, "l3-dbg-irq", l3); 28162306a36Sopenharmony_ci if (ret) { 28262306a36Sopenharmony_ci dev_err(l3->dev, "request_irq failed for %d\n", 28362306a36Sopenharmony_ci l3->debug_irq); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci l3->app_irq = platform_get_irq(pdev, 1); 28862306a36Sopenharmony_ci ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler, 28962306a36Sopenharmony_ci IRQF_NO_THREAD, "l3-app-irq", l3); 29062306a36Sopenharmony_ci if (ret) 29162306a36Sopenharmony_ci dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/** 29962306a36Sopenharmony_ci * l3_resume_noirq() - resume function for l3_noc 30062306a36Sopenharmony_ci * @dev: pointer to l3_noc device structure 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * We only have the resume handler only since we 30362306a36Sopenharmony_ci * have already maintained the delta register 30462306a36Sopenharmony_ci * configuration as part of configuring the system 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic int l3_resume_noirq(struct device *dev) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct omap_l3 *l3 = dev_get_drvdata(dev); 30962306a36Sopenharmony_ci int i; 31062306a36Sopenharmony_ci struct l3_flagmux_data *flag_mux; 31162306a36Sopenharmony_ci void __iomem *base, *mask_regx = NULL; 31262306a36Sopenharmony_ci u32 mask_val; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci for (i = 0; i < l3->num_modules; i++) { 31562306a36Sopenharmony_ci base = l3->l3_base[i]; 31662306a36Sopenharmony_ci flag_mux = l3->l3_flagmux[i]; 31762306a36Sopenharmony_ci if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits) 31862306a36Sopenharmony_ci continue; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + 32162306a36Sopenharmony_ci (L3_APPLICATION_ERROR << 3); 32262306a36Sopenharmony_ci mask_val = readl_relaxed(mask_regx); 32362306a36Sopenharmony_ci mask_val &= ~(flag_mux->mask_app_bits); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci writel_relaxed(mask_val, mask_regx); 32662306a36Sopenharmony_ci mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + 32762306a36Sopenharmony_ci (L3_DEBUG_ERROR << 3); 32862306a36Sopenharmony_ci mask_val = readl_relaxed(mask_regx); 32962306a36Sopenharmony_ci mask_val &= ~(flag_mux->mask_dbg_bits); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci writel_relaxed(mask_val, mask_regx); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Dummy read to force OCP barrier */ 33562306a36Sopenharmony_ci if (mask_regx) 33662306a36Sopenharmony_ci (void)readl(mask_regx); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct dev_pm_ops l3_dev_pm_ops = { 34262306a36Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq) 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci#define L3_DEV_PM_OPS (&l3_dev_pm_ops) 34662306a36Sopenharmony_ci#else 34762306a36Sopenharmony_ci#define L3_DEV_PM_OPS NULL 34862306a36Sopenharmony_ci#endif 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic struct platform_driver omap_l3_driver = { 35162306a36Sopenharmony_ci .probe = omap_l3_probe, 35262306a36Sopenharmony_ci .driver = { 35362306a36Sopenharmony_ci .name = "omap_l3_noc", 35462306a36Sopenharmony_ci .pm = L3_DEV_PM_OPS, 35562306a36Sopenharmony_ci .of_match_table = of_match_ptr(l3_noc_match), 35662306a36Sopenharmony_ci }, 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int __init omap_l3_init(void) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci return platform_driver_register(&omap_l3_driver); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_cipostcore_initcall_sync(omap_l3_init); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void __exit omap_l3_exit(void) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci platform_driver_unregister(&omap_l3_driver); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_cimodule_exit(omap_l3_exit); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ciMODULE_AUTHOR("Santosh Shilimkar"); 37262306a36Sopenharmony_ciMODULE_AUTHOR("Sricharan R"); 37362306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP L3 Interconnect error handling driver"); 37462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 375