board: purism: add the Purism Librem5 phone

Initial commit of Librem5 u-boot and SPL

Signed-off-by: Angus Ainslie <angus@akkea.ca>
Co-developed-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Reviewed-by: Fabio Estevam <festevam@denx.de>
This commit is contained in:
Angus Ainslie 2022-08-25 06:46:02 -07:00 committed by Stefano Babic
parent 1f650d9ec6
commit 466a9ea2a1
19 changed files with 5400 additions and 1 deletions

View file

@ -974,7 +974,8 @@ dtb-$(CONFIG_ARCH_IMX8M) += \
imx8mp-venice-gw74xx.dtb \
imx8mp-verdin-wifi-dev.dtb \
imx8mq-pico-pi.dtb \
imx8mq-kontron-pitx-imx8m.dtb
imx8mq-kontron-pitx-imx8m.dtb \
imx8mq-librem5-r4.dtb
dtb-$(CONFIG_ARCH_IMX9) += \
imx93-11x11-evk.dtb

View file

@ -0,0 +1,24 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
#include "imx8mq-u-boot.dtsi"
&pinctrl_uart1 {
u-boot,dm-spl;
};
&uart1 { /* console */
u-boot,dm-spl;
};
&binman {
/delete-node/ signed-hdmi;
signed-hdmi {
filename = "signed_hdmi.bin";
signed-dp-imx8m {
filename = "signed_dp_imx8m.bin";
type = "blob-ext";
};
};
};

View file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
// Copyright (C) 2020 Purism SPC <kernel@puri.sm>
/dts-v1/;
#include "imx8mq-librem5.dtsi"
/ {
model = "Purism Librem 5r4";
compatible = "purism,librem5r4", "purism,librem5", "fsl,imx8mq";
};
&accel_gyro {
mount-matrix = "1", "0", "0",
"0", "1", "0",
"0", "0", "-1";
};
&bat {
maxim,rsns-microohm = <1667>;
};
&bq25895 {
ti,battery-regulation-voltage = <4200000>; /* uV */
ti,charge-current = <1500000>; /* uA */
ti,termination-current = <144000>; /* uA */
};
&led_backlight {
led-max-microamp = <25000>;
};
&proximity {
proximity-near-level = <10>;
};

File diff suppressed because it is too large Load diff

View file

@ -269,6 +269,14 @@ config TARGET_IMX8MP_RSB3720A1_6G
select IMX8MP
select SUPPORT_SPL
select IMX8M_LPDDR4
config TARGET_LIBREM5
bool "Purism Librem5 Phone"
select BINMAN
select IMX8MQ
select SUPPORT_SPL
select IMX8M_LPDDR4
endchoice
source "board/advantech/imx8mp_rsb3720a1/Kconfig"
@ -290,6 +298,7 @@ source "board/kontron/sl-mx8mm/Kconfig"
source "board/menlo/mx8menlo/Kconfig"
source "board/phytec/phycore_imx8mm/Kconfig"
source "board/phytec/phycore_imx8mp/Kconfig"
source "board/purism/librem5/Kconfig"
source "board/ronetix/imx8mq-cm/Kconfig"
source "board/technexion/pico-imx8mq/Kconfig"
source "board/variscite/imx8mn_var_som/Kconfig"

View file

@ -0,0 +1,15 @@
if TARGET_LIBREM5
config SYS_BOARD
default "librem5"
config SYS_VENDOR
default "purism"
config SYS_CONFIG_NAME
default "librem5"
config IMX_CONFIG
default "board/purism/librem5/imximage-8mq-lpddr4.cfg"
endif

View file

@ -0,0 +1,8 @@
PURISM LIBREM5 PHONE
M: Angus Ainslie <angus@akkea.ca>
R: kernel@puri.sm
S: Supported
F: arch/arm/dts/imx8mq-librem5*
F: board/purism/librem5/
F: configs/librem5_defconfig
F: include/configs/librem5.h

View file

@ -0,0 +1,13 @@
#
# Copyright 2017 NXP
# Copyright 2019 Purism
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += librem5.o
ifdef CONFIG_SPL_BUILD
obj-y += spl.o
obj-$(CONFIG_IMX8M_LPDDR4) += lpddr4_timing.o lpddr4_timing_b0.o
endif

View file

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2021 NXP
*/
FIT
BOOT_FROM sd
SIGNED_HDMI signed_hdmi.bin
LOADER u-boot-spl-ddr.bin 0x7E1000

View file

