blob: c1d1826fd142c5b2d307d3a57fec3851fa6fd907 [file] [log] [blame]
Chris Morgan8c4e3042023-04-21 10:59:19 -05001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2023 Chris Morgan <macromorgan@hotmail.com>
4 */
5
6#include <abuf.h>
7#include <adc.h>
8#include <asm/io.h>
Chris Morgan3db220e2023-05-15 11:00:30 -05009#include <display.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050010#include <dm.h>
Chris Morgana824aa62023-05-15 11:00:29 -050011#include <dm/lists.h>
12#include <env.h>
13#include <fdt_support.h>
Chris Morgan8bff9a62024-09-19 09:00:19 -050014#include <i2c.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050015#include <linux/delay.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050016#include <mipi_dsi.h>
Chris Morgana824aa62023-05-15 11:00:29 -050017#include <mmc.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050018#include <panel.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050019#include <pwm.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050020#include <stdlib.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050021#include <video_bridge.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050022
Chris Morgan8bff9a62024-09-19 09:00:19 -050023DECLARE_GLOBAL_DATA_PTR;
24
Chris Morgan8c4e3042023-04-21 10:59:19 -050025#define GPIO0_BASE 0xfdd60000
Chris Morgan3db220e2023-05-15 11:00:30 -050026#define GPIO4_BASE 0xfe770000
Chris Morgana824aa62023-05-15 11:00:29 -050027#define GPIO_SWPORT_DR_L 0x0000
Chris Morgan8c4e3042023-04-21 10:59:19 -050028#define GPIO_SWPORT_DR_H 0x0004
Chris Morgana824aa62023-05-15 11:00:29 -050029#define GPIO_SWPORT_DDR_L 0x0008
Chris Morgan8c4e3042023-04-21 10:59:19 -050030#define GPIO_SWPORT_DDR_H 0x000c
Chris Morgan3db220e2023-05-15 11:00:30 -050031#define GPIO_A0 BIT(0)
Chris Morgana824aa62023-05-15 11:00:29 -050032#define GPIO_C5 BIT(5)
33#define GPIO_C6 BIT(6)
34#define GPIO_C7 BIT(7)
Chris Morgan8c4e3042023-04-21 10:59:19 -050035
36#define GPIO_WRITEMASK(bits) ((bits) << 16)
37
38#define DTB_DIR "rockchip/"
39
40struct rg3xx_model {
Chris Morgana824aa62023-05-15 11:00:29 -050041 const u16 adc_value;
Chris Morgan8c4e3042023-04-21 10:59:19 -050042 const char *board;
43 const char *board_name;
44 const char *fdtfile;
Chris Morgana63ff502024-01-02 09:46:48 -060045 const bool detect_panel;
Chris Morganedff9672024-09-19 09:00:20 -050046 const bool detect_regulator;
Chris Morgan8bff9a62024-09-19 09:00:19 -050047 const bool uart_con;
Chris Morgan8c4e3042023-04-21 10:59:19 -050048};
49
50enum rgxx3_device_id {
Chris Morgan8bff9a62024-09-19 09:00:19 -050051 RG353M = 1,
Chris Morgan8c4e3042023-04-21 10:59:19 -050052 RG353P,
53 RG353V,
Chris Morgan8c4e3042023-04-21 10:59:19 -050054 RG503,
Chris Morgan95db0c02024-01-02 09:46:53 -060055 RGB30,
56 RK2023,
57 RGARCD,
Chris Morgan4b181142024-02-05 12:58:53 -060058 RGB10MAX3,
Chris Morgana824aa62023-05-15 11:00:29 -050059 /* Devices with duplicate ADC value */
60 RG353PS,
61 RG353VS,
Chris Morgan95db0c02024-01-02 09:46:53 -060062 RGARCS,
Chris Morgan8c4e3042023-04-21 10:59:19 -050063};
64
65static const struct rg3xx_model rg3xx_model_details[] = {
66 [RG353M] = {
Chris Morgana63ff502024-01-02 09:46:48 -060067 .adc_value = 517, /* Observed average from device */
68 .board = "rk3566-anbernic-rg353m",
Chris Morgan8bff9a62024-09-19 09:00:19 -050069 .board_name = "Anbernic RG353M",
Chris Morgana63ff502024-01-02 09:46:48 -060070 /* Device is identical to RG353P. */
71 .fdtfile = DTB_DIR "rk3566-anbernic-rg353p.dtb",
72 .detect_panel = 1,
Chris Morganedff9672024-09-19 09:00:20 -050073 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -050074 .uart_con = 1,
Chris Morgan8c4e3042023-04-21 10:59:19 -050075 },
76 [RG353P] = {
Chris Morgana63ff502024-01-02 09:46:48 -060077 .adc_value = 860, /* Documented value of 860 */
78 .board = "rk3566-anbernic-rg353p",
Chris Morgan8bff9a62024-09-19 09:00:19 -050079 .board_name = "Anbernic RG353P",
Chris Morgana63ff502024-01-02 09:46:48 -060080 .fdtfile = DTB_DIR "rk3566-anbernic-rg353p.dtb",
81 .detect_panel = 1,
Chris Morganedff9672024-09-19 09:00:20 -050082 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -050083 .uart_con = 1,
Chris Morgan8c4e3042023-04-21 10:59:19 -050084 },
85 [RG353V] = {
Chris Morgana63ff502024-01-02 09:46:48 -060086 .adc_value = 695, /* Observed average from device */
87 .board = "rk3566-anbernic-rg353v",
Chris Morgan8bff9a62024-09-19 09:00:19 -050088 .board_name = "Anbernic RG353V",
Chris Morgana63ff502024-01-02 09:46:48 -060089 .fdtfile = DTB_DIR "rk3566-anbernic-rg353v.dtb",
90 .detect_panel = 1,
Chris Morganedff9672024-09-19 09:00:20 -050091 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -050092 .uart_con = 1,
Chris Morgan8c4e3042023-04-21 10:59:19 -050093 },
Chris Morgan8c4e3042023-04-21 10:59:19 -050094 [RG503] = {
Chris Morgana63ff502024-01-02 09:46:48 -060095 .adc_value = 1023, /* Observed average from device */
96 .board = "rk3566-anbernic-rg503",
Chris Morgan8bff9a62024-09-19 09:00:19 -050097 .board_name = "Anbernic RG503",
Chris Morgana63ff502024-01-02 09:46:48 -060098 .fdtfile = DTB_DIR "rk3566-anbernic-rg503.dtb",
99 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500100 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500101 .uart_con = 1,
Chris Morgan8c4e3042023-04-21 10:59:19 -0500102 },
Chris Morgan95db0c02024-01-02 09:46:53 -0600103 [RGB30] = {
104 .adc_value = 383, /* Gathered from second hand information */
105 .board = "rk3566-powkiddy-rgb30",
Chris Morgan8bff9a62024-09-19 09:00:19 -0500106 .board_name = "Powkiddy RGB30",
Chris Morgan95db0c02024-01-02 09:46:53 -0600107 .fdtfile = DTB_DIR "rk3566-powkiddy-rgb30.dtb",
108 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500109 .detect_regulator = 1,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500110 .uart_con = 0,
Chris Morgan95db0c02024-01-02 09:46:53 -0600111 },
112 [RK2023] = {
113 .adc_value = 635, /* Observed average from device */
114 .board = "rk3566-powkiddy-rk2023",
Chris Morgan8bff9a62024-09-19 09:00:19 -0500115 .board_name = "Powkiddy RK2023",
Chris Morgan95db0c02024-01-02 09:46:53 -0600116 .fdtfile = DTB_DIR "rk3566-powkiddy-rk2023.dtb",
117 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500118 .detect_regulator = 1,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500119 .uart_con = 0,
Chris Morgan95db0c02024-01-02 09:46:53 -0600120 },
121 [RGARCD] = {
122 .adc_value = 183, /* Observed average from device */
123 .board = "rk3566-anbernic-rg-arc-d",
124 .board_name = "Anbernic RG ARC-D",
125 .fdtfile = DTB_DIR "rk3566-anbernic-rg-arc-d.dtb",
126 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500127 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500128 .uart_con = 1,
Chris Morgan95db0c02024-01-02 09:46:53 -0600129 },
Chris Morgan4b181142024-02-05 12:58:53 -0600130 [RGB10MAX3] = {
131 .adc_value = 765, /* Observed average from device */
132 .board = "rk3566-powkiddy-rgb10max3",
133 .board_name = "Powkiddy RGB10MAX3",
134 .fdtfile = DTB_DIR "rk3566-powkiddy-rgb10max3.dtb",
135 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500136 .detect_regulator = 1,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500137 .uart_con = 0,
Chris Morgan4b181142024-02-05 12:58:53 -0600138 },
Chris Morgana824aa62023-05-15 11:00:29 -0500139 /* Devices with duplicate ADC value */
140 [RG353PS] = {
Chris Morgana63ff502024-01-02 09:46:48 -0600141 .adc_value = 860, /* Observed average from device */
142 .board = "rk3566-anbernic-rg353ps",
Chris Morgan8bff9a62024-09-19 09:00:19 -0500143 .board_name = "Anbernic RG353PS",
Chris Morgana63ff502024-01-02 09:46:48 -0600144 .fdtfile = DTB_DIR "rk3566-anbernic-rg353ps.dtb",
145 .detect_panel = 1,
Chris Morganedff9672024-09-19 09:00:20 -0500146 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500147 .uart_con = 1,
Chris Morgana824aa62023-05-15 11:00:29 -0500148 },
149 [RG353VS] = {
Chris Morgana63ff502024-01-02 09:46:48 -0600150 .adc_value = 695, /* Gathered from second hand information */
151 .board = "rk3566-anbernic-rg353vs",
Chris Morgan8bff9a62024-09-19 09:00:19 -0500152 .board_name = "Anbernic RG353VS",
Chris Morgana63ff502024-01-02 09:46:48 -0600153 .fdtfile = DTB_DIR "rk3566-anbernic-rg353vs.dtb",
154 .detect_panel = 1,
Chris Morganedff9672024-09-19 09:00:20 -0500155 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500156 .uart_con = 1,
Chris Morgana824aa62023-05-15 11:00:29 -0500157 },
Chris Morgan95db0c02024-01-02 09:46:53 -0600158 [RGARCS] = {
159 .adc_value = 183, /* Observed average from device */
160 .board = "rk3566-anbernic-rg-arc-s",
161 .board_name = "Anbernic RG ARC-S",
162 .fdtfile = DTB_DIR "rk3566-anbernic-rg-arc-s.dtb",
163 .detect_panel = 0,
Chris Morganedff9672024-09-19 09:00:20 -0500164 .detect_regulator = 0,
Chris Morgan8bff9a62024-09-19 09:00:19 -0500165 .uart_con = 1,
Chris Morgan95db0c02024-01-02 09:46:53 -0600166 },
Chris Morgan8c4e3042023-04-21 10:59:19 -0500167};
168
Chris Morgan3db220e2023-05-15 11:00:30 -0500169struct rg353_panel {
170 const u16 id;
Chris Morgana63ff502024-01-02 09:46:48 -0600171 const char *panel_compat[2];
Chris Morgan3db220e2023-05-15 11:00:30 -0500172};
173
174static const struct rg353_panel rg353_panel_details[] = {
Chris Morgana63ff502024-01-02 09:46:48 -0600175 {
176 .id = 0x3052,
177 .panel_compat[0] = "anbernic,rg353p-panel",
178 .panel_compat[1] = "newvision,nv3051d",
179 },
180 {
181 .id = 0x3821,
182 .panel_compat[0] = "anbernic,rg353v-panel-v2",
183 .panel_compat[1] = NULL,
184 },
Chris Morgan3db220e2023-05-15 11:00:30 -0500185};
186
Chris Morganedff9672024-09-19 09:00:20 -0500187struct powkiddy_regulators {
188 const u8 addr;
189 const char *regulator_compat;
190};
191
192static const struct powkiddy_regulators regulator_details[] = {
193 {
194 .addr = 0x1c,
195 .regulator_compat = "tcs,tcs4525",
196 },
197 {
198 .addr = 0x40,
199 .regulator_compat = "fcs,fan53555",
200 },
201};
202
Chris Morgan8c4e3042023-04-21 10:59:19 -0500203/*
204 * Start LED very early so user knows device is on. Set color
Chris Morgana824aa62023-05-15 11:00:29 -0500205 * to red.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500206 */
207void spl_board_init(void)
208{
Chris Morgana824aa62023-05-15 11:00:29 -0500209 /* Set GPIO0_C5, GPIO0_C6, and GPIO0_C7 to output. */
Chris Morgan8bff9a62024-09-19 09:00:19 -0500210 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) |
Chris Morgana824aa62023-05-15 11:00:29 -0500211 (GPIO_C7 | GPIO_C6 | GPIO_C5),
Chris Morgan8c4e3042023-04-21 10:59:19 -0500212 (GPIO0_BASE + GPIO_SWPORT_DDR_H));
Chris Morgana824aa62023-05-15 11:00:29 -0500213 /* Set GPIO0_C5 and GPIO_C6 to 0 and GPIO0_C7 to 1. */
214 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C7,
Chris Morgan8c4e3042023-04-21 10:59:19 -0500215 (GPIO0_BASE + GPIO_SWPORT_DR_H));
216}
217
Chris Morgan8c4e3042023-04-21 10:59:19 -0500218/*
219 * Buzz the buzzer so the user knows something is going on. Make it
Chris Morgan8bff9a62024-09-19 09:00:19 -0500220 * optional in case PWM is disabled or if CONFIG_DM_PWM is not
221 * enabled.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500222 */
223void __maybe_unused startup_buzz(void)
224{
225 struct udevice *dev;
226 int err;
227
Chris Morgan8bff9a62024-09-19 09:00:19 -0500228 if (!IS_ENABLED(CONFIG_DM_PWM))
229 return;
230
231 /* Probe the PWM controller. */
232 err = uclass_get_device_by_name(UCLASS_PWM,
233 "pwm@fe6e0010", &dev);
Chris Morgan8c4e3042023-04-21 10:59:19 -0500234 if (err)
Chris Morgan8bff9a62024-09-19 09:00:19 -0500235 return;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500236
237 pwm_set_enable(dev, 0, 1);
238 mdelay(200);
239 pwm_set_enable(dev, 0, 0);
240}
241
Chris Morgan3db220e2023-05-15 11:00:30 -0500242/*
243 * Provide the bare minimum to identify the panel for the RG353
244 * series. Since we don't have a working framebuffer device, no
245 * need to init the panel; just identify it and provide the
246 * clocks so we know what to set the different clock values to.
247 */
248
249static const struct display_timing rg353_default_timing = {
250 .pixelclock.typ = 24150000,
251 .hactive.typ = 640,
252 .hfront_porch.typ = 40,
253 .hback_porch.typ = 80,
254 .hsync_len.typ = 2,
255 .vactive.typ = 480,
256 .vfront_porch.typ = 18,
257 .vback_porch.typ = 28,
258 .vsync_len.typ = 2,
259 .flags = DISPLAY_FLAGS_HSYNC_HIGH |
260 DISPLAY_FLAGS_VSYNC_HIGH,
261};
262
263static int anbernic_rg353_panel_get_timing(struct udevice *dev,
264 struct display_timing *timings)
265{
266 memcpy(timings, &rg353_default_timing, sizeof(*timings));
267
268 return 0;
269}
270
271static int anbernic_rg353_panel_probe(struct udevice *dev)
272{
273 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
274
275 plat->lanes = 4;
276 plat->format = MIPI_DSI_FMT_RGB888;
277 plat->mode_flags = MIPI_DSI_MODE_VIDEO |
278 MIPI_DSI_MODE_VIDEO_BURST |
279 MIPI_DSI_MODE_EOT_PACKET |
280 MIPI_DSI_MODE_LPM;
281
282 return 0;
283}
284
285static const struct panel_ops anbernic_rg353_panel_ops = {
286 .get_display_timing = anbernic_rg353_panel_get_timing,
287};
288
289U_BOOT_DRIVER(anbernic_rg353_panel) = {
290 .name = "anbernic_rg353_panel",
291 .id = UCLASS_PANEL,
292 .ops = &anbernic_rg353_panel_ops,
293 .probe = anbernic_rg353_panel_probe,
294 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
295};
296
Chris Morgan8bff9a62024-09-19 09:00:19 -0500297/*
298 * The Anbernic 353 series shipped with 2 distinct displays requiring
299 * 2 distinct drivers, with no way for a user to know which panel is
300 * which. This function queries the DSI panel for the panel ID to
301 * determine which panel is present so the device-tree can be corrected
302 * automatically.
303 */
Chris Morgan3db220e2023-05-15 11:00:30 -0500304int rgxx3_detect_display(void)
305{
306 struct udevice *dev;
307 struct mipi_dsi_device *dsi;
308 struct mipi_dsi_panel_plat *mplat;
309 const struct rg353_panel *panel;
310 int ret = 0;
311 int i;
312 u8 panel_id[2];
313
314 /*
315 * Take panel out of reset status.
316 * Set GPIO4_A0 to output.
317 */
318 writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
319 (GPIO4_BASE + GPIO_SWPORT_DDR_L));
320 /* Set GPIO4_A0 to 1. */
321 writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
322 (GPIO4_BASE + GPIO_SWPORT_DR_L));
323
324 /* Probe the DSI controller. */
325 ret = uclass_get_device_by_name(UCLASS_VIDEO_BRIDGE,
326 "dsi@fe060000", &dev);
327 if (ret) {
328 printf("DSI host not probed: %d\n", ret);
329 return ret;
330 }
331
332 /* Probe the DSI panel. */
333 ret = device_bind_driver_to_node(dev, "anbernic_rg353_panel",
334 "anbernic_rg353_panel",
335 dev_ofnode(dev), NULL);
336 if (ret) {
337 printf("Failed to probe RG353 panel: %d\n", ret);
338 return ret;
339 }
340
341 /*
342 * Attach the DSI controller which will also probe and attach
343 * the DSIDPHY.
344 */
345 ret = video_bridge_attach(dev);
346 if (ret) {
347 printf("Failed to attach DSI controller: %d\n", ret);
348 return ret;
349 }
350
351 /*
352 * Get the panel which should have already been probed by the
353 * video_bridge_attach() function.
354 */
355 ret = uclass_first_device_err(UCLASS_PANEL, &dev);
356 if (ret) {
357 printf("Panel device error: %d\n", ret);
358 return ret;
359 }
360
361 /* Now call the panel via DSI commands to get the panel ID. */
362 mplat = dev_get_plat(dev);
363 dsi = mplat->device;
364 mipi_dsi_set_maximum_return_packet_size(dsi, sizeof(panel_id));
365 ret = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_ID, &panel_id,
366 sizeof(panel_id));
367 if (ret < 0) {
368 printf("Unable to read panel ID: %d\n", ret);
369 return ret;
370 }
371
372 /* Get the correct panel compatible from the table. */
373 for (i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) {
374 if (rg353_panel_details[i].id == ((panel_id[0] << 8) |
375 panel_id[1])) {
376 panel = &rg353_panel_details[i];
377 break;
378 }
379 }
380
381 if (!panel) {
382 printf("Unable to identify panel_id %x\n",
383 (panel_id[0] << 8) | panel_id[1]);
Chris Morgan3db220e2023-05-15 11:00:30 -0500384 return -EINVAL;
385 }
386
Chris Morgana63ff502024-01-02 09:46:48 -0600387 env_set("panel", panel->panel_compat[0]);
Chris Morgan3db220e2023-05-15 11:00:30 -0500388
389 return 0;
390}
391
Chris Morganedff9672024-09-19 09:00:20 -0500392/*
393 * Some of the Powkiddy devices switched the CPU regulator, but users
394 * are not able to determine this by looking at their hardware.
395 * Attempt to auto-detect this situation and fixup the device-tree.
396 */
397int rgxx3_detect_regulator(void)
398{
399 struct udevice *bus;
400 struct udevice *chip;
401 u8 val;
402 int ret;
403
404 /* Get the correct i2c bus (i2c0). */
405 ret = uclass_get_device_by_name(UCLASS_I2C,
406 "i2c@fdd40000", &bus);
407 if (ret)
408 return ret;
409
410 /*
411 * Check for all vdd_cpu regulators and read an arbitrary
412 * register to confirm it's present.
413 */
414 for (int i = 0; i < ARRAY_SIZE(regulator_details); i++) {
415 ret = i2c_get_chip(bus, regulator_details[i].addr,
416 1, &chip);
417 if (ret)
418 return ret;
419
420 ret = dm_i2c_read(chip, 0, &val, 1);
421 if (!ret) {
422 env_set("vdd_cpu", regulator_details[i].regulator_compat);
423 break;
424 }
425 }
426
427 return 0;
428}
429
Chris Morgan8bff9a62024-09-19 09:00:19 -0500430int rgxx3_read_board_id(void)
Chris Morgan8c4e3042023-04-21 10:59:19 -0500431{
432 u32 adc_info;
Chris Morgan8bff9a62024-09-19 09:00:19 -0500433 int ret;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500434
435 ret = adc_channel_single_shot("saradc@fe720000", 1, &adc_info);
436 if (ret) {
437 printf("Read SARADC failed with error %d\n", ret);
438 return ret;
439 }
440
Chris Morgana824aa62023-05-15 11:00:29 -0500441 /*
442 * Get the correct device from the table. The ADC value is
443 * determined by a resistor on ADC channel 0. The hardware
444 * design calls for no more than a 1% variance on the
445 * resistor, so assume a +- value of 15 should be enough.
446 */
Chris Morgan8bff9a62024-09-19 09:00:19 -0500447 for (int i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) {
Chris Morgana824aa62023-05-15 11:00:29 -0500448 u32 adc_min = rg3xx_model_details[i].adc_value - 15;
449 u32 adc_max = rg3xx_model_details[i].adc_value + 15;
450
Chris Morgan8bff9a62024-09-19 09:00:19 -0500451 if (adc_min < adc_info && adc_max > adc_info)
452 return i;
Chris Morgana824aa62023-05-15 11:00:29 -0500453 }
Chris Morgan8c4e3042023-04-21 10:59:19 -0500454
Chris Morgan8bff9a62024-09-19 09:00:19 -0500455 return -ENODEV;
456}
457
458/* Detect which Anbernic RGXX3 device we are using so as to load the
459 * correct devicetree for Linux. Set an environment variable once
460 * found. The detection depends on the value of ADC channel 1 and the
461 * presence of an eMMC on mmc0.
462 */
463int rgxx3_detect_device(void)
464{
465 int ret;
466 int board_id;
467 struct mmc *mmc;
468
469 board_id = rgxx3_read_board_id();
470 if (board_id < 0)
471 return board_id;
472
Chris Morgan8c4e3042023-04-21 10:59:19 -0500473 /*
Chris Morgan95db0c02024-01-02 09:46:53 -0600474 * Try to access the eMMC on an RG353V, RG353P, or RG Arc D.
475 * If it's missing, it's an RG353VS, RG353PS, or RG Arc S.
476 * Note we could also check for a touchscreen at 0x1a on i2c2.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500477 */
Chris Morgan95db0c02024-01-02 09:46:53 -0600478 if (board_id == RG353V || board_id == RG353P || board_id == RGARCD) {
Chris Morgan8c4e3042023-04-21 10:59:19 -0500479 mmc = find_mmc_device(0);
480 if (mmc) {
481 ret = mmc_init(mmc);
Chris Morgana824aa62023-05-15 11:00:29 -0500482 if (ret) {
483 if (board_id == RG353V)
484 board_id = RG353VS;
Chris Morgan95db0c02024-01-02 09:46:53 -0600485 else if (board_id == RG353P)
Chris Morgana824aa62023-05-15 11:00:29 -0500486 board_id = RG353PS;
Chris Morgan95db0c02024-01-02 09:46:53 -0600487 else
488 board_id = RGARCS;
Chris Morgana824aa62023-05-15 11:00:29 -0500489 }
Chris Morgan8c4e3042023-04-21 10:59:19 -0500490 }
491 }
492
Chris Morgan8bff9a62024-09-19 09:00:19 -0500493 return board_id;
494}
Chris Morgan8c4e3042023-04-21 10:59:19 -0500495
Chris Morgan8bff9a62024-09-19 09:00:19 -0500496/*
497 * Check the loaded device tree to set the correct gd->board_type.
498 * Disable the console if the board doesn't support a console.
499 */
500int set_gd_value(void)
501{
502 const char *model;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500503
Chris Morgan8bff9a62024-09-19 09:00:19 -0500504 model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
Chris Morgan3db220e2023-05-15 11:00:30 -0500505
Chris Morgan8bff9a62024-09-19 09:00:19 -0500506 for (int i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) {
507 if (strcmp(rg3xx_model_details[i].board_name, model) == 0) {
508 gd->board_type = i;
509 if (!rg3xx_model_details[i].uart_con)
510 gd->flags |= GD_FLG_SILENT |
511 GD_FLG_DISABLE_CONSOLE;
512 return 0;
513 }
514 }
Chris Morgan3db220e2023-05-15 11:00:30 -0500515
Chris Morgan8bff9a62024-09-19 09:00:19 -0500516 return -ENODEV;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500517}
518
519int rk_board_late_init(void)
520{
521 int ret;
522
Chris Morgan8bff9a62024-09-19 09:00:19 -0500523 ret = set_gd_value();
Chris Morgan8c4e3042023-04-21 10:59:19 -0500524 if (ret) {
Chris Morgan8bff9a62024-09-19 09:00:19 -0500525 printf("Unable to auto-detect device\n");
526 goto end;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500527 }
528
Chris Morgan8bff9a62024-09-19 09:00:19 -0500529 /*
530 * Change the model number on the RG353M since it uses the same
531 * tree as the RG353P.
532 */
533 if (gd->board_type == RG353P) {
534 ret = rgxx3_read_board_id();
535 if (ret > 0)
536 gd->board_type = ret;
537 }
538
539 env_set("board", rg3xx_model_details[gd->board_type].board);
540 env_set("board_name",
541 rg3xx_model_details[gd->board_type].board_name);
542 env_set("fdtfile", rg3xx_model_details[gd->board_type].fdtfile);
543
544 /*
545 * Skip panel detection if not needed. Warn but don't fail for
546 * errors in auto-detection of the panel.
547 */
548 if (rg3xx_model_details[gd->board_type].detect_panel) {
549 ret = rgxx3_detect_display();
550 if (ret)
551 printf("Failed to detect panel type\n");
552 }
553
Chris Morganedff9672024-09-19 09:00:20 -0500554 /*
555 * Skip vdd_cpu regulator detection if not needed. Warn but
556 * don't fail for errors in auto-detection of regulator.
557 */
558 if (rg3xx_model_details[gd->board_type].detect_regulator) {
559 ret = rgxx3_detect_regulator();
560 if (ret)
561 printf("Unable to detect vdd_cpu regulator\n");
562 }
563
Chris Morgan8bff9a62024-09-19 09:00:19 -0500564end:
Chris Morgana824aa62023-05-15 11:00:29 -0500565 /* Turn off red LED and turn on orange LED. */
566 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C6,
567 (GPIO0_BASE + GPIO_SWPORT_DR_H));
568
Chris Morgan8bff9a62024-09-19 09:00:19 -0500569 startup_buzz();
Chris Morgan8c4e3042023-04-21 10:59:19 -0500570
571 return 0;
572}
Chris Morgana824aa62023-05-15 11:00:29 -0500573
Chris Morgan8bff9a62024-09-19 09:00:19 -0500574int rgxx3_panel_fixup(void *blob)
Chris Morgana824aa62023-05-15 11:00:29 -0500575{
Chris Morgana63ff502024-01-02 09:46:48 -0600576 const struct rg353_panel *panel = NULL;
Chris Morgan8bff9a62024-09-19 09:00:19 -0500577 int node, ret;
Chris Morgana824aa62023-05-15 11:00:29 -0500578 char *env;
579
Chris Morgana63ff502024-01-02 09:46:48 -0600580 env = env_get("panel");
581 if (!env) {
582 printf("Can't get panel env\n");
Chris Morgan8bff9a62024-09-19 09:00:19 -0500583 return -EINVAL;
Chris Morgana63ff502024-01-02 09:46:48 -0600584 }
585
Chris Morgan3db220e2023-05-15 11:00:30 -0500586 /*
587 * Check if the environment variable doesn't equal the panel.
588 * If it doesn't, update the devicetree to the correct panel.
589 */
590 node = fdt_path_offset(blob, "/dsi@fe060000/panel@0");
591 if (!(node > 0)) {
592 printf("Can't find the DSI node\n");
593 return -ENODEV;
594 }
595
Chris Morgan3db220e2023-05-15 11:00:30 -0500596 ret = fdt_node_check_compatible(blob, node, env);
597 if (ret < 0)
598 return -ENODEV;
599
600 /* Panels match, return 0. */
601 if (!ret)
602 return 0;
603
Chris Morgana63ff502024-01-02 09:46:48 -0600604 /* Panels don't match, search by first compatible value. */
Chris Morgan8bff9a62024-09-19 09:00:19 -0500605 for (int i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) {
Chris Morgana63ff502024-01-02 09:46:48 -0600606 if (!strcmp(env, rg353_panel_details[i].panel_compat[0])) {
607 panel = &rg353_panel_details[i];
608 break;
609 }
610 }
611
612 if (!panel) {
613 printf("Unable to identify panel by compat string\n");
614 return -ENODEV;
615 }
616
617 /* Set the compatible with the auto-detected values */
618 fdt_setprop_string(blob, node, "compatible", panel->panel_compat[0]);
619 if (panel->panel_compat[1])
620 fdt_appendprop_string(blob, node, "compatible",
621 panel->panel_compat[1]);
Chris Morgan3db220e2023-05-15 11:00:30 -0500622
Chris Morgana824aa62023-05-15 11:00:29 -0500623 return 0;
624}
Chris Morgan8bff9a62024-09-19 09:00:19 -0500625
Chris Morganedff9672024-09-19 09:00:20 -0500626int rgxx3_regulator_fixup(void *blob)
627{
628 const struct powkiddy_regulators *vdd_cpu = NULL;
629 int node, ret, i;
630 char path[] = "/i2c@fdd40000/regulator@00";
631 char name[] = "regulator@00";
632 char *env;
633
634 env = env_get("vdd_cpu");
635 if (!env) {
636 printf("Can't get vdd_cpu env\n");
637 return -EINVAL;
638 }
639
640 /*
641 * Find the device we have in our tree, which may or may not
642 * be present.
643 */
644 for (i = 0; i < ARRAY_SIZE(regulator_details); i++) {
645 sprintf(path, "/i2c@fdd40000/regulator@%02x",
646 regulator_details[i].addr);
647 node = fdt_path_offset(blob, path);
648 if (node > 0)
649 break;
650
651 printf("Unable to find vdd_cpu\n");
652 return -ENODEV;
653 }
654
655 node = fdt_path_offset(blob, path);
656 if (!(node > 0)) {
657 printf("Can't find the vdd_cpu node\n");
658 return -ENODEV;
659 }
660
661 ret = fdt_node_check_compatible(blob, node, env);
662 if (ret < 0)
663 return -ENODEV;
664
665 /* vdd_cpu regulators match, return 0. */
666 if (!ret)
667 return 0;
668
669 /* Regulators don't match, search by first compatible value. */
670 for (i = 0; i < ARRAY_SIZE(regulator_details); i++) {
671 if (!strcmp(env, regulator_details[i].regulator_compat)) {
672 vdd_cpu = &regulator_details[i];
673 break;
674 }
675 }
676
677 if (!vdd_cpu) {
678 printf("Unable to identify vdd_cpu by compat string\n");
679 return -ENODEV;
680 }
681
682 /* Set the compatible and reg with the auto-detected values */
683 fdt_setprop_string(blob, node, "compatible", vdd_cpu->regulator_compat);
684 fdt_setprop_u32(blob, node, "reg", vdd_cpu->addr);
685 sprintf(name, "regulator@%02x", vdd_cpu->addr);
686 fdt_set_name(blob, node, name);
687
688 return 0;
689}
690
Chris Morgan8bff9a62024-09-19 09:00:19 -0500691int ft_board_setup(void *blob, struct bd_info *bd)
692{
693 int ret;
694
695 if (gd->board_type == RG353M)
696 fdt_setprop(blob, 0, "model",
697 rg3xx_model_details[RG353M].board_name,
698 sizeof(rg3xx_model_details[RG353M].board_name));
699
700 if (rg3xx_model_details[gd->board_type].detect_panel) {
701 ret = rgxx3_panel_fixup(blob);
702 if (ret)
703 printf("Unable to update panel compat\n");
704 }
705
Chris Morganedff9672024-09-19 09:00:20 -0500706 if (rg3xx_model_details[gd->board_type].detect_regulator) {
707 ret = rgxx3_regulator_fixup(blob);
708 if (ret)
709 printf("Unable to update vdd_cpu compat\n");
710 }
711
Chris Morgan8bff9a62024-09-19 09:00:19 -0500712 return 0;
713}
714
715int board_fit_config_name_match(const char *name)
716{
717 int ret;
718
719 if (gd->board_type == 0) {
720 ret = rgxx3_detect_device();
721 if (ret < 0)
722 return ret;
723 gd->board_type = ret;
724 }
725
726 if (strcmp(name, rg3xx_model_details[gd->board_type].fdtfile) == 0)
727 return 0;
728
729 return -ENXIO;
730}