- 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:
Tom Rini 2023-10-16 09:09:54 -04:00
commit 2e1577e836
33 changed files with 2095 additions and 91 deletions

View File

@ -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

View File

@ -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 {

View 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_ */

View File

@ -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

View File

@ -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(&regs);
shmem_input = (void *)regs.regs[0];
regs.regs[0] = FN_GET_SHARE_MEM_OUTPUT_BASE;
smc_call(&regs);
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(&regs);
err = sm_call_read(dev, buffer, size,
MESON_SMC_CMD_EFUSE_READ, &regs);
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(&regs);
err = sm_call_write(dev, buffer, size,
MESON_SMC_CMD_EFUSE_WRITE, &regs);
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, &regs);
if (err < 0)
pr_err("Failed to read serial number (%d)\n", err);
smc_call(&regs);
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(&regs);
err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, &regs);
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;
}

View File

@ -764,6 +764,10 @@
};
};
};
sm: secure-monitor {
compatible = "sandbox,sm";
};
};
fpga {

View File

@ -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

View File

@ -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

View File

@ -118,6 +118,8 @@ source "drivers/scsi/Kconfig"
source "drivers/serial/Kconfig"
source "drivers/sm/Kconfig"
source "drivers/smem/Kconfig"
source "drivers/sound/Kconfig"

View File

@ -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/

View File

@ -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

View File

@ -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
View 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;
}

View File

@ -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

View File

@ -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,
},
{ }
};

View File

@ -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);
}

View File

@ -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
View 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
View 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
View 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, &regs);
if (!priv->sm_shmem_in)
return -ENOMEM;
priv->sm_shmem_out =
(void *)__meson_sm_call(priv->data->cmd_get_shmem_out, &regs);
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
View 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
View 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,
};

View File

@ -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,
},
{ }
};

View File

@ -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 */

View 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 */

View 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 */

View 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
View 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
View 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
View 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
View 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__ */

View File

@ -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
View 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, &regs);
ut_asserteq(ret, -EINVAL);
ret = sm_call(dev, SANDBOX_SMC_CMD_COMMON, &val, &regs);
ut_asserteq(ret, 0);
ut_asserteq(val, 0);
ret = sm_call_write(dev, buffer, sizeof(buffer),
SANDBOX_SMC_CMD_COUNT, &regs);
ut_asserteq(ret, -EINVAL);
ret = sm_call_write(dev, buffer, SZ_4K + 1,
SANDBOX_SMC_CMD_WRITE_MEM, &regs);
ut_asserteq(ret, -EINVAL);
ret = sm_call_write(dev, buffer, sizeof(buffer),
SANDBOX_SMC_CMD_COUNT, &regs);
ut_asserteq(ret, -EINVAL);
ret = sm_call_write(dev, buffer, SZ_4K + 1,
SANDBOX_SMC_CMD_READ_MEM, &regs);
ut_asserteq(ret, -EINVAL);
ret = sm_call_write(dev, test_string, sizeof(test_string),
SANDBOX_SMC_CMD_WRITE_MEM, &regs);
ut_asserteq(ret, sizeof(test_string));
ret = sm_call_read(dev, buffer, sizeof(buffer),
SANDBOX_SMC_CMD_READ_MEM, &regs);
ut_asserteq(ret, sizeof(buffer));
ut_asserteq_str(buffer, test_string);
return 0;
}
DM_TEST(dm_test_sm, UT_TESTF_SCAN_FDT);