@ -0,0 +1,425 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
* Copyright 2021 Purism
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <asm/io.h>
#include <miiphy.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm-generic/gpio.h>
#include <asm/arch/sys_proto.h>
#include <fsl_esdhc.h>
#include <mmc.h>
#include <asm/arch/imx8mq_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/gpio.h>
#include <asm/mach-imx/mxc_i2c.h>
#include <asm/arch/clock.h>
#include <asm/mach-imx/video.h>
#include <fuse.h>
#include <i2c.h>
#include <spl.h>
#include <usb.h>
#include <dwc3-uboot.h>
#include <linux/delay.h>
#include <linux/bitfield.h>
#include <power/regulator.h>
#include <usb/xhci.h>
#include "librem5.h"
DECLARE_GLOBAL_DATA_PTR;
int board_early_init_f(void)
{
return 0;
}
#if IS_ENABLED(CONFIG_LOAD_ENV_FROM_MMC_BOOT_PARTITION)
uint board_mmc_get_env_part(struct mmc *mmc)
{
uint part = (mmc->part_config >> 3) & PART_ACCESS_MASK;
if (part == 7)
part = 0;
return part;
}
#endif
int tps65982_wait_for_app(int timeout, int timeout_step)
{
int ret;
char response[6];
struct udevice *udev, *bus;
log_debug("%s: starting\n", __func__);
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 0, &bus);
if (ret) {
log_err("%s: No bus %d\n", __func__, 0);
return 1;
}
ret = i2c_get_chip(bus, 0x3f, 1, &udev);
if (ret) {
log_err("%s: setting chip offset failed %d\n", __func__, ret);
return 1;
}
while (timeout > 0) {
ret = dm_i2c_read(udev, 0x03, (u8 *)response, 5);
log_debug("tps65982 mode %s\n", response);
if (response[1] == 'A')
return 0;
mdelay(timeout_step);
timeout -= timeout_step;
log_debug("tps65982 waited %d ms %c\n", timeout_step, response[1]);
}
return 1;
}
int tps65982_clear_dead_battery(void)
{
int ret;
char cmd[5] = "\04DBfg";
struct udevice *udev, *bus;
log_debug("%s: starting\n", __func__);
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 0, &bus);
if (ret) {
log_err("%s: No bus %d\n", __func__, 0);
return 1;
}
ret = i2c_get_chip(bus, 0x3f, 1, &udev);
if (ret) {
log_err("%s: setting chip offset failed %d\n", __func__, ret);
return 1;
}
/* clearing the dead battery flag when not in dead battery condition
* is a no-op, so there's no need to check if it's in effect
*/
ret = dm_i2c_write(udev, 0x08, cmd, 5);
if (ret) {
log_err("%s: writing 4CC command failed %d", __func__, ret);
return 1;
}
return 0;
}
#define TPS_POWER_STATUS_PWROPMODE(x) FIELD_GET(GENMASK(3, 2), x)
#define TPS_PDO_CONTRACT_TYPE(x) FIELD_GET(GENMASK(31, 30), x)
#define TPS_PDO_CONTRACT_FIXED 0
#define TPS_PDO_CONTRACT_BATTERY 1
#define TPS_PDO_CONTRACT_VARIABLE 2
#define TPS_TYPEC_PWR_MODE_USB 0
#define TPS_TYPEC_PWR_MODE_1_5A 1
#define TPS_TYPEC_PWR_MODE_3_0A 2
#define TPS_TYPEC_PWR_MODE_PD 3
#define TPS_PDO_FIXED_CONTRACT_MAX_CURRENT(x) (FIELD_GET(GENMASK(9, 0), x) * 10)
#define TPS_PDO_VAR_CONTRACT_MAX_CURRENT(x) (FIELD_GET(GENMASK(9, 0), x) * 10)
#define TPS_PDO_BAT_CONTRACT_MAX_VOLTAGE(x) (FIELD_GET(GENMASK(29, 20), x) * 50)
#define TPS_PDO_BAT_CONTRACT_MAX_POWER(x) (FIELD_GET(GENMASK(9, 0), x) * 250)
int tps65982_get_max_current(void)
{
int ret;
u8 buf[7];
u8 pwr_status;
u32 contract;
int type, mode;
struct udevice *udev, *bus;
log_debug("%s: starting\n", __func__);
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 0, &bus);
if (ret) {
log_debug("%s: No bus %d\n", __func__, 0);
return -1;
}
ret = i2c_get_chip(bus, 0x3f, 1, &udev);
if (ret) {
log_debug("%s: setting chip offset failed %d\n", __func__, ret);
return -1;
}
ret = dm_i2c_read(udev, 0x3f, buf, 3);
if (ret) {
log_debug("%s: reading pwr_status failed %d\n", __func__, ret);
return -1;
}
pwr_status = buf[1];
if (!(pwr_status & 1))
return 0;
mode = TPS_POWER_STATUS_PWROPMODE(pwr_status);
switch (mode) {
case TPS_TYPEC_PWR_MODE_1_5A:
return 1500;
case TPS_TYPEC_PWR_MODE_3_0A:
return 3000;
case TPS_TYPEC_PWR_MODE_PD:
ret = dm_i2c_read(udev, 0x34, buf, 7);
if (ret) {
log_debug("%s: reading active contract failed %d\n", __func__, ret);
return -1;
}
contract = buf[1] + (buf[2] << 8) + (buf[3] << 16) + (buf[4] << 24);
type = TPS_PDO_CONTRACT_TYPE(contract);
switch (type) {
case TPS_PDO_CONTRACT_FIXED:
return TPS_PDO_FIXED_CONTRACT_MAX_CURRENT(contract);
case TPS_PDO_CONTRACT_BATTERY:
return 1000 * TPS_PDO_BAT_CONTRACT_MAX_POWER(contract)
/ TPS_PDO_BAT_CONTRACT_MAX_VOLTAGE(contract);
case TPS_PDO_CONTRACT_VARIABLE:
return TPS_PDO_VAR_CONTRACT_MAX_CURRENT(contract);
default:
log_debug("Unknown contract type: %d\n", type);
return -1;
}
case TPS_TYPEC_PWR_MODE_USB:
return 500;
default:
log_debug("Unknown power mode: %d\n", mode);
return -1;
}
}
int init_tps65982(void)
{
log_debug("%s: starting\n", __func__);
if (tps65982_wait_for_app(500, 100)) {
log_err("tps65982 APP boot failed\n");
return 1;
}
log_info("tps65982 boot successful\n");
return 0;
}
int bq25895_set_iinlim(int current)
{
u8 val, iinlim;
int ret;
struct udevice *udev, *bus;
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 3, &bus);
if (ret) {
log_err("%s: No bus 3\n", __func__);
return ret;
}
ret = i2c_get_chip(bus, 0x6a, 1, &udev);
if (ret) {
log_err("%s: setting chip offset failed %d\n", __func__, ret);
return ret;
}
if (current > 3250)
current = 3250;
if (current < 100)
current = 100;
val = dm_i2c_reg_read(udev, 0x00);
iinlim = ((current - 100) / 50) & 0x3f;
val = (val & 0xc0) | iinlim;
dm_i2c_reg_write(udev, 0x00, val);
log_debug("REG00 0x%x\n", val);
return 0;
}
bool bq25895_battery_present(void)
{
u8 val;
int ret;
struct udevice *udev, *bus;
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 3, &bus);
if (ret) {
log_err("%s: No bus 3\n", __func__);
return ret;
}
ret = i2c_get_chip(bus, 0x6a, 1, &udev);
if (ret) {
log_err("%s: setting chip offset failed %d\n", __func__, ret);
return ret;
}
/* note that this may return false negatives when there's
* no external power applied and the battery voltage is below
* Vsys. this isn't a problem when used for clearing the dead
* battery flag though, since it's certain that there's an external
* power applied in this case
*/
val = dm_i2c_reg_read(udev, 0x0e) & 0x7f;
if (val == 0x00 || val == 0x7f)
return false;
return true;
}
/*
* set some safe defaults for the battery charger
*/
int init_charger_bq25895(void)
{
u8 val;
int iinlim, ret;
struct udevice *udev, *bus;
/* Set the i2c bus */
ret = uclass_get_device_by_seq(UCLASS_I2C, 3, &bus);
if (ret) {
log_debug("%s: No bus 3\n", __func__);
return ret;
}
ret = i2c_get_chip(bus, 0x6a, 1, &udev);
if (ret) {
log_debug("%s: setting chip offset failed %d\n", __func__, ret);
return ret;
}
val = dm_i2c_reg_read(udev, 0x0b);
log_debug("REG0B 0x%x\n", val);
log_debug("VBUS_STAT 0x%x\n", val >> 5);
switch (val >> 5) {
case 0:
log_debug("VBUS not detected\n");
break;
case 1:
log_debug("USB SDP IINLIM 500mA\n");
break;
case 2:
log_debug("USB CDP IINLIM 1500mA\n");
break;
case 3:
log_debug("USB DCP IINLIM 3500mA\n");
break;
case 4:
log_debug("MAXCHARGE IINLIM 1500mA\n");
break;
case 5:
log_debug("Unknown IINLIM 500mA\n");
break;
case 6:
log_debug("DIVIDER IINLIM > 1000mA\n");
break;
case 7:
log_debug("OTG\n");
break;
};
log_debug("CHRG_STAT 0x%x\n", (val >> 3) & 0x3);
log_debug("PG_STAT 0x%x\n", (val >> 2) & 1);
log_debug("SDP_STAT 0x%x\n", (val >> 1) & 1);
log_debug("VSYS_STAT 0x%x\n", val & 1);
val = dm_i2c_reg_read(udev, 0x00);
log_debug("REG00 0x%x\n", val);
iinlim = 100 + (val & 0x3f) * 50;
log_debug("IINLIM %d mA\n", iinlim);
log_debug("EN_HIZ 0x%x\n", (val >> 7) & 1);
log_debug("EN_ILIM 0x%x\n", (val >> 6) & 1);
/* set 1.6A charge limit */
dm_i2c_reg_write(udev, 0x04, 0x19);
/* re-enable charger */
val = dm_i2c_reg_read(udev, 0x03);
val = val | 0x10;
dm_i2c_reg_write(udev, 0x03, val);
return 0;
}
int board_init(void)
{
struct udevice *dev;
int tps_ret;
if (IS_ENABLED(CONFIG_USB_DWC3) || IS_ENABLED(CONFIG_USB_XHCI_IMX8M)) {
log_debug("%s: initializing USB clk\n", __func__);
/* init_usb_clk won't enable the second clock if it's a USB boot */
if (is_usb_boot()) {
clock_enable(CCGR_USB_CTRL2, 1);
clock_enable(CCGR_USB_PHY2, 1);
}
printf("Enabling regulator-hub\n");
if (!regulator_get_by_devname("regulator-hub", &dev)) {
if (regulator_set_enable(dev, true))
pr_err("Failed to enable regulator-hub\n");
}
}
tps_ret = init_tps65982();
init_charger_bq25895();
if (!tps_ret) {
int current = tps65982_get_max_current();
if (current > 500)
bq25895_set_iinlim(current);
if (bq25895_battery_present())
tps65982_clear_dead_battery();
}
return 0;
}
int board_late_init(void)
{
if (IS_ENABLED(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG)) {
u32 rev;
char rev_str[3];
env_set("board_name", "librem5");
if (fuse_read(9, 0, &rev)) {
env_set("board_rev", BOARD_REV_ERROR);
} else if (rev == 0) {
env_set("board_rev", BOARD_REV_UNKNOWN);
} else if (rev > 0) {
sprintf(rev_str, "%u", rev);
env_set("board_rev", rev_str);
}
printf("Board name: %s\n", env_get("board_name"));
printf("Board rev: %s\n", env_get("board_rev"));
}
if (is_usb_boot()) {
puts("USB Boot\n");
env_set("bootcmd", "fastboot 0");
}
return 0;
}

