mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-samsung
- Fix issues reported by smatch - exynos4210-origen cleanups - e850-96 improvements
This commit is contained in:
@@ -88,7 +88,7 @@ config TARGET_S5PC210_UNIVERSAL
|
||||
select MISC_COMMON
|
||||
|
||||
config TARGET_ORIGEN
|
||||
bool "Exynos4412 Origen board"
|
||||
bool "Exynos4210 Origen board"
|
||||
select EXYNOS4210
|
||||
select SUPPORT_SPL
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ static void exynos5_uart_config(int peripheral)
|
||||
count = 2;
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return;
|
||||
}
|
||||
for (i = start; i < start + count; i++) {
|
||||
@@ -63,7 +63,7 @@ static void exynos5420_uart_config(int peripheral)
|
||||
count = 2;
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,12 +97,12 @@ static int exynos5_mmc_config(int peripheral, int flags)
|
||||
start_ext = 0;
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) {
|
||||
debug("SDMMC device %d does not support 8bit mode",
|
||||
peripheral);
|
||||
debug("SDMMC device %d does not support 8bit mode\n",
|
||||
peripheral);
|
||||
return -1;
|
||||
}
|
||||
if (flags & PINMUX_FLAG_8BIT_MODE) {
|
||||
@@ -145,12 +145,12 @@ static int exynos5420_mmc_config(int peripheral, int flags)
|
||||
break;
|
||||
default:
|
||||
start = 0;
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) {
|
||||
debug("SDMMC device %d does not support 8bit mode",
|
||||
debug("SDMMC device %d does not support 8bit mode\n",
|
||||
peripheral);
|
||||
return -1;
|
||||
}
|
||||
@@ -453,7 +453,7 @@ void exynos5420_spi_config(int peripheral)
|
||||
default:
|
||||
cfg = 0;
|
||||
pin = 0;
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -522,7 +522,7 @@ static int exynos5_pinmux_config(int peripheral, int flags)
|
||||
gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_FUNC(2));
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -570,7 +570,7 @@ static int exynos5420_pinmux_config(int peripheral, int flags)
|
||||
gpio_cfg_pin(EXYNOS5420_GPIO_B20, S5P_GPIO_FUNC(2));
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -683,7 +683,7 @@ static void exynos4_uart_config(int peripheral)
|
||||
count = 2;
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return;
|
||||
}
|
||||
for (i = start; i < (start + count); i++) {
|
||||
@@ -797,7 +797,7 @@ static void exynos4x12_uart_config(int peripheral)
|
||||
count = 2;
|
||||
break;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return;
|
||||
}
|
||||
for (i = start; i < (start + count); i++) {
|
||||
@@ -834,7 +834,7 @@ static int exynos4_pinmux_config(int peripheral, int flags)
|
||||
debug("SDMMC device %d not implemented\n", peripheral);
|
||||
return -1;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -869,7 +869,7 @@ static int exynos4x12_pinmux_config(int peripheral, int flags)
|
||||
debug("SDMMC device %d not implemented\n", peripheral);
|
||||
return -1;
|
||||
default:
|
||||
debug("%s: invalid peripheral %d", __func__, peripheral);
|
||||
debug("%s: invalid peripheral %d\n", __func__, peripheral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ static int exynos4_pinmux_decode_periph_id(const void *blob, int node)
|
||||
u32 cell[3];
|
||||
|
||||
err = fdtdec_get_int_array(blob, node, "interrupts", cell,
|
||||
ARRAY_SIZE(cell));
|
||||
ARRAY_SIZE(cell));
|
||||
if (err) {
|
||||
debug(" invalid peripheral id\n");
|
||||
return PERIPH_ID_NONE;
|
||||
@@ -917,7 +917,7 @@ static int exynos5_pinmux_decode_periph_id(const void *blob, int node)
|
||||
u32 cell[3];
|
||||
|
||||
err = fdtdec_get_int_array(blob, node, "interrupts", cell,
|
||||
ARRAY_SIZE(cell));
|
||||
ARRAY_SIZE(cell));
|
||||
if (err)
|
||||
return PERIPH_ID_NONE;
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# Copyright (C) 2024, Linaro Limited
|
||||
# Sam Protsenko <semen.protsenko@linaro.org>
|
||||
|
||||
obj-y := e850-96.o fw.o
|
||||
obj-y := e850-96.o fw.o acpm.o pmic.o
|
||||
|
||||
169
board/samsung/e850-96/acpm.c
Normal file
169
board/samsung/e850-96/acpm.c
Normal file
@@ -0,0 +1,169 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2025 Linaro Ltd.
|
||||
* Author: Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* ACPM (Active Clock and Power Management) is an IPC protocol for communicating
|
||||
* with APM (Active Power Management) core. The message exchange between AP
|
||||
* (Application Processor) and APM is happening by using shared memory in SRAM
|
||||
* (iRAM) and generating interrupts using Mailbox block. By using this IPC
|
||||
* interface it's possible to offload power management tasks to APM core, which
|
||||
* acts as a supervisor for CPU. One of the main tasks of APM is controlling
|
||||
* PMIC chip over I3C bus. So in order to access PMIC chip registers it's
|
||||
* recommended to do so by sending corresponding commands to APM via ACPM IPC
|
||||
* protocol. The IPC interaction sequence looks like this:
|
||||
*
|
||||
* AP (CPU) <-> ACPM IPC (Mailbox + SRAM) <-> APM <-> I3C <-> PMIC
|
||||
*
|
||||
* This file contains functions for accessing I3C bus via APM block using
|
||||
* ACPM IPC.
|
||||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/time.h>
|
||||
#include <asm/io.h>
|
||||
#include "acpm.h"
|
||||
|
||||
/* Mailbox registers */
|
||||
#define MBOX_INTGR0 0x8 /* Interrupt Generation */
|
||||
#define MBOX_INTCR1 0x20 /* Interrupt Clear */
|
||||
#define MBOX_INTSR1 0x28 /* Interrupt Status */
|
||||
#define MBOX_INTGR_OFFSET 16
|
||||
#define MBOX_TIMEOUT (1 * USEC_PER_SEC)
|
||||
|
||||
/* APM shared memory registers */
|
||||
#define SHMEM_SR0 0x0
|
||||
#define SHMEM_SR1 0x4
|
||||
#define SHMEM_SR2 0x8
|
||||
#define SHMEM_SR3 0xc
|
||||
|
||||
/* IPC functions */
|
||||
#define IPC_FUNC_READ 0x0
|
||||
#define IPC_FUNC_WRITE 0x1
|
||||
/* Command 0 shifts and masks */
|
||||
#define IPC_REG_SHIFT 0
|
||||
#define IPC_REG_MASK 0xff
|
||||
#define IPC_TYPE_SHIFT 8
|
||||
#define IPC_TYPE_MASK 0xf
|
||||
#define IPC_CHANNEL_SHIFT 12
|
||||
#define IPC_CHANNEL_MASK 0xf
|
||||
/* Command 1 shifts and masks */
|
||||
#define IPC_FUNC_SHIFT 0
|
||||
#define IPC_FUNC_MASK 0xff
|
||||
#define IPC_WRITE_VAL_SHIFT 8
|
||||
#define IPC_WRITE_VAL_MASK 0xff
|
||||
/* Command 3 shifts and masks */
|
||||
#define IPC_DEST_SHIFT 8
|
||||
#define IPC_DEST_MASK 0xff
|
||||
#define IPC_RETURN_SHIFT 24
|
||||
#define IPC_RETURN_MASK 0xff
|
||||
|
||||
/**
|
||||
* acpm_ipc_send_data_async() - Send data to I3C block over ACPM IPC
|
||||
* @acpm: ACPM data
|
||||
* @cmd0: Command 0 value to send
|
||||
* @cmd1: Command 1 value to send
|
||||
*/
|
||||
static void acpm_ipc_send_data_async(struct acpm *acpm, u32 cmd0, u32 cmd1)
|
||||
{
|
||||
u32 irq_bit = 1 << acpm->ipc_ch;
|
||||
u32 intgr = irq_bit << MBOX_INTGR_OFFSET;
|
||||
|
||||
/* Write data to the shared memory */
|
||||
writel(cmd0, acpm->sram_base + SHMEM_SR0);
|
||||
writel(cmd1, acpm->sram_base + SHMEM_SR1);
|
||||
dsb();
|
||||
|
||||
/* Generate interrupt for I3C block */
|
||||
writel(intgr, acpm->mbox_base + MBOX_INTGR0);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_ipc_wait_resp() - Read response data from I3C block over ACPM IPC
|
||||
* @acpm: ACPM data
|
||||
* @cmd2: Will contain read value for command 2
|
||||
* @cmd3: Will contain read value for command 3
|
||||
*
|
||||
* Return: 0 on success or negative value on error.
|
||||
*/
|
||||
static int acpm_ipc_wait_resp(struct acpm *acpm, u32 *cmd2, u32 *cmd3)
|
||||
{
|
||||
u32 irq_bit = 1 << acpm->ipc_ch;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* Wait for the interrupt from I3C block */
|
||||
ret = readl_poll_timeout(acpm->mbox_base + MBOX_INTSR1, reg,
|
||||
reg & irq_bit, MBOX_TIMEOUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Clear the interrupt */
|
||||
writel(irq_bit, acpm->mbox_base + MBOX_INTCR1);
|
||||
|
||||
/* Read data from the shared memory */
|
||||
*cmd2 = readl(acpm->sram_base + SHMEM_SR2);
|
||||
*cmd3 = readl(acpm->sram_base + SHMEM_SR3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_i3c_read() - Read an I3C register of some I3C slave device
|
||||
* @acpm: ACPM data
|
||||
* @ch: I3C channel (bus) number (0-15)
|
||||
* @addr: I3C address of slave device (0-15)
|
||||
* @reg: Address of I3C register in the slave device to read from
|
||||
* @val: Will contain the read value
|
||||
*
|
||||
* Return: 0 on success or non-zero code on error (may be positive).
|
||||
*/
|
||||
int acpm_i3c_read(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 *val)
|
||||
{
|
||||
u32 cmd[4] = { 0 };
|
||||
u8 ret;
|
||||
|
||||
cmd[0] = (ch & IPC_CHANNEL_MASK) << IPC_CHANNEL_SHIFT |
|
||||
(addr & IPC_TYPE_MASK) << IPC_TYPE_SHIFT |
|
||||
(reg & IPC_REG_MASK) << IPC_REG_SHIFT;
|
||||
cmd[1] = IPC_FUNC_READ << IPC_FUNC_SHIFT;
|
||||
|
||||
acpm_ipc_send_data_async(acpm, cmd[0], cmd[1]);
|
||||
ret = acpm_ipc_wait_resp(acpm, &cmd[2], &cmd[3]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (cmd[3] >> IPC_DEST_SHIFT) & IPC_DEST_MASK;
|
||||
ret = (cmd[3] >> IPC_RETURN_SHIFT) & IPC_RETURN_MASK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_i3c_write() - Write an I3C register of some I3C slave device
|
||||
* @acpm: ACPM data
|
||||
* @ch: I3C channel (bus) number (0-15)
|
||||
* @addr: I3C address of slave device (0-15)
|
||||
* @reg: Address of I3C register in the slave device to write into
|
||||
* @val: Value to write
|
||||
*
|
||||
* Return: 0 on success or non-zero code on error (may be positive).
|
||||
*/
|
||||
int acpm_i3c_write(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 val)
|
||||
{
|
||||
u32 cmd[4] = { 0 };
|
||||
u8 ret;
|
||||
|
||||
cmd[0] = (ch & IPC_CHANNEL_MASK) << IPC_CHANNEL_SHIFT |
|
||||
(addr & IPC_TYPE_MASK) << IPC_TYPE_SHIFT |
|
||||
(reg & IPC_REG_MASK) << IPC_REG_SHIFT;
|
||||
cmd[1] = IPC_FUNC_WRITE << IPC_FUNC_SHIFT |
|
||||
(val & IPC_WRITE_VAL_MASK) << IPC_WRITE_VAL_SHIFT;
|
||||
|
||||
acpm_ipc_send_data_async(acpm, cmd[0], cmd[1]);
|
||||
ret = acpm_ipc_wait_resp(acpm, &cmd[2], &cmd[3]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = (cmd[3] >> IPC_RETURN_SHIFT) & IPC_RETURN_MASK;
|
||||
return ret;
|
||||
}
|
||||
27
board/samsung/e850-96/acpm.h
Normal file
27
board/samsung/e850-96/acpm.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2025 Linaro Ltd.
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*/
|
||||
|
||||
#ifndef __E850_96_ACPM_H
|
||||
#define __E850_96_ACPM_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct acpm - Data for I3C communication over ACPM IPC protocol
|
||||
* @mbox_base: Base address of APM mailbox block
|
||||
* @sram_base: Base address of shared memory used for APM messages
|
||||
* @ipc_ch: Mailbox channel number used for communication with I3C block (0-15)
|
||||
*/
|
||||
struct acpm {
|
||||
void __iomem *mbox_base;
|
||||
void __iomem *sram_base;
|
||||
u8 ipc_ch;
|
||||
};
|
||||
|
||||
int acpm_i3c_read(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 *val);
|
||||
int acpm_i3c_write(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 val);
|
||||
|
||||
#endif /* __E850_96_ACPM_H */
|
||||
@@ -8,13 +8,28 @@
|
||||
#include <env.h>
|
||||
#include <init.h>
|
||||
#include <mapmem.h>
|
||||
#include <net.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include "fw.h"
|
||||
#include "pmic.h"
|
||||
|
||||
/* OTP Controller base address and register offsets */
|
||||
#define EXYNOS850_OTP_BASE 0x10000000
|
||||
#define OTP_CHIPID0 0x4
|
||||
#define OTP_CHIPID1 0x8
|
||||
#define EXYNOS850_OTP_BASE 0x10000000
|
||||
#define OTP_CHIPID0 0x4
|
||||
#define OTP_CHIPID1 0x8
|
||||
|
||||
/* ACPM and PMIC definitions */
|
||||
#define EXYNOS850_MBOX_APM2AP_BASE 0x11900000
|
||||
#define EXYNOS850_APM_SRAM_BASE 0x02039000 /* in iRAM */
|
||||
#define EXYNOS850_APM_SHMEM_OFFSET 0x3200
|
||||
#define EXYNOS850_IPC_AP_I3C 10
|
||||
|
||||
/* LDFW firmware definitions */
|
||||
#define LDFW_NWD_ADDR 0x88000000
|
||||
#define EMMC_IFNAME "mmc"
|
||||
#define EMMC_DEV_NUM 0
|
||||
#define EMMC_ESP_PART 1
|
||||
|
||||
struct efi_fw_image fw_images[] = {
|
||||
{
|
||||
@@ -55,6 +70,13 @@ struct efi_capsule_update_info update_info = {
|
||||
.images = fw_images,
|
||||
};
|
||||
|
||||
static struct acpm acpm = {
|
||||
.mbox_base = (void __iomem *)EXYNOS850_MBOX_APM2AP_BASE,
|
||||
.sram_base = (void __iomem *)(EXYNOS850_APM_SRAM_BASE +
|
||||
EXYNOS850_APM_SHMEM_OFFSET),
|
||||
.ipc_ch = EXYNOS850_IPC_AP_I3C,
|
||||
};
|
||||
|
||||
int dram_init(void)
|
||||
{
|
||||
return fdtdec_setup_mem_size_base();
|
||||
@@ -92,19 +114,74 @@ static void setup_serial(void)
|
||||
env_set("serial#", serial_str);
|
||||
}
|
||||
|
||||
int board_late_init(void)
|
||||
static void setup_ethaddr(void)
|
||||
{
|
||||
u64 serial_num;
|
||||
u32 mac_hi, mac_lo;
|
||||
u8 mac_addr[6];
|
||||
|
||||
if (env_get("ethaddr"))
|
||||
return;
|
||||
|
||||
serial_num = get_chip_id();
|
||||
mac_lo = (u32)serial_num; /* OTP_CHIPID0 */
|
||||
mac_hi = (u32)(serial_num >> 32UL); /* OTP_CHIPID1 */
|
||||
mac_addr[0] = (mac_hi >> 8) & 0xff;
|
||||
mac_addr[1] = mac_hi & 0xff;
|
||||
mac_addr[2] = (mac_lo >> 24) & 0xff;
|
||||
mac_addr[3] = (mac_lo >> 16) & 0xff;
|
||||
mac_addr[4] = (mac_lo >> 8) & 0xff;
|
||||
mac_addr[5] = mac_lo & 0xff;
|
||||
mac_addr[0] &= ~0x1; /* make sure it's not a multicast address */
|
||||
if (is_valid_ethaddr(mac_addr))
|
||||
eth_env_set_enetaddr("ethaddr", mac_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this in board_late_init() to avoid probing block devices before
|
||||
* efi_init_early().
|
||||
*/
|
||||
void load_firmware(void)
|
||||
{
|
||||
const char *ifname;
|
||||
ulong dev, part;
|
||||
int err;
|
||||
|
||||
setup_serial();
|
||||
ifname = env_get("bootdev");
|
||||
if (!ifname)
|
||||
ifname = EMMC_IFNAME;
|
||||
dev = env_get_ulong("bootdevnum", 10, EMMC_DEV_NUM);
|
||||
part = env_get_ulong("bootdevpart", 10, EMMC_ESP_PART);
|
||||
|
||||
/*
|
||||
* Do this in board_late_init() to make sure MMC is not probed before
|
||||
* efi_init_early().
|
||||
*/
|
||||
err = load_ldfw();
|
||||
if (!strcmp(ifname, "usb")) {
|
||||
printf("Starting USB (bootdev=usb)...\n");
|
||||
err = usb_init();
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Loading LDFW firmware (from %s %ld)...\n", ifname, dev);
|
||||
err = load_ldfw(ifname, dev, part, LDFW_NWD_ADDR);
|
||||
if (err)
|
||||
printf("ERROR: LDFW loading failed (%d)\n", err);
|
||||
}
|
||||
|
||||
int board_late_init(void)
|
||||
{
|
||||
setup_serial();
|
||||
setup_ethaddr();
|
||||
load_firmware();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int power_init_board(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pmic_init(&acpm);
|
||||
if (err)
|
||||
printf("ERROR: Failed to configure PMIC (%d)\n", err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ fdt_addr_r=0x8c000000
|
||||
scriptaddr=0x8c100000
|
||||
pxefile_addr_r=0x8c200000
|
||||
ramdisk_addr_r=0x8c300000
|
||||
fdtfile=CONFIG_DEFAULT_FDT_FILE
|
||||
fdtfile=exynos/exynos850-e850-96.dtb
|
||||
|
||||
dfu_alt_info=
|
||||
rawemmc raw 0 0x747c000 mmcpart 1;
|
||||
|
||||
@@ -11,13 +11,9 @@
|
||||
#include <linux/arm-smccc.h>
|
||||
#include "fw.h"
|
||||
|
||||
#define EMMC_IFACE "mmc"
|
||||
#define EMMC_DEV_NUM 0
|
||||
#define LDFW_RAW_PART "ldfw"
|
||||
#define LDFW_FAT_PART "esp"
|
||||
#define LDFW_FAT_PATH "/EFI/firmware/ldfw.bin"
|
||||
|
||||
#define LDFW_NWD_ADDR 0x88000000
|
||||
#define LDFW_MAGIC 0x10adab1e
|
||||
#define SMC_CMD_LOAD_LDFW -0x500
|
||||
#define SDM_HW_RESET_STATUS 0x1230
|
||||
@@ -39,21 +35,25 @@ struct ldfw_header {
|
||||
};
|
||||
|
||||
/* Load LDFW binary as a file from FAT partition */
|
||||
static int read_fw_from_fat(const char *part_name, const char *path, void *buf)
|
||||
static int read_fw_from_fat(const char *ifname, int dev, int part,
|
||||
const char *path, void *buf)
|
||||
{
|
||||
char dev_part_str[8];
|
||||
struct blk_desc *blk_desc;
|
||||
loff_t len_read;
|
||||
int err;
|
||||
|
||||
snprintf(dev_part_str, sizeof(dev_part_str), "%d#%s", EMMC_DEV_NUM,
|
||||
LDFW_FAT_PART);
|
||||
|
||||
err = fs_set_blk_dev(EMMC_IFACE, dev_part_str, FS_TYPE_FAT);
|
||||
if (err) {
|
||||
debug("%s: Can't set block device\n", __func__);
|
||||
blk_desc = blk_get_dev(ifname, dev);
|
||||
if (!blk_desc) {
|
||||
debug("%s: Can't get block device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = fs_set_blk_dev_with_part(blk_desc, part);
|
||||
if (err) {
|
||||
debug("%s: Can't set partition\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
err = fs_read(path, (ulong)buf, 0, 0, &len_read);
|
||||
if (err) {
|
||||
debug("%s: Can't read LDFW file\n", __func__);
|
||||
@@ -64,16 +64,17 @@ static int read_fw_from_fat(const char *part_name, const char *path, void *buf)
|
||||
}
|
||||
|
||||
/* Load LDFW binary from raw partition on block device into RAM buffer */
|
||||
static int read_fw_from_raw(const char *part_name, void *buf)
|
||||
static int read_fw_from_raw(const char *ifname, int dev, const char *part_name,
|
||||
void *buf)
|
||||
{
|
||||
struct blk_desc *blk_desc;
|
||||
struct disk_partition part;
|
||||
unsigned long cnt;
|
||||
int part_num;
|
||||
|
||||
blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM);
|
||||
blk_desc = blk_get_dev(ifname, dev);
|
||||
if (!blk_desc) {
|
||||
debug("%s: Can't get eMMC device\n", __func__);
|
||||
debug("%s: Can't get block device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -92,9 +93,17 @@ static int read_fw_from_raw(const char *part_name, void *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_ldfw(void)
|
||||
/**
|
||||
* load_ldfw - Load the loadable firmware (LDFW)
|
||||
* @ifname: Interface name of the block device to load the firmware from
|
||||
* @dev: Device number
|
||||
* @part: Partition number
|
||||
* @addr: Temporary memory (Normal World) to use for loading the firmware
|
||||
*
|
||||
* Return: 0 on success or a negative value on error.
|
||||
*/
|
||||
int load_ldfw(const char *ifname, int dev, int part, phys_addr_t addr)
|
||||
{
|
||||
const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR;
|
||||
struct ldfw_header *hdr;
|
||||
struct arm_smccc_res res;
|
||||
void *buf = (void *)addr;
|
||||
@@ -102,9 +111,9 @@ int load_ldfw(void)
|
||||
int err, i;
|
||||
|
||||
/* First try to read LDFW from EFI partition, then from the raw one */
|
||||
err = read_fw_from_fat(LDFW_FAT_PART, LDFW_FAT_PATH, buf);
|
||||
err = read_fw_from_fat(ifname, dev, part, LDFW_FAT_PATH, buf);
|
||||
if (err) {
|
||||
err = read_fw_from_raw(LDFW_RAW_PART, buf);
|
||||
err = read_fw_from_raw(ifname, dev, LDFW_RAW_PART, buf);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifndef __E850_96_FW_H
|
||||
#define __E850_96_FW_H
|
||||
|
||||
int load_ldfw(void);
|
||||
#include <asm/types.h>
|
||||
|
||||
int load_ldfw(const char *ifname, int dev, int part, phys_addr_t addr);
|
||||
|
||||
#endif /* __E850_96_FW_H */
|
||||
|
||||
144
board/samsung/e850-96/pmic.c
Normal file
144
board/samsung/e850-96/pmic.c
Normal file
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2025 Linaro Ltd.
|
||||
* Author: Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*
|
||||
* This file contains functions for S2MPU12 PMIC regulators configuration.
|
||||
*
|
||||
* Example of voltage calculation for LDO24 and LDO32:
|
||||
* - V_min = 1800 mV
|
||||
* - V_step = 25 mV
|
||||
* - V_wanted = 3300 mV
|
||||
* - register value: (V_wanted - V_min) / V_step = 60 = 0x3c
|
||||
*
|
||||
* NOTE: 0x3c value might mean different voltage for other LDOs.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "pmic.h"
|
||||
|
||||
/* PMIC definitions */
|
||||
#define S2MPU12_CHANNEL 0 /* I3C bus number of PMIC */
|
||||
#define S2MPU12_PM_ADDR 0x1 /* I3C slave addr of PM part */
|
||||
|
||||
/* PMIC I3C registers */
|
||||
#define S2MPU12_PM_LDO1_CTRL 0x2b
|
||||
#define S2MPU12_PM_LDO_CTRL(n) (S2MPU12_PM_LDO1_CTRL + (n) - 1)
|
||||
|
||||
/* LDOx_CTRL values */
|
||||
#define S2MPU12_LDO_CTRL_OUT_MASK (0x3 << 6)
|
||||
#define S2MPU12_LDO_CTRL_OUT_ALWAYS_ON (0x3 << 6)
|
||||
|
||||
struct pmic_ldo {
|
||||
u8 num; /* LDO number */
|
||||
u8 en; /* "enable" bits value in LDOx_CTRL register */
|
||||
u8 out; /* "output voltage" bits value in LDOx_CTRL register */
|
||||
};
|
||||
|
||||
/* List of LDOs to enable only */
|
||||
static u8 pmic_ldos_en[] = {
|
||||
2, /* 1.8V/450mA: multiple lines */
|
||||
11, /* 3.0V/150mA: AVDD33_USB20 */
|
||||
23, /* 2.85V/800mA: VDD_EMMC_2P85 */
|
||||
27, /* 3.0V/150mA: MIPI_SWITCH_3V3 */
|
||||
28, /* 1.8V/150mA: HDMI_CONV_1V8 */
|
||||
30, /* 1.8V/150mA: NPU_VDD18 */
|
||||
};
|
||||
|
||||
/* List of LDOs to enable and set output voltage */
|
||||
static struct pmic_ldo pmic_ldos_en_out[] = {
|
||||
{
|
||||
.num = 24, /* 3.0V/800mA: VDD_LAN (LAN9514) */
|
||||
.en = S2MPU12_LDO_CTRL_OUT_ALWAYS_ON,
|
||||
.out = 0x3c, /* means 3.3V for LDO24 */
|
||||
}, {
|
||||
.num = 32, /* 3.3V/300mA: CAM_VDD (RPi camera module) */
|
||||
.en = S2MPU12_LDO_CTRL_OUT_ALWAYS_ON,
|
||||
.out = 0x3c, /* means 3.3V for LDO32 */
|
||||
},
|
||||
};
|
||||
|
||||
/* Enable specified LDO */
|
||||
static int pmic_ldo_set_en(struct acpm *acpm, u8 ldo)
|
||||
{
|
||||
const u8 reg = S2MPU12_PM_LDO_CTRL(ldo);
|
||||
u8 val;
|
||||
int err;
|
||||
|
||||
err = acpm_i3c_read(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val &= ~S2MPU12_LDO_CTRL_OUT_MASK;
|
||||
val |= S2MPU12_LDO_CTRL_OUT_ALWAYS_ON;
|
||||
|
||||
return acpm_i3c_write(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, val);
|
||||
}
|
||||
|
||||
/* Enable specified LDO and set its voltage to 0xc0 value */
|
||||
static int pmic_ldo_set_en_out(struct acpm *acpm, struct pmic_ldo *ldo)
|
||||
{
|
||||
const u8 reg = S2MPU12_PM_LDO_CTRL(ldo->num);
|
||||
const u8 val = ldo->en | ldo->out;
|
||||
|
||||
return acpm_i3c_write(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, val);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void pmic_trace_ldo(struct acpm *acpm, u8 ldo)
|
||||
{
|
||||
const u8 reg = S2MPU12_PM_LDO_CTRL(ldo);
|
||||
u8 val;
|
||||
int err;
|
||||
|
||||
err = acpm_i3c_read(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, &val);
|
||||
if (err)
|
||||
printf(" S2MPU12_PM_LDO%u_CTRL: Read error!\n", ldo);
|
||||
else
|
||||
printf(" S2MPU12_PM_LDO%u_CTRL: 0x%x\n", ldo, val);
|
||||
}
|
||||
|
||||
static void pmic_trace_ldos(struct acpm *acpm)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
printf("Tracing LDOs...\n");
|
||||
for (i = 0; i < ARRAY_SIZE(pmic_ldos_en); ++i)
|
||||
pmic_trace_ldo(acpm, pmic_ldos_en[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(pmic_ldos_en_out); ++i)
|
||||
pmic_trace_ldo(acpm, pmic_ldos_en_out[i].num);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* pmic_init() - Enable power regulators in S2MPU12 PMIC.
|
||||
* @acpm: Data for I3C communication with PMIC over ACPM protocol
|
||||
*
|
||||
* Enable LDOs needed for devices used in the bootloader and kernel.
|
||||
*
|
||||
* Return: 0 on success or non-zero code on error.
|
||||
*/
|
||||
int pmic_init(struct acpm *acpm)
|
||||
{
|
||||
size_t i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmic_ldos_en); ++i) {
|
||||
err = pmic_ldo_set_en(acpm, pmic_ldos_en[i]);
|
||||
if (err)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmic_ldos_en_out); ++i) {
|
||||
err = pmic_ldo_set_en_out(acpm, &pmic_ldos_en_out[i]);
|
||||
if (err)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
pmic_trace_ldos(acpm);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
board/samsung/e850-96/pmic.h
Normal file
14
board/samsung/e850-96/pmic.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2025 Linaro Ltd.
|
||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||
*/
|
||||
|
||||
#ifndef __E850_96_PMIC_H
|
||||
#define __E850_96_PMIC_H
|
||||
|
||||
#include "acpm.h"
|
||||
|
||||
int pmic_init(struct acpm *acpm);
|
||||
|
||||
#endif /* __E850_96_PMIC_H */
|
||||
@@ -19,7 +19,6 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
|
||||
CONFIG_EFI_CAPSULE_ON_DISK=y
|
||||
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
|
||||
CONFIG_BOOTSTD_FULL=y
|
||||
CONFIG_DEFAULT_FDT_FILE="exynos850-e850-96.dtb"
|
||||
# CONFIG_DISPLAY_CPUINFO is not set
|
||||
# CONFIG_BOARD_INIT is not set
|
||||
CONFIG_BOARD_LATE_INIT=y
|
||||
@@ -30,6 +29,8 @@ CONFIG_CMD_CLK=y
|
||||
CONFIG_CMD_DFU=y
|
||||
CONFIG_CMD_GPT=y
|
||||
CONFIG_CMD_MMC=y
|
||||
CONFIG_CMD_USB=y
|
||||
CONFIG_CMD_USB_MASS_STORAGE=y
|
||||
CONFIG_CMD_EFIDEBUG=y
|
||||
# CONFIG_CMD_DATE is not set
|
||||
CONFIG_CMD_RTC=y
|
||||
@@ -41,7 +42,7 @@ CONFIG_ENV_IS_IN_MMC=y
|
||||
CONFIG_ENV_REDUNDANT=y
|
||||
CONFIG_ENV_RELOC_GD_ENV_ADDR=y
|
||||
CONFIG_ENV_MMC_EMMC_HW_PARTITION=2
|
||||
CONFIG_NO_NET=y
|
||||
CONFIG_NET_LWIP=y
|
||||
CONFIG_CLK_EXYNOS850=y
|
||||
CONFIG_DFU_MMC=y
|
||||
CONFIG_USB_FUNCTION_FASTBOOT=y
|
||||
@@ -64,8 +65,13 @@ CONFIG_SYSRESET=y
|
||||
CONFIG_SYSRESET_SYSCON=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_DM_USB_GADGET=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_XHCI_DWC3=y
|
||||
CONFIG_USB_XHCI_DWC3_OF_SIMPLE=y
|
||||
CONFIG_USB_DWC3=y
|
||||
CONFIG_USB_DWC3_GENERIC=y
|
||||
CONFIG_USB_HOST_ETHER=y
|
||||
CONFIG_USB_ETHER_SMSC95XX=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_GADGET_MANUFACTURER="Samsung"
|
||||
CONFIG_USB_GADGET_VENDOR_NUM=0x18d1
|
||||
|
||||
@@ -192,7 +192,7 @@ static int exynos7420_clk_top0_probe(struct udevice *dev)
|
||||
static ulong exynos7420_peric1_get_rate(struct clk *clk)
|
||||
{
|
||||
struct clk in_clk;
|
||||
unsigned int ret;
|
||||
int ret;
|
||||
unsigned long freq = 0;
|
||||
|
||||
switch (clk->id) {
|
||||
|
||||
@@ -105,14 +105,10 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo,
|
||||
uint out_bytes, in_bytes;
|
||||
int toread;
|
||||
unsigned start = get_timer(0);
|
||||
int stopping;
|
||||
int step;
|
||||
|
||||
out_bytes = in_bytes = todo;
|
||||
|
||||
stopping = priv->skip_preamble && (flags & SPI_XFER_END) &&
|
||||
!(priv->mode & SPI_SLAVE);
|
||||
|
||||
/*
|
||||
* Try to transfer words if we can. This helps read performance at
|
||||
* SPI clock speeds above about 20MHz.
|
||||
@@ -161,12 +157,10 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo,
|
||||
while (rx_lvl >= step) {
|
||||
temp = readl(®s->rx_data);
|
||||
if (priv->skip_preamble) {
|
||||
if (temp == SPI_PREAMBLE_END_BYTE) {
|
||||
if (temp == SPI_PREAMBLE_END_BYTE)
|
||||
priv->skip_preamble = 0;
|
||||
stopping = 0;
|
||||
}
|
||||
} else {
|
||||
if (rxp || stopping) {
|
||||
if (rxp) {
|
||||
if (step == 4)
|
||||
*(uint32_t *)rxp = temp;
|
||||
else
|
||||
|
||||
@@ -91,6 +91,7 @@ static int dwc3_of_simple_remove(struct udevice *dev)
|
||||
static const struct udevice_id dwc3_of_simple_ids[] = {
|
||||
{ .compatible = "amlogic,meson-gxl-dwc3" },
|
||||
{ .compatible = "rockchip,rk3399-dwc3" },
|
||||
{ .compatible = "samsung,exynos850-dwusb3" },
|
||||
{ .compatible = "ti,dwc3" },
|
||||
{ }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user