19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
287d0bab2SHiroshi DOYU /*
387d0bab2SHiroshi DOYU * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
487d0bab2SHiroshi DOYU * Copyright (C) 2011 Google, Inc.
587d0bab2SHiroshi DOYU *
687d0bab2SHiroshi DOYU * Author:
787d0bab2SHiroshi DOYU * Jay Cheng <jacheng@nvidia.com>
887d0bab2SHiroshi DOYU * James Wylder <james.wylder@motorola.com>
987d0bab2SHiroshi DOYU * Benoit Goby <benoit@android.com>
1087d0bab2SHiroshi DOYU * Colin Cross <ccross@android.com>
1187d0bab2SHiroshi DOYU * Hiroshi DOYU <hdoyu@nvidia.com>
1287d0bab2SHiroshi DOYU */
1387d0bab2SHiroshi DOYU
14903b33e0SThierry Reding #include <linux/err.h>
1587d0bab2SHiroshi DOYU #include <linux/kernel.h>
1687d0bab2SHiroshi DOYU #include <linux/module.h>
1787d0bab2SHiroshi DOYU #include <linux/platform_device.h>
1887d0bab2SHiroshi DOYU #include <linux/io.h>
19049e4b3fSPaul Walmsley #include <linux/of.h>
20306a7f91SThierry Reding
21306a7f91SThierry Reding #include <soc/tegra/ahb.h>
2287d0bab2SHiroshi DOYU
2387d0bab2SHiroshi DOYU #define DRV_NAME "tegra-ahb"
2487d0bab2SHiroshi DOYU
25049e4b3fSPaul Walmsley #define AHB_ARBITRATION_DISABLE 0x04
26049e4b3fSPaul Walmsley #define AHB_ARBITRATION_PRIORITY_CTRL 0x08
2787d0bab2SHiroshi DOYU #define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29)
2887d0bab2SHiroshi DOYU #define PRIORITY_SELECT_USB BIT(6)
2987d0bab2SHiroshi DOYU #define PRIORITY_SELECT_USB2 BIT(18)
3087d0bab2SHiroshi DOYU #define PRIORITY_SELECT_USB3 BIT(17)
3187d0bab2SHiroshi DOYU
32049e4b3fSPaul Walmsley #define AHB_GIZMO_AHB_MEM 0x10
3387d0bab2SHiroshi DOYU #define ENB_FAST_REARBITRATE BIT(2)
3487d0bab2SHiroshi DOYU #define DONT_SPLIT_AHB_WR BIT(7)
3587d0bab2SHiroshi DOYU
36049e4b3fSPaul Walmsley #define AHB_GIZMO_APB_DMA 0x14
37049e4b3fSPaul Walmsley #define AHB_GIZMO_IDE 0x1c
38049e4b3fSPaul Walmsley #define AHB_GIZMO_USB 0x20
39049e4b3fSPaul Walmsley #define AHB_GIZMO_AHB_XBAR_BRIDGE 0x24
40049e4b3fSPaul Walmsley #define AHB_GIZMO_CPU_AHB_BRIDGE 0x28
41049e4b3fSPaul Walmsley #define AHB_GIZMO_COP_AHB_BRIDGE 0x2c
42049e4b3fSPaul Walmsley #define AHB_GIZMO_XBAR_APB_CTLR 0x30
43049e4b3fSPaul Walmsley #define AHB_GIZMO_VCP_AHB_BRIDGE 0x34
44049e4b3fSPaul Walmsley #define AHB_GIZMO_NAND 0x40
45049e4b3fSPaul Walmsley #define AHB_GIZMO_SDMMC4 0x48
46049e4b3fSPaul Walmsley #define AHB_GIZMO_XIO 0x4c
47049e4b3fSPaul Walmsley #define AHB_GIZMO_BSEV 0x64
48049e4b3fSPaul Walmsley #define AHB_GIZMO_BSEA 0x74
49049e4b3fSPaul Walmsley #define AHB_GIZMO_NOR 0x78
50049e4b3fSPaul Walmsley #define AHB_GIZMO_USB2 0x7c
51049e4b3fSPaul Walmsley #define AHB_GIZMO_USB3 0x80
5287d0bab2SHiroshi DOYU #define IMMEDIATE BIT(18)
5387d0bab2SHiroshi DOYU
54049e4b3fSPaul Walmsley #define AHB_GIZMO_SDMMC1 0x84
55049e4b3fSPaul Walmsley #define AHB_GIZMO_SDMMC2 0x88
56049e4b3fSPaul Walmsley #define AHB_GIZMO_SDMMC3 0x8c
57049e4b3fSPaul Walmsley #define AHB_MEM_PREFETCH_CFG_X 0xdc
58049e4b3fSPaul Walmsley #define AHB_ARBITRATION_XBAR_CTRL 0xe0
59049e4b3fSPaul Walmsley #define AHB_MEM_PREFETCH_CFG3 0xe4
60049e4b3fSPaul Walmsley #define AHB_MEM_PREFETCH_CFG4 0xe8
61049e4b3fSPaul Walmsley #define AHB_MEM_PREFETCH_CFG1 0xf0
62049e4b3fSPaul Walmsley #define AHB_MEM_PREFETCH_CFG2 0xf4
6387d0bab2SHiroshi DOYU #define PREFETCH_ENB BIT(31)
6487d0bab2SHiroshi DOYU #define MST_ID(x) (((x) & 0x1f) << 26)
6587d0bab2SHiroshi DOYU #define AHBDMA_MST_ID MST_ID(5)
6687d0bab2SHiroshi DOYU #define USB_MST_ID MST_ID(6)
6787d0bab2SHiroshi DOYU #define USB2_MST_ID MST_ID(18)
6887d0bab2SHiroshi DOYU #define USB3_MST_ID MST_ID(17)
6987d0bab2SHiroshi DOYU #define ADDR_BNDRY(x) (((x) & 0xf) << 21)
7087d0bab2SHiroshi DOYU #define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0)
7187d0bab2SHiroshi DOYU
72049e4b3fSPaul Walmsley #define AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID 0xfc
7387d0bab2SHiroshi DOYU
7489c788baSHiroshi DOYU #define AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE BIT(17)
7589c788baSHiroshi DOYU
76ce7a10b0SPaul Walmsley /*
77ce7a10b0SPaul Walmsley * INCORRECT_BASE_ADDR_LOW_BYTE: Legacy kernel DT files for Tegra SoCs
78ce7a10b0SPaul Walmsley * prior to Tegra124 generally use a physical base address ending in
79ce7a10b0SPaul Walmsley * 0x4 for the AHB IP block. According to the TRM, the low byte
80ce7a10b0SPaul Walmsley * should be 0x0. During device probing, this macro is used to detect
81ce7a10b0SPaul Walmsley * whether the passed-in physical address is incorrect, and if so, to
82ce7a10b0SPaul Walmsley * correct it.
83ce7a10b0SPaul Walmsley */
84ce7a10b0SPaul Walmsley #define INCORRECT_BASE_ADDR_LOW_BYTE 0x4
85ce7a10b0SPaul Walmsley
8689c788baSHiroshi DOYU static struct platform_driver tegra_ahb_driver;
8789c788baSHiroshi DOYU
8887d0bab2SHiroshi DOYU static const u32 tegra_ahb_gizmo[] = {
8987d0bab2SHiroshi DOYU AHB_ARBITRATION_DISABLE,
9087d0bab2SHiroshi DOYU AHB_ARBITRATION_PRIORITY_CTRL,
9187d0bab2SHiroshi DOYU AHB_GIZMO_AHB_MEM,
9287d0bab2SHiroshi DOYU AHB_GIZMO_APB_DMA,
9387d0bab2SHiroshi DOYU AHB_GIZMO_IDE,
9487d0bab2SHiroshi DOYU AHB_GIZMO_USB,
9587d0bab2SHiroshi DOYU AHB_GIZMO_AHB_XBAR_BRIDGE,
9687d0bab2SHiroshi DOYU AHB_GIZMO_CPU_AHB_BRIDGE,
9787d0bab2SHiroshi DOYU AHB_GIZMO_COP_AHB_BRIDGE,
9887d0bab2SHiroshi DOYU AHB_GIZMO_XBAR_APB_CTLR,
9987d0bab2SHiroshi DOYU AHB_GIZMO_VCP_AHB_BRIDGE,
10087d0bab2SHiroshi DOYU AHB_GIZMO_NAND,
10187d0bab2SHiroshi DOYU AHB_GIZMO_SDMMC4,
10287d0bab2SHiroshi DOYU AHB_GIZMO_XIO,
10387d0bab2SHiroshi DOYU AHB_GIZMO_BSEV,
10487d0bab2SHiroshi DOYU AHB_GIZMO_BSEA,
10587d0bab2SHiroshi DOYU AHB_GIZMO_NOR,
10687d0bab2SHiroshi DOYU AHB_GIZMO_USB2,
10787d0bab2SHiroshi DOYU AHB_GIZMO_USB3,
10887d0bab2SHiroshi DOYU AHB_GIZMO_SDMMC1,
10987d0bab2SHiroshi DOYU AHB_GIZMO_SDMMC2,
11087d0bab2SHiroshi DOYU AHB_GIZMO_SDMMC3,
11187d0bab2SHiroshi DOYU AHB_MEM_PREFETCH_CFG_X,
11287d0bab2SHiroshi DOYU AHB_ARBITRATION_XBAR_CTRL,
11387d0bab2SHiroshi DOYU AHB_MEM_PREFETCH_CFG3,
11487d0bab2SHiroshi DOYU AHB_MEM_PREFETCH_CFG4,
11587d0bab2SHiroshi DOYU AHB_MEM_PREFETCH_CFG1,
11687d0bab2SHiroshi DOYU AHB_MEM_PREFETCH_CFG2,
11787d0bab2SHiroshi DOYU AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID,
11887d0bab2SHiroshi DOYU };
11987d0bab2SHiroshi DOYU
12087d0bab2SHiroshi DOYU struct tegra_ahb {
12187d0bab2SHiroshi DOYU void __iomem *regs;
12287d0bab2SHiroshi DOYU struct device *dev;
123*06f3a5a4SGustavo A. R. Silva u32 ctx[];
12487d0bab2SHiroshi DOYU };
12587d0bab2SHiroshi DOYU
gizmo_readl(struct tegra_ahb * ahb,u32 offset)12687d0bab2SHiroshi DOYU static inline u32 gizmo_readl(struct tegra_ahb *ahb, u32 offset)
12787d0bab2SHiroshi DOYU {
128ce7a10b0SPaul Walmsley return readl(ahb->regs + offset);
12987d0bab2SHiroshi DOYU }
13087d0bab2SHiroshi DOYU
gizmo_writel(struct tegra_ahb * ahb,u32 value,u32 offset)13187d0bab2SHiroshi DOYU static inline void gizmo_writel(struct tegra_ahb *ahb, u32 value, u32 offset)
13287d0bab2SHiroshi DOYU {
133ce7a10b0SPaul Walmsley writel(value, ahb->regs + offset);
13487d0bab2SHiroshi DOYU }
13587d0bab2SHiroshi DOYU
136b44bf43bSHiroshi Doyu #ifdef CONFIG_TEGRA_IOMMU_SMMU
tegra_ahb_enable_smmu(struct device_node * dn)13789c788baSHiroshi DOYU int tegra_ahb_enable_smmu(struct device_node *dn)
13889c788baSHiroshi DOYU {
13989c788baSHiroshi DOYU struct device *dev;
14089c788baSHiroshi DOYU u32 val;
14189c788baSHiroshi DOYU struct tegra_ahb *ahb;
14289c788baSHiroshi DOYU
143cfba5de9SSuzuki K Poulose dev = driver_find_device_by_of_node(&tegra_ahb_driver.driver, dn);
14489c788baSHiroshi DOYU if (!dev)
14589c788baSHiroshi DOYU return -EPROBE_DEFER;
14689c788baSHiroshi DOYU ahb = dev_get_drvdata(dev);
14789c788baSHiroshi DOYU val = gizmo_readl(ahb, AHB_ARBITRATION_XBAR_CTRL);
14889c788baSHiroshi DOYU val |= AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE;
14989c788baSHiroshi DOYU gizmo_writel(ahb, val, AHB_ARBITRATION_XBAR_CTRL);
15089c788baSHiroshi DOYU return 0;
15189c788baSHiroshi DOYU }
15289c788baSHiroshi DOYU EXPORT_SYMBOL(tegra_ahb_enable_smmu);
15389c788baSHiroshi DOYU #endif
15489c788baSHiroshi DOYU
tegra_ahb_suspend(struct device * dev)1556d616560SArnd Bergmann static int __maybe_unused tegra_ahb_suspend(struct device *dev)
15687d0bab2SHiroshi DOYU {
15787d0bab2SHiroshi DOYU int i;
15887d0bab2SHiroshi DOYU struct tegra_ahb *ahb = dev_get_drvdata(dev);
15987d0bab2SHiroshi DOYU
16087d0bab2SHiroshi DOYU for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
16187d0bab2SHiroshi DOYU ahb->ctx[i] = gizmo_readl(ahb, tegra_ahb_gizmo[i]);
16287d0bab2SHiroshi DOYU return 0;
16387d0bab2SHiroshi DOYU }
16487d0bab2SHiroshi DOYU
tegra_ahb_resume(struct device * dev)1656d616560SArnd Bergmann static int __maybe_unused tegra_ahb_resume(struct device *dev)
16687d0bab2SHiroshi DOYU {
16787d0bab2SHiroshi DOYU int i;
16887d0bab2SHiroshi DOYU struct tegra_ahb *ahb = dev_get_drvdata(dev);
16987d0bab2SHiroshi DOYU
17087d0bab2SHiroshi DOYU for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
17187d0bab2SHiroshi DOYU gizmo_writel(ahb, ahb->ctx[i], tegra_ahb_gizmo[i]);
17287d0bab2SHiroshi DOYU return 0;
17387d0bab2SHiroshi DOYU }
17487d0bab2SHiroshi DOYU
17587d0bab2SHiroshi DOYU static UNIVERSAL_DEV_PM_OPS(tegra_ahb_pm,
17687d0bab2SHiroshi DOYU tegra_ahb_suspend,
17787d0bab2SHiroshi DOYU tegra_ahb_resume, NULL);
17887d0bab2SHiroshi DOYU
tegra_ahb_gizmo_init(struct tegra_ahb * ahb)17987d0bab2SHiroshi DOYU static void tegra_ahb_gizmo_init(struct tegra_ahb *ahb)
18087d0bab2SHiroshi DOYU {
18187d0bab2SHiroshi DOYU u32 val;
18287d0bab2SHiroshi DOYU
18387d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_GIZMO_AHB_MEM);
18487d0bab2SHiroshi DOYU val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR;
18587d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_GIZMO_AHB_MEM);
18687d0bab2SHiroshi DOYU
18787d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_GIZMO_USB);
18887d0bab2SHiroshi DOYU val |= IMMEDIATE;
18987d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_GIZMO_USB);
19087d0bab2SHiroshi DOYU
19187d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_GIZMO_USB2);
19287d0bab2SHiroshi DOYU val |= IMMEDIATE;
19387d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_GIZMO_USB2);
19487d0bab2SHiroshi DOYU
19587d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_GIZMO_USB3);
19687d0bab2SHiroshi DOYU val |= IMMEDIATE;
19787d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_GIZMO_USB3);
19887d0bab2SHiroshi DOYU
19987d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_ARBITRATION_PRIORITY_CTRL);
20087d0bab2SHiroshi DOYU val |= PRIORITY_SELECT_USB |
20187d0bab2SHiroshi DOYU PRIORITY_SELECT_USB2 |
20287d0bab2SHiroshi DOYU PRIORITY_SELECT_USB3 |
20387d0bab2SHiroshi DOYU AHB_PRIORITY_WEIGHT(7);
20487d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_ARBITRATION_PRIORITY_CTRL);
20587d0bab2SHiroshi DOYU
20687d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG1);
20787d0bab2SHiroshi DOYU val &= ~MST_ID(~0);
20887d0bab2SHiroshi DOYU val |= PREFETCH_ENB |
20987d0bab2SHiroshi DOYU AHBDMA_MST_ID |
21087d0bab2SHiroshi DOYU ADDR_BNDRY(0xc) |
21187d0bab2SHiroshi DOYU INACTIVITY_TIMEOUT(0x1000);
21287d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG1);
21387d0bab2SHiroshi DOYU
21487d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG2);
21587d0bab2SHiroshi DOYU val &= ~MST_ID(~0);
21687d0bab2SHiroshi DOYU val |= PREFETCH_ENB |
21787d0bab2SHiroshi DOYU USB_MST_ID |
21887d0bab2SHiroshi DOYU ADDR_BNDRY(0xc) |
21987d0bab2SHiroshi DOYU INACTIVITY_TIMEOUT(0x1000);
22087d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG2);
22187d0bab2SHiroshi DOYU
22287d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG3);
22387d0bab2SHiroshi DOYU val &= ~MST_ID(~0);
22487d0bab2SHiroshi DOYU val |= PREFETCH_ENB |
22587d0bab2SHiroshi DOYU USB3_MST_ID |
22687d0bab2SHiroshi DOYU ADDR_BNDRY(0xc) |
22787d0bab2SHiroshi DOYU INACTIVITY_TIMEOUT(0x1000);
22887d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG3);
22987d0bab2SHiroshi DOYU
23087d0bab2SHiroshi DOYU val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG4);
23187d0bab2SHiroshi DOYU val &= ~MST_ID(~0);
23287d0bab2SHiroshi DOYU val |= PREFETCH_ENB |
23387d0bab2SHiroshi DOYU USB2_MST_ID |
23487d0bab2SHiroshi DOYU ADDR_BNDRY(0xc) |
23587d0bab2SHiroshi DOYU INACTIVITY_TIMEOUT(0x1000);
23687d0bab2SHiroshi DOYU gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG4);
23787d0bab2SHiroshi DOYU }
23887d0bab2SHiroshi DOYU
tegra_ahb_probe(struct platform_device * pdev)239c571b211SGreg Kroah-Hartman static int tegra_ahb_probe(struct platform_device *pdev)
24087d0bab2SHiroshi DOYU {
24187d0bab2SHiroshi DOYU struct resource *res;
24287d0bab2SHiroshi DOYU struct tegra_ahb *ahb;
24387d0bab2SHiroshi DOYU size_t bytes;
24487d0bab2SHiroshi DOYU
24587d0bab2SHiroshi DOYU bytes = sizeof(*ahb) + sizeof(u32) * ARRAY_SIZE(tegra_ahb_gizmo);
24687d0bab2SHiroshi DOYU ahb = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
24787d0bab2SHiroshi DOYU if (!ahb)
24887d0bab2SHiroshi DOYU return -ENOMEM;
24987d0bab2SHiroshi DOYU
25087d0bab2SHiroshi DOYU res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251ce7a10b0SPaul Walmsley
252ce7a10b0SPaul Walmsley /* Correct the IP block base address if necessary */
253ce7a10b0SPaul Walmsley if (res &&
254ce7a10b0SPaul Walmsley (res->start & INCORRECT_BASE_ADDR_LOW_BYTE) ==
255ce7a10b0SPaul Walmsley INCORRECT_BASE_ADDR_LOW_BYTE) {
256ce7a10b0SPaul Walmsley dev_warn(&pdev->dev, "incorrect AHB base address in DT data - enabling workaround\n");
257ce7a10b0SPaul Walmsley res->start -= INCORRECT_BASE_ADDR_LOW_BYTE;
258ce7a10b0SPaul Walmsley }
259ce7a10b0SPaul Walmsley
260903b33e0SThierry Reding ahb->regs = devm_ioremap_resource(&pdev->dev, res);
261903b33e0SThierry Reding if (IS_ERR(ahb->regs))
262903b33e0SThierry Reding return PTR_ERR(ahb->regs);
26387d0bab2SHiroshi DOYU
26487d0bab2SHiroshi DOYU ahb->dev = &pdev->dev;
26587d0bab2SHiroshi DOYU platform_set_drvdata(pdev, ahb);
26687d0bab2SHiroshi DOYU tegra_ahb_gizmo_init(ahb);
26787d0bab2SHiroshi DOYU return 0;
26887d0bab2SHiroshi DOYU }
26987d0bab2SHiroshi DOYU
270c571b211SGreg Kroah-Hartman static const struct of_device_id tegra_ahb_of_match[] = {
27187d0bab2SHiroshi DOYU { .compatible = "nvidia,tegra30-ahb", },
27287d0bab2SHiroshi DOYU { .compatible = "nvidia,tegra20-ahb", },
27387d0bab2SHiroshi DOYU {},
27487d0bab2SHiroshi DOYU };
27587d0bab2SHiroshi DOYU
27687d0bab2SHiroshi DOYU static struct platform_driver tegra_ahb_driver = {
27787d0bab2SHiroshi DOYU .probe = tegra_ahb_probe,
27887d0bab2SHiroshi DOYU .driver = {
27987d0bab2SHiroshi DOYU .name = DRV_NAME,
28087d0bab2SHiroshi DOYU .of_match_table = tegra_ahb_of_match,
28187d0bab2SHiroshi DOYU .pm = &tegra_ahb_pm,
28287d0bab2SHiroshi DOYU },
28387d0bab2SHiroshi DOYU };
28487d0bab2SHiroshi DOYU module_platform_driver(tegra_ahb_driver);
28587d0bab2SHiroshi DOYU
28687d0bab2SHiroshi DOYU MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
28787d0bab2SHiroshi DOYU MODULE_DESCRIPTION("Tegra AHB driver");
28887d0bab2SHiroshi DOYU MODULE_ALIAS("platform:" DRV_NAME);
28987d0bab2SHiroshi DOYU