View file

@ -0,0 +1,181 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2021 Purism
*/
#ifndef __LIBREM5_H__
#define __LIBREM5_H__
#define CAMERA_EN IMX_GPIO_NR(1, 0)
#define SD_EN IMX_GPIO_NR(1, 3)
#define AUDIO_EN IMX_GPIO_NR(1, 4)
#define DSI_EN IMX_GPIO_NR(1, 5)
#define SMC_EN IMX_GPIO_NR(1, 6)
#define TYPEC_MUX_EN IMX_GPIO_NR(1, 11)
#define HUB_NRESET IMX_GPIO_NR(1, 12)
#define HUB_EN IMX_GPIO_NR(1, 14)
#define VOL_UP IMX_GPIO_NR(1, 16)
#define VOL_DOWN IMX_GPIO_NR(1, 17)
#define DSI_BIAS_EN IMX_GPIO_NR(1, 20)
#define FLASH_EN IMX_GPIO_NR(1, 23)
#define WWAN_NRESET IMX_GPIO_NR(3, 1)
#define CHG_EN IMX_GPIO_NR(3, 2)
#define CHG_OTG_OUT_EN IMX_GPIO_NR(3, 4)
#define WIFI_EN IMX_GPIO_NR(3, 10)
#define GPS_EN IMX_GPIO_NR(3, 12)
#define BL_EN IMX_GPIO_NR(3, 14)
#define WWAN_EN IMX_GPIO_NR(3, 18)
#define NFC_EN IMX_GPIO_NR(4, 28)
#define LED_G IMX_GPIO_NR(5, 2)
#define LED_R IMX_GPIO_NR(5, 3)
#define LED_B IMX_GPIO_NR(1, 13)
#define MOTO IMX_GPIO_NR(5, 5)
#define SPI1_SCLK IMX_GPIO_NR(5, 6)
#define SPI1_MOSI IMX_GPIO_NR(5, 7)
#define SPI1_MISO IMX_GPIO_NR(5, 8)
#define SPI1_SS0 IMX_GPIO_NR(5, 9)
#define UART1_TX IMX_GPIO_NR(5, 23)
#define UART1_RX IMX_GPIO_NR(5, 22)
#define UART2_TX IMX_GPIO_NR(5, 25)
#define UART2_RX IMX_GPIO_NR(5, 24)
#define UART3_TX IMX_GPIO_NR(5, 27)
#define UART3_RX IMX_GPIO_NR(5, 26)
#define UART4_TX IMX_GPIO_NR(5, 11)
#define UART4_RX IMX_GPIO_NR(5, 10)
#define TPS_RESET IMX_GPIO_NR(3, 24)
#define PURISM_VID 0x316d
#define PURISM_PID 0x4c05
#define BOARD_REV_ERROR "unknown"
#define BOARD_REV_BIRCH "1"
#define BOARD_REV_CHESTNUT "2"
#define BOARD_REV_DOGWOOD "3"
#define BOARD_REV_EVERGREEN "4"
/* Could be ASPEN, BIRCH or CHESTNUT. assume CHESTNUT */
#define BOARD_REV_UNKNOWN BOARD_REV_CHESTNUT
#ifdef CONFIG_SPL_BUILD
static const iomux_v3_cfg_t configure_pads[] = {
IMX8MQ_PAD_GPIO1_IO00__GPIO1_IO0 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_GPIO1_IO03__GPIO1_IO3 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_GPIO1_IO04__GPIO1_IO4 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_GPIO1_IO05__GPIO1_IO5 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_GPIO1_IO06__GPIO1_IO6 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_GPIO1_IO11__GPIO1_IO11 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_GPIO1_IO12__GPIO1_IO12 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_GPIO1_IO13__GPIO1_IO13 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_GPIO1_IO14__GPIO1_IO14 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_ENET_MDC__GPIO1_IO16 | MUX_PAD_CTRL(PAD_CTL_PUE),
IMX8MQ_PAD_ENET_MDIO__GPIO1_IO17 | MUX_PAD_CTRL(PAD_CTL_PUE),
IMX8MQ_PAD_ENET_TD1__GPIO1_IO20 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_ENET_TXC__GPIO1_IO23 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_CE0_B__GPIO3_IO1 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_CE1_B__GPIO3_IO2 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_CE3_B__GPIO3_IO4 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_DATA04__GPIO3_IO10 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_DATA06__GPIO3_IO12 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_DQS__GPIO3_IO14 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_NAND_WP_B__GPIO3_IO18 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_SAI3_RXFS__GPIO4_IO28 | MUX_PAD_CTRL(PAD_CTL_DSE6),
IMX8MQ_PAD_SAI3_MCLK__GPIO5_IO2 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_SPDIF_TX__GPIO5_IO3 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
IMX8MQ_PAD_SAI5_RXD3__GPIO3_IO24 | MUX_PAD_CTRL(PAD_CTL_DSE6) | MUX_MODE_SION,
};
static inline void init_pinmux(void)
{
imx_iomux_v3_setup_multiple_pads(configure_pads, ARRAY_SIZE(configure_pads));
gpio_request(LED_R, "LED_R");
gpio_request(LED_G, "LED_G");
gpio_request(LED_B, "LED_B");
gpio_request(VOL_UP, "VOL_UP");
gpio_request(VOL_DOWN, "VOL_DOWN");
gpio_request(NFC_EN, "NFC_EN");
gpio_request(CHG_EN, "CHG_EN");
gpio_request(CHG_OTG_OUT_EN, "CHG_OTG_OUT_EN");
gpio_request(TYPEC_MUX_EN, "TYPEC_MUX_EN");
gpio_request(TPS_RESET, "TPS_RESET");
gpio_request(WWAN_EN, "WWAN_EN");
gpio_request(WWAN_NRESET, "WWAN_NRESET");
gpio_request(HUB_EN, "HUB_EN");
gpio_request(HUB_NRESET, "HUB_NRESET");
gpio_request(SD_EN, "SD_EN");
gpio_request(AUDIO_EN, "AUDIO_EN");
gpio_request(DSI_EN, "DSI_EN");
gpio_request(SMC_EN, "SMC_EN");
gpio_request(CAMERA_EN, "CAMERA_EN");
gpio_request(FLASH_EN, "FLASH_EN");
gpio_request(DSI_BIAS_EN, "DSI_BIAS_EN");
gpio_request(GPS_EN, "GPS_EN");
gpio_request(BL_EN, "BL_EN");
#ifndef CONSOLE_ON_UART4
gpio_request(WIFI_EN, "WIFI_EN");
gpio_direction_output(WIFI_EN, 0);
#endif /* CONSOLE_ON_UART4 */
gpio_direction_input(VOL_UP);
gpio_direction_input(VOL_DOWN);
/* ensure charger is in the automated mode */
gpio_direction_output(NFC_EN, 0);
gpio_direction_output(CHG_EN, 0);
gpio_direction_output(CHG_OTG_OUT_EN, 0);
gpio_direction_input(TYPEC_MUX_EN);
gpio_direction_output(TPS_RESET, 0);
gpio_direction_output(WWAN_EN, 0);
gpio_direction_output(WWAN_NRESET, 1);
gpio_direction_output(HUB_EN, 1);
gpio_direction_output(HUB_NRESET, 1);
mdelay(10);
gpio_direction_output(SD_EN, 1);
gpio_direction_output(SMC_EN, 0);
gpio_direction_output(CAMERA_EN, 0);
gpio_direction_output(FLASH_EN, 0);
gpio_direction_output(DSI_BIAS_EN, 0);
gpio_direction_output(GPS_EN, 0);
gpio_direction_output(BL_EN, 0);
/* turn these on for i2c busses */
gpio_direction_output(AUDIO_EN, 1);
gpio_direction_output(DSI_EN, 1);
}
#endif /* CONFIG_SPL_BUILD */
#define USB1_BASE_ADDR 0x38100000
#define USB2_BASE_ADDR 0x38200000
#define USB1_PHY_BASE_ADDR 0x381F0000
#define USB2_PHY_BASE_ADDR 0x382F0000
#define USB_PHY_CTRL0 0xF0040
#define USB_PHY_CTRL0_REF_SSP_EN BIT(2)
#define USB_PHY_CTRL0_SSC_RANGE_MASK GENMASK(23, 21)
#define USB_PHY_CTRL0_SSC_RANGE_4003PPM (0x2 << 21)
#define USB_PHY_CTRL1 0xF0044
#define USB_PHY_CTRL1_RESET BIT(0)
#define USB_PHY_CTRL1_COMMONONN BIT(1)
#define USB_PHY_CTRL1_ATERESET BIT(3)
#define USB_PHY_CTRL1_VDATSRCENB0 BIT(19)
#define USB_PHY_CTRL1_VDATDETENB0 BIT(20)
#define USB_PHY_CTRL2 0xF0048
#define USB_PHY_CTRL2_TXENABLEN0 BIT(8)
#define USB_PHY_CTRL6 0x18
#define USB_PHY_CTRL6_RXTERM_OVERRIDE_SEL BIT(29)
extern struct dram_timing_info dram_timing_b0;
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

