mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-16 09:54:35 +00:00

Some of the Powkiddy devices switched to using a different vendor for the vdd_cpu regulator. Unfortunately the device does not have a new revision to denote this, so users have no way of knowing in advance. Add code to detect if a device is present at addresses 0x1c or 0x40 on the i2c0 bus and update the devicetree if needed. Signed-off-by: Chris Morgan <macromorgan@hotmail.com> Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
730 lines
17 KiB
C
730 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2023 Chris Morgan <macromorgan@hotmail.com>
|
|
*/
|
|
|
|
#include <abuf.h>
|
|
#include <adc.h>
|
|
#include <asm/io.h>
|
|
#include <display.h>
|
|
#include <dm.h>
|
|
#include <dm/lists.h>
|
|
#include <env.h>
|
|
#include <fdt_support.h>
|
|
#include <i2c.h>
|
|
#include <linux/delay.h>
|
|
#include <mipi_dsi.h>
|
|
#include <mmc.h>
|
|
#include <panel.h>
|
|
#include <pwm.h>
|
|
#include <stdlib.h>
|
|
#include <video_bridge.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define GPIO0_BASE 0xfdd60000
|
|
#define GPIO4_BASE 0xfe770000
|
|
#define GPIO_SWPORT_DR_L 0x0000
|
|
#define GPIO_SWPORT_DR_H 0x0004
|
|
#define GPIO_SWPORT_DDR_L 0x0008
|
|
#define GPIO_SWPORT_DDR_H 0x000c
|
|
#define GPIO_A0 BIT(0)
|
|
#define GPIO_C5 BIT(5)
|
|
#define GPIO_C6 BIT(6)
|
|
#define GPIO_C7 BIT(7)
|
|
|
|
#define GPIO_WRITEMASK(bits) ((bits) << 16)
|
|
|
|
#define DTB_DIR "rockchip/"
|
|
|
|
struct rg3xx_model {
|
|
const u16 adc_value;
|
|
const char *board;
|
|
const char *board_name;
|
|
const char *fdtfile;
|
|
const bool detect_panel;
|
|
const bool detect_regulator;
|
|
const bool uart_con;
|
|
};
|
|
|
|
enum rgxx3_device_id {
|
|
RG353M = 1,
|
|
RG353P,
|
|
RG353V,
|
|
RG503,
|
|
RGB30,
|
|
RK2023,
|
|
RGARCD,
|
|
RGB10MAX3,
|
|
/* Devices with duplicate ADC value */
|
|
RG353PS,
|
|
RG353VS,
|
|
RGARCS,
|
|
};
|
|
|
|
static const struct rg3xx_model rg3xx_model_details[] = {
|
|
[RG353M] = {
|
|
.adc_value = 517, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg353m",
|
|
.board_name = "Anbernic RG353M",
|
|
/* Device is identical to RG353P. */
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg353p.dtb",
|
|
.detect_panel = 1,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RG353P] = {
|
|
.adc_value = 860, /* Documented value of 860 */
|
|
.board = "rk3566-anbernic-rg353p",
|
|
.board_name = "Anbernic RG353P",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg353p.dtb",
|
|
.detect_panel = 1,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RG353V] = {
|
|
.adc_value = 695, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg353v",
|
|
.board_name = "Anbernic RG353V",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg353v.dtb",
|
|
.detect_panel = 1,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RG503] = {
|
|
.adc_value = 1023, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg503",
|
|
.board_name = "Anbernic RG503",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg503.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RGB30] = {
|
|
.adc_value = 383, /* Gathered from second hand information */
|
|
.board = "rk3566-powkiddy-rgb30",
|
|
.board_name = "Powkiddy RGB30",
|
|
.fdtfile = DTB_DIR "rk3566-powkiddy-rgb30.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 1,
|
|
.uart_con = 0,
|
|
},
|
|
[RK2023] = {
|
|
.adc_value = 635, /* Observed average from device */
|
|
.board = "rk3566-powkiddy-rk2023",
|
|
.board_name = "Powkiddy RK2023",
|
|
.fdtfile = DTB_DIR "rk3566-powkiddy-rk2023.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 1,
|
|
.uart_con = 0,
|
|
},
|
|
[RGARCD] = {
|
|
.adc_value = 183, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg-arc-d",
|
|
.board_name = "Anbernic RG ARC-D",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg-arc-d.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RGB10MAX3] = {
|
|
.adc_value = 765, /* Observed average from device */
|
|
.board = "rk3566-powkiddy-rgb10max3",
|
|
.board_name = "Powkiddy RGB10MAX3",
|
|
.fdtfile = DTB_DIR "rk3566-powkiddy-rgb10max3.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 1,
|
|
.uart_con = 0,
|
|
},
|
|
/* Devices with duplicate ADC value */
|
|
[RG353PS] = {
|
|
.adc_value = 860, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg353ps",
|
|
.board_name = "Anbernic RG353PS",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg353ps.dtb",
|
|
.detect_panel = 1,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RG353VS] = {
|
|
.adc_value = 695, /* Gathered from second hand information */
|
|
.board = "rk3566-anbernic-rg353vs",
|
|
.board_name = "Anbernic RG353VS",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg353vs.dtb",
|
|
.detect_panel = 1,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
[RGARCS] = {
|
|
.adc_value = 183, /* Observed average from device */
|
|
.board = "rk3566-anbernic-rg-arc-s",
|
|
.board_name = "Anbernic RG ARC-S",
|
|
.fdtfile = DTB_DIR "rk3566-anbernic-rg-arc-s.dtb",
|
|
.detect_panel = 0,
|
|
.detect_regulator = 0,
|
|
.uart_con = 1,
|
|
},
|
|
};
|
|
|
|
struct rg353_panel {
|
|
const u16 id;
|
|
const char *panel_compat[2];
|
|
};
|
|
|
|
static const struct rg353_panel rg353_panel_details[] = {
|
|
{
|
|
.id = 0x3052,
|
|
.panel_compat[0] = "anbernic,rg353p-panel",
|
|
.panel_compat[1] = "newvision,nv3051d",
|
|
},
|
|
{
|
|
.id = 0x3821,
|
|
.panel_compat[0] = "anbernic,rg353v-panel-v2",
|
|
.panel_compat[1] = NULL,
|
|
},
|
|
};
|
|
|
|
struct powkiddy_regulators {
|
|
const u8 addr;
|
|
const char *regulator_compat;
|
|
};
|
|
|
|
static const struct powkiddy_regulators regulator_details[] = {
|
|
{
|
|
.addr = 0x1c,
|
|
.regulator_compat = "tcs,tcs4525",
|
|
},
|
|
{
|
|
.addr = 0x40,
|
|
.regulator_compat = "fcs,fan53555",
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Start LED very early so user knows device is on. Set color
|
|
* to red.
|
|
*/
|
|
void spl_board_init(void)
|
|
{
|
|
/* Set GPIO0_C5, GPIO0_C6, and GPIO0_C7 to output. */
|
|
writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) |
|
|
(GPIO_C7 | GPIO_C6 | GPIO_C5),
|
|
(GPIO0_BASE + GPIO_SWPORT_DDR_H));
|
|
/* Set GPIO0_C5 and GPIO_C6 to 0 and GPIO0_C7 to 1. */
|
|
writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C7,
|
|
(GPIO0_BASE + GPIO_SWPORT_DR_H));
|
|
}
|
|
|
|
/*
|
|
* Buzz the buzzer so the user knows something is going on. Make it
|
|
* optional in case PWM is disabled or if CONFIG_DM_PWM is not
|
|
* enabled.
|
|
*/
|
|
void __maybe_unused startup_buzz(void)
|
|
{
|
|
struct udevice *dev;
|
|
int err;
|
|
|
|
if (!IS_ENABLED(CONFIG_DM_PWM))
|
|
return;
|
|
|
|
/* Probe the PWM controller. */
|
|
err = uclass_get_device_by_name(UCLASS_PWM,
|
|
"pwm@fe6e0010", &dev);
|
|
if (err)
|
|
return;
|
|
|
|
pwm_set_enable(dev, 0, 1);
|
|
mdelay(200);
|
|
pwm_set_enable(dev, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Provide the bare minimum to identify the panel for the RG353
|
|
* series. Since we don't have a working framebuffer device, no
|
|
* need to init the panel; just identify it and provide the
|
|
* clocks so we know what to set the different clock values to.
|
|
*/
|
|
|
|
static const struct display_timing rg353_default_timing = {
|
|
.pixelclock.typ = 24150000,
|
|
.hactive.typ = 640,
|
|
.hfront_porch.typ = 40,
|
|
.hback_porch.typ = 80,
|
|
.hsync_len.typ = 2,
|
|
.vactive.typ = 480,
|
|
.vfront_porch.typ = 18,
|
|
.vback_porch.typ = 28,
|
|
.vsync_len.typ = 2,
|
|
.flags = DISPLAY_FLAGS_HSYNC_HIGH |
|
|
DISPLAY_FLAGS_VSYNC_HIGH,
|
|
};
|
|
|
|
static int anbernic_rg353_panel_get_timing(struct udevice *dev,
|
|
struct display_timing *timings)
|
|
{
|
|
memcpy(timings, &rg353_default_timing, sizeof(*timings));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int anbernic_rg353_panel_probe(struct udevice *dev)
|
|
{
|
|
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
|
|
|
|
plat->lanes = 4;
|
|
plat->format = MIPI_DSI_FMT_RGB888;
|
|
plat->mode_flags = MIPI_DSI_MODE_VIDEO |
|
|
MIPI_DSI_MODE_VIDEO_BURST |
|
|
MIPI_DSI_MODE_EOT_PACKET |
|
|
MIPI_DSI_MODE_LPM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct panel_ops anbernic_rg353_panel_ops = {
|
|
.get_display_timing = anbernic_rg353_panel_get_timing,
|
|
};
|
|
|
|
U_BOOT_DRIVER(anbernic_rg353_panel) = {
|
|
.name = "anbernic_rg353_panel",
|
|
.id = UCLASS_PANEL,
|
|
.ops = &anbernic_rg353_panel_ops,
|
|
.probe = anbernic_rg353_panel_probe,
|
|
.plat_auto = sizeof(struct mipi_dsi_panel_plat),
|
|
};
|
|
|
|
/*
|
|
* The Anbernic 353 series shipped with 2 distinct displays requiring
|
|
* 2 distinct drivers, with no way for a user to know which panel is
|
|
* which. This function queries the DSI panel for the panel ID to
|
|
* determine which panel is present so the device-tree can be corrected
|
|
* automatically.
|
|
*/
|
|
int rgxx3_detect_display(void)
|
|
{
|
|
struct udevice *dev;
|
|
struct mipi_dsi_device *dsi;
|
|
struct mipi_dsi_panel_plat *mplat;
|
|
const struct rg353_panel *panel;
|
|
int ret = 0;
|
|
int i;
|
|
u8 panel_id[2];
|
|
|
|
/*
|
|
* Take panel out of reset status.
|
|
* Set GPIO4_A0 to output.
|
|
*/
|
|
writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
|
|
(GPIO4_BASE + GPIO_SWPORT_DDR_L));
|
|
/* Set GPIO4_A0 to 1. */
|
|
writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
|
|
(GPIO4_BASE + GPIO_SWPORT_DR_L));
|
|
|
|
/* Probe the DSI controller. */
|
|
ret = uclass_get_device_by_name(UCLASS_VIDEO_BRIDGE,
|
|
"dsi@fe060000", &dev);
|
|
if (ret) {
|
|
printf("DSI host not probed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Probe the DSI panel. */
|
|
ret = device_bind_driver_to_node(dev, "anbernic_rg353_panel",
|
|
"anbernic_rg353_panel",
|
|
dev_ofnode(dev), NULL);
|
|
if (ret) {
|
|
printf("Failed to probe RG353 panel: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Attach the DSI controller which will also probe and attach
|
|
* the DSIDPHY.
|
|
*/
|
|
ret = video_bridge_attach(dev);
|
|
if (ret) {
|
|
printf("Failed to attach DSI controller: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Get the panel which should have already been probed by the
|
|
* video_bridge_attach() function.
|
|
*/
|
|
ret = uclass_first_device_err(UCLASS_PANEL, &dev);
|
|
if (ret) {
|
|
printf("Panel device error: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Now call the panel via DSI commands to get the panel ID. */
|
|
mplat = dev_get_plat(dev);
|
|
dsi = mplat->device;
|
|
mipi_dsi_set_maximum_return_packet_size(dsi, sizeof(panel_id));
|
|
ret = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_ID, &panel_id,
|
|
sizeof(panel_id));
|
|
if (ret < 0) {
|
|
printf("Unable to read panel ID: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Get the correct panel compatible from the table. */
|
|
for (i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) {
|
|
if (rg353_panel_details[i].id == ((panel_id[0] << 8) |
|
|
panel_id[1])) {
|
|
panel = &rg353_panel_details[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!panel) {
|
|
printf("Unable to identify panel_id %x\n",
|
|
(panel_id[0] << 8) | panel_id[1]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
env_set("panel", panel->panel_compat[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Some of the Powkiddy devices switched the CPU regulator, but users
|
|
* are not able to determine this by looking at their hardware.
|
|
* Attempt to auto-detect this situation and fixup the device-tree.
|
|
*/
|
|
int rgxx3_detect_regulator(void)
|
|
{
|
|
struct udevice *bus;
|
|
struct udevice *chip;
|
|
u8 val;
|
|
int ret;
|
|
|
|
/* Get the correct i2c bus (i2c0). */
|
|
ret = uclass_get_device_by_name(UCLASS_I2C,
|
|
"i2c@fdd40000", &bus);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Check for all vdd_cpu regulators and read an arbitrary
|
|
* register to confirm it's present.
|
|
*/
|
|
for (int i = 0; i < ARRAY_SIZE(regulator_details); i++) {
|
|
ret = i2c_get_chip(bus, regulator_details[i].addr,
|
|
1, &chip);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = dm_i2c_read(chip, 0, &val, 1);
|
|
if (!ret) {
|
|
env_set("vdd_cpu", regulator_details[i].regulator_compat);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rgxx3_read_board_id(void)
|
|
{
|
|
u32 adc_info;
|
|
int ret;
|
|
|
|
ret = adc_channel_single_shot("saradc@fe720000", 1, &adc_info);
|
|
if (ret) {
|
|
printf("Read SARADC failed with error %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Get the correct device from the table. The ADC value is
|
|
* determined by a resistor on ADC channel 0. The hardware
|
|
* design calls for no more than a 1% variance on the
|
|
* resistor, so assume a +- value of 15 should be enough.
|
|
*/
|
|
for (int i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) {
|
|
u32 adc_min = rg3xx_model_details[i].adc_value - 15;
|
|
u32 adc_max = rg3xx_model_details[i].adc_value + 15;
|
|
|
|
if (adc_min < adc_info && adc_max > adc_info)
|
|
return i;
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Detect which Anbernic RGXX3 device we are using so as to load the
|
|
* correct devicetree for Linux. Set an environment variable once
|
|
* found. The detection depends on the value of ADC channel 1 and the
|
|
* presence of an eMMC on mmc0.
|
|
*/
|
|
int rgxx3_detect_device(void)
|
|
{
|
|
int ret;
|
|
int board_id;
|
|
struct mmc *mmc;
|
|
|
|
board_id = rgxx3_read_board_id();
|
|
if (board_id < 0)
|
|
return board_id;
|
|
|
|
/*
|
|
* Try to access the eMMC on an RG353V, RG353P, or RG Arc D.
|
|
* If it's missing, it's an RG353VS, RG353PS, or RG Arc S.
|
|
* Note we could also check for a touchscreen at 0x1a on i2c2.
|
|
*/
|
|
if (board_id == RG353V || board_id == RG353P || board_id == RGARCD) {
|
|
mmc = find_mmc_device(0);
|
|
if (mmc) {
|
|
ret = mmc_init(mmc);
|
|
if (ret) {
|
|
if (board_id == RG353V)
|
|
board_id = RG353VS;
|
|
else if (board_id == RG353P)
|
|
board_id = RG353PS;
|
|
else
|
|
board_id = RGARCS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return board_id;
|
|
}
|
|
|
|
/*
|
|
* Check the loaded device tree to set the correct gd->board_type.
|
|
* Disable the console if the board doesn't support a console.
|
|
*/
|
|
int set_gd_value(void)
|
|
{
|
|
const char *model;
|
|
|
|
model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) {
|
|
if (strcmp(rg3xx_model_details[i].board_name, model) == 0) {
|
|
gd->board_type = i;
|
|
if (!rg3xx_model_details[i].uart_con)
|
|
gd->flags |= GD_FLG_SILENT |
|
|
GD_FLG_DISABLE_CONSOLE;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
int rk_board_late_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = set_gd_value();
|
|
if (ret) {
|
|
printf("Unable to auto-detect device\n");
|
|
goto end;
|
|
}
|
|
|
|
/*
|
|
* Change the model number on the RG353M since it uses the same
|
|
* tree as the RG353P.
|
|
*/
|
|
if (gd->board_type == RG353P) {
|
|
ret = rgxx3_read_board_id();
|
|
if (ret > 0)
|
|
gd->board_type = ret;
|
|
}
|
|
|
|
env_set("board", rg3xx_model_details[gd->board_type].board);
|
|
env_set("board_name",
|
|
rg3xx_model_details[gd->board_type].board_name);
|
|
env_set("fdtfile", rg3xx_model_details[gd->board_type].fdtfile);
|
|
|
|
/*
|
|
* Skip panel detection if not needed. Warn but don't fail for
|
|
* errors in auto-detection of the panel.
|
|
*/
|
|
if (rg3xx_model_details[gd->board_type].detect_panel) {
|
|
ret = rgxx3_detect_display();
|
|
if (ret)
|
|
printf("Failed to detect panel type\n");
|
|
}
|
|
|
|
/*
|
|
* Skip vdd_cpu regulator detection if not needed. Warn but
|
|
* don't fail for errors in auto-detection of regulator.
|
|
*/
|
|
if (rg3xx_model_details[gd->board_type].detect_regulator) {
|
|
ret = rgxx3_detect_regulator();
|
|
if (ret)
|
|
printf("Unable to detect vdd_cpu regulator\n");
|
|
}
|
|
|
|
end:
|
|
/* Turn off red LED and turn on orange LED. */
|
|
writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C6,
|
|
(GPIO0_BASE + GPIO_SWPORT_DR_H));
|
|
|
|
startup_buzz();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rgxx3_panel_fixup(void *blob)
|
|
{
|
|
const struct rg353_panel *panel = NULL;
|
|
int node, ret;
|
|
char *env;
|
|
|
|
env = env_get("panel");
|
|
if (!env) {
|
|
printf("Can't get panel env\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Check if the environment variable doesn't equal the panel.
|
|
* If it doesn't, update the devicetree to the correct panel.
|
|
*/
|
|
node = fdt_path_offset(blob, "/dsi@fe060000/panel@0");
|
|
if (!(node > 0)) {
|
|
printf("Can't find the DSI node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = fdt_node_check_compatible(blob, node, env);
|
|
if (ret < 0)
|
|
return -ENODEV;
|
|
|
|
/* Panels match, return 0. */
|
|
if (!ret)
|
|
return 0;
|
|
|
|
/* Panels don't match, search by first compatible value. */
|
|
for (int i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) {
|
|
if (!strcmp(env, rg353_panel_details[i].panel_compat[0])) {
|
|
panel = &rg353_panel_details[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!panel) {
|
|
printf("Unable to identify panel by compat string\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Set the compatible with the auto-detected values */
|
|
fdt_setprop_string(blob, node, "compatible", panel->panel_compat[0]);
|
|
if (panel->panel_compat[1])
|
|
fdt_appendprop_string(blob, node, "compatible",
|
|
panel->panel_compat[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rgxx3_regulator_fixup(void *blob)
|
|
{
|
|
const struct powkiddy_regulators *vdd_cpu = NULL;
|
|
int node, ret, i;
|
|
char path[] = "/i2c@fdd40000/regulator@00";
|
|
char name[] = "regulator@00";
|
|
char *env;
|
|
|
|
env = env_get("vdd_cpu");
|
|
if (!env) {
|
|
printf("Can't get vdd_cpu env\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Find the device we have in our tree, which may or may not
|
|
* be present.
|
|
*/
|
|
for (i = 0; i < ARRAY_SIZE(regulator_details); i++) {
|
|
sprintf(path, "/i2c@fdd40000/regulator@%02x",
|
|
regulator_details[i].addr);
|
|
node = fdt_path_offset(blob, path);
|
|
if (node > 0)
|
|
break;
|
|
|
|
printf("Unable to find vdd_cpu\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
node = fdt_path_offset(blob, path);
|
|
if (!(node > 0)) {
|
|
printf("Can't find the vdd_cpu node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = fdt_node_check_compatible(blob, node, env);
|
|
if (ret < 0)
|
|
return -ENODEV;
|
|
|
|
/* vdd_cpu regulators match, return 0. */
|
|
if (!ret)
|
|
return 0;
|
|
|
|
/* Regulators don't match, search by first compatible value. */
|
|
for (i = 0; i < ARRAY_SIZE(regulator_details); i++) {
|
|
if (!strcmp(env, regulator_details[i].regulator_compat)) {
|
|
vdd_cpu = ®ulator_details[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!vdd_cpu) {
|
|
printf("Unable to identify vdd_cpu by compat string\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Set the compatible and reg with the auto-detected values */
|
|
fdt_setprop_string(blob, node, "compatible", vdd_cpu->regulator_compat);
|
|
fdt_setprop_u32(blob, node, "reg", vdd_cpu->addr);
|
|
sprintf(name, "regulator@%02x", vdd_cpu->addr);
|
|
fdt_set_name(blob, node, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ft_board_setup(void *blob, struct bd_info *bd)
|
|
{
|
|
int ret;
|
|
|
|
if (gd->board_type == RG353M)
|
|
fdt_setprop(blob, 0, "model",
|
|
rg3xx_model_details[RG353M].board_name,
|
|
sizeof(rg3xx_model_details[RG353M].board_name));
|
|
|
|
if (rg3xx_model_details[gd->board_type].detect_panel) {
|
|
ret = rgxx3_panel_fixup(blob);
|
|
if (ret)
|
|
printf("Unable to update panel compat\n");
|
|
}
|
|
|
|
if (rg3xx_model_details[gd->board_type].detect_regulator) {
|
|
ret = rgxx3_regulator_fixup(blob);
|
|
if (ret)
|
|
printf("Unable to update vdd_cpu compat\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int board_fit_config_name_match(const char *name)
|
|
{
|
|
int ret;
|
|
|
|
if (gd->board_type == 0) {
|
|
ret = rgxx3_detect_device();
|
|
if (ret < 0)
|
|
return ret;
|
|
gd->board_type = ret;
|
|
}
|
|
|
|
if (strcmp(name, rg3xx_model_details[gd->board_type].fdtfile) == 0)
|
|
return 0;
|
|
|
|
return -ENXIO;
|
|
}
|