11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20ee7261cSSantosh Shilimkar /*
30ee7261cSSantosh Shilimkar * OMAP3XXX L3 Interconnect Driver
40ee7261cSSantosh Shilimkar *
50ee7261cSSantosh Shilimkar * Copyright (C) 2011 Texas Corporation
60ee7261cSSantosh Shilimkar * Felipe Balbi <balbi@ti.com>
70ee7261cSSantosh Shilimkar * Santosh Shilimkar <santosh.shilimkar@ti.com>
80ee7261cSSantosh Shilimkar * Sricharan <r.sricharan@ti.com>
90ee7261cSSantosh Shilimkar */
100ee7261cSSantosh Shilimkar
110ee7261cSSantosh Shilimkar #include <linux/kernel.h>
120ee7261cSSantosh Shilimkar #include <linux/slab.h>
130ee7261cSSantosh Shilimkar #include <linux/platform_device.h>
140ee7261cSSantosh Shilimkar #include <linux/interrupt.h>
150ee7261cSSantosh Shilimkar #include <linux/io.h>
16aa25729cSTony Lindgren #include <linux/module.h>
17aa25729cSTony Lindgren #include <linux/of.h>
18aa25729cSTony Lindgren
190ee7261cSSantosh Shilimkar #include "omap_l3_smx.h"
200ee7261cSSantosh Shilimkar
omap3_l3_readll(void __iomem * base,u16 reg)210ee7261cSSantosh Shilimkar static inline u64 omap3_l3_readll(void __iomem *base, u16 reg)
220ee7261cSSantosh Shilimkar {
230ee7261cSSantosh Shilimkar return __raw_readll(base + reg);
240ee7261cSSantosh Shilimkar }
250ee7261cSSantosh Shilimkar
omap3_l3_writell(void __iomem * base,u16 reg,u64 value)260ee7261cSSantosh Shilimkar static inline void omap3_l3_writell(void __iomem *base, u16 reg, u64 value)
270ee7261cSSantosh Shilimkar {
280ee7261cSSantosh Shilimkar __raw_writell(value, base + reg);
290ee7261cSSantosh Shilimkar }
300ee7261cSSantosh Shilimkar
omap3_l3_decode_error_code(u64 error)310ee7261cSSantosh Shilimkar static inline enum omap3_l3_code omap3_l3_decode_error_code(u64 error)
320ee7261cSSantosh Shilimkar {
330ee7261cSSantosh Shilimkar return (error & 0x0f000000) >> L3_ERROR_LOG_CODE;
340ee7261cSSantosh Shilimkar }
350ee7261cSSantosh Shilimkar
omap3_l3_decode_addr(u64 error_addr)360ee7261cSSantosh Shilimkar static inline u32 omap3_l3_decode_addr(u64 error_addr)
370ee7261cSSantosh Shilimkar {
380ee7261cSSantosh Shilimkar return error_addr & 0xffffffff;
390ee7261cSSantosh Shilimkar }
400ee7261cSSantosh Shilimkar
omap3_l3_decode_cmd(u64 error)410ee7261cSSantosh Shilimkar static inline unsigned omap3_l3_decode_cmd(u64 error)
420ee7261cSSantosh Shilimkar {
430ee7261cSSantosh Shilimkar return (error & 0x07) >> L3_ERROR_LOG_CMD;
440ee7261cSSantosh Shilimkar }
450ee7261cSSantosh Shilimkar
omap3_l3_decode_initid(u64 error)460ee7261cSSantosh Shilimkar static inline enum omap3_l3_initiator_id omap3_l3_decode_initid(u64 error)
470ee7261cSSantosh Shilimkar {
480ee7261cSSantosh Shilimkar return (error & 0xff00) >> L3_ERROR_LOG_INITID;
490ee7261cSSantosh Shilimkar }
500ee7261cSSantosh Shilimkar
omap3_l3_decode_req_info(u64 error)510ee7261cSSantosh Shilimkar static inline unsigned omap3_l3_decode_req_info(u64 error)
520ee7261cSSantosh Shilimkar {
530ee7261cSSantosh Shilimkar return (error >> 32) & 0xffff;
540ee7261cSSantosh Shilimkar }
550ee7261cSSantosh Shilimkar
omap3_l3_code_string(u8 code)560ee7261cSSantosh Shilimkar static char *omap3_l3_code_string(u8 code)
570ee7261cSSantosh Shilimkar {
580ee7261cSSantosh Shilimkar switch (code) {
590ee7261cSSantosh Shilimkar case OMAP_L3_CODE_NOERROR:
600ee7261cSSantosh Shilimkar return "No Error";
610ee7261cSSantosh Shilimkar case OMAP_L3_CODE_UNSUP_CMD:
620ee7261cSSantosh Shilimkar return "Unsupported Command";
630ee7261cSSantosh Shilimkar case OMAP_L3_CODE_ADDR_HOLE:
640ee7261cSSantosh Shilimkar return "Address Hole";
650ee7261cSSantosh Shilimkar case OMAP_L3_CODE_PROTECT_VIOLATION:
660ee7261cSSantosh Shilimkar return "Protection Violation";
670ee7261cSSantosh Shilimkar case OMAP_L3_CODE_IN_BAND_ERR:
680ee7261cSSantosh Shilimkar return "In-band Error";
690ee7261cSSantosh Shilimkar case OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT:
700ee7261cSSantosh Shilimkar return "Request Timeout Not Accepted";
710ee7261cSSantosh Shilimkar case OMAP_L3_CODE_REQ_TOUT_NO_RESP:
720ee7261cSSantosh Shilimkar return "Request Timeout, no response";
730ee7261cSSantosh Shilimkar default:
740ee7261cSSantosh Shilimkar return "UNKNOWN error";
750ee7261cSSantosh Shilimkar }
760ee7261cSSantosh Shilimkar }
770ee7261cSSantosh Shilimkar
omap3_l3_initiator_string(u8 initid)780ee7261cSSantosh Shilimkar static char *omap3_l3_initiator_string(u8 initid)
790ee7261cSSantosh Shilimkar {
800ee7261cSSantosh Shilimkar switch (initid) {
810ee7261cSSantosh Shilimkar case OMAP_L3_LCD:
820ee7261cSSantosh Shilimkar return "LCD";
830ee7261cSSantosh Shilimkar case OMAP_L3_SAD2D:
840ee7261cSSantosh Shilimkar return "SAD2D";
850ee7261cSSantosh Shilimkar case OMAP_L3_IA_MPU_SS_1:
860ee7261cSSantosh Shilimkar case OMAP_L3_IA_MPU_SS_2:
870ee7261cSSantosh Shilimkar case OMAP_L3_IA_MPU_SS_3:
880ee7261cSSantosh Shilimkar case OMAP_L3_IA_MPU_SS_4:
890ee7261cSSantosh Shilimkar case OMAP_L3_IA_MPU_SS_5:
900ee7261cSSantosh Shilimkar return "MPU";
910ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_1:
920ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_2:
930ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_3:
940ee7261cSSantosh Shilimkar return "IVA_SS";
950ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_1:
960ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_2:
970ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_3:
980ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_4:
990ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_5:
1000ee7261cSSantosh Shilimkar case OMAP_L3_IA_IVA_SS_DMA_6:
1010ee7261cSSantosh Shilimkar return "IVA_SS_DMA";
1020ee7261cSSantosh Shilimkar case OMAP_L3_IA_SGX:
1030ee7261cSSantosh Shilimkar return "SGX";
1040ee7261cSSantosh Shilimkar case OMAP_L3_IA_CAM_1:
1050ee7261cSSantosh Shilimkar case OMAP_L3_IA_CAM_2:
1060ee7261cSSantosh Shilimkar case OMAP_L3_IA_CAM_3:
1070ee7261cSSantosh Shilimkar return "CAM";
1080ee7261cSSantosh Shilimkar case OMAP_L3_IA_DAP:
1090ee7261cSSantosh Shilimkar return "DAP";
1100ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_WR_1:
1110ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_WR_2:
1120ee7261cSSantosh Shilimkar return "SDMA_WR";
1130ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_RD_1:
1140ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_RD_2:
1150ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_RD_3:
1160ee7261cSSantosh Shilimkar case OMAP_L3_SDMA_RD_4:
1170ee7261cSSantosh Shilimkar return "SDMA_RD";
1180ee7261cSSantosh Shilimkar case OMAP_L3_USBOTG:
1190ee7261cSSantosh Shilimkar return "USB_OTG";
1200ee7261cSSantosh Shilimkar case OMAP_L3_USBHOST:
1210ee7261cSSantosh Shilimkar return "USB_HOST";
1220ee7261cSSantosh Shilimkar default:
1230ee7261cSSantosh Shilimkar return "UNKNOWN Initiator";
1240ee7261cSSantosh Shilimkar }
1250ee7261cSSantosh Shilimkar }
1260ee7261cSSantosh Shilimkar
1270ee7261cSSantosh Shilimkar /*
1280ee7261cSSantosh Shilimkar * omap3_l3_block_irq - handles a register block's irq
1290ee7261cSSantosh Shilimkar * @l3: struct omap3_l3 *
1300ee7261cSSantosh Shilimkar * @base: register block base address
1310ee7261cSSantosh Shilimkar * @error: L3_ERROR_LOG register of our block
1320ee7261cSSantosh Shilimkar *
1330ee7261cSSantosh Shilimkar * Called in hard-irq context. Caller should take care of locking
1340ee7261cSSantosh Shilimkar *
1350ee7261cSSantosh Shilimkar * OMAP36xx TRM gives, on page 2001, Figure 9-10, the Typical Error
1360ee7261cSSantosh Shilimkar * Analysis Sequence, we are following that sequence here, please
1370ee7261cSSantosh Shilimkar * refer to that Figure for more information on the subject.
1380ee7261cSSantosh Shilimkar */
omap3_l3_block_irq(struct omap3_l3 * l3,u64 error,int error_addr)1390ee7261cSSantosh Shilimkar static irqreturn_t omap3_l3_block_irq(struct omap3_l3 *l3,
1400ee7261cSSantosh Shilimkar u64 error, int error_addr)
1410ee7261cSSantosh Shilimkar {
1420ee7261cSSantosh Shilimkar u8 code = omap3_l3_decode_error_code(error);
1430ee7261cSSantosh Shilimkar u8 initid = omap3_l3_decode_initid(error);
1440ee7261cSSantosh Shilimkar u8 multi = error & L3_ERROR_LOG_MULTI;
1450ee7261cSSantosh Shilimkar u32 address = omap3_l3_decode_addr(error_addr);
1460ee7261cSSantosh Shilimkar
1470ee7261cSSantosh Shilimkar pr_err("%s seen by %s %s at address %x\n",
1480ee7261cSSantosh Shilimkar omap3_l3_code_string(code),
1490ee7261cSSantosh Shilimkar omap3_l3_initiator_string(initid),
1500ee7261cSSantosh Shilimkar multi ? "Multiple Errors" : "", address);
1510ee7261cSSantosh Shilimkar WARN_ON(1);
1520ee7261cSSantosh Shilimkar
1530ee7261cSSantosh Shilimkar return IRQ_HANDLED;
1540ee7261cSSantosh Shilimkar }
1550ee7261cSSantosh Shilimkar
omap3_l3_app_irq(int irq,void * _l3)1560ee7261cSSantosh Shilimkar static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
1570ee7261cSSantosh Shilimkar {
1580ee7261cSSantosh Shilimkar struct omap3_l3 *l3 = _l3;
1590ee7261cSSantosh Shilimkar u64 status, clear;
1600ee7261cSSantosh Shilimkar u64 error;
1610ee7261cSSantosh Shilimkar u64 error_addr;
1620ee7261cSSantosh Shilimkar u64 err_source = 0;
1630ee7261cSSantosh Shilimkar void __iomem *base;
1640ee7261cSSantosh Shilimkar int int_type;
1650ee7261cSSantosh Shilimkar irqreturn_t ret = IRQ_NONE;
1660ee7261cSSantosh Shilimkar
1670ee7261cSSantosh Shilimkar int_type = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
168*99f13d7aSSicelo A. Mhlongo if (!int_type)
1690ee7261cSSantosh Shilimkar status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_0);
170*99f13d7aSSicelo A. Mhlongo else
1710ee7261cSSantosh Shilimkar status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_1);
1720ee7261cSSantosh Shilimkar
1730ee7261cSSantosh Shilimkar /* identify the error source */
1740ee7261cSSantosh Shilimkar err_source = __ffs(status);
1750ee7261cSSantosh Shilimkar
1760ee7261cSSantosh Shilimkar base = l3->rt + omap3_l3_bases[int_type][err_source];
1770ee7261cSSantosh Shilimkar error = omap3_l3_readll(base, L3_ERROR_LOG);
1780ee7261cSSantosh Shilimkar if (error) {
1790ee7261cSSantosh Shilimkar error_addr = omap3_l3_readll(base, L3_ERROR_LOG_ADDR);
1800ee7261cSSantosh Shilimkar ret |= omap3_l3_block_irq(l3, error, error_addr);
1810ee7261cSSantosh Shilimkar }
1820ee7261cSSantosh Shilimkar
183*99f13d7aSSicelo A. Mhlongo /*
184*99f13d7aSSicelo A. Mhlongo * if we have a timeout error, there's nothing we can
185*99f13d7aSSicelo A. Mhlongo * do besides rebooting the board. So let's BUG on any
186*99f13d7aSSicelo A. Mhlongo * of such errors and handle the others. timeout error
187*99f13d7aSSicelo A. Mhlongo * is severe and not expected to occur.
188*99f13d7aSSicelo A. Mhlongo */
189*99f13d7aSSicelo A. Mhlongo BUG_ON(!int_type && status & L3_STATUS_0_TIMEOUT_MASK);
190*99f13d7aSSicelo A. Mhlongo
1910ee7261cSSantosh Shilimkar /* Clear the status register */
1920ee7261cSSantosh Shilimkar clear = (L3_AGENT_STATUS_CLEAR_IA << int_type) |
1930ee7261cSSantosh Shilimkar L3_AGENT_STATUS_CLEAR_TA;
1940ee7261cSSantosh Shilimkar omap3_l3_writell(base, L3_AGENT_STATUS, clear);
1950ee7261cSSantosh Shilimkar
1960ee7261cSSantosh Shilimkar /* clear the error log register */
1970ee7261cSSantosh Shilimkar omap3_l3_writell(base, L3_ERROR_LOG, error);
1980ee7261cSSantosh Shilimkar
1990ee7261cSSantosh Shilimkar return ret;
2000ee7261cSSantosh Shilimkar }
2010ee7261cSSantosh Shilimkar
202aa25729cSTony Lindgren #if IS_BUILTIN(CONFIG_OF)
203aa25729cSTony Lindgren static const struct of_device_id omap3_l3_match[] = {
204aa25729cSTony Lindgren {
205aa25729cSTony Lindgren .compatible = "ti,omap3-l3-smx",
206aa25729cSTony Lindgren },
207aa25729cSTony Lindgren { },
208aa25729cSTony Lindgren };
209aa25729cSTony Lindgren MODULE_DEVICE_TABLE(of, omap3_l3_match);
210aa25729cSTony Lindgren #endif
211aa25729cSTony Lindgren
omap3_l3_probe(struct platform_device * pdev)212aa25729cSTony Lindgren static int omap3_l3_probe(struct platform_device *pdev)
2130ee7261cSSantosh Shilimkar {
2140ee7261cSSantosh Shilimkar struct omap3_l3 *l3;
2150ee7261cSSantosh Shilimkar struct resource *res;
2160ee7261cSSantosh Shilimkar int ret;
2170ee7261cSSantosh Shilimkar
2180ee7261cSSantosh Shilimkar l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
2190ee7261cSSantosh Shilimkar if (!l3)
2200ee7261cSSantosh Shilimkar return -ENOMEM;
2210ee7261cSSantosh Shilimkar
2220ee7261cSSantosh Shilimkar platform_set_drvdata(pdev, l3);
2230ee7261cSSantosh Shilimkar
2240ee7261cSSantosh Shilimkar res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2250ee7261cSSantosh Shilimkar if (!res) {
2260ee7261cSSantosh Shilimkar dev_err(&pdev->dev, "couldn't find resource\n");
2270ee7261cSSantosh Shilimkar ret = -ENODEV;
2280ee7261cSSantosh Shilimkar goto err0;
2290ee7261cSSantosh Shilimkar }
2300ee7261cSSantosh Shilimkar l3->rt = ioremap(res->start, resource_size(res));
2310ee7261cSSantosh Shilimkar if (!l3->rt) {
2320ee7261cSSantosh Shilimkar dev_err(&pdev->dev, "ioremap failed\n");
2330ee7261cSSantosh Shilimkar ret = -ENOMEM;
2340ee7261cSSantosh Shilimkar goto err0;
2350ee7261cSSantosh Shilimkar }
2360ee7261cSSantosh Shilimkar
2370ee7261cSSantosh Shilimkar l3->debug_irq = platform_get_irq(pdev, 0);
238d8bf368dSValentin Rothberg ret = request_irq(l3->debug_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING,
2390ee7261cSSantosh Shilimkar "l3-debug-irq", l3);
2400ee7261cSSantosh Shilimkar if (ret) {
2410ee7261cSSantosh Shilimkar dev_err(&pdev->dev, "couldn't request debug irq\n");
2420ee7261cSSantosh Shilimkar goto err1;
2430ee7261cSSantosh Shilimkar }
2440ee7261cSSantosh Shilimkar
2450ee7261cSSantosh Shilimkar l3->app_irq = platform_get_irq(pdev, 1);
246d8bf368dSValentin Rothberg ret = request_irq(l3->app_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING,
2470ee7261cSSantosh Shilimkar "l3-app-irq", l3);
2480ee7261cSSantosh Shilimkar if (ret) {
2490ee7261cSSantosh Shilimkar dev_err(&pdev->dev, "couldn't request app irq\n");
2500ee7261cSSantosh Shilimkar goto err2;
2510ee7261cSSantosh Shilimkar }
2520ee7261cSSantosh Shilimkar
2530ee7261cSSantosh Shilimkar return 0;
2540ee7261cSSantosh Shilimkar
2550ee7261cSSantosh Shilimkar err2:
2560ee7261cSSantosh Shilimkar free_irq(l3->debug_irq, l3);
2570ee7261cSSantosh Shilimkar err1:
2580ee7261cSSantosh Shilimkar iounmap(l3->rt);
2590ee7261cSSantosh Shilimkar err0:
2600ee7261cSSantosh Shilimkar kfree(l3);
2610ee7261cSSantosh Shilimkar return ret;
2620ee7261cSSantosh Shilimkar }
2630ee7261cSSantosh Shilimkar
omap3_l3_remove(struct platform_device * pdev)264aa25729cSTony Lindgren static int omap3_l3_remove(struct platform_device *pdev)
2650ee7261cSSantosh Shilimkar {
2660ee7261cSSantosh Shilimkar struct omap3_l3 *l3 = platform_get_drvdata(pdev);
2670ee7261cSSantosh Shilimkar
2680ee7261cSSantosh Shilimkar free_irq(l3->app_irq, l3);
2690ee7261cSSantosh Shilimkar free_irq(l3->debug_irq, l3);
2700ee7261cSSantosh Shilimkar iounmap(l3->rt);
2710ee7261cSSantosh Shilimkar kfree(l3);
2720ee7261cSSantosh Shilimkar
2730ee7261cSSantosh Shilimkar return 0;
2740ee7261cSSantosh Shilimkar }
2750ee7261cSSantosh Shilimkar
2760ee7261cSSantosh Shilimkar static struct platform_driver omap3_l3_driver = {
277aa25729cSTony Lindgren .probe = omap3_l3_probe,
278aa25729cSTony Lindgren .remove = omap3_l3_remove,
2790ee7261cSSantosh Shilimkar .driver = {
2800ee7261cSSantosh Shilimkar .name = "omap_l3_smx",
281aa25729cSTony Lindgren .of_match_table = of_match_ptr(omap3_l3_match),
2820ee7261cSSantosh Shilimkar },
2830ee7261cSSantosh Shilimkar };
2840ee7261cSSantosh Shilimkar
omap3_l3_init(void)2850ee7261cSSantosh Shilimkar static int __init omap3_l3_init(void)
2860ee7261cSSantosh Shilimkar {
287aa25729cSTony Lindgren return platform_driver_register(&omap3_l3_driver);
2880ee7261cSSantosh Shilimkar }
2890ee7261cSSantosh Shilimkar postcore_initcall_sync(omap3_l3_init);
2900ee7261cSSantosh Shilimkar
omap3_l3_exit(void)2910ee7261cSSantosh Shilimkar static void __exit omap3_l3_exit(void)
2920ee7261cSSantosh Shilimkar {
2930ee7261cSSantosh Shilimkar platform_driver_unregister(&omap3_l3_driver);
2940ee7261cSSantosh Shilimkar }
2950ee7261cSSantosh Shilimkar module_exit(omap3_l3_exit);
296be60566eSArnd Bergmann
297be60566eSArnd Bergmann MODULE_AUTHOR("Felipe Balbi");
298be60566eSArnd Bergmann MODULE_AUTHOR("Santosh Shilimkar");
299be60566eSArnd Bergmann MODULE_AUTHOR("Sricharan R");
300be60566eSArnd Bergmann MODULE_DESCRIPTION("OMAP3XXX L3 Interconnect Driver");
301be60566eSArnd Bergmann MODULE_LICENSE("GPL");
302