592
board/purism/librem5/spl.c Normal file
View file

@ -0,0 +1,592 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
* Copyright 2021 Purism
*/
#include <common.h>
#include <asm/io.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/ddr.h>
#include <asm/arch/imx8mq_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/clock.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/mach-imx/gpio.h>
#include <asm/mach-imx/mxc_i2c.h>
#include <fsl_esdhc_imx.h>
#include <mmc.h>
#include <power/pmic.h>
#include <power/bd71837.h>
#include <hang.h>
#include <init.h>
#include <spl.h>
#include <usb.h>
#include <dwc3-uboot.h>
#include <linux/delay.h>
#include "librem5.h"
DECLARE_GLOBAL_DATA_PTR;
void spl_dram_init(void)
{
/* ddr init */
if ((get_cpu_rev() & 0xfff) == CHIP_REV_2_1)
ddr_init(&dram_timing);
else
ddr_init(&dram_timing_b0);
}
int spl_board_boot_device(enum boot_device boot_dev_spl)
{
log_debug("%s : starting\n", __func__);
switch (boot_dev_spl) {
case SD1_BOOT:
case MMC1_BOOT:
return BOOT_DEVICE_MMC1;
case USB_BOOT:
return BOOT_DEVICE_BOARD;
default:
return BOOT_DEVICE_NONE;
}
}
#define ECSPI_PAD_CTRL (PAD_CTL_DSE2 | PAD_CTL_HYS)
static const iomux_v3_cfg_t ecspi_pads[] = {
IMX8MQ_PAD_ECSPI1_SCLK__ECSPI1_SCLK | MUX_PAD_CTRL(ECSPI_PAD_CTRL),
IMX8MQ_PAD_ECSPI1_SS0__GPIO5_IO9 | MUX_PAD_CTRL(ECSPI_PAD_CTRL),
IMX8MQ_PAD_ECSPI1_MOSI__ECSPI1_MOSI | MUX_PAD_CTRL(ECSPI_PAD_CTRL),
IMX8MQ_PAD_ECSPI1_MISO__ECSPI1_MISO | MUX_PAD_CTRL(ECSPI_PAD_CTRL),
};
int board_ecspi_init(void)
{
imx_iomux_v3_setup_multiple_pads(ecspi_pads, ARRAY_SIZE(ecspi_pads));
return 0;
}
int board_spi_cs_gpio(unsigned int bus, unsigned int cs)
{
return (bus == 0 && cs == 0) ? (SPI1_SS0) : -1;
}
#define I2C_PAD_CTRL (PAD_CTL_PUE | PAD_CTL_ODE | PAD_CTL_DSE7 | PAD_CTL_FSEL3)
#define PC MUX_PAD_CTRL(I2C_PAD_CTRL)
struct i2c_pads_info i2c_pad_info1 = {
.scl = {
.i2c_mode = IMX8MQ_PAD_I2C1_SCL__I2C1_SCL | PC,
.gpio_mode = IMX8MQ_PAD_I2C1_SCL__GPIO5_IO14 | PC,
.gp = IMX_GPIO_NR(5, 14),
},
.sda = {
.i2c_mode = IMX8MQ_PAD_I2C1_SDA__I2C1_SDA | PC,
.gpio_mode = IMX8MQ_PAD_I2C1_SDA__GPIO5_IO15 | PC,
.gp = IMX_GPIO_NR(5, 15),
},
};
struct i2c_pads_info i2c_pad_info2 = {
.scl = {
.i2c_mode = IMX8MQ_PAD_I2C2_SCL__I2C2_SCL | PC,
.gpio_mode = IMX8MQ_PAD_I2C2_SCL__GPIO5_IO16 | PC,
.gp = IMX_GPIO_NR(5, 16),
},
.sda = {
.i2c_mode = IMX8MQ_PAD_I2C2_SDA__I2C2_SDA | PC,
.gpio_mode = IMX8MQ_PAD_I2C2_SDA__GPIO5_IO17 | PC,
.gp = IMX_GPIO_NR(5, 17),
},
};
struct i2c_pads_info i2c_pad_info3 = {
.scl = {
.i2c_mode = IMX8MQ_PAD_I2C3_SCL__I2C3_SCL | PC,
.gpio_mode = IMX8MQ_PAD_I2C3_SCL__GPIO5_IO18 | PC,
.gp = IMX_GPIO_NR(5, 18),
},
.sda = {
.i2c_mode = IMX8MQ_PAD_I2C3_SDA__I2C3_SDA | PC,
.gpio_mode = IMX8MQ_PAD_I2C3_SDA__GPIO5_IO19 | PC,
.gp = IMX_GPIO_NR(5, 19),
},
};
struct i2c_pads_info i2c_pad_info4 = {
.scl = {
.i2c_mode = IMX8MQ_PAD_I2C4_SCL__I2C4_SCL | PC,
.gpio_mode = IMX8MQ_PAD_I2C4_SCL__GPIO5_IO20 | PC,
.gp = IMX_GPIO_NR(5, 20),
},
.sda = {
.i2c_mode = IMX8MQ_PAD_I2C4_SDA__I2C4_SDA | PC,
.gpio_mode = IMX8MQ_PAD_I2C4_SDA__GPIO5_IO21 | PC,
.gp = IMX_GPIO_NR(5, 21),
},
};
#define UART_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_FSEL1)
static const iomux_v3_cfg_t uart_pads[] = {
IMX8MQ_PAD_UART1_RXD__UART1_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART1_TXD__UART1_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART2_RXD__UART2_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART2_TXD__UART2_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART3_RXD__UART3_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART3_TXD__UART3_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_ECSPI2_SCLK__UART4_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_ECSPI2_MOSI__UART4_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
};
#define USDHC1_PWR_GPIO IMX_GPIO_NR(2, 10)
#define USDHC2_PWR_GPIO IMX_GPIO_NR(2, 19)
int board_mmc_getcd(struct mmc *mmc)
{
struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
int ret = 0;
switch (cfg->esdhc_base) {
case USDHC1_BASE_ADDR:
ret = 1;
break;
case USDHC2_BASE_ADDR:
ret = 1;
break;
}
return ret;
}
#define USDHC_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_HYS | PAD_CTL_PUE | \
PAD_CTL_FSEL1)
#define USDHC_GPIO_PAD_CTRL (PAD_CTL_PUE | PAD_CTL_DSE1)
static const iomux_v3_cfg_t usdhc1_pads[] = {
IMX8MQ_PAD_SD1_CLK__USDHC1_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_CMD__USDHC1_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA0__USDHC1_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA1__USDHC1_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA2__USDHC1_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA3__USDHC1_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA4__USDHC1_DATA4 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA5__USDHC1_DATA5 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA6__USDHC1_DATA6 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA7__USDHC1_DATA7 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_RESET_B__GPIO2_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
static const iomux_v3_cfg_t usdhc2_pads[] = {
IMX8MQ_PAD_SD2_CLK__USDHC2_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0xd6 */
IMX8MQ_PAD_SD2_CMD__USDHC2_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0xd6 */
IMX8MQ_PAD_SD2_DATA0__USDHC2_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0xd6 */
IMX8MQ_PAD_SD2_DATA1__USDHC2_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0xd6 */
IMX8MQ_PAD_SD2_DATA2__USDHC2_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0x16 */
IMX8MQ_PAD_SD2_DATA3__USDHC2_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL), /* 0xd6 */
IMX8MQ_PAD_SD2_RESET_B__GPIO2_IO19 | MUX_PAD_CTRL(USDHC_GPIO_PAD_CTRL),
};
static struct fsl_esdhc_cfg usdhc_cfg[2] = {
{USDHC1_BASE_ADDR, 0, 8},
{USDHC2_BASE_ADDR, 0, 4},
};
int board_mmc_init(struct bd_info *bis)
{
int i, ret;
/*
* According to the board_mmc_init() the following map is done:
* (U-Boot device node) (Physical Port)
* mmc0 USDHC1
* mmc1 USDHC2
*/
for (i = 0; i < CONFIG_SYS_FSL_USDHC_NUM; i++) {
log_debug("Initializing FSL USDHC port %d\n", i);
switch (i) {
case 0:
init_clk_usdhc(0);
usdhc_cfg[0].sdhc_clk = mxc_get_clock(USDHC1_CLK_ROOT);
imx_iomux_v3_setup_multiple_pads(usdhc1_pads,
ARRAY_SIZE(usdhc1_pads));
gpio_request(USDHC1_PWR_GPIO, "usdhc1_reset");
gpio_direction_output(USDHC1_PWR_GPIO, 0);
udelay(500);
gpio_direction_output(USDHC1_PWR_GPIO, 1);
break;
case 1:
init_clk_usdhc(1);
usdhc_cfg[1].sdhc_clk = mxc_get_clock(USDHC2_CLK_ROOT);
imx_iomux_v3_setup_multiple_pads(usdhc2_pads,
ARRAY_SIZE(usdhc2_pads));
gpio_request(USDHC2_PWR_GPIO, "usdhc2_reset");
gpio_direction_output(USDHC2_PWR_GPIO, 0);
udelay(500);
gpio_direction_output(USDHC2_PWR_GPIO, 1);
break;
default:
log_err("Warning: USDHC controller(%d) not supported\n", i + 1);
return -EINVAL;
}
ret = fsl_esdhc_initialize(bis, &usdhc_cfg[i]);
if (ret)
return ret;
}
return 0;
}
#define LDO_VOLT_EN BIT(6)
/*
* Disable the charger - it will be re-enabled in u-boot
*/
void disable_charger_bq25895(void)
{
u8 val;
int timeout = 1000; // ms
/* Set the i2c bus */
i2c_set_bus_num(3);
/* disable ship mode if BATFET_DLY is set */
val = i2c_reg_read(0x6a, 0x09);
log_debug("REG09 0x%x\n", val);
if (val & 0x28) {
val = val & ~0x28;
i2c_reg_write(0x6a, 0x09, val);
}
/* disable and trigger DPDM, ICO, HVDCP and MaxCharge */
val = i2c_reg_read(0x6a, 0x02);
log_debug("REG02 0x%x\n", val);
val &= 0xe0;
i2c_reg_write(0x6a, 0x02, val);
/* disable charger and enable BAT_LOADEN */
val = i2c_reg_read(0x6a, 0x03);
log_debug("REG03 0x%x\n", val);
val = (val | 0x80) & ~0x10;
i2c_reg_write(0x6a, 0x03, val);
mdelay(10);
/* force ADC conversions */
val = i2c_reg_read(0x6a, 0x02);
log_debug("REG02 0x%x\n", val);
val = (val | 0x80) & ~0x40;
i2c_reg_write(0x6a, 0x02, val);
do {
mdelay(10);
timeout -= 10;
} while ((i2c_reg_read(0x6a, 0x02) & 0x80) && (timeout > 0));
/* enable STAT pin */
val = i2c_reg_read(0x6a, 0x07);
log_debug("REG07 0x%x\n", val);
val = val & ~0x40;
i2c_reg_write(0x6a, 0x07, val);
/* check VBUS */
val = i2c_reg_read(0x6a, 0x11);
log_debug("VBUS good %d\n", (val >> 7) & 1);
log_debug("VBUS mV %d\n", (val & 0x7f) * 100 + 2600);
/* check VBAT */
val = i2c_reg_read(0x6a, 0x0e);
log_debug("VBAT mV %d\n", (val & 0x7f) * 20 + 2304);
/* limit the VINDPM to 3.9V */
i2c_reg_write(0x6a, 0x0d, 0x8d);
/* set the max voltage to 4.192V */
val = i2c_reg_read(0x6a, 0x6);
val = (val & ~0xFC) | 0x16 << 2;
i2c_reg_write(0x6a, 0x6, val);
/* set the SYS_MIN to 3.7V */
val = i2c_reg_read(0x6a, 0x3);
val = val | 0xE;
i2c_reg_write(0x6a, 0x3, val);
/* disable BAT_LOADEN */
val = i2c_reg_read(0x6a, 0x03);
log_debug("REG03 0x%x\n", val);
val = val & ~0x80;
i2c_reg_write(0x6a, 0x03, val);
}
#define I2C_PMIC 0
int power_bd71837_init(unsigned char bus)
{
static const char name[] = BD718XX_REGULATOR_DRIVER;
struct pmic *p = pmic_alloc();
if (!p) {
log_err("%s: POWER allocation error!\n", __func__);
return -ENOMEM;
}
p->name = name;
p->interface = I2C_PMIC;
p->number_of_regs = BD718XX_MAX_REGISTER;
p->hw.i2c.addr = CONFIG_POWER_BD71837_I2C_ADDR;
p->hw.i2c.tx_num = 1;
p->bus = bus;
return 0;
}
int power_init_board(void)
{
struct pmic *p;
int ldo[] = {BD718XX_LDO5_VOLT, BD718XX_LDO6_VOLT,
BD71837_LDO7_VOLT};
u32 val;
int i, rv;
/* Set the i2c bus */
setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1);
/*
* Init PMIC
*/
rv = power_bd71837_init(CONFIG_POWER_BD71837_I2C_BUS);
if (rv) {
log_err("%s: power_bd71837_init(%d) error %d\n", __func__,
CONFIG_POWER_BD71837_I2C_BUS, rv);
goto out;
}
p = pmic_get(BD718XX_REGULATOR_DRIVER);
if (!p) {
log_err("%s: pmic_get(%s) failed\n", __func__, BD718XX_REGULATOR_DRIVER);
rv = -ENODEV;
goto out;
}
rv = pmic_probe(p);
if (rv) {
log_err("%s: pmic_probe() error %d\n", __func__, rv);
goto out;
}
/*
* Unlock all regs
*/
pmic_reg_write(p, BD718XX_REGLOCK, 0);
/* find the reset cause */
pmic_reg_read(p, 0x29, &val);
log_debug("%s: reset cause %d\n", __func__, val);
/*
* Reconfigure default voltages and disable:
* - BUCK3: VDD_GPU_0V9 (1.00 -> 0.90)
* - BUCK4: VDD_VPU_0V9 (1.00 -> 0.90)
*/
pmic_reg_write(p, BD71837_BUCK3_VOLT_RUN, 0x14);
pmic_reg_write(p, BD71837_BUCK4_VOLT_RUN, 0x14);
/*
* Enable PHYs voltages: LDO5-7
*/
for (i = 0; i < ARRAY_SIZE(ldo); i++) {
rv = pmic_reg_read(p, ldo[i], &val);
if (rv) {
log_err("%s: pmic_read(%x) error %d\n", __func__,
ldo[i], rv);
continue;
}
pmic_reg_write(p, ldo[i], val | LDO_VOLT_EN);
}
udelay(500);
rv = 0;
out:
return rv;
}
int usb_gadget_handle_interrupts(void)
{
dwc3_uboot_handle_interrupt(0);
return 0;
}
static void dwc3_nxp_usb_phy_init(struct dwc3_device *dwc3)
{
u32 RegData;
RegData = readl(dwc3->base + USB_PHY_CTRL1);
RegData &= ~(USB_PHY_CTRL1_VDATSRCENB0 | USB_PHY_CTRL1_VDATDETENB0 |
USB_PHY_CTRL1_COMMONONN);
RegData |= USB_PHY_CTRL1_RESET | USB_PHY_CTRL1_ATERESET;
writel(RegData, dwc3->base + USB_PHY_CTRL1);
RegData = readl(dwc3->base + USB_PHY_CTRL0);
RegData |= USB_PHY_CTRL0_REF_SSP_EN;
RegData &= ~USB_PHY_CTRL0_SSC_RANGE_MASK;
RegData |= USB_PHY_CTRL0_SSC_RANGE_4003PPM;
writel(RegData, dwc3->base + USB_PHY_CTRL0);
RegData = readl(dwc3->base + USB_PHY_CTRL2);
RegData |= USB_PHY_CTRL2_TXENABLEN0;
writel(RegData, dwc3->base + USB_PHY_CTRL2);
RegData = readl(dwc3->base + USB_PHY_CTRL1);
RegData &= ~(USB_PHY_CTRL1_RESET | USB_PHY_CTRL1_ATERESET);
writel(RegData, dwc3->base + USB_PHY_CTRL1);
/* Disable rx term override */
RegData = readl(dwc3->base + USB_PHY_CTRL6);
RegData &= ~USB_PHY_CTRL6_RXTERM_OVERRIDE_SEL;
writel(RegData, dwc3->base + USB_PHY_CTRL6);
}
static struct dwc3_device dwc3_device0_data = {
.maximum_speed = USB_SPEED_HIGH,
.base = USB1_BASE_ADDR,
.dr_mode = USB_DR_MODE_PERIPHERAL,
.index = 0,
};
static struct dwc3_device dwc3_device1_data = {
.maximum_speed = USB_SPEED_HIGH,
.base = USB2_BASE_ADDR,
.dr_mode = USB_DR_MODE_HOST,
.index = 1,
};
int board_usb_init(int index, enum usb_init_type init)
{
int ret = 0;
printf("%s : index %d type %d\n", __func__, index, init);
if (index == 0 && init == USB_INIT_DEVICE) {
dwc3_nxp_usb_phy_init(&dwc3_device0_data);
ret = dwc3_uboot_init(&dwc3_device0_data);
}
if (index == 1 && init == USB_INIT_HOST) {
dwc3_nxp_usb_phy_init(&dwc3_device1_data);
ret = dwc3_uboot_init(&dwc3_device1_data);
}
return ret;
}
int board_usb_cleanup(int index, enum usb_init_type init)
{
u32 RegData;
struct dwc3_device *dwc3;
printf("%s : %d\n", __func__, index);
if (index == 0 && init == USB_INIT_DEVICE)
dwc3 = &dwc3_device0_data;
if (index == 1 && init == USB_INIT_HOST)
dwc3 = &dwc3_device1_data;
dwc3_uboot_exit(index);
/* reset the phy */
RegData = readl(dwc3->base + USB_PHY_CTRL1);
RegData &= ~(USB_PHY_CTRL1_VDATSRCENB0 | USB_PHY_CTRL1_VDATDETENB0 |
USB_PHY_CTRL1_COMMONONN);
RegData |= USB_PHY_CTRL1_RESET | USB_PHY_CTRL1_ATERESET;
writel(RegData, dwc3->base + USB_PHY_CTRL1);
/* enable rx term override */
RegData = readl(dwc3->base + USB_PHY_CTRL6);
RegData |= USB_PHY_CTRL6_RXTERM_OVERRIDE_SEL;
writel(RegData, dwc3->base + USB_PHY_CTRL6);
return 0;
}
void spl_board_init(void)
{
if (is_usb_boot())
puts("USB Boot\n");
else
puts("Normal Boot\n");
}
void board_boot_order(u32 *spl_boot_list)
{
if (is_usb_boot())
spl_boot_list[0] = BOOT_DEVICE_BOARD;
else
spl_boot_list[0] = BOOT_DEVICE_MMC1;
}
#define WDOG_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_HYS | PAD_CTL_PUE)
static const iomux_v3_cfg_t wdog_pads[] = {
IMX8MQ_PAD_GPIO1_IO02__WDOG1_WDOG_B | MUX_PAD_CTRL(WDOG_PAD_CTRL),
};
void board_init_f(ulong dummy)
{
int ret;
struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR;
arch_cpu_init();
imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads));
set_wdog_reset(wdog);
init_uart_clk(CONSOLE_UART_CLK);
imx_iomux_v3_setup_multiple_pads(uart_pads, ARRAY_SIZE(uart_pads));
#ifdef CONSOLE_ON_UART4
gpio_request(WIFI_EN, "WIFI_EN");
gpio_direction_output(WIFI_EN, 1);
#endif
board_early_init_f();
timer_init();
preloader_console_init();
ret = spl_init();
if (ret) {
log_err("spl_init() failed: %d\n", ret);
hang();
}
enable_tzc380();
printf("Initializing pinmux\n");
init_pinmux();
gpio_direction_output(LED_G, 1);
gpio_direction_output(MOTO, 1);
mdelay(50);
gpio_direction_output(MOTO, 0);
/* Enable and configure i2c buses not used below */
setup_i2c(1, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info2);
setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info3);
setup_i2c(3, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info4);
power_init_board();
disable_charger_bq25895();
/* initialize this for M4 even if u-boot doesn't have SF_CMD */
printf("Initializing ECSPI\n");
board_ecspi_init();
/* DDR initialization */
printf("Initializing DRAM\n");
spl_dram_init();
}

