- add Amlogic A1 clock driver
- add Amlogic A1 reset support - add USB Device support for Amlogic A1 - enable RNG on Amlogic A1 & Amlogic S4 - move Amlogic Secure Monitor to standalone driver -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPVPGJshWBf4d9CyLd9zb2sjISdEFAmUrvwIACgkQd9zb2sjI SdFhpRAAjJQXAE72iRoAii47hoYY/qhWAHZUe0tlu7lUFbhGRTTISukh3pZ48WCu ZXLgIUa30eXzGGa9eItmkt+cH2UuXWOPurQBILXOh/20eCrke47Y41rBfjmfcrWi UCDcf7BUojnCnC3zaSDNqUvJtsZvMwep3bjcPFstxZIzdZUML4VcwPNWClVhLABU uA5reiJjMiRVrAYp4LDJ1rrG8VgTdEbGt8+E4jPzQRvLfzYC3378SelxNW7UCxfK g88CHDNbWWPReiq71ylmKSSiN1TwnbbEjCmVVnv4DwPV8azwR0FkXJcebcpm4myP ckrjcb31hIiSEMNTbipytAZtW8qFAsidxuzRY8aEuTrsN+j1cM3QhmZNDUmASYgo r0pmkOiGUHFMFFMNXI2g5apJ4vIJfjHo+14TKoTLi+0xj8DqI0bnw5gGnrI6DlGv Dk/pOxvjPiiY/3JYxed3LzK3b1vKng4CMLzBp8GvQOZrINNGhFYo9ycML44EUwUs QVTW31GJEcuzoYlqV2e1JudGteUG3aoJ3QWW7aAcsG9+nHPHy9nslIcCOAgBA8Ja XiB5Z4bcRmTA9XRU93hkc2/rnyeNdsbyCBuqTswmVxsKn+/g5E+1qMG38OPJ9yot jVMIxCb6fOEmJIXnfD0FnNLRTySfCLCaS2dZVHIjoYCfLCOI1vk= =MjfD -----END PGP SIGNATURE----- Merge tag 'u-boot-amlogic-20231015' of https://source.denx.de/u-boot/custodians/u-boot-amlogic - add Amlogic A1 clock driver - add Amlogic A1 reset support - add USB Device support for Amlogic A1 - enable RNG on Amlogic A1 & Amlogic S4 - move Amlogic Secure Monitor to standalone driver
This commit is contained in:
commit
2e1577e836
|
@ -161,6 +161,7 @@ F: drivers/net/phy/meson-gxl.c
|
|||
F: drivers/adc/meson-saradc.c
|
||||
F: drivers/phy/meson*
|
||||
F: drivers/mmc/meson_gx_mmc.c
|
||||
F: drivers/sm/meson-sm.c
|
||||
F: drivers/spi/meson_spifc.c
|
||||
F: drivers/pinctrl/meson/
|
||||
F: drivers/power/domain/meson-gx-pwrc-vpu.c
|
||||
|
|
|
@ -124,6 +124,17 @@
|
|||
clock-names = "xtal", "pclk", "baud";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
hwrng: rng@5118 {
|
||||
compatible = "amlogic,meson-rng";
|
||||
reg = <0x0 0x5118 0x0 0x4>;
|
||||
};
|
||||
|
||||
sec_AO: ao-secure@5a20 {
|
||||
compatible = "amlogic,meson-gx-ao-secure", "syscon";
|
||||
reg = <0x0 0x5a20 0x0 0x140>;
|
||||
amlogic,has-chip-id;
|
||||
};
|
||||
};
|
||||
|
||||
gic: interrupt-controller@ff901000 {
|
||||
|
|
23
arch/arm/include/asm/arch-meson/clock-a1.h
Normal file
23
arch/arm/include/asm/arch-meson/clock-a1.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2018 - AmLogic, Inc.
|
||||
* Copyright 2023 (C) SberDevices, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_MESON_CLOCK_A1_H_
|
||||
#define _ARCH_MESON_CLOCK_A1_H_
|
||||
|
||||
/*
|
||||
* Clock controller register offsets
|
||||
*/
|
||||
#define A1_SYS_OSCIN_CTRL 0x0
|
||||
#define A1_SYS_CLK_CTRL0 0x10
|
||||
#define A1_SYS_CLK_EN0 0x1c
|
||||
#define A1_SAR_ADC_CLK_CTR 0xc0
|
||||
#define A1_SPIFC_CLK_CTRL 0xd8
|
||||
#define A1_USB_BUSCLK_CTRL 0xdc
|
||||
#define A1_SD_EMMC_CLK_CTRL 0xe0
|
||||
|
||||
#define A1_ANACTRL_FIXPLL_CTRL0 0x0
|
||||
|
||||
#endif /* _ARCH_MESON_CLOCK_A1_H_ */
|
|
@ -11,6 +11,7 @@ config MESON64_COMMON
|
|||
select PWRSEQ
|
||||
select MMC_PWRSEQ
|
||||
select BOARD_LATE_INIT
|
||||
select MESON_SM
|
||||
imply CMD_DM
|
||||
|
||||
config MESON_GX
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <regmap.h>
|
||||
#include <sm.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/arch/sm.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/global_data.h>
|
||||
|
@ -14,74 +18,63 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <dm.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <meson/sm.h>
|
||||
|
||||
#define FN_GET_SHARE_MEM_INPUT_BASE 0x82000020
|
||||
#define FN_GET_SHARE_MEM_OUTPUT_BASE 0x82000021
|
||||
#define FN_EFUSE_READ 0x82000030
|
||||
#define FN_EFUSE_WRITE 0x82000031
|
||||
#define FN_CHIP_ID 0x82000044
|
||||
#define FN_PWRDM_SET 0x82000093
|
||||
|
||||
static void *shmem_input;
|
||||
static void *shmem_output;
|
||||
|
||||
static void meson_init_shmem(void)
|
||||
static inline struct udevice *meson_get_sm_device(void)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
struct udevice *dev;
|
||||
int err;
|
||||
|
||||
if (shmem_input && shmem_output)
|
||||
return;
|
||||
err = uclass_first_device_err(UCLASS_SM, &dev);
|
||||
if (err) {
|
||||
pr_err("Mesom SM device not found\n");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
regs.regs[0] = FN_GET_SHARE_MEM_INPUT_BASE;
|
||||
smc_call(®s);
|
||||
shmem_input = (void *)regs.regs[0];
|
||||
|
||||
regs.regs[0] = FN_GET_SHARE_MEM_OUTPUT_BASE;
|
||||
smc_call(®s);
|
||||
shmem_output = (void *)regs.regs[0];
|
||||
|
||||
debug("Secure Monitor shmem: 0x%p 0x%p\n", shmem_input, shmem_output);
|
||||
return dev;
|
||||
}
|
||||
|
||||
ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
struct udevice *dev;
|
||||
struct pt_regs regs = { 0 };
|
||||
int err;
|
||||
|
||||
meson_init_shmem();
|
||||
dev = meson_get_sm_device();
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
regs.regs[0] = FN_EFUSE_READ;
|
||||
regs.regs[1] = offset;
|
||||
regs.regs[2] = size;
|
||||
|
||||
smc_call(®s);
|
||||
err = sm_call_read(dev, buffer, size,
|
||||
MESON_SMC_CMD_EFUSE_READ, ®s);
|
||||
if (err < 0)
|
||||
pr_err("Failed to read efuse memory (%d)\n", err);
|
||||
|
||||
if (regs.regs[0] == 0)
|
||||
return -1;
|
||||
|
||||
memcpy(buffer, shmem_output, min(size, regs.regs[0]));
|
||||
|
||||
return regs.regs[0];
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t meson_sm_write_efuse(uintptr_t offset, void *buffer, size_t size)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
struct udevice *dev;
|
||||
struct pt_regs regs = { 0 };
|
||||
int err;
|
||||
|
||||
meson_init_shmem();
|
||||
dev = meson_get_sm_device();
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
memcpy(shmem_input, buffer, size);
|
||||
|
||||
regs.regs[0] = FN_EFUSE_WRITE;
|
||||
regs.regs[1] = offset;
|
||||
regs.regs[2] = size;
|
||||
|
||||
smc_call(®s);
|
||||
err = sm_call_write(dev, buffer, size,
|
||||
MESON_SMC_CMD_EFUSE_WRITE, ®s);
|
||||
if (err < 0)
|
||||
pr_err("Failed to write efuse memory (%d)\n", err);
|
||||
|
||||
return regs.regs[0];
|
||||
return err;
|
||||
}
|
||||
|
||||
#define SM_CHIP_ID_LENGTH 119
|
||||
|
@ -90,18 +83,21 @@ ssize_t meson_sm_write_efuse(uintptr_t offset, void *buffer, size_t size)
|
|||
|
||||
int meson_sm_get_serial(void *buffer, size_t size)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
struct udevice *dev;
|
||||
struct pt_regs regs = { 0 };
|
||||
u8 id_buffer[SM_CHIP_ID_LENGTH];
|
||||
int err;
|
||||
|
||||
meson_init_shmem();
|
||||
dev = meson_get_sm_device();
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
regs.regs[0] = FN_CHIP_ID;
|
||||
regs.regs[1] = 0;
|
||||
regs.regs[2] = 0;
|
||||
err = sm_call_read(dev, id_buffer, SM_CHIP_ID_LENGTH,
|
||||
MESON_SMC_CMD_CHIP_ID_GET, ®s);
|
||||
if (err < 0)
|
||||
pr_err("Failed to read serial number (%d)\n", err);
|
||||
|
||||
smc_call(®s);
|
||||
|
||||
memcpy(buffer, shmem_output + SM_CHIP_ID_OFFSET,
|
||||
min_t(size_t, size, SM_CHIP_ID_SIZE));
|
||||
memcpy(buffer, id_buffer + SM_CHIP_ID_OFFSET, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -141,13 +137,21 @@ int meson_sm_get_reboot_reason(void)
|
|||
|
||||
int meson_sm_pwrdm_set(size_t index, int cmd)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
struct udevice *dev;
|
||||
struct pt_regs regs = { 0 };
|
||||
int err;
|
||||
|
||||
dev = meson_get_sm_device();
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
regs.regs[0] = FN_PWRDM_SET;
|
||||
regs.regs[1] = index;
|
||||
regs.regs[2] = cmd;
|
||||
|
||||
smc_call(®s);
|
||||
err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, ®s);
|
||||
if (err)
|
||||
pr_err("Failed to %s power domain ind=%zu (%d)\n", cmd == PWRDM_ON ?
|
||||
"enable" : "disable", index, err);
|
||||
|
||||
return regs.regs[0];
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -764,6 +764,10 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
sm: secure-monitor {
|
||||
compatible = "sandbox,sm";
|
||||
};
|
||||
};
|
||||
|
||||
fpga {
|
||||
|
|
|
@ -51,4 +51,7 @@ CONFIG_DEBUG_UART_SKIP_INIT=y
|
|||
CONFIG_MESON_SERIAL=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_DM_SPI=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_DM_USB_GADGET=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_WDT=y
|
||||
|
|
|
@ -280,6 +280,7 @@ CONFIG_RTC_HT1380=y
|
|||
CONFIG_SCSI=y
|
||||
CONFIG_DM_SCSI=y
|
||||
CONFIG_SANDBOX_SERIAL=y
|
||||
CONFIG_SM=y
|
||||
CONFIG_SMEM=y
|
||||
CONFIG_SANDBOX_SMEM=y
|
||||
CONFIG_SOUND=y
|
||||
|
|
|
@ -118,6 +118,8 @@ source "drivers/scsi/Kconfig"
|
|||
|
||||
source "drivers/serial/Kconfig"
|
||||
|
||||
source "drivers/sm/Kconfig"
|
||||
|
||||
source "drivers/smem/Kconfig"
|
||||
|
||||
source "drivers/sound/Kconfig"
|
||||
|
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/
|
|||
obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/
|
||||
obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
|
||||
obj-$(CONFIG_$(SPL_)SYSINFO) += sysinfo/
|
||||
obj-$(CONFIG_$(SPL_TPL_)SM) += sm/
|
||||
obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
|
||||
obj-$(CONFIG_$(SPL_)NVME) += nvme/
|
||||
obj-$(CONFIG_XEN) += xen/
|
||||
|
|
|
@ -21,3 +21,11 @@ config CLK_MESON_G12A
|
|||
help
|
||||
Enable clock support for the Amlogic G12A SoC family, such as
|
||||
the S905X/D2
|
||||
|
||||
config CLK_MESON_A1
|
||||
bool "Enable clock support for Amlogic A1"
|
||||
depends on CLK && ARCH_MESON
|
||||
default MESON_A1
|
||||
help
|
||||
Enable clock support for the Amlogic A1 SoC family, such as
|
||||
the A113L
|
||||
|
|
|
@ -8,3 +8,4 @@ obj-$(CONFIG_CLK_MESON_AXG) += axg.o
|
|||
obj-$(CONFIG_CLK_MESON_AXG) += axg-ao.o
|
||||
obj-$(CONFIG_CLK_MESON_G12A) += g12a.o
|
||||
obj-$(CONFIG_CLK_MESON_G12A) += g12a-ao.o
|
||||
obj-$(CONFIG_CLK_MESON_A1) += a1.o
|
||||
|
|
729
drivers/clk/meson/a1.c
Normal file
729
drivers/clk/meson/a1.c
Normal file
|
@ -0,0 +1,729 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2023 SberDevices, Inc.
|
||||
* Author: Igor Prusov <ivprusov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <asm/arch/clock-a1.h>
|
||||
#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
|
||||
#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
|
||||
#include "clk_meson.h"
|
||||
|
||||
/*
|
||||
* This driver supports both PLL and peripherals clock sources.
|
||||
* Following operations are supported:
|
||||
* - calculating clock frequency on a limited tree
|
||||
* - reading muxes and dividers
|
||||
* - enabling/disabling gates without propagation
|
||||
* - reparenting without rate propagation, only on muxes
|
||||
* - setting rates with limited reparenting, only on dividers with mux parent
|
||||
*/
|
||||
|
||||
#define NR_CLKS 154
|
||||
#define NR_PLL_CLKS 11
|
||||
|
||||
/* External clock IDs. Those should not overlap with regular IDs */
|
||||
#define EXTERNAL_XTAL (NR_CLKS + 0)
|
||||
#define EXTERNAL_FCLK_DIV2 (NR_CLKS + 1)
|
||||
#define EXTERNAL_FCLK_DIV3 (NR_CLKS + 2)
|
||||
#define EXTERNAL_FCLK_DIV5 (NR_CLKS + 3)
|
||||
#define EXTERNAL_FCLK_DIV7 (NR_CLKS + 4)
|
||||
|
||||
#define EXTERNAL_FIXPLL_IN (NR_PLL_CLKS + 1)
|
||||
|
||||
#define SET_PARM_VALUE(_priv, _parm, _val) \
|
||||
regmap_update_bits((_priv)->map, (_parm)->reg_off, \
|
||||
SETPMASK((_parm)->width, (_parm)->shift), \
|
||||
(_val) << (_parm)->shift)
|
||||
|
||||
#define GET_PARM_VALUE(_priv, _parm) \
|
||||
({ \
|
||||
uint _reg; \
|
||||
regmap_read((_priv)->map, (_parm)->reg_off, &_reg); \
|
||||
PARM_GET((_parm)->width, (_parm)->shift, _reg); \
|
||||
})
|
||||
|
||||
struct meson_clk {
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum meson_clk_type - The type of clock
|
||||
* @MESON_CLK_ANY: Special value that matches any clock type
|
||||
* @MESON_CLK_GATE: This clock is a gate
|
||||
* @MESON_CLK_MUX: This clock is a multiplexer
|
||||
* @MESON_CLK_DIV: This clock is a configurable divider
|
||||
* @MESON_CLK_FIXED_DIV: This clock is a configurable divider
|
||||
* @MESON_CLK_EXTERNAL: This is an external clock from different clock provider
|
||||
* @MESON_CLK_PLL: This is a PLL
|
||||
*/
|
||||
enum meson_clk_type {
|
||||
MESON_CLK_ANY = 0,
|
||||
MESON_CLK_GATE,
|
||||
MESON_CLK_MUX,
|
||||
MESON_CLK_DIV,
|
||||
MESON_CLK_FIXED_DIV,
|
||||
MESON_CLK_EXTERNAL,
|
||||
MESON_CLK_PLL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct meson_clk_info - The parameters defining a clock
|
||||
* @name: Name of the clock
|
||||
* @parm: Register bits description for muxes and dividers
|
||||
* @div: Fixed divider value
|
||||
* @parents: List of parent clock IDs
|
||||
* @type: Clock type
|
||||
*/
|
||||
struct meson_clk_info {
|
||||
const char *name;
|
||||
union {
|
||||
const struct parm *parm;
|
||||
u8 div;
|
||||
};
|
||||
const unsigned int *parents;
|
||||
const enum meson_clk_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct meson_clk_data - Clocks supported by clock provider
|
||||
* @num_clocks: Number of clocks
|
||||
* @clocks: Array of clock descriptions
|
||||
*
|
||||
*/
|
||||
struct meson_clk_data {
|
||||
const u8 num_clocks;
|
||||
const struct meson_clk_info **clocks;
|
||||
};
|
||||
|
||||
/* Clock description initialization macros */
|
||||
|
||||
/* A multiplexer */
|
||||
#define CLK_MUX(_name, _reg, _shift, _width, ...) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.parents = (const unsigned int[])__VA_ARGS__, \
|
||||
.parm = &(struct parm) { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
}, \
|
||||
.name = (_name), \
|
||||
.type = MESON_CLK_MUX, \
|
||||
})
|
||||
|
||||
/* A divider with an integral divisor */
|
||||
#define CLK_DIV(_name, _reg, _shift, _width, _parent) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.parents = (const unsigned int[]) { (_parent) }, \
|
||||
.parm = &(struct parm) { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
}, \
|
||||
.name = (_name), \
|
||||
.type = MESON_CLK_DIV, \
|
||||
})
|
||||
|
||||
/* A fixed divider */
|
||||
#define CLK_DIV_FIXED(_name, _div, _parent) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.parents = (const unsigned int[]) { (_parent) }, \
|
||||
.div = (_div), \
|
||||
.name = (_name), \
|
||||
.type = MESON_CLK_FIXED_DIV, \
|
||||
})
|
||||
|
||||
/* An external clock */
|
||||
#define CLK_EXTERNAL(_name) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.name = (_name), \
|
||||
.parents = (const unsigned int[]) { -ENOENT }, \
|
||||
.type = MESON_CLK_EXTERNAL, \
|
||||
})
|
||||
|
||||
/* A clock gate */
|
||||
#define CLK_GATE(_name, _reg, _shift, _parent) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.parents = (const unsigned int[]) { (_parent) }, \
|
||||
.parm = &(struct parm) { \
|
||||
.reg_off = (_reg), \
|
||||
.shift = (_shift), \
|
||||
.width = 1, \
|
||||
}, \
|
||||
.name = (_name), \
|
||||
.type = MESON_CLK_GATE, \
|
||||
})
|
||||
|
||||
/* A PLL clock */
|
||||
#define CLK_PLL(_name, _parent, ...) \
|
||||
(&(struct meson_clk_info){ \
|
||||
.name = (_name), \
|
||||
.parents = (const unsigned int[]) { (_parent) }, \
|
||||
.parm = (const struct parm[])__VA_ARGS__, \
|
||||
.type = MESON_CLK_PLL, \
|
||||
})
|
||||
|
||||
/* A1 peripherals clocks */
|
||||
static const struct meson_clk_info *meson_clocks[] = {
|
||||
[CLKID_SPIFC_SEL] = CLK_MUX("spifc_sel", A1_SPIFC_CLK_CTRL, 9, 2, {
|
||||
EXTERNAL_FCLK_DIV2,
|
||||
EXTERNAL_FCLK_DIV3,
|
||||
EXTERNAL_FCLK_DIV5,
|
||||
-ENOENT,
|
||||
}),
|
||||
[CLKID_SPIFC_SEL2] = CLK_MUX("spifc_sel2", A1_SPIFC_CLK_CTRL, 15, 1, {
|
||||
CLKID_SPIFC_DIV,
|
||||
EXTERNAL_XTAL,
|
||||
}),
|
||||
[CLKID_USB_BUS_SEL] = CLK_MUX("usb_bus_sel", A1_USB_BUSCLK_CTRL, 9, 2, {
|
||||
-ENOENT,
|
||||
CLKID_SYS,
|
||||
EXTERNAL_FCLK_DIV3,
|
||||
EXTERNAL_FCLK_DIV5,
|
||||
}),
|
||||
[CLKID_SYS] = CLK_MUX("sys", A1_SYS_CLK_CTRL0, 31, 1, {
|
||||
CLKID_SYS_A,
|
||||
CLKID_SYS_B,
|
||||
}),
|
||||
[CLKID_SYS_A_SEL] = CLK_MUX("sys_a_sel", A1_SYS_CLK_CTRL0, 10, 3, {
|
||||
-ENOENT,
|
||||
EXTERNAL_FCLK_DIV2,
|
||||
EXTERNAL_FCLK_DIV3,
|
||||
EXTERNAL_FCLK_DIV5,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
}),
|
||||
[CLKID_SYS_B_SEL] = CLK_MUX("sys_b_sel", A1_SYS_CLK_CTRL0, 26, 3, {
|
||||
-ENOENT,
|
||||
EXTERNAL_FCLK_DIV2,
|
||||
EXTERNAL_FCLK_DIV3,
|
||||
EXTERNAL_FCLK_DIV5,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
-ENOENT,
|
||||
}),
|
||||
|
||||
[CLKID_SPIFC_DIV] = CLK_DIV("spifc_div", A1_SPIFC_CLK_CTRL, 0, 8,
|
||||
CLKID_SPIFC_SEL
|
||||
),
|
||||
[CLKID_USB_BUS_DIV] = CLK_DIV("usb_bus_div", A1_USB_BUSCLK_CTRL, 0, 8,
|
||||
CLKID_USB_BUS_SEL
|
||||
),
|
||||
[CLKID_SYS_A_DIV] = CLK_DIV("sys_a_div", A1_SYS_CLK_CTRL0, 0, 10,
|
||||
CLKID_SYS_A_SEL
|
||||
),
|
||||
[CLKID_SYS_B_DIV] = CLK_DIV("sys_b_div", A1_SYS_CLK_CTRL0, 16, 10,
|
||||
CLKID_SYS_B_SEL
|
||||
),
|
||||
|
||||
[CLKID_SPIFC] = CLK_GATE("spifc", A1_SPIFC_CLK_CTRL, 8,
|
||||
CLKID_SPIFC_SEL2
|
||||
),
|
||||
[CLKID_USB_BUS] = CLK_GATE("usb_bus", A1_USB_BUSCLK_CTRL, 8,
|
||||
CLKID_USB_BUS_DIV
|
||||
),
|
||||
[CLKID_SYS_A] = CLK_GATE("sys_a", A1_SYS_CLK_CTRL0, 13,
|
||||
CLKID_SYS_A_DIV
|
||||
),
|
||||
[CLKID_SYS_B] = CLK_GATE("sys_b", A1_SYS_CLK_CTRL0, 29,
|
||||
CLKID_SYS_B_DIV
|
||||
),
|
||||
[CLKID_FIXPLL_IN] = CLK_GATE("fixpll_in", A1_SYS_OSCIN_CTRL, 1,
|
||||
EXTERNAL_XTAL
|
||||
),
|
||||
[CLKID_USB_PHY_IN] = CLK_GATE("usb_phy_in", A1_SYS_OSCIN_CTRL, 2,
|
||||
EXTERNAL_XTAL
|
||||
),
|
||||
[CLKID_USB_PHY] = CLK_GATE("usb_phy", A1_SYS_CLK_EN0, 27,
|
||||
CLKID_SYS
|
||||
),
|
||||
[CLKID_SARADC] = CLK_GATE("saradc", A1_SAR_ADC_CLK_CTR, 8,
|
||||
-ENOENT
|
||||
),
|
||||
[CLKID_SARADC_EN] = CLK_GATE("saradc_en", A1_SYS_CLK_EN0, 13,
|
||||
CLKID_SYS
|
||||
),
|
||||
|
||||
[EXTERNAL_XTAL] = CLK_EXTERNAL("xtal"),
|
||||
[EXTERNAL_FCLK_DIV2] = CLK_EXTERNAL("fclk_div2"),
|
||||
[EXTERNAL_FCLK_DIV3] = CLK_EXTERNAL("fclk_div3"),
|
||||
[EXTERNAL_FCLK_DIV5] = CLK_EXTERNAL("fclk_div5"),
|
||||
[EXTERNAL_FCLK_DIV7] = CLK_EXTERNAL("fclk_div7"),
|
||||
};
|
||||
|
||||
/* A1 PLL clocks */
|
||||
static const struct meson_clk_info *meson_pll_clocks[] = {
|
||||
[EXTERNAL_FIXPLL_IN] = CLK_EXTERNAL("fixpll_in"),
|
||||
|
||||
[CLKID_FIXED_PLL_DCO] = CLK_PLL("fixed_pll_dco", EXTERNAL_FIXPLL_IN, {
|
||||
{A1_ANACTRL_FIXPLL_CTRL0, 0, 8},
|
||||
{A1_ANACTRL_FIXPLL_CTRL0, 10, 5},
|
||||
}),
|
||||
|
||||
[CLKID_FCLK_DIV2_DIV] = CLK_DIV_FIXED("fclk_div2_div", 2,
|
||||
CLKID_FIXED_PLL
|
||||
),
|
||||
[CLKID_FCLK_DIV3_DIV] = CLK_DIV_FIXED("fclk_div3_div", 3,
|
||||
CLKID_FIXED_PLL
|
||||
),
|
||||
[CLKID_FCLK_DIV5_DIV] = CLK_DIV_FIXED("fclk_div5_div", 5,
|
||||
CLKID_FIXED_PLL
|
||||
),
|
||||
[CLKID_FCLK_DIV7_DIV] = CLK_DIV_FIXED("fclk_div7_div", 7,
|
||||
CLKID_FIXED_PLL
|
||||
),
|
||||
|
||||
[CLKID_FIXED_PLL] = CLK_GATE("fixed_pll", A1_ANACTRL_FIXPLL_CTRL0, 20,
|
||||
CLKID_FIXED_PLL_DCO
|
||||
),
|
||||
[CLKID_FCLK_DIV2] = CLK_GATE("fclk_div2", A1_ANACTRL_FIXPLL_CTRL0, 21,
|
||||
CLKID_FCLK_DIV2_DIV
|
||||
),
|
||||
[CLKID_FCLK_DIV3] = CLK_GATE("fclk_div3", A1_ANACTRL_FIXPLL_CTRL0, 22,
|
||||
CLKID_FCLK_DIV3_DIV
|
||||
),
|
||||
[CLKID_FCLK_DIV5] = CLK_GATE("fclk_div5", A1_ANACTRL_FIXPLL_CTRL0, 23,
|
||||
CLKID_FCLK_DIV5_DIV
|
||||
),
|
||||
[CLKID_FCLK_DIV7] = CLK_GATE("fclk_div7", A1_ANACTRL_FIXPLL_CTRL0, 24,
|
||||
CLKID_FCLK_DIV7_DIV
|
||||
),
|
||||
};
|
||||
|
||||
static const struct meson_clk_info *meson_clk_get_info(struct clk *clk, ulong id,
|
||||
enum meson_clk_type type)
|
||||
{
|
||||
struct meson_clk_data *data;
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
data = (struct meson_clk_data *)dev_get_driver_data(clk->dev);
|
||||
if (id >= data->num_clocks)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
info = data->clocks[id];
|
||||
if (!info)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (type != MESON_CLK_ANY && type != info->type)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id);
|
||||
|
||||
static int meson_set_gate(struct clk *clk, bool on)
|
||||
{
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
debug("%s: %sabling %lu\n", __func__, on ? "en" : "dis", clk->id);
|
||||
|
||||
info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
SET_PARM_VALUE(priv, info->parm, on);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_clk_enable(struct clk *clk)
|
||||
{
|
||||
return meson_set_gate(clk, true);
|
||||
}
|
||||
|
||||
static int meson_clk_disable(struct clk *clk)
|
||||
{
|
||||
return meson_set_gate(clk, false);
|
||||
}
|
||||
|
||||
static ulong meson_div_get_rate(struct clk *clk, unsigned long id)
|
||||
{
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
u16 n;
|
||||
ulong rate;
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_DIV);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
/* Actual divider value is (field value + 1), hence the increment */
|
||||
n = GET_PARM_VALUE(priv, info->parm) + 1;
|
||||
|
||||
rate = meson_clk_get_rate_by_id(clk, info->parents[0]);
|
||||
|
||||
return rate / n;
|
||||
}
|
||||
|
||||
static int meson_clk_get_parent(struct clk *clk, unsigned long id)
|
||||
{
|
||||
uint reg = 0;
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_ANY);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
/* For muxes we read currently selected parent from register,
|
||||
* for other types there is always only one element in parents array.
|
||||
*/
|
||||
if (info->type == MESON_CLK_MUX) {
|
||||
reg = GET_PARM_VALUE(priv, info->parm);
|
||||
if (IS_ERR_VALUE(reg))
|
||||
return reg;
|
||||
}
|
||||
|
||||
return info->parents[reg];
|
||||
}
|
||||
|
||||
static ulong meson_pll_get_rate(struct clk *clk, unsigned long id)
|
||||
{
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
const struct meson_clk_info *info;
|
||||
const struct parm *pm, *pn;
|
||||
ulong parent_rate_mhz;
|
||||
unsigned int parent;
|
||||
u16 n, m;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_ANY);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
pm = &info->parm[0];
|
||||
pn = &info->parm[1];
|
||||
|
||||
n = GET_PARM_VALUE(priv, pn);
|
||||
m = GET_PARM_VALUE(priv, pm);
|
||||
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
|
||||
parent = info->parents[0];
|
||||
parent_rate_mhz = meson_clk_get_rate_by_id(clk, parent) / 1000000;
|
||||
|
||||
return parent_rate_mhz * m / n * 1000000;
|
||||
}
|
||||
|
||||
static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id)
|
||||
{
|
||||
ulong rate, parent;
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
if (IS_ERR_VALUE(id))
|
||||
return id;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_ANY);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
switch (info->type) {
|
||||
case MESON_CLK_PLL:
|
||||
rate = meson_pll_get_rate(clk, id);
|
||||
break;
|
||||
case MESON_CLK_GATE:
|
||||
case MESON_CLK_MUX:
|
||||
parent = meson_clk_get_parent(clk, id);
|
||||
rate = meson_clk_get_rate_by_id(clk, parent);
|
||||
break;
|
||||
case MESON_CLK_DIV:
|
||||
rate = meson_div_get_rate(clk, id);
|
||||
break;
|
||||
case MESON_CLK_FIXED_DIV:
|
||||
parent = meson_clk_get_parent(clk, id);
|
||||
rate = meson_clk_get_rate_by_id(clk, parent) / info->div;
|
||||
break;
|
||||
case MESON_CLK_EXTERNAL: {
|
||||
int ret;
|
||||
struct clk external_clk;
|
||||
|
||||
ret = clk_get_by_name(clk->dev, info->name, &external_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rate = clk_get_rate(&external_clk);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rate = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static ulong meson_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
return meson_clk_get_rate_by_id(clk, clk->id);
|
||||
}
|
||||
|
||||
/* This implements rate propagation for dividers placed after multiplexer:
|
||||
* ---------|\
|
||||
* ..... | |---DIV--
|
||||
* ---------|/
|
||||
*/
|
||||
static ulong meson_composite_set_rate(struct clk *clk, ulong id, ulong rate)
|
||||
{
|
||||
unsigned int i, best_div_val;
|
||||
unsigned long best_delta, best_parent;
|
||||
const struct meson_clk_info *div;
|
||||
const struct meson_clk_info *mux;
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
|
||||
div = meson_clk_get_info(clk, id, MESON_CLK_DIV);
|
||||
if (IS_ERR(div))
|
||||
return PTR_ERR(div);
|
||||
|
||||
mux = meson_clk_get_info(clk, div->parents[0], MESON_CLK_MUX);
|
||||
if (IS_ERR(mux))
|
||||
return PTR_ERR(mux);
|
||||
|
||||
best_parent = -EINVAL;
|
||||
best_delta = ULONG_MAX;
|
||||
for (i = 0; i < (1 << mux->parm->width); i++) {
|
||||
unsigned long parent_rate, delta;
|
||||
unsigned int div_val;
|
||||
|
||||
parent_rate = meson_clk_get_rate_by_id(clk, mux->parents[i]);
|
||||
if (IS_ERR_VALUE(parent_rate))
|
||||
continue;
|
||||
|
||||
/* If overflow, try to use max divider value */
|
||||
div_val = min(DIV_ROUND_CLOSEST(parent_rate, rate),
|
||||
(1UL << div->parm->width));
|
||||
|
||||
delta = abs(rate - (parent_rate / div_val));
|
||||
if (delta < best_delta) {
|
||||
best_delta = delta;
|
||||
best_div_val = div_val;
|
||||
best_parent = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR_VALUE(best_parent))
|
||||
return best_parent;
|
||||
|
||||
SET_PARM_VALUE(priv, mux->parm, best_parent);
|
||||
/* Divider is set to (field value + 1), hence the decrement */
|
||||
SET_PARM_VALUE(priv, div->parm, best_div_val - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate);
|
||||
|
||||
static ulong meson_mux_set_rate(struct clk *clk, unsigned long id, ulong rate)
|
||||
{
|
||||
int i;
|
||||
ulong ret = -EINVAL;
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_MUX);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
for (i = 0; i < (1 << info->parm->width); i++) {
|
||||
ret = meson_clk_set_rate_by_id(clk, info->parents[i], rate);
|
||||
if (!ret) {
|
||||
SET_PARM_VALUE(priv, info->parm, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Rate propagation is implemented for a subcection of a clock tree, that is
|
||||
* required at boot stage.
|
||||
*/
|
||||
static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate)
|
||||
{
|
||||
switch (id) {
|
||||
case CLKID_SPIFC_DIV:
|
||||
case CLKID_USB_BUS_DIV:
|
||||
return meson_composite_set_rate(clk, id, rate);
|
||||
case CLKID_SPIFC:
|
||||
case CLKID_USB_BUS: {
|
||||
unsigned long parent = meson_clk_get_parent(clk, id);
|
||||
|
||||
return meson_clk_set_rate_by_id(clk, parent, rate);
|
||||
}
|
||||
case CLKID_SPIFC_SEL2:
|
||||
return meson_mux_set_rate(clk, id, rate);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ulong meson_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
return meson_clk_set_rate_by_id(clk, clk->id, rate);
|
||||
}
|
||||
|
||||
static int meson_mux_set_parent_by_id(struct clk *clk, unsigned int parent_id)
|
||||
{
|
||||
unsigned int i, parent_index;
|
||||
struct meson_clk *priv = dev_get_priv(clk->dev);
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
info = meson_clk_get_info(clk, clk->id, MESON_CLK_MUX);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
parent_index = -EINVAL;
|
||||
for (i = 0; i < (1 << info->parm->width); i++) {
|
||||
if (parent_id == info->parents[i]) {
|
||||
parent_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR_VALUE(parent_index))
|
||||
return parent_index;
|
||||
|
||||
SET_PARM_VALUE(priv, info->parm, parent_index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_clk_set_parent(struct clk *clk, struct clk *parent_clk)
|
||||
{
|
||||
return meson_mux_set_parent_by_id(clk, parent_clk->id);
|
||||
}
|
||||
|
||||
static struct clk_ops meson_clk_ops = {
|
||||
.disable = meson_clk_disable,
|
||||
.enable = meson_clk_enable,
|
||||
.get_rate = meson_clk_get_rate,
|
||||
.set_rate = meson_clk_set_rate,
|
||||
.set_parent = meson_clk_set_parent,
|
||||
};
|
||||
|
||||
static int meson_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct meson_clk *priv = dev_get_priv(dev);
|
||||
|
||||
return regmap_init_mem(dev_ofnode(dev), &priv->map);
|
||||
}
|
||||
|
||||
struct meson_clk_data meson_a1_peripherals_info = {
|
||||
.clocks = meson_clocks,
|
||||
.num_clocks = ARRAY_SIZE(meson_clocks),
|
||||
};
|
||||
|
||||
struct meson_clk_data meson_a1_pll_info = {
|
||||
.clocks = meson_pll_clocks,
|
||||
.num_clocks = ARRAY_SIZE(meson_pll_clocks),
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_clk_ids[] = {
|
||||
{
|
||||
.compatible = "amlogic,a1-peripherals-clkc",
|
||||
.data = (ulong)&meson_a1_peripherals_info,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,a1-pll-clkc",
|
||||
.data = (ulong)&meson_a1_pll_info,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_clk) = {
|
||||
.name = "meson-clk-a1",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = meson_clk_ids,
|
||||
.priv_auto = sizeof(struct meson_clk),
|
||||
.ops = &meson_clk_ops,
|
||||
.probe = meson_clk_probe,
|
||||
};
|
||||
|
||||
static const char *meson_clk_get_name(struct clk *clk, int id)
|
||||
{
|
||||
const struct meson_clk_info *info;
|
||||
|
||||
info = meson_clk_get_info(clk, id, MESON_CLK_ANY);
|
||||
|
||||
return IS_ERR(info) ? "unknown" : info->name;
|
||||
}
|
||||
|
||||
static int meson_clk_dump(struct clk *clk)
|
||||
{
|
||||
const struct meson_clk_info *info;
|
||||
struct meson_clk *priv;
|
||||
unsigned long rate;
|
||||
char *state, frequency[80];
|
||||
int parent;
|
||||
|
||||
priv = dev_get_priv(clk->dev);
|
||||
|
||||
info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY);
|
||||
if (IS_ERR(info) || !info->name)
|
||||
return -EINVAL;
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
if (IS_ERR_VALUE(rate))
|
||||
sprintf(frequency, "unknown");
|
||||
else
|
||||
sprintf(frequency, "%lu", rate);
|
||||
|
||||
if (info->type == MESON_CLK_GATE)
|
||||
state = GET_PARM_VALUE(priv, info->parm) ? "enabled" : "disabled";
|
||||
else
|
||||
state = "N/A";
|
||||
|
||||
parent = meson_clk_get_parent(clk, clk->id);
|
||||
printf("%15s%20s%20s%15s\n",
|
||||
info->name,
|
||||
frequency,
|
||||
meson_clk_get_name(clk, parent),
|
||||
state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_clk_dump_dev(struct udevice *dev)
|
||||
{
|
||||
int i;
|
||||
struct meson_clk_data *data;
|
||||
const char *sep = "--------------------";
|
||||
|
||||
printf("%s:\n", dev->name);
|
||||
printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
|
||||
printf("%15s%20s%20s%15s\n", "clk", "frequency", "parent", "state");
|
||||
printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
|
||||
|
||||
data = (struct meson_clk_data *)dev_get_driver_data(dev);
|
||||
for (i = 0; i < data->num_clocks; i++) {
|
||||
meson_clk_dump(&(struct clk){
|
||||
.dev = dev,
|
||||
.id = i
|
||||
});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int soc_clk_dump(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int i = 0;
|
||||
|
||||
while (!uclass_get_device(UCLASS_CLK, i++, &dev)) {
|
||||
if (dev->driver == DM_DRIVER_GET(meson_clk)) {
|
||||
meson_clk_dump_dev(dev);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -200,7 +200,7 @@ config MESON_GXL_USB_PHY
|
|||
|
||||
config MESON_G12A_USB_PHY
|
||||
bool "Amlogic Meson G12A USB PHYs"
|
||||
depends on PHY && ARCH_MESON && MESON_G12A
|
||||
depends on PHY && ARCH_MESON && (MESON_G12A || MESON_A1)
|
||||
imply REGMAP
|
||||
help
|
||||
This is the generic phy driver for the Amlogic Meson G12A
|
||||
|
|
|
@ -19,17 +19,34 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/printk.h>
|
||||
#include <power/regulator.h>
|
||||
#include <power-domain.h>
|
||||
#include <reset.h>
|
||||
#include <clk.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define PHY_CTRL_R0 0x0
|
||||
#define PHY_CTRL_R1 0x4
|
||||
#define PHY_CTRL_R2 0x8
|
||||
|
||||
#define PHY_CTRL_R3 0xc
|
||||
#define PHY_CTRL_R3_SQUELCH_REF GENMASK(1, 0)
|
||||
#define PHY_CTRL_R3_HSDIC_REF GENMASK(3, 2)
|
||||
#define PHY_CTRL_R3_DISC_THRESH GENMASK(7, 4)
|
||||
|
||||
#define PHY_CTRL_R4 0x10
|
||||
#define PHY_CTRL_R4_CALIB_CODE_7_0 GENMASK(7, 0)
|
||||
#define PHY_CTRL_R4_CALIB_CODE_15_8 GENMASK(15, 8)
|
||||
#define PHY_CTRL_R4_CALIB_CODE_23_16 GENMASK(23, 16)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_EN BIT(24)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_RESET_N BIT(25)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_DONE BIT(26)
|
||||
#define PHY_CTRL_R4_TEST_BYPASS_MODE_EN BIT(27)
|
||||
#define PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0 GENMASK(29, 28)
|
||||
#define PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2 GENMASK(31, 30)
|
||||
|
||||
#define PHY_CTRL_R5 0x14
|
||||
#define PHY_CTRL_R6 0x18
|
||||
#define PHY_CTRL_R7 0x1c
|
||||
|
@ -38,35 +55,131 @@
|
|||
#define PHY_CTRL_R10 0x28
|
||||
#define PHY_CTRL_R11 0x2c
|
||||
#define PHY_CTRL_R12 0x30
|
||||
|
||||
#define PHY_CTRL_R13 0x34
|
||||
#define PHY_CTRL_R13_CUSTOM_PATTERN_19 GENMASK(7, 0)
|
||||
#define PHY_CTRL_R13_LOAD_STAT BIT(14)
|
||||
#define PHY_CTRL_R13_UPDATE_PMA_SIGNALS BIT(15)
|
||||
#define PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET GENMASK(20, 16)
|
||||
#define PHY_CTRL_R13_CLEAR_HOLD_HS_DISCONNECT BIT(21)
|
||||
#define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_VAL BIT(22)
|
||||
#define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_EN BIT(23)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_EN BIT(24)
|
||||
#define PHY_CTRL_R13_I_C2L_FS_EN BIT(25)
|
||||
#define PHY_CTRL_R13_I_C2L_LS_EN BIT(26)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_OE BIT(27)
|
||||
#define PHY_CTRL_R13_I_C2L_FS_OE BIT(28)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_RX_EN BIT(29)
|
||||
#define PHY_CTRL_R13_I_C2L_FSLS_RX_EN BIT(30)
|
||||
|
||||
#define PHY_CTRL_R14 0x38
|
||||
#define PHY_CTRL_R15 0x3c
|
||||
|
||||
#define PHY_CTRL_R16 0x40
|
||||
#define PHY_CTRL_R16_MPLL_M GENMASK(8, 0)
|
||||
#define PHY_CTRL_R16_MPLL_N GENMASK(14, 10)
|
||||
#define PHY_CTRL_R16_MPLL_TDC_MODE BIT(20)
|
||||
#define PHY_CTRL_R16_MPLL_SDM_EN BIT(21)
|
||||
#define PHY_CTRL_R16_MPLL_LOAD BIT(22)
|
||||
#define PHY_CTRL_R16_MPLL_DCO_SDM_EN BIT(23)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_LONG GENMASK(25, 24)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_F BIT(26)
|
||||
#define PHY_CTRL_R16_MPLL_FAST_LOCK BIT(27)
|
||||
#define PHY_CTRL_R16_MPLL_EN BIT(28)
|
||||
#define PHY_CTRL_R16_MPLL_RESET BIT(29)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK BIT(30)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_DIG BIT(31)
|
||||
|
||||
#define PHY_CTRL_R17 0x44
|
||||
#define PHY_CTRL_R17_MPLL_FRAC_IN GENMASK(13, 0)
|
||||
#define PHY_CTRL_R17_MPLL_FIX_EN BIT(16)
|
||||
#define PHY_CTRL_R17_MPLL_LAMBDA1 GENMASK(19, 17)
|
||||
#define PHY_CTRL_R17_MPLL_LAMBDA0 GENMASK(22, 20)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_MODE BIT(23)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_PVT2 GENMASK(27, 24)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_PVT1 GENMASK(31, 28)
|
||||
|
||||
#define PHY_CTRL_R18 0x48
|
||||
#define PHY_CTRL_R18_MPLL_LKW_SEL GENMASK(1, 0)
|
||||
#define PHY_CTRL_R18_MPLL_LK_W GENMASK(5, 2)
|
||||
#define PHY_CTRL_R18_MPLL_LK_S GENMASK(11, 6)
|
||||
#define PHY_CTRL_R18_MPLL_DCO_M_EN BIT(12)
|
||||
#define PHY_CTRL_R18_MPLL_DCO_CLK_SEL BIT(13)
|
||||
#define PHY_CTRL_R18_MPLL_PFD_GAIN GENMASK(15, 14)
|
||||
#define PHY_CTRL_R18_MPLL_ROU GENMASK(18, 16)
|
||||
#define PHY_CTRL_R18_MPLL_DATA_SEL GENMASK(21, 19)
|
||||
#define PHY_CTRL_R18_MPLL_BIAS_ADJ GENMASK(23, 22)
|
||||
#define PHY_CTRL_R18_MPLL_BB_MODE GENMASK(25, 24)
|
||||
#define PHY_CTRL_R18_MPLL_ALPHA GENMASK(28, 26)
|
||||
#define PHY_CTRL_R18_MPLL_ADJ_LDO GENMASK(30, 29)
|
||||
#define PHY_CTRL_R18_MPLL_ACG_RANGE BIT(31)
|
||||
|
||||
#define PHY_CTRL_R19 0x4c
|
||||
|
||||
#define PHY_CTRL_R20 0x50
|
||||
#define PHY_CTRL_R20_USB2_IDDET_EN BIT(0)
|
||||
#define PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0 GENMASK(3, 1)
|
||||
#define PHY_CTRL_R20_USB2_OTG_VBUSDET_EN BIT(4)
|
||||
#define PHY_CTRL_R20_USB2_AMON_EN BIT(5)
|
||||
#define PHY_CTRL_R20_USB2_CAL_CODE_R5 BIT(6)
|
||||
#define PHY_CTRL_R20_BYPASS_OTG_DET BIT(7)
|
||||
#define PHY_CTRL_R20_USB2_DMON_EN BIT(8)
|
||||
#define PHY_CTRL_R20_USB2_DMON_SEL_3_0 GENMASK(12, 9)
|
||||
#define PHY_CTRL_R20_USB2_EDGE_DRV_EN BIT(13)
|
||||
#define PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0 GENMASK(15, 14)
|
||||
#define PHY_CTRL_R20_USB2_BGR_ADJ_4_0 GENMASK(20, 16)
|
||||
#define PHY_CTRL_R20_USB2_BGR_START BIT(21)
|
||||
#define PHY_CTRL_R20_USB2_BGR_VREF_4_0 GENMASK(28, 24)
|
||||
#define PHY_CTRL_R20_USB2_BGR_DBG_1_0 GENMASK(30, 29)
|
||||
#define PHY_CTRL_R20_BYPASS_CAL_DONE_R5 BIT(31)
|
||||
|
||||
#define PHY_CTRL_R21 0x54
|
||||
#define PHY_CTRL_R21_USB2_BGR_FORCE BIT(0)
|
||||
#define PHY_CTRL_R21_USB2_CAL_ACK_EN BIT(1)
|
||||
#define PHY_CTRL_R21_USB2_OTG_ACA_EN BIT(2)
|
||||
#define PHY_CTRL_R21_USB2_TX_STRG_PD BIT(3)
|
||||
#define PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0 GENMASK(5, 4)
|
||||
#define PHY_CTRL_R21_BYPASS_UTMI_CNTR GENMASK(15, 6)
|
||||
#define PHY_CTRL_R21_BYPASS_UTMI_REG GENMASK(25, 20)
|
||||
|
||||
#define PHY_CTRL_R22 0x58
|
||||
#define PHY_CTRL_R23 0x5c
|
||||
|
||||
#define RESET_COMPLETE_TIME 1000
|
||||
#define PLL_RESET_COMPLETE_TIME 100
|
||||
|
||||
enum meson_soc_id {
|
||||
MESON_SOC_A1,
|
||||
MESON_SOC_G12A,
|
||||
};
|
||||
|
||||
struct phy_meson_g12a_usb2_priv {
|
||||
struct regmap *regmap;
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
struct clk clk;
|
||||
#endif
|
||||
struct reset_ctl reset;
|
||||
#if CONFIG_IS_ENABLED(POWER_DOMAIN)
|
||||
struct power_domain pwrdm;
|
||||
#endif
|
||||
int soc_id;
|
||||
};
|
||||
|
||||
static int phy_meson_g12a_usb2_init(struct phy *phy)
|
||||
{
|
||||
struct udevice *dev = phy->dev;
|
||||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
|
||||
pr_err("failed to enable PHY clock\n");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = reset_assert(&priv->reset);
|
||||
udelay(1);
|
||||
ret |= reset_deassert(&priv->reset);
|
||||
|
@ -79,25 +192,91 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
|
|||
regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
|
||||
|
||||
/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16,
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) |
|
||||
PHY_CTRL_R16_MPLL_LOAD |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) |
|
||||
PHY_CTRL_R16_MPLL_FAST_LOCK |
|
||||
PHY_CTRL_R16_MPLL_EN |
|
||||
PHY_CTRL_R16_MPLL_RESET);
|
||||
|
||||
regmap_write(priv->regmap, PHY_CTRL_R17,
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FRAC_IN, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA1, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA0, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9));
|
||||
|
||||
value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
|
||||
PHY_CTRL_R18_MPLL_ACG_RANGE;
|
||||
|
||||
if (priv->soc_id == MESON_SOC_A1)
|
||||
value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL;
|
||||
|
||||
regmap_write(priv->regmap, PHY_CTRL_R18, value);
|
||||
|
||||
udelay(PLL_RESET_COMPLETE_TIME);
|
||||
|
||||
/* UnReset PLL */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16,
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) |
|
||||
PHY_CTRL_R16_MPLL_LOAD |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) |
|
||||
PHY_CTRL_R16_MPLL_FAST_LOCK |
|
||||
PHY_CTRL_R16_MPLL_EN);
|
||||
|
||||
/* PHY Tuning */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R20,
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0, 4) |
|
||||
PHY_CTRL_R20_USB2_OTG_VBUSDET_EN |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_DMON_SEL_3_0, 15) |
|
||||
PHY_CTRL_R20_USB2_EDGE_DRV_EN |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_ADJ_4_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0));
|
||||
|
||||
if (priv->soc_id == MESON_SOC_G12A)
|
||||
regmap_write(priv->regmap, PHY_CTRL_R4,
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
|
||||
PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
|
||||
FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
|
||||
else if (priv->soc_id == MESON_SOC_A1)
|
||||
regmap_write(priv->regmap, PHY_CTRL_R21,
|
||||
PHY_CTRL_R21_USB2_CAL_ACK_EN |
|
||||
PHY_CTRL_R21_USB2_TX_STRG_PD |
|
||||
FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2));
|
||||
|
||||
/* Tuning Disconnect Threshold */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R3,
|
||||
FIELD_PREP(PHY_CTRL_R3_SQUELCH_REF, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3));
|
||||
|
||||
/* Analog Settings */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R14, 0);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
|
||||
if (priv->soc_id == MESON_SOC_G12A) {
|
||||
regmap_write(priv->regmap, PHY_CTRL_R14, 0);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R13,
|
||||
PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
|
||||
FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
|
||||
} else if (priv->soc_id == MESON_SOC_A1) {
|
||||
regmap_write(priv->regmap, PHY_CTRL_R13,
|
||||
FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -108,6 +287,10 @@ static int phy_meson_g12a_usb2_exit(struct phy *phy)
|
|||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
clk_disable(&priv->clk);
|
||||
#endif
|
||||
|
||||
ret = reset_assert(&priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -125,6 +308,8 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev)
|
|||
struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->soc_id = (enum meson_soc_id)dev_get_driver_data(dev);
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -141,24 +326,40 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(POWER_DOMAIN)
|
||||
ret = power_domain_get(dev, &priv->pwrdm);
|
||||
if (ret < 0 && ret != -ENODEV) {
|
||||
pr_err("failed to get power domain\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != -ENODEV) {
|
||||
ret = power_domain_on(&priv->pwrdm);
|
||||
if (ret < 0) {
|
||||
pr_err("failed to enable power domain\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_get_by_index(dev, 0, &priv->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
|
||||
pr_err("failed to enable PHY clock\n");
|
||||
clk_free(&priv->clk);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id meson_g12a_usb2_phy_ids[] = {
|
||||
{ .compatible = "amlogic,g12a-usb2-phy" },
|
||||
{
|
||||
.compatible = "amlogic,g12a-usb2-phy",
|
||||
.data = (ulong)MESON_SOC_G12A,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,a1-usb2-phy",
|
||||
.data = (ulong)MESON_SOC_A1,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -13,18 +13,26 @@
|
|||
#include <reset-uclass.h>
|
||||
#include <regmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define REG_COUNT 8
|
||||
#define BITS_PER_REG 32
|
||||
#define LEVEL_OFFSET 0x7c
|
||||
|
||||
struct meson_reset_drvdata {
|
||||
unsigned int reg_count;
|
||||
unsigned int level_offset;
|
||||
};
|
||||
|
||||
struct meson_reset_priv {
|
||||
struct regmap *regmap;
|
||||
struct meson_reset_drvdata *drvdata;
|
||||
};
|
||||
|
||||
static int meson_reset_request(struct reset_ctl *reset_ctl)
|
||||
{
|
||||
if (reset_ctl->id > (REG_COUNT * BITS_PER_REG))
|
||||
struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
|
||||
struct meson_reset_drvdata *data = priv->drvdata;
|
||||
|
||||
if (reset_ctl->id > (data->reg_count * BITS_PER_REG))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
@ -33,9 +41,10 @@ static int meson_reset_request(struct reset_ctl *reset_ctl)
|
|||
static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert)
|
||||
{
|
||||
struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
|
||||
struct meson_reset_drvdata *data = priv->drvdata;
|
||||
uint bank = reset_ctl->id / BITS_PER_REG;
|
||||
uint offset = reset_ctl->id % BITS_PER_REG;
|
||||
uint reg_offset = LEVEL_OFFSET + (bank << 2);
|
||||
uint reg_offset = data->level_offset + (bank << 2);
|
||||
uint val;
|
||||
|
||||
regmap_read(priv->regmap, reg_offset, &val);
|
||||
|
@ -64,15 +73,36 @@ struct reset_ops meson_reset_ops = {
|
|||
.rst_deassert = meson_reset_deassert,
|
||||
};
|
||||
|
||||
static const struct meson_reset_drvdata meson_gxbb_data = {
|
||||
.reg_count = 8,
|
||||
.level_offset = 0x7c,
|
||||
};
|
||||
|
||||
static const struct meson_reset_drvdata meson_a1_data = {
|
||||
.reg_count = 3,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_reset_ids[] = {
|
||||
{ .compatible = "amlogic,meson-gxbb-reset" },
|
||||
{ .compatible = "amlogic,meson-axg-reset" },
|
||||
{
|
||||
.compatible = "amlogic,meson-gxbb-reset",
|
||||
.data = (ulong)&meson_gxbb_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-axg-reset",
|
||||
.data = (ulong)&meson_gxbb_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-a1-reset",
|
||||
.data = (ulong)&meson_a1_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int meson_reset_probe(struct udevice *dev)
|
||||
{
|
||||
struct meson_reset_priv *priv = dev_get_priv(dev);
|
||||
priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev);
|
||||
|
||||
return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,23 @@
|
|||
#include <dm.h>
|
||||
#include <rng.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#define RNG_DATA 0x00
|
||||
#define RNG_S4_DATA 0x08
|
||||
#define RNG_S4_CFG 0x00
|
||||
|
||||
#define RUN_BIT BIT(0)
|
||||
#define SEED_READY_STS_BIT BIT(31)
|
||||
|
||||
struct meson_rng_priv {
|
||||
u32 (*read)(fdt_addr_t base);
|
||||
};
|
||||
|
||||
struct meson_rng_plat {
|
||||
fdt_addr_t base;
|
||||
struct clk clk;
|
||||
struct meson_rng_priv *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -27,10 +40,11 @@ struct meson_rng_plat {
|
|||
static int meson_rng_read(struct udevice *dev, void *data, size_t len)
|
||||
{
|
||||
struct meson_rng_plat *pdata = dev_get_plat(dev);
|
||||
struct meson_rng_priv *priv = pdata->priv;
|
||||
char *buffer = (char *)data;
|
||||
|
||||
while (len) {
|
||||
u32 rand = readl(pdata->base);
|
||||
u32 rand = priv->read(pdata->base);
|
||||
size_t step;
|
||||
|
||||
if (len >= 4)
|
||||
|
@ -44,6 +58,47 @@ static int meson_rng_read(struct udevice *dev, void *data, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int meson_rng_wait_status(void __iomem *cfg_addr, int bit)
|
||||
{
|
||||
u32 status = 0;
|
||||
int ret;
|
||||
|
||||
ret = readl_relaxed_poll_timeout(cfg_addr,
|
||||
status, !(status & bit),
|
||||
10000);
|
||||
if (ret)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 meson_common_rng_read(fdt_addr_t base)
|
||||
{
|
||||
return readl(base);
|
||||
}
|
||||
|
||||
static u32 meson_s4_rng_read(fdt_addr_t base)
|
||||
{
|
||||
void __iomem *cfg_addr = (void *)base + RNG_S4_CFG;
|
||||
int err;
|
||||
|
||||
writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr);
|
||||
|
||||
err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT);
|
||||
if (err) {
|
||||
pr_err("Seed isn't ready, try again\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = meson_rng_wait_status(cfg_addr, RUN_BIT);
|
||||
if (err) {
|
||||
pr_err("Can't get random number, try again\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return readl_relaxed(base + RNG_S4_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* meson_rng_probe() - probe rng device
|
||||
*
|
||||
|
@ -59,6 +114,8 @@ static int meson_rng_probe(struct udevice *dev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
pdata->priv = (struct meson_rng_priv *)dev_get_driver_data(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -102,9 +159,22 @@ static const struct dm_rng_ops meson_rng_ops = {
|
|||
.read = meson_rng_read,
|
||||
};
|
||||
|
||||
static const struct meson_rng_priv meson_rng_priv = {
|
||||
.read = meson_common_rng_read,
|
||||
};
|
||||
|
||||
static const struct meson_rng_priv meson_rng_priv_s4 = {
|
||||
.read = meson_s4_rng_read,
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_rng_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-rng",
|
||||
.data = (ulong)&meson_rng_priv,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-s4-rng",
|
||||
.data = (ulong)&meson_rng_priv_s4,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
|
9
drivers/sm/Kconfig
Normal file
9
drivers/sm/Kconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
config SM
|
||||
bool "Enable Secure Monitor driver support"
|
||||
|
||||
config MESON_SM
|
||||
bool "Amlogic Secure Monitor driver"
|
||||
select SM
|
||||
default n
|
||||
help
|
||||
Say y here to enable the Amlogic secure monitor driver.
|
5
drivers/sm/Makefile
Normal file
5
drivers/sm/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-y += sm-uclass.o
|
||||
obj-$(CONFIG_SANDBOX) += sandbox-sm.o
|
||||
obj-$(CONFIG_MESON_SM) += meson-sm.o
|
198
drivers/sm/meson-sm.c
Normal file
198
drivers/sm/meson-sm.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <sm.h>
|
||||
#include <sm-uclass.h>
|
||||
#include <stdlib.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/system.h>
|
||||
#include <meson/sm.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
struct meson_sm_cmd {
|
||||
u32 smc_id;
|
||||
};
|
||||
|
||||
#define SET_CMD(index, id) \
|
||||
[index] = { \
|
||||
.smc_id = (id), \
|
||||
}
|
||||
|
||||
struct meson_sm_data {
|
||||
u32 cmd_get_shmem_in;
|
||||
u32 cmd_get_shmem_out;
|
||||
unsigned int shmem_size;
|
||||
struct meson_sm_cmd cmd[];
|
||||
};
|
||||
|
||||
struct meson_sm_priv {
|
||||
void *sm_shmem_in;
|
||||
void *sm_shmem_out;
|
||||
const struct meson_sm_data *data;
|
||||
};
|
||||
|
||||
static unsigned long __meson_sm_call(u32 cmd, const struct pt_regs *args)
|
||||
{
|
||||
struct pt_regs r = *args;
|
||||
|
||||
r.regs[0] = cmd;
|
||||
smc_call(&r);
|
||||
|
||||
return r.regs[0];
|
||||
};
|
||||
|
||||
static u32 meson_sm_get_cmd(const struct meson_sm_data *data,
|
||||
u32 cmd_index)
|
||||
{
|
||||
struct meson_sm_cmd cmd;
|
||||
|
||||
if (cmd_index >= MESON_SMC_CMD_COUNT)
|
||||
return 0;
|
||||
|
||||
cmd = data->cmd[cmd_index];
|
||||
return cmd.smc_id;
|
||||
}
|
||||
|
||||
static int meson_sm_call(struct udevice *dev, u32 cmd_index, s32 *retval,
|
||||
struct pt_regs *args)
|
||||
{
|
||||
struct meson_sm_priv *priv = dev_get_priv(dev);
|
||||
u32 cmd, ret;
|
||||
|
||||
cmd = meson_sm_get_cmd(priv->data, cmd_index);
|
||||
if (!cmd)
|
||||
return -ENOENT;
|
||||
|
||||
ret = __meson_sm_call(cmd, args);
|
||||
if (retval)
|
||||
*retval = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd_index, struct pt_regs *args)
|
||||
{
|
||||
struct meson_sm_priv *priv = dev_get_priv(dev);
|
||||
s32 nbytes;
|
||||
int ret;
|
||||
|
||||
if (!buffer || size > priv->data->shmem_size)
|
||||
return -EINVAL;
|
||||
|
||||
ret = meson_sm_call(dev, cmd_index, &nbytes, args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nbytes < 0 || nbytes > size)
|
||||
return -ENOBUFS;
|
||||
|
||||
/* In some cases (for example GET_CHIP_ID command),
|
||||
* SMC doesn't return the number of bytes read, even
|
||||
* though the bytes were actually read into sm_shmem_out.
|
||||
* So this check is needed.
|
||||
*/
|
||||
ret = nbytes;
|
||||
if (!nbytes)
|
||||
nbytes = size;
|
||||
|
||||
memcpy(buffer, priv->sm_shmem_out, nbytes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd_index, struct pt_regs *args)
|
||||
{
|
||||
struct meson_sm_priv *priv = dev_get_priv(dev);
|
||||
s32 nbytes;
|
||||
int ret;
|
||||
|
||||
if (!buffer || size > priv->data->shmem_size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(priv->sm_shmem_in, buffer, size);
|
||||
|
||||
ret = meson_sm_call(dev, cmd_index, &nbytes, args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nbytes <= 0 || nbytes > size)
|
||||
return -EIO;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static int meson_sm_probe(struct udevice *dev)
|
||||
{
|
||||
struct meson_sm_priv *priv = dev_get_priv(dev);
|
||||
struct pt_regs regs = { 0 };
|
||||
|
||||
priv->data = (struct meson_sm_data *)dev_get_driver_data(dev);
|
||||
if (!priv->data)
|
||||
return -EINVAL;
|
||||
|
||||
priv->sm_shmem_in =
|
||||
(void *)__meson_sm_call(priv->data->cmd_get_shmem_in, ®s);
|
||||
|
||||
if (!priv->sm_shmem_in)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->sm_shmem_out =
|
||||
(void *)__meson_sm_call(priv->data->cmd_get_shmem_out, ®s);
|
||||
|
||||
if (!priv->sm_shmem_out)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("meson sm driver probed\n"
|
||||
"shmem_in addr: 0x%p, shmem_out addr: 0x%p\n",
|
||||
priv->sm_shmem_in,
|
||||
priv->sm_shmem_out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct meson_sm_data meson_sm_gxbb_data = {
|
||||
.cmd_get_shmem_in = 0x82000020,
|
||||
.cmd_get_shmem_out = 0x82000021,
|
||||
.shmem_size = SZ_4K,
|
||||
.cmd = {
|
||||
SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030),
|
||||
SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031),
|
||||
SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044),
|
||||
SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_sm_ids[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-gxbb-sm",
|
||||
.data = (ulong)&meson_sm_gxbb_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct sm_ops sm_ops = {
|
||||
.sm_call = meson_sm_call,
|
||||
.sm_call_read = meson_sm_call_read,
|
||||
.sm_call_write = meson_sm_call_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_sm) = {
|
||||
.name = "meson_sm",
|
||||
.id = UCLASS_SM,
|
||||
.of_match = meson_sm_ids,
|
||||
.probe = meson_sm_probe,
|
||||
.priv_auto = sizeof(struct meson_sm_priv),
|
||||
.ops = &sm_ops,
|
||||
};
|
76
drivers/sm/sandbox-sm.c
Normal file
76
drivers/sm/sandbox-sm.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <sm.h>
|
||||
#include <sm-uclass.h>
|
||||
#include <sandbox-sm.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <dm/device.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
static u8 test_buffer[SZ_4K];
|
||||
|
||||
static int sandbox_sm_call(struct udevice *dev, u32 cmd_index, s32 *smc_ret,
|
||||
struct pt_regs *args)
|
||||
{
|
||||
if (cmd_index >= SANDBOX_SMC_CMD_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (smc_ret)
|
||||
*smc_ret = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_sm_call_read(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd_index, struct pt_regs *args)
|
||||
{
|
||||
if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer)
|
||||
return -EINVAL;
|
||||
|
||||
if (size > sizeof(test_buffer))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(buffer, test_buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int sandbox_sm_call_write(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd_index, struct pt_regs *args)
|
||||
{
|
||||
if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer)
|
||||
return -EINVAL;
|
||||
|
||||
if (size > sizeof(test_buffer))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(test_buffer, buffer, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct udevice_id sandbox_sm_ids[] = {
|
||||
{
|
||||
.compatible = "sandbox,sm",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct sm_ops sandbox_sm_ops = {
|
||||
.sm_call = sandbox_sm_call,
|
||||
.sm_call_read = sandbox_sm_call_read,
|
||||
.sm_call_write = sandbox_sm_call_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sm) = {
|
||||
.name = "sm",
|
||||
.id = UCLASS_SM,
|
||||
.of_match = sandbox_sm_ids,
|
||||
.ops = &sandbox_sm_ops,
|
||||
};
|
55
drivers/sm/sm-uclass.c
Normal file
55
drivers/sm/sm-uclass.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <sm-uclass.h>
|
||||
|
||||
static const struct sm_ops *get_sm_ops(struct udevice *dev)
|
||||
{
|
||||
return (const struct sm_ops *)dev->driver->ops;
|
||||
}
|
||||
|
||||
int sm_call(struct udevice *dev, u32 cmd, s32 *ret, struct pt_regs *args)
|
||||
{
|
||||
const struct sm_ops *ops = get_sm_ops(dev);
|
||||
|
||||
if (ops->sm_call)
|
||||
return ops->sm_call(dev, cmd, ret, args);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int sm_call_read(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd, struct pt_regs *args)
|
||||
{
|
||||
const struct sm_ops *ops = get_sm_ops(dev);
|
||||
|
||||
if (ops->sm_call_read)
|
||||
return ops->sm_call_read(dev, buffer, size, cmd,
|
||||
args);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int sm_call_write(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd, struct pt_regs *args)
|
||||
{
|
||||
const struct sm_ops *ops = get_sm_ops(dev);
|
||||
|
||||
if (ops->sm_call_write)
|
||||
return ops->sm_call_write(dev, buffer, size, cmd,
|
||||
args);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(sm) = {
|
||||
.name = "sm",
|
||||
.id = UCLASS_SM,
|
||||
};
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
|
||||
|
||||
/* USB2 Ports Control Registers */
|
||||
|
||||
|
@ -103,10 +104,22 @@ enum {
|
|||
PHY_COUNT,
|
||||
};
|
||||
|
||||
static const char *phy_names[PHY_COUNT] = {
|
||||
static const char *const dwc3_meson_g12a_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
static const char *const dwc3_meson_a1_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1"
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a;
|
||||
|
||||
struct dwc3_meson_g12a_drvdata {
|
||||
const char *const *phy_names;
|
||||
unsigned int phy_cnt;
|
||||
int (*clk_init)(struct dwc3_meson_g12a *priv);
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
struct udevice *dev;
|
||||
struct regmap *regmap;
|
||||
|
@ -120,6 +133,7 @@ struct dwc3_meson_g12a {
|
|||
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||
struct udevice *vbus_supply;
|
||||
#endif
|
||||
struct dwc3_meson_g12a_drvdata *drvdata;
|
||||
};
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
|
@ -294,10 +308,11 @@ int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
|
|||
|
||||
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
struct dwc3_meson_g12a_drvdata *data = priv->drvdata;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = generic_phy_get_by_name(priv->dev, phy_names[i],
|
||||
for (i = 0 ; i < data->phy_cnt; ++i) {
|
||||
ret = generic_phy_get_by_name(priv->dev, data->phy_names[i],
|
||||
&priv->phys[i]);
|
||||
if (ret == -ENOENT || ret == -ENODATA)
|
||||
continue;
|
||||
|
@ -355,18 +370,36 @@ static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_a1_clk_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_name(priv->dev, "usb_bus", &priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_plat(dev);
|
||||
struct dwc3_meson_g12a_drvdata *data =
|
||||
(struct dwc3_meson_g12a_drvdata *)dev_get_driver_data(dev);
|
||||
int ret, i;
|
||||
|
||||
priv->drvdata = data;
|
||||
priv->dev = dev;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_clk_init(priv);
|
||||
ret = data->clk_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -399,7 +432,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
for (i = 0 ; i < data->phy_cnt; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
|
@ -408,7 +441,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev)
|
|||
goto err_phy_init;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHY_COUNT; ++i) {
|
||||
for (i = 0; i < data->phy_cnt; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
|
@ -420,7 +453,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev)
|
|||
return 0;
|
||||
|
||||
err_phy_init:
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
for (i = 0 ; i < data->phy_cnt ; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
|
@ -433,20 +466,21 @@ err_phy_init:
|
|||
static int dwc3_meson_g12a_remove(struct udevice *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_plat(dev);
|
||||
struct dwc3_meson_g12a_drvdata *data = priv->drvdata;
|
||||
int i;
|
||||
|
||||
reset_release_all(&priv->reset, 1);
|
||||
|
||||
clk_release_all(&priv->clk, 1);
|
||||
|
||||
for (i = 0; i < PHY_COUNT; ++i) {
|
||||
for (i = 0; i < data->phy_cnt; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
generic_phy_power_off(&priv->phys[i]);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
for (i = 0 ; i < data->phy_cnt; ++i) {
|
||||
if (!priv->phys[i].dev)
|
||||
continue;
|
||||
|
||||
|
@ -456,11 +490,26 @@ static int dwc3_meson_g12a_remove(struct udevice *dev)
|
|||
return dm_scan_fdt_dev(dev);
|
||||
}
|
||||
|
||||
static const struct dwc3_meson_g12a_drvdata meson_g12a_drvdata = {
|
||||
.phy_names = dwc3_meson_g12a_phy_names,
|
||||
.phy_cnt = ARRAY_SIZE(dwc3_meson_g12a_phy_names),
|
||||
.clk_init = dwc3_meson_g12a_clk_init,
|
||||
};
|
||||
|
||||
static const struct dwc3_meson_g12a_drvdata meson_a1_drvdata = {
|
||||
.phy_names = dwc3_meson_a1_phy_names,
|
||||
.phy_cnt = ARRAY_SIZE(dwc3_meson_a1_phy_names),
|
||||
.clk_init = dwc3_meson_a1_clk_init,
|
||||
};
|
||||
|
||||
static int dwc3_meson_g12a_child_pre_probe(struct udevice *dev)
|
||||
{
|
||||
if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb"))
|
||||
return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL);
|
||||
|
||||
if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb"))
|
||||
return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -469,11 +518,21 @@ static int dwc3_meson_g12a_child_post_remove(struct udevice *dev)
|
|||
if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb"))
|
||||
return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST);
|
||||
|
||||
if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb"))
|
||||
return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id dwc3_meson_g12a_ids[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-usb-ctrl",
|
||||
.data = (ulong)&meson_g12a_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-a1-usb-ctrl",
|
||||
.data = (ulong)&meson_a1_drvdata,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ enum uclass_id {
|
|||
UCLASS_MDIO, /* MDIO bus */
|
||||
UCLASS_MDIO_MUX, /* MDIO MUX/switch */
|
||||
UCLASS_MEMORY, /* Memory Controller device */
|
||||
UCLASS_SM, /* Secure Monitor driver */
|
||||
UCLASS_MISC, /* Miscellaneous device */
|
||||
UCLASS_MMC, /* SD / MMC card or chip */
|
||||
UCLASS_MOD_EXP, /* RSA Mod Exp device */
|
||||
|
|
168
include/dt-bindings/clock/amlogic,a1-peripherals-clkc.h
Normal file
168
include/dt-bindings/clock/amlogic,a1-peripherals-clkc.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
|
||||
/*
|
||||
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
||||
* Author: Jian Hu <jian.hu@amlogic.com>
|
||||
*
|
||||
* Copyright (c) 2023, SberDevices. All Rights Reserved.
|
||||
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
|
||||
*/
|
||||
|
||||
#ifndef __A1_PERIPHERALS_CLKC_H
|
||||
#define __A1_PERIPHERALS_CLKC_H
|
||||
|
||||
#define CLKID_XTAL_IN 0
|
||||
#define CLKID_FIXPLL_IN 1
|
||||
#define CLKID_USB_PHY_IN 2
|
||||
#define CLKID_USB_CTRL_IN 3
|
||||
#define CLKID_HIFIPLL_IN 4
|
||||
#define CLKID_SYSPLL_IN 5
|
||||
#define CLKID_DDS_IN 6
|
||||
#define CLKID_SYS 7
|
||||
#define CLKID_CLKTREE 8
|
||||
#define CLKID_RESET_CTRL 9
|
||||
#define CLKID_ANALOG_CTRL 10
|
||||
#define CLKID_PWR_CTRL 11
|
||||
#define CLKID_PAD_CTRL 12
|
||||
#define CLKID_SYS_CTRL 13
|
||||
#define CLKID_TEMP_SENSOR 14
|
||||
#define CLKID_AM2AXI_DIV 15
|
||||
#define CLKID_SPICC_B 16
|
||||
#define CLKID_SPICC_A 17
|
||||
#define CLKID_MSR 18
|
||||
#define CLKID_AUDIO 19
|
||||
#define CLKID_JTAG_CTRL 20
|
||||
#define CLKID_SARADC_EN 21
|
||||
#define CLKID_PWM_EF 22
|
||||
#define CLKID_PWM_CD 23
|
||||
#define CLKID_PWM_AB 24
|
||||
#define CLKID_CEC 25
|
||||
#define CLKID_I2C_S 26
|
||||
#define CLKID_IR_CTRL 27
|
||||
#define CLKID_I2C_M_D 28
|
||||
#define CLKID_I2C_M_C 29
|
||||
#define CLKID_I2C_M_B 30
|
||||
#define CLKID_I2C_M_A 31
|
||||
#define CLKID_ACODEC 32
|
||||
#define CLKID_OTP 33
|
||||
#define CLKID_SD_EMMC_A 34
|
||||
#define CLKID_USB_PHY 35
|
||||
#define CLKID_USB_CTRL 36
|
||||
#define CLKID_SYS_DSPB 37
|
||||
#define CLKID_SYS_DSPA 38
|
||||
#define CLKID_DMA 39
|
||||
#define CLKID_IRQ_CTRL 40
|
||||
#define CLKID_NIC 41
|
||||
#define CLKID_GIC 42
|
||||
#define CLKID_UART_C 43
|
||||
#define CLKID_UART_B 44
|
||||
#define CLKID_UART_A 45
|
||||
#define CLKID_SYS_PSRAM 46
|
||||
#define CLKID_RSA 47
|
||||
#define CLKID_CORESIGHT 48
|
||||
#define CLKID_AM2AXI_VAD 49
|
||||
#define CLKID_AUDIO_VAD 50
|
||||
#define CLKID_AXI_DMC 51
|
||||
#define CLKID_AXI_PSRAM 52
|
||||
#define CLKID_RAMB 53
|
||||
#define CLKID_RAMA 54
|
||||
#define CLKID_AXI_SPIFC 55
|
||||
#define CLKID_AXI_NIC 56
|
||||
#define CLKID_AXI_DMA 57
|
||||
#define CLKID_CPU_CTRL 58
|
||||
#define CLKID_ROM 59
|
||||
#define CLKID_PROC_I2C 60
|
||||
#define CLKID_DSPA_SEL 61
|
||||
#define CLKID_DSPB_SEL 62
|
||||
#define CLKID_DSPA_EN 63
|
||||
#define CLKID_DSPA_EN_NIC 64
|
||||
#define CLKID_DSPB_EN 65
|
||||
#define CLKID_DSPB_EN_NIC 66
|
||||
#define CLKID_RTC 67
|
||||
#define CLKID_CECA_32K 68
|
||||
#define CLKID_CECB_32K 69
|
||||
#define CLKID_24M 70
|
||||
#define CLKID_12M 71
|
||||
#define CLKID_FCLK_DIV2_DIVN 72
|
||||
#define CLKID_GEN 73
|
||||
#define CLKID_SARADC_SEL 74
|
||||
#define CLKID_SARADC 75
|
||||
#define CLKID_PWM_A 76
|
||||
#define CLKID_PWM_B 77
|
||||
#define CLKID_PWM_C 78
|
||||
#define CLKID_PWM_D 79
|
||||
#define CLKID_PWM_E 80
|
||||
#define CLKID_PWM_F 81
|
||||
#define CLKID_SPICC 82
|
||||
#define CLKID_TS 83
|
||||
#define CLKID_SPIFC 84
|
||||
#define CLKID_USB_BUS 85
|
||||
#define CLKID_SD_EMMC 86
|
||||
#define CLKID_PSRAM 87
|
||||
#define CLKID_DMC 88
|
||||
#define CLKID_SYS_A_SEL 89
|
||||
#define CLKID_SYS_A_DIV 90
|
||||
#define CLKID_SYS_A 91
|
||||
#define CLKID_SYS_B_SEL 92
|
||||
#define CLKID_SYS_B_DIV 93
|
||||
#define CLKID_SYS_B 94
|
||||
#define CLKID_DSPA_A_SEL 95
|
||||
#define CLKID_DSPA_A_DIV 96
|
||||
#define CLKID_DSPA_A 97
|
||||
#define CLKID_DSPA_B_SEL 98
|
||||
#define CLKID_DSPA_B_DIV 99
|
||||
#define CLKID_DSPA_B 100
|
||||
#define CLKID_DSPB_A_SEL 101
|
||||
#define CLKID_DSPB_A_DIV 102
|
||||
#define CLKID_DSPB_A 103
|
||||
#define CLKID_DSPB_B_SEL 104
|
||||
#define CLKID_DSPB_B_DIV 105
|
||||
#define CLKID_DSPB_B 106
|
||||
#define CLKID_RTC_32K_IN 107
|
||||
#define CLKID_RTC_32K_DIV 108
|
||||
#define CLKID_RTC_32K_XTAL 109
|
||||
#define CLKID_RTC_32K_SEL 110
|
||||
#define CLKID_CECB_32K_IN 111
|
||||
#define CLKID_CECB_32K_DIV 112
|
||||
#define CLKID_CECB_32K_SEL_PRE 113
|
||||
#define CLKID_CECB_32K_SEL 114
|
||||
#define CLKID_CECA_32K_IN 115
|
||||
#define CLKID_CECA_32K_DIV 116
|
||||
#define CLKID_CECA_32K_SEL_PRE 117
|
||||
#define CLKID_CECA_32K_SEL 118
|
||||
#define CLKID_DIV2_PRE 119
|
||||
#define CLKID_24M_DIV2 120
|
||||
#define CLKID_GEN_SEL 121
|
||||
#define CLKID_GEN_DIV 122
|
||||
#define CLKID_SARADC_DIV 123
|
||||
#define CLKID_PWM_A_SEL 124
|
||||
#define CLKID_PWM_A_DIV 125
|
||||
#define CLKID_PWM_B_SEL 126
|
||||
#define CLKID_PWM_B_DIV 127
|
||||
#define CLKID_PWM_C_SEL 128
|
||||
#define CLKID_PWM_C_DIV 129
|
||||
#define CLKID_PWM_D_SEL 130
|
||||
#define CLKID_PWM_D_DIV 131
|
||||
#define CLKID_PWM_E_SEL 132
|
||||
#define CLKID_PWM_E_DIV 133
|
||||
#define CLKID_PWM_F_SEL 134
|
||||
#define CLKID_PWM_F_DIV 135
|
||||
#define CLKID_SPICC_SEL 136
|
||||
#define CLKID_SPICC_DIV 137
|
||||
#define CLKID_SPICC_SEL2 138
|
||||
#define CLKID_TS_DIV 139
|
||||
#define CLKID_SPIFC_SEL 140
|
||||
#define CLKID_SPIFC_DIV 141
|
||||
#define CLKID_SPIFC_SEL2 142
|
||||
#define CLKID_USB_BUS_SEL 143
|
||||
#define CLKID_USB_BUS_DIV 144
|
||||
#define CLKID_SD_EMMC_SEL 145
|
||||
#define CLKID_SD_EMMC_DIV 146
|
||||
#define CLKID_SD_EMMC_SEL2 147
|
||||
#define CLKID_PSRAM_SEL 148
|
||||
#define CLKID_PSRAM_DIV 149
|
||||
#define CLKID_PSRAM_SEL2 150
|
||||
#define CLKID_DMC_SEL 151
|
||||
#define CLKID_DMC_DIV 152
|
||||
#define CLKID_DMC_SEL2 153
|
||||
|
||||
#endif /* __A1_PERIPHERALS_CLKC_H */
|
25
include/dt-bindings/clock/amlogic,a1-pll-clkc.h
Normal file
25
include/dt-bindings/clock/amlogic,a1-pll-clkc.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
|
||||
/*
|
||||
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
||||
* Author: Jian Hu <jian.hu@amlogic.com>
|
||||
*
|
||||
* Copyright (c) 2023, SberDevices. All Rights Reserved.
|
||||
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
|
||||
*/
|
||||
|
||||
#ifndef __A1_PLL_CLKC_H
|
||||
#define __A1_PLL_CLKC_H
|
||||
|
||||
#define CLKID_FIXED_PLL_DCO 0
|
||||
#define CLKID_FIXED_PLL 1
|
||||
#define CLKID_FCLK_DIV2_DIV 2
|
||||
#define CLKID_FCLK_DIV3_DIV 3
|
||||
#define CLKID_FCLK_DIV5_DIV 4
|
||||
#define CLKID_FCLK_DIV7_DIV 5
|
||||
#define CLKID_FCLK_DIV2 6
|
||||
#define CLKID_FCLK_DIV3 7
|
||||
#define CLKID_FCLK_DIV5 8
|
||||
#define CLKID_FCLK_DIV7 9
|
||||
#define CLKID_HIFI_PLL 10
|
||||
|
||||
#endif /* __A1_PLL_CLKC_H */
|
76
include/dt-bindings/reset/amlogic,meson-a1-reset.h
Normal file
76
include/dt-bindings/reset/amlogic,meson-a1-reset.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
||||
* Author: Xingyu Chen <xingyu.chen@amlogic.com>
|
||||
*
|
||||
* Copyright (c) 2023, SberDevices, Inc.
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_AMLOGIC_MESON_A1_RESET_H
|
||||
#define _DT_BINDINGS_AMLOGIC_MESON_A1_RESET_H
|
||||
|
||||
/* RESET0 */
|
||||
/* 0 */
|
||||
#define RESET_AM2AXI_VAD 1
|
||||
/* 2-3 */
|
||||
#define RESET_PSRAM 4
|
||||
#define RESET_PAD_CTRL 5
|
||||
/* 6 */
|
||||
#define RESET_TEMP_SENSOR 7
|
||||
#define RESET_AM2AXI_DEV 8
|
||||
/* 9 */
|
||||
#define RESET_SPICC_A 10
|
||||
#define RESET_MSR_CLK 11
|
||||
#define RESET_AUDIO 12
|
||||
#define RESET_ANALOG_CTRL 13
|
||||
#define RESET_SAR_ADC 14
|
||||
#define RESET_AUDIO_VAD 15
|
||||
#define RESET_CEC 16
|
||||
#define RESET_PWM_EF 17
|
||||
#define RESET_PWM_CD 18
|
||||
#define RESET_PWM_AB 19
|
||||
/* 20 */
|
||||
#define RESET_IR_CTRL 21
|
||||
#define RESET_I2C_S_A 22
|
||||
/* 23 */
|
||||
#define RESET_I2C_M_D 24
|
||||
#define RESET_I2C_M_C 25
|
||||
#define RESET_I2C_M_B 26
|
||||
#define RESET_I2C_M_A 27
|
||||
#define RESET_I2C_PROD_AHB 28
|
||||
#define RESET_I2C_PROD 29
|
||||
/* 30-31 */
|
||||
|
||||
/* RESET1 */
|
||||
#define RESET_ACODEC 32
|
||||
#define RESET_DMA 33
|
||||
#define RESET_SD_EMMC_A 34
|
||||
/* 35 */
|
||||
#define RESET_USBCTRL 36
|
||||
/* 37 */
|
||||
#define RESET_USBPHY 38
|
||||
/* 39-41 */
|
||||
#define RESET_RSA 42
|
||||
#define RESET_DMC 43
|
||||
/* 44 */
|
||||
#define RESET_IRQ_CTRL 45
|
||||
/* 46 */
|
||||
#define RESET_NIC_VAD 47
|
||||
#define RESET_NIC_AXI 48
|
||||
#define RESET_RAMA 49
|
||||
#define RESET_RAMB 50
|
||||
/* 51-52 */
|
||||
#define RESET_ROM 53
|
||||
#define RESET_SPIFC 54
|
||||
#define RESET_GIC 55
|
||||
#define RESET_UART_C 56
|
||||
#define RESET_UART_B 57
|
||||
#define RESET_UART_A 58
|
||||
#define RESET_OSC_RING 59
|
||||
/* 60-63 */
|
||||
|
||||
/* RESET2 */
|
||||
/* 64-95 */
|
||||
|
||||
#endif
|
19
include/meson/sm.h
Normal file
19
include/meson/sm.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_SM_CMD_H__
|
||||
#define __MESON_SM_CMD_H__
|
||||
|
||||
enum meson_smc_cmd {
|
||||
MESON_SMC_CMD_EFUSE_READ, /* read efuse memory */
|
||||
MESON_SMC_CMD_EFUSE_WRITE, /* write efuse memory */
|
||||
MESON_SMC_CMD_CHIP_ID_GET, /* readh chip unique id */
|
||||
MESON_SMC_CMD_PWRDM_SET, /* do command at specified power domain */
|
||||
MESON_SMC_CMD_COUNT,
|
||||
};
|
||||
|
||||
#endif
|
18
include/sandbox-sm.h
Normal file
18
include/sandbox-sm.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#ifndef __SANDBOX_SM_H__
|
||||
#define __SANDBOX_SM_H__
|
||||
|
||||
enum sandbox_smc_cmd {
|
||||
SANDBOX_SMC_CMD_READ_MEM,
|
||||
SANDBOX_SMC_CMD_WRITE_MEM,
|
||||
SANDBOX_SMC_CMD_COMMON,
|
||||
SANDBOX_SMC_CMD_COUNT,
|
||||
};
|
||||
|
||||
#endif
|
72
include/sm-uclass.h
Normal file
72
include/sm-uclass.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#ifndef __SM_UCLASS_H__
|
||||
#define __SM_UCLASS_H__
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
struct udevice;
|
||||
|
||||
/**
|
||||
* struct sm_ops - The functions that a SM driver must implement.
|
||||
*
|
||||
* @sm_call: Request a secure monitor call with specified command.
|
||||
*
|
||||
* @sm_call_read: Request a secure monitor call and retrieve data
|
||||
* from secure-monitor (depends on specified command).
|
||||
*
|
||||
* @sm_call_write: Request a secure monitor call and send data
|
||||
* to secure-monitor (depends on specified command).
|
||||
*
|
||||
* The individual methods are described more fully below.
|
||||
*/
|
||||
struct sm_ops {
|
||||
/**
|
||||
* sm_call - generic SMC call to the secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_SM device
|
||||
* @cmd_index: Index of the SMC function ID
|
||||
* @smc_ret: Returned value from secure world
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: 0 on success, a negative value on error
|
||||
*/
|
||||
int (*sm_call)(struct udevice *dev, u32 cmd, s32 *smc_ret,
|
||||
struct pt_regs *args);
|
||||
|
||||
/**
|
||||
* sm_call_write - send data to secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_SM device
|
||||
* @buffer: Buffer containing data to send
|
||||
* @size: Size of the buffer
|
||||
* @cmd: Index of the SMC function ID
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: size of sent data on success, a negative value on error
|
||||
*/
|
||||
int (*sm_call_write)(struct udevice *dev, void *buffer,
|
||||
size_t size, u32 cmd, struct pt_regs *args);
|
||||
|
||||
/**
|
||||
* sm_call_read - retrieve data from secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_SM device
|
||||
* @buffer: Buffer to store the retrieved data
|
||||
* @size: Size of the buffer
|
||||
* @cmd: Index of the SMC function ID
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: size of read data on success, a negative value on error
|
||||
*/
|
||||
int (*sm_call_read)(struct udevice *dev, void *buffer,
|
||||
size_t size, u32 cmd, struct pt_regs *args);
|
||||
};
|
||||
|
||||
#endif /* __SM_UCLASS_H__ */
|
67
include/sm.h
Normal file
67
include/sm.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.ru>
|
||||
*/
|
||||
|
||||
#ifndef __SM_H__
|
||||
#define __SM_H__
|
||||
|
||||
/*
|
||||
* NOTE: UCLASS_SM is designed with the idea that
|
||||
* each driver should convert @cmd to some raw
|
||||
* value, which is known only for driver, and set this
|
||||
* value to the first element of the @args->regs array.
|
||||
* Therefore, it is necessary to pass the remaining
|
||||
* arguments starting at index = 1. Anyway, driver
|
||||
* implementation may vary, so, please, check the specific
|
||||
* implementation of the driver you are using.
|
||||
*/
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
struct udevice;
|
||||
|
||||
/**
|
||||
* sm_call - generic SMC call to the secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_SM device
|
||||
* @cmd_index: Index of the SMC function ID
|
||||
* @smc_ret: Returned value from secure world
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: 0 on success, a negative value on error
|
||||
*/
|
||||
int sm_call(struct udevice *dev, u32 cmd, s32 *ret, struct pt_regs *args);
|
||||
|
||||
/**
|
||||
* sm_call_read - retrieve data from secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_MESON_SM device
|
||||
* @buffer: Buffer to store the retrieved data
|
||||
* @size: Size of the buffer
|
||||
* @cmd: Index of the SMC function ID
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: size of read data on success, a negative value on error
|
||||
*/
|
||||
int sm_call_read(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd, struct pt_regs *args);
|
||||
|
||||
/**
|
||||
* sm_call_write - send data to secure-monitor
|
||||
*
|
||||
* @dev: Pointer to UCLASS_SM device
|
||||
* @buffer: Buffer containing data to send
|
||||
* @size: Size of the buffer
|
||||
* @cmd: Index of the SMC function ID
|
||||
* @args: SMC arguments
|
||||
*
|
||||
* @return: size of sent data on success, a negative value on error
|
||||
*/
|
||||
int sm_call_write(struct udevice *dev, void *buffer, size_t size,
|
||||
u32 cmd, struct pt_regs *args);
|
||||
|
||||
#endif /* __SM_H__ */
|
|
@ -115,6 +115,7 @@ obj-$(CONFIG_DM_SPI) += spi.o
|
|||
obj-$(CONFIG_SPMI) += spmi.o
|
||||
obj-y += syscon.o
|
||||
obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
|
||||
obj-$(CONFIG_SM) += sm.o
|
||||
obj-$(CONFIG_SYSINFO) += sysinfo.o
|
||||
obj-$(CONFIG_SYSINFO_GPIO) += sysinfo-gpio.o
|
||||
obj-$(CONFIG_UT_DM) += tag.o
|
||||
|
|
65
test/dm/sm.c
Normal file
65
test/dm/sm.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2023 SberDevices, Inc.
|
||||
*
|
||||
* Author: Alexey Romanov <avromanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <sm.h>
|
||||
#include <sandbox-sm.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/test.h>
|
||||
#include <test/ut.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
static int dm_test_sm(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct pt_regs regs;
|
||||
char buffer[128] = { 0 };
|
||||
char test_string[] = "secure-monitor";
|
||||
int ret, val;
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_SM,
|
||||
"secure-monitor", &dev));
|
||||
|
||||
ret = sm_call(dev, SANDBOX_SMC_CMD_COUNT, NULL, ®s);
|
||||
ut_asserteq(ret, -EINVAL);
|
||||
|
||||
ret = sm_call(dev, SANDBOX_SMC_CMD_COMMON, &val, ®s);
|
||||
ut_asserteq(ret, 0);
|
||||
ut_asserteq(val, 0);
|
||||
|
||||
ret = sm_call_write(dev, buffer, sizeof(buffer),
|
||||
SANDBOX_SMC_CMD_COUNT, ®s);
|
||||
ut_asserteq(ret, -EINVAL);
|
||||
|
||||
ret = sm_call_write(dev, buffer, SZ_4K + 1,
|
||||
SANDBOX_SMC_CMD_WRITE_MEM, ®s);
|
||||
ut_asserteq(ret, -EINVAL);
|
||||
|
||||
ret = sm_call_write(dev, buffer, sizeof(buffer),
|
||||
SANDBOX_SMC_CMD_COUNT, ®s);
|
||||
ut_asserteq(ret, -EINVAL);
|
||||
|
||||
ret = sm_call_write(dev, buffer, SZ_4K + 1,
|
||||
SANDBOX_SMC_CMD_READ_MEM, ®s);
|
||||
ut_asserteq(ret, -EINVAL);
|
||||
|
||||
ret = sm_call_write(dev, test_string, sizeof(test_string),
|
||||
SANDBOX_SMC_CMD_WRITE_MEM, ®s);
|
||||
ut_asserteq(ret, sizeof(test_string));
|
||||
|
||||
ret = sm_call_read(dev, buffer, sizeof(buffer),
|
||||
SANDBOX_SMC_CMD_READ_MEM, ®s);
|
||||
ut_asserteq(ret, sizeof(buffer));
|
||||
|
||||
ut_asserteq_str(buffer, test_string);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_sm, UT_TESTF_SCAN_FDT);
|
Loading…
Reference in New Issue
Block a user