1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * TI da8xx master peripheral priority driver |
4 | * |
5 | * Copyright (C) 2016 BayLibre SAS |
6 | * |
7 | * Author: |
8 | * Bartosz Golaszewski <bgolaszewski@baylibre.com> |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/io.h> |
15 | #include <linux/regmap.h> |
16 | |
17 | /* |
18 | * REVISIT: Linux doesn't have a good framework for the kind of performance |
19 | * knobs this driver controls. We can't use device tree properties as it deals |
20 | * with hardware configuration rather than description. We also don't want to |
21 | * commit to maintaining some random sysfs attributes. |
22 | * |
23 | * For now we just hardcode the register values for the boards that need |
24 | * some changes (as is the case for the LCD controller on da850-lcdk - the |
25 | * first board we support here). When linux gets an appropriate framework, |
26 | * we'll easily convert the driver to it. |
27 | */ |
28 | |
29 | #define DA8XX_MSTPRI0_OFFSET 0 |
30 | #define DA8XX_MSTPRI1_OFFSET 4 |
31 | #define DA8XX_MSTPRI2_OFFSET 8 |
32 | |
33 | enum { |
34 | DA8XX_MSTPRI_ARM_I = 0, |
35 | DA8XX_MSTPRI_ARM_D, |
36 | DA8XX_MSTPRI_UPP, |
37 | DA8XX_MSTPRI_SATA, |
38 | DA8XX_MSTPRI_PRU0, |
39 | DA8XX_MSTPRI_PRU1, |
40 | DA8XX_MSTPRI_EDMA30TC0, |
41 | DA8XX_MSTPRI_EDMA30TC1, |
42 | DA8XX_MSTPRI_EDMA31TC0, |
43 | DA8XX_MSTPRI_VPIF_DMA_0, |
44 | DA8XX_MSTPRI_VPIF_DMA_1, |
45 | DA8XX_MSTPRI_EMAC, |
46 | DA8XX_MSTPRI_USB0CFG, |
47 | DA8XX_MSTPRI_USB0CDMA, |
48 | DA8XX_MSTPRI_UHPI, |
49 | DA8XX_MSTPRI_USB1, |
50 | DA8XX_MSTPRI_LCDC, |
51 | }; |
52 | |
53 | struct da8xx_mstpri_descr { |
54 | int reg; |
55 | int shift; |
56 | int mask; |
57 | }; |
58 | |
59 | static const struct da8xx_mstpri_descr da8xx_mstpri_priority_list[] = { |
60 | [DA8XX_MSTPRI_ARM_I] = { |
61 | .reg = DA8XX_MSTPRI0_OFFSET, |
62 | .shift = 0, |
63 | .mask = 0x0000000f, |
64 | }, |
65 | [DA8XX_MSTPRI_ARM_D] = { |
66 | .reg = DA8XX_MSTPRI0_OFFSET, |
67 | .shift = 4, |
68 | .mask = 0x000000f0, |
69 | }, |
70 | [DA8XX_MSTPRI_UPP] = { |
71 | .reg = DA8XX_MSTPRI0_OFFSET, |
72 | .shift = 16, |
73 | .mask = 0x000f0000, |
74 | }, |
75 | [DA8XX_MSTPRI_SATA] = { |
76 | .reg = DA8XX_MSTPRI0_OFFSET, |
77 | .shift = 20, |
78 | .mask = 0x00f00000, |
79 | }, |
80 | [DA8XX_MSTPRI_PRU0] = { |
81 | .reg = DA8XX_MSTPRI1_OFFSET, |
82 | .shift = 0, |
83 | .mask = 0x0000000f, |
84 | }, |
85 | [DA8XX_MSTPRI_PRU1] = { |
86 | .reg = DA8XX_MSTPRI1_OFFSET, |
87 | .shift = 4, |
88 | .mask = 0x000000f0, |
89 | }, |
90 | [DA8XX_MSTPRI_EDMA30TC0] = { |
91 | .reg = DA8XX_MSTPRI1_OFFSET, |
92 | .shift = 8, |
93 | .mask = 0x00000f00, |
94 | }, |
95 | [DA8XX_MSTPRI_EDMA30TC1] = { |
96 | .reg = DA8XX_MSTPRI1_OFFSET, |
97 | .shift = 12, |
98 | .mask = 0x0000f000, |
99 | }, |
100 | [DA8XX_MSTPRI_EDMA31TC0] = { |
101 | .reg = DA8XX_MSTPRI1_OFFSET, |
102 | .shift = 16, |
103 | .mask = 0x000f0000, |
104 | }, |
105 | [DA8XX_MSTPRI_VPIF_DMA_0] = { |
106 | .reg = DA8XX_MSTPRI1_OFFSET, |
107 | .shift = 24, |
108 | .mask = 0x0f000000, |
109 | }, |
110 | [DA8XX_MSTPRI_VPIF_DMA_1] = { |
111 | .reg = DA8XX_MSTPRI1_OFFSET, |
112 | .shift = 28, |
113 | .mask = 0xf0000000, |
114 | }, |
115 | [DA8XX_MSTPRI_EMAC] = { |
116 | .reg = DA8XX_MSTPRI2_OFFSET, |
117 | .shift = 0, |
118 | .mask = 0x0000000f, |
119 | }, |
120 | [DA8XX_MSTPRI_USB0CFG] = { |
121 | .reg = DA8XX_MSTPRI2_OFFSET, |
122 | .shift = 8, |
123 | .mask = 0x00000f00, |
124 | }, |
125 | [DA8XX_MSTPRI_USB0CDMA] = { |
126 | .reg = DA8XX_MSTPRI2_OFFSET, |
127 | .shift = 12, |
128 | .mask = 0x0000f000, |
129 | }, |
130 | [DA8XX_MSTPRI_UHPI] = { |
131 | .reg = DA8XX_MSTPRI2_OFFSET, |
132 | .shift = 20, |
133 | .mask = 0x00f00000, |
134 | }, |
135 | [DA8XX_MSTPRI_USB1] = { |
136 | .reg = DA8XX_MSTPRI2_OFFSET, |
137 | .shift = 24, |
138 | .mask = 0x0f000000, |
139 | }, |
140 | [DA8XX_MSTPRI_LCDC] = { |
141 | .reg = DA8XX_MSTPRI2_OFFSET, |
142 | .shift = 28, |
143 | .mask = 0xf0000000, |
144 | }, |
145 | }; |
146 | |
147 | struct da8xx_mstpri_priority { |
148 | int which; |
149 | u32 val; |
150 | }; |
151 | |
152 | struct da8xx_mstpri_board_priorities { |
153 | const char *board; |
154 | const struct da8xx_mstpri_priority *priorities; |
155 | size_t numprio; |
156 | }; |
157 | |
158 | /* |
159 | * Default memory settings of da850 do not meet the throughput/latency |
160 | * requirements of tilcdc. This results in the image displayed being |
161 | * incorrect and the following warning being displayed by the LCDC |
162 | * drm driver: |
163 | * |
164 | * tilcdc da8xx_lcdc.0: tilcdc_crtc_irq(0x00000020): FIFO underfow |
165 | */ |
166 | static const struct da8xx_mstpri_priority da850_lcdk_priorities[] = { |
167 | { |
168 | .which = DA8XX_MSTPRI_LCDC, |
169 | .val = 0, |
170 | }, |
171 | { |
172 | .which = DA8XX_MSTPRI_EDMA30TC1, |
173 | .val = 0, |
174 | }, |
175 | { |
176 | .which = DA8XX_MSTPRI_EDMA30TC0, |
177 | .val = 1, |
178 | }, |
179 | }; |
180 | |
181 | static const struct da8xx_mstpri_board_priorities da8xx_mstpri_board_confs[] = { |
182 | { |
183 | .board = "ti,da850-lcdk" , |
184 | .priorities = da850_lcdk_priorities, |
185 | .numprio = ARRAY_SIZE(da850_lcdk_priorities), |
186 | }, |
187 | }; |
188 | |
189 | static const struct da8xx_mstpri_board_priorities * |
190 | da8xx_mstpri_get_board_prio(void) |
191 | { |
192 | const struct da8xx_mstpri_board_priorities *board_prio; |
193 | int i; |
194 | |
195 | for (i = 0; i < ARRAY_SIZE(da8xx_mstpri_board_confs); i++) { |
196 | board_prio = &da8xx_mstpri_board_confs[i]; |
197 | |
198 | if (of_machine_is_compatible(compat: board_prio->board)) |
199 | return board_prio; |
200 | } |
201 | |
202 | return NULL; |
203 | } |
204 | |
205 | static int da8xx_mstpri_probe(struct platform_device *pdev) |
206 | { |
207 | const struct da8xx_mstpri_board_priorities *prio_list; |
208 | const struct da8xx_mstpri_descr *prio_descr; |
209 | const struct da8xx_mstpri_priority *prio; |
210 | struct device *dev = &pdev->dev; |
211 | struct resource *res; |
212 | void __iomem *mstpri; |
213 | u32 reg; |
214 | int i; |
215 | |
216 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
217 | mstpri = devm_ioremap_resource(dev, res); |
218 | if (IS_ERR(ptr: mstpri)) { |
219 | dev_err(dev, "unable to map MSTPRI registers\n" ); |
220 | return PTR_ERR(ptr: mstpri); |
221 | } |
222 | |
223 | prio_list = da8xx_mstpri_get_board_prio(); |
224 | if (!prio_list) { |
225 | dev_err(dev, "no master priorities defined for this board\n" ); |
226 | return -EINVAL; |
227 | } |
228 | |
229 | for (i = 0; i < prio_list->numprio; i++) { |
230 | prio = &prio_list->priorities[i]; |
231 | prio_descr = &da8xx_mstpri_priority_list[prio->which]; |
232 | |
233 | if (prio_descr->reg + sizeof(u32) > resource_size(res)) { |
234 | dev_warn(dev, "register offset out of range\n" ); |
235 | continue; |
236 | } |
237 | |
238 | reg = readl(addr: mstpri + prio_descr->reg); |
239 | reg &= ~prio_descr->mask; |
240 | reg |= prio->val << prio_descr->shift; |
241 | |
242 | writel(val: reg, addr: mstpri + prio_descr->reg); |
243 | } |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static const struct of_device_id da8xx_mstpri_of_match[] = { |
249 | { .compatible = "ti,da850-mstpri" , }, |
250 | { }, |
251 | }; |
252 | |
253 | static struct platform_driver da8xx_mstpri_driver = { |
254 | .probe = da8xx_mstpri_probe, |
255 | .driver = { |
256 | .name = "da8xx-mstpri" , |
257 | .of_match_table = da8xx_mstpri_of_match, |
258 | }, |
259 | }; |
260 | module_platform_driver(da8xx_mstpri_driver); |
261 | |
262 | MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>" ); |
263 | MODULE_DESCRIPTION("TI da8xx master peripheral priority driver" ); |
264 | MODULE_LICENSE("GPL v2" ); |
265 | |