152
configs/librem5_defconfig Normal file
View file

@ -0,0 +1,152 @@
CONFIG_ARM=y
CONFIG_ARCH_IMX8M=y
CONFIG_SYS_TEXT_BASE=0x40200000
CONFIG_SPL_GPIO=y
CONFIG_SPL_LIBCOMMON_SUPPORT=y
CONFIG_SPL_LIBGENERIC_SUPPORT=y
CONFIG_ENV_SIZE=0x2000
CONFIG_ENV_OFFSET=0x3FE000
CONFIG_SYS_I2C_MXC_I2C1=y
CONFIG_SYS_I2C_MXC_I2C2=y
CONFIG_SYS_I2C_MXC_I2C3=y
CONFIG_SYS_I2C_MXC_I2C4=y
CONFIG_DM_GPIO=y
CONFIG_DEFAULT_DEVICE_TREE="imx8mq-librem5-r4"
CONFIG_SPL_TEXT_BASE=0x7E1000
CONFIG_TARGET_LIBREM5=y
CONFIG_SPL_MMC=y
CONFIG_SPL_SERIAL=y
CONFIG_SPL_DRIVERS_MISC=y
CONFIG_SPL=y
CONFIG_IMX_BOOTAUX=y
CONFIG_SYS_LOAD_ADDR=0x40480000
CONFIG_DISTRO_DEFAULTS=y
CONFIG_REMAKE_ELF=y
CONFIG_FIT=y
CONFIG_FIT_EXTERNAL_OFFSET=0x3000
CONFIG_SPL_LOAD_FIT=y
# CONFIG_USE_SPL_FIT_GENERATOR is not set
CONFIG_OF_SYSTEM_SETUP=y
CONFIG_BOOTDELAY=0
CONFIG_SYS_CONSOLE_IS_IN_ENV=y
# CONFIG_SYS_DEVICE_NULLDEV is not set
CONFIG_ARCH_MISC_INIT=y
CONFIG_BOARD_EARLY_INIT_F=y
CONFIG_BOARD_LATE_INIT=y
CONFIG_SPL_MAX_SIZE=0x25000
CONFIG_SPL_HAS_BSS_LINKER_SECTION=y
CONFIG_SPL_BSS_START_ADDR=0x180000
CONFIG_SPL_BSS_MAX_SIZE=0x2000
CONFIG_SPL_BOARD_INIT=y
# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
CONFIG_SPL_STACK=0x187ff0
CONFIG_SYS_SPL_MALLOC=y
CONFIG_HAS_CUSTOM_SPL_MALLOC_START=y
CONFIG_CUSTOM_SYS_SPL_MALLOC_ADDR=0x42200000
CONFIG_SYS_SPL_MALLOC_SIZE=0x80000
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR=y
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x300
CONFIG_SPL_I2C=y
CONFIG_SPL_POWER=y
CONFIG_SPL_USB_GADGET=y
CONFIG_SPL_USB_SDP_SUPPORT=y
CONFIG_SPL_WATCHDOG=y
CONFIG_SYS_PROMPT="u-boot=> "
CONFIG_SYS_MAXARGS=64
CONFIG_SYS_PBSIZE=1050
CONFIG_CMD_MEMTEST=y
CONFIG_SYS_ALT_MEMTEST=y
CONFIG_CMD_CLK=y
CONFIG_CMD_FUSE=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_USB=y
CONFIG_CMD_USB_SDP=y
CONFIG_CMD_CACHE=y
CONFIG_CMD_REGULATOR=y
CONFIG_CMD_EXT4_WRITE=y
CONFIG_OF_CONTROL=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_MMC=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
CONFIG_SPL_DM=y
CONFIG_DEVRES=y
# CONFIG_SPL_BLK is not set
CONFIG_BUTTON=y
CONFIG_BUTTON_GPIO=y
CONFIG_SAVED_DRAM_TIMING_BASE=0x40000000
CONFIG_DMA=y
CONFIG_DMA_CHANNELS=y
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_BUF_ADDR=0x43000000
CONFIG_FASTBOOT_BUF_SIZE=0x40000000
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_UUU_SUPPORT=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_FASTBOOT_MMC_BOOT_SUPPORT=y
CONFIG_FASTBOOT_MMC_USER_SUPPORT=y
CONFIG_FASTBOOT_CMD_OEM_PARTCONF=y
CONFIG_FASTBOOT_CMD_OEM_BOOTBUS=y
# CONFIG_SPL_DM_GPIO is not set
CONFIG_GPIO_HOG=y
CONFIG_DM_GPIO_LOOKUP_LABEL=y
CONFIG_MXC_GPIO=y
CONFIG_DM_I2C=y
# CONFIG_SPL_DM_I2C is not set
CONFIG_SPL_SYS_I2C_LEGACY=y
CONFIG_SYS_I2C_EARLY_INIT=y
CONFIG_SYS_MXC_I2C1_SPEED=50000
CONFIG_SYS_MXC_I2C2_SPEED=50000
CONFIG_SYS_MXC_I2C3_SPEED=50000
CONFIG_SYS_MXC_I2C4_SPEED=50000
CONFIG_SYS_I2C_SPEED=50000
CONFIG_LED=y
CONFIG_LED_BLINK=y
CONFIG_LED_GPIO=y
CONFIG_MISC=y
CONFIG_PWRSEQ=y
CONFIG_MMC_BROKEN_CD=y
# CONFIG_SPL_DM_MMC is not set
CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_MMC_IO_VOLTAGE=y
CONFIG_MMC_UHS_SUPPORT=y
CONFIG_MMC_HS400_ES_SUPPORT=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_FSL_USDHC=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_DM_ETH=y
CONFIG_PHY=y
CONFIG_PHY_IMX8MQ_USB=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_IMX8M=y
CONFIG_POWER_LEGACY=y
CONFIG_POWER_DOMAIN=y
CONFIG_IMX8M_POWER_DOMAIN=y
CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_GPIO=y
CONFIG_POWER_I2C=y
CONFIG_DM_RESET=y
CONFIG_MXC_UART=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_MXC_SPI=y
CONFIG_DM_THERMAL=y
CONFIG_IMX_TMU=y
CONFIG_USB=y
CONFIG_DM_USB_GADGET=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_DWC3=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_GENERIC=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_MANUFACTURER="Purism"
CONFIG_USB_GADGET_VENDOR_NUM=0x0525
CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
CONFIG_SDP_LOADADDR=0x40400000
CONFIG_USB_FUNCTION_ACM=y

View file

@ -28,6 +28,7 @@ Board-specific doc
nokia/index
nxp/index
openpiton/index
purism/index
qualcomm/index
rockchip/index
samsung/index

View file

@ -0,0 +1,9 @@
.. SPDX-License-Identifier: GPL-2.0+
Purism SPC
==========
.. toctree::
:maxdepth: 2
librem5

View file

@ -0,0 +1,60 @@
.. SPDX-License-Identifier: GPL-2.0+
Librem5
==========
U-Boot for the Purism Librem5 phone
Quick Start
-----------
- Build the ARM Trusted firmware binary
- Get ddr and hdmi firmware
- Build U-Boot
Get and Build the ARM Trusted firmware
--------------------------------------
Note: srctree is U-Boot source directory
Get ATF from: https://source.puri.sm/Librem5/arm-trusted-firmware
branch: librem5
.. code-block:: bash
$ make PLAT=imx8mq CROSS_COMPILE=aarch64-linux-gnu- bl31
$ cp build/imx8mq/release/bl31.bin $(builddir)
Get the ddr and display port firmware
-------------------------------------
.. code-block:: bash
$ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.15.bin
$ chmod +x firmware-imx-8.15.bin
$ ./firmware-imx-8.15.bin
$ cp firmware-imx-8.15/firmware/hdmi/cadence/signed_dp_imx8m.bin $(builddir)
$ cp firmware-imx-8.15/firmware/ddr/synopsys/lpddr4*.bin $(builddir)
Build U-Boot
------------
.. code-block:: bash
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make librem5_defconfig
$ make ARCH=arm
Burn the flash.bin
------------------
Use uuu to burn flash.bin. Power on the phone while holding vol+ to get it
into uuu mode.
.. code-block:: bash
$ git clone https://source.puri.sm/Librem5/librem5-devkit-tools.git
$ cd librem5-devkit-tools
$ cp $(builddir)/flash.bin files/u-boot-librem5.imx
$ uuu uuu_scripts/u-boot_flash_librem5.lst
Reboot the phone.

95
include/configs/librem5.h Normal file
View file

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2017 NXP
* Copyright 2018 Emcraft Systems
* Copyright 2022 Purism
*
*/
#ifndef __LIBREM5_H
#define __LIBREM5_H
/* #define DEBUG */
#include <version.h>
#include <linux/sizes.h>
#include <asm/arch/imx-regs.h>
#define CONFIG_SYS_MONITOR_LEN (512 * 1024)
#ifdef CONFIG_SPL_BUILD
#define CONFIG_SPL_ABORT_ON_RAW_IMAGE /* For RAW image gives a error info not panic */
#define CONFIG_POWER_BD71837
#define CONFIG_POWER_BD71837_I2C_BUS 0
#define CONFIG_POWER_BD71837_I2C_ADDR 0x4B
#endif /* CONFIG_SPL_BUILD*/
#define CONFIG_SYS_FSL_USDHC_NUM 2
#define CONFIG_USBD_HS
#define CONSOLE_ON_UART1
#ifdef CONSOLE_ON_UART1
#define CONFIG_MXC_UART_BASE UART1_BASE_ADDR
#define CONSOLE_UART_CLK 0
#define CONSOLE "ttymxc0"
#elif defined(CONSOLE_ON_UART2)
#define CONFIG_MXC_UART_BASE UART2_BASE_ADDR
#define CONSOLE_UART_CLK 1
#define CONSOLE "ttymxc1"
#elif defined(CONSOLE_ON_UART3)
#define CONFIG_MXC_UART_BASE UART3_BASE_ADDR
#define CONSOLE_UART_CLK 2
#define CONSOLE "ttymxc2"
#elif defined(CONSOLE_ON_UART4)
#define CONFIG_MXC_UART_BASE UART4_BASE_ADDR
#define CONSOLE_UART_CLK 3
#define CONSOLE "ttymxc3"
#else
#define CONFIG_MXC_UART_BASE UART1_BASE_ADDR
#define CONSOLE_UART_CLK 0
#define CONSOLE "ttymxc0"
#endif
#ifndef CONFIG_SPL_BUILD
#define BOOT_TARGET_DEVICES(func) \
func(MMC, mmc, 0) \
func(USB, usb, 0) \
func(DHCP, dhcp, na)
#include <config_distro_bootcmd.h>
#else
#define BOOTENV
#endif
/* Initial environment variables */
#define CONFIG_EXTRA_ENV_SETTINGS \
"scriptaddr=0x80000000\0" \
"pxefile_addr_r=0x80100000\0" \
"kernel_addr_r=0x80800000\0" \
"fdt_addr_r=0x84800000\0" \
"ramdisk_addr_r=0x85000000\0" \
"console=" CONSOLE ",115200\0" \
"bootargs=u_boot_version=" PLAIN_VERSION "\0" \
"stdin=usbacm,serial\0" \
"stdout=usbacm,serial\0" \
"stderr=usbacm,serial\0" \
BOOTENV
/* Link Definitions */
#define CONFIG_SYS_INIT_RAM_ADDR 0x40000000
#define CONFIG_SYS_INIT_RAM_SIZE 0x80000
#define CONFIG_SYS_SDRAM_BASE 0x40000000
#define PHYS_SDRAM 0x40000000
#define PHYS_SDRAM_SIZE 0xc0000000 /* 3GB LPDDR4 one Rank */
/* Monitor Command Prompt */
#define CONFIG_SYS_FSL_ESDHC_ADDR 0
#endif