Commit | Line | Data |
---|---|---|
a1820845 GC |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (C) 2023, STMicroelectronics - All Rights Reserved | |
4 | */ | |
5 | ||
6 | #include <linux/bitfield.h> | |
7 | #include <linux/bits.h> | |
8 | #include <linux/device.h> | |
9 | #include <linux/err.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/io.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_platform.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/types.h> | |
18 | ||
19 | #include "stm32_firewall.h" | |
20 | ||
21 | /* | |
22 | * RIFSC offset register | |
23 | */ | |
24 | #define RIFSC_RISC_SECCFGR0 0x10 | |
25 | #define RIFSC_RISC_PRIVCFGR0 0x30 | |
26 | #define RIFSC_RISC_PER0_CIDCFGR 0x100 | |
27 | #define RIFSC_RISC_PER0_SEMCR 0x104 | |
28 | #define RIFSC_RISC_HWCFGR2 0xFEC | |
29 | ||
30 | /* | |
31 | * SEMCR register | |
32 | */ | |
33 | #define SEMCR_MUTEX BIT(0) | |
34 | ||
35 | /* | |
36 | * HWCFGR2 register | |
37 | */ | |
38 | #define HWCFGR2_CONF1_MASK GENMASK(15, 0) | |
39 | #define HWCFGR2_CONF2_MASK GENMASK(23, 16) | |
40 | #define HWCFGR2_CONF3_MASK GENMASK(31, 24) | |
41 | ||
42 | /* | |
43 | * RIFSC miscellaneous | |
44 | */ | |
45 | #define RIFSC_RISC_CFEN_MASK BIT(0) | |
46 | #define RIFSC_RISC_SEM_EN_MASK BIT(1) | |
47 | #define RIFSC_RISC_SCID_MASK GENMASK(6, 4) | |
48 | #define RIFSC_RISC_SEML_SHIFT 16 | |
49 | #define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16) | |
50 | #define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24) | |
51 | ||
52 | #define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \ | |
53 | RIFSC_RISC_SEM_EN_MASK | \ | |
54 | RIFSC_RISC_SCID_MASK | \ | |
55 | RIFSC_RISC_SEMWL_MASK) | |
56 | ||
57 | #define IDS_PER_RISC_SEC_PRIV_REGS 32 | |
58 | ||
59 | /* RIF miscellaneous */ | |
60 | /* | |
61 | * CIDCFGR register fields | |
62 | */ | |
63 | #define CIDCFGR_CFEN BIT(0) | |
64 | #define CIDCFGR_SEMEN BIT(1) | |
65 | #define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x)) | |
66 | ||
67 | #define SEMWL_SHIFT 16 | |
68 | ||
69 | /* Compartiment IDs */ | |
70 | #define RIF_CID0 0x0 | |
71 | #define RIF_CID1 0x1 | |
72 | ||
73 | static bool stm32_rifsc_is_semaphore_available(void __iomem *addr) | |
74 | { | |
75 | return !(readl(addr) & SEMCR_MUTEX); | |
76 | } | |
77 | ||
78 | static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller, | |
79 | int id) | |
80 | { | |
81 | void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id; | |
82 | ||
83 | writel(SEMCR_MUTEX, addr); | |
84 | ||
85 | /* Check that CID1 has the semaphore */ | |
86 | if (stm32_rifsc_is_semaphore_available(addr) || | |
87 | FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1) | |
88 | return -EACCES; | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller, | |
94 | int id) | |
95 | { | |
96 | void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id; | |
97 | ||
98 | if (stm32_rifsc_is_semaphore_available(addr)) | |
99 | return; | |
100 | ||
101 | writel(SEMCR_MUTEX, addr); | |
102 | ||
103 | /* Ok if another compartment takes the semaphore before the check */ | |
104 | WARN_ON(!stm32_rifsc_is_semaphore_available(addr) && | |
105 | FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1); | |
106 | } | |
107 | ||
108 | static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id) | |
109 | { | |
110 | struct stm32_firewall_controller *rifsc_controller = ctrl; | |
111 | u32 reg_offset, reg_id, sec_reg_value, cid_reg_value; | |
112 | int rc; | |
113 | ||
114 | if (firewall_id >= rifsc_controller->max_entries) { | |
115 | dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id); | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
119 | /* | |
120 | * RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle configuration access for | |
121 | * 32 peripherals. On the other hand, there is one _RIFSC_RISC_PERx_CIDCFGR register | |
122 | * per peripheral | |
123 | */ | |
124 | reg_id = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS; | |
125 | reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS; | |
126 | sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); | |
127 | cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id); | |
128 | ||
129 | /* First check conditions for semaphore mode, which doesn't take into account static CID. */ | |
130 | if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { | |
131 | if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) { | |
132 | /* Static CID is irrelevant if semaphore mode */ | |
133 | goto skip_cid_check; | |
134 | } else { | |
135 | dev_dbg(rifsc_controller->dev, | |
136 | "Invalid bus semaphore configuration: index %d\n", firewall_id); | |
137 | return -EACCES; | |
138 | } | |
139 | } | |
140 | ||
141 | /* | |
142 | * Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which | |
143 | * corresponds to whatever CID. | |
144 | */ | |
145 | if (!(cid_reg_value & CIDCFGR_CFEN) || | |
146 | FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0) | |
147 | goto skip_cid_check; | |
148 | ||
149 | /* Coherency check with the CID configuration */ | |
150 | if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { | |
151 | dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", | |
152 | firewall_id); | |
153 | return -EACCES; | |
154 | } | |
155 | ||
156 | skip_cid_check: | |
157 | /* Check security configuration */ | |
158 | if (sec_reg_value & BIT(reg_offset)) { | |
159 | dev_dbg(rifsc_controller->dev, | |
160 | "Invalid security configuration for peripheral: %d\n", firewall_id); | |
161 | return -EACCES; | |
162 | } | |
163 | ||
164 | /* | |
165 | * If the peripheral is in semaphore mode, take the semaphore so that | |
166 | * the CID1 has the ownership. | |
167 | */ | |
168 | if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { | |
169 | rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id); | |
170 | if (rc) { | |
171 | dev_err(rifsc_controller->dev, | |
172 | "Couldn't acquire semaphore for peripheral: %d\n", firewall_id); | |
173 | return rc; | |
174 | } | |
175 | } | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id) | |
181 | { | |
182 | stm32_rif_release_semaphore(ctrl, firewall_id); | |
183 | } | |
184 | ||
185 | static int stm32_rifsc_probe(struct platform_device *pdev) | |
186 | { | |
187 | struct stm32_firewall_controller *rifsc_controller; | |
188 | struct device_node *np = pdev->dev.of_node; | |
189 | u32 nb_risup, nb_rimu, nb_risal; | |
190 | struct resource *res; | |
191 | void __iomem *mmio; | |
192 | int rc; | |
193 | ||
194 | rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL); | |
195 | if (!rifsc_controller) | |
196 | return -ENOMEM; | |
197 | ||
198 | mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); | |
199 | if (IS_ERR(mmio)) | |
200 | return PTR_ERR(mmio); | |
201 | ||
202 | rifsc_controller->dev = &pdev->dev; | |
203 | rifsc_controller->mmio = mmio; | |
204 | rifsc_controller->name = dev_driver_string(rifsc_controller->dev); | |
205 | rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL; | |
206 | rifsc_controller->grant_access = stm32_rifsc_grant_access; | |
207 | rifsc_controller->release_access = stm32_rifsc_release_access; | |
208 | ||
209 | /* Get number of RIFSC entries*/ | |
210 | nb_risup = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF1_MASK; | |
211 | nb_rimu = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF2_MASK; | |
212 | nb_risal = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF3_MASK; | |
213 | rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal; | |
214 | ||
215 | platform_set_drvdata(pdev, rifsc_controller); | |
216 | ||
217 | rc = stm32_firewall_controller_register(rifsc_controller); | |
218 | if (rc) { | |
219 | dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d", | |
220 | rc); | |
221 | return rc; | |
222 | } | |
223 | ||
224 | rc = stm32_firewall_populate_bus(rifsc_controller); | |
225 | if (rc) { | |
226 | dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d", | |
227 | rc); | |
228 | return rc; | |
229 | } | |
230 | ||
231 | /* Populate all allowed nodes */ | |
232 | return of_platform_populate(np, NULL, NULL, &pdev->dev); | |
233 | } | |
234 | ||
235 | static const struct of_device_id stm32_rifsc_of_match[] = { | |
236 | { .compatible = "st,stm32mp25-rifsc" }, | |
237 | {} | |
238 | }; | |
239 | MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match); | |
240 | ||
241 | static struct platform_driver stm32_rifsc_driver = { | |
242 | .probe = stm32_rifsc_probe, | |
243 | .driver = { | |
244 | .name = "stm32-rifsc", | |
245 | .of_match_table = stm32_rifsc_of_match, | |
246 | }, | |
247 | }; | |
248 | module_platform_driver(stm32_rifsc_driver); | |
249 | ||
250 | MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>"); | |
251 | MODULE_DESCRIPTION("STMicroelectronics RIFSC driver"); | |
252 | MODULE_LICENSE("GPL"); |