]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/soc/tegra/pmc-tegra186.c
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[karo-tx-linux.git] / drivers / soc / tegra / pmc-tegra186.c
1 /*
2  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  */
13
14 #define pr_fmt(fmt) "tegra-pmc: " fmt
15
16 #include <linux/io.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include <linux/platform_device.h>
20 #include <linux/reboot.h>
21
22 #include <asm/system_misc.h>
23
24 #define PMC_CNTRL 0x000
25 #define  PMC_CNTRL_MAIN_RST BIT(4)
26
27 #define PMC_RST_STATUS 0x070
28
29 #define WAKE_AOWAKE_CTRL 0x4f4
30 #define  WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0)
31
32 #define SCRATCH_SCRATCH0 0x2000
33 #define  SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31)
34 #define  SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30)
35 #define  SCRATCH_SCRATCH0_MODE_RCM BIT(1)
36 #define  SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \
37                                      SCRATCH_SCRATCH0_MODE_BOOTLOADER | \
38                                      SCRATCH_SCRATCH0_MODE_RCM)
39
40 struct tegra_pmc {
41         struct device *dev;
42         void __iomem *regs;
43         void __iomem *wake;
44         void __iomem *aotag;
45         void __iomem *scratch;
46
47         void (*system_restart)(enum reboot_mode mode, const char *cmd);
48         struct notifier_block restart;
49 };
50
51 static int tegra186_pmc_restart_notify(struct notifier_block *nb,
52                                        unsigned long action,
53                                        void *data)
54 {
55         struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart);
56         const char *cmd = data;
57         u32 value;
58
59         value = readl(pmc->scratch + SCRATCH_SCRATCH0);
60         value &= ~SCRATCH_SCRATCH0_MODE_MASK;
61
62         if (cmd) {
63                 if (strcmp(cmd, "recovery") == 0)
64                         value |= SCRATCH_SCRATCH0_MODE_RECOVERY;
65
66                 if (strcmp(cmd, "bootloader") == 0)
67                         value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER;
68
69                 if (strcmp(cmd, "forced-recovery") == 0)
70                         value |= SCRATCH_SCRATCH0_MODE_RCM;
71         }
72
73         writel(value, pmc->scratch + SCRATCH_SCRATCH0);
74
75         /*
76          * If available, call the system restart implementation that was
77          * registered earlier (typically PSCI).
78          */
79         if (pmc->system_restart) {
80                 pmc->system_restart(reboot_mode, cmd);
81                 return NOTIFY_DONE;
82         }
83
84         /* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */
85         value = readl(pmc->regs + PMC_CNTRL);
86         value |= PMC_CNTRL_MAIN_RST;
87         writel(value, pmc->regs + PMC_CNTRL);
88
89         return NOTIFY_DONE;
90 }
91
92 static int tegra186_pmc_setup(struct tegra_pmc *pmc)
93 {
94         struct device_node *np = pmc->dev->of_node;
95         bool invert;
96         u32 value;
97
98         invert = of_property_read_bool(np, "nvidia,invert-interrupt");
99
100         value = readl(pmc->wake + WAKE_AOWAKE_CTRL);
101
102         if (invert)
103                 value |= WAKE_AOWAKE_CTRL_INTR_POLARITY;
104         else
105                 value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY;
106
107         writel(value, pmc->wake + WAKE_AOWAKE_CTRL);
108
109         /*
110          * We need to hook any system restart implementation registered
111          * previously so we can write SCRATCH_SCRATCH0 before reset.
112          */
113         pmc->system_restart = arm_pm_restart;
114         arm_pm_restart = NULL;
115
116         pmc->restart.notifier_call = tegra186_pmc_restart_notify;
117         pmc->restart.priority = 128;
118
119         return register_restart_handler(&pmc->restart);
120 }
121
122 static int tegra186_pmc_probe(struct platform_device *pdev)
123 {
124         struct tegra_pmc *pmc;
125         struct resource *res;
126
127         pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
128         if (!pmc)
129                 return -ENOMEM;
130
131         pmc->dev = &pdev->dev;
132
133         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc");
134         pmc->regs = devm_ioremap_resource(&pdev->dev, res);
135         if (IS_ERR(pmc->regs))
136                 return PTR_ERR(pmc->regs);
137
138         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
139         pmc->wake = devm_ioremap_resource(&pdev->dev, res);
140         if (IS_ERR(pmc->wake))
141                 return PTR_ERR(pmc->wake);
142
143         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
144         pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
145         if (IS_ERR(pmc->aotag))
146                 return PTR_ERR(pmc->aotag);
147
148         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
149         pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
150         if (IS_ERR(pmc->scratch))
151                 return PTR_ERR(pmc->scratch);
152
153         return tegra186_pmc_setup(pmc);
154 }
155
156 static const struct of_device_id tegra186_pmc_of_match[] = {
157         { .compatible = "nvidia,tegra186-pmc" },
158         { /* sentinel */ }
159 };
160 MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match);
161
162 static struct platform_driver tegra186_pmc_driver = {
163         .driver = {
164                 .name = "tegra186-pmc",
165                 .of_match_table = tegra186_pmc_of_match,
166         },
167         .probe = tegra186_pmc_probe,
168 };
169 builtin_platform_driver(tegra186_pmc_driver);