From e93c53e06562f3c05684b8509522e6fb3386b7fa Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 17 Sep 2025 12:37:52 +0200 Subject: [PATCH 01/85] arm64: Add ADI ADSP-SC598 SoC Signed-off-by: Philip Molloy --- arch/arm64/Kconfig.platforms | 13 ++ include/linux/soc/adi/adi_system_config.h | 42 ++++++ include/linux/soc/adi/adsp-gpio-port.h | 91 +++++++++++++ include/linux/soc/adi/rcu.h | 92 +++++++++++++ include/linux/soc/adi/sc59x.h | 151 ++++++++++++++++++++++ 5 files changed, 389 insertions(+) create mode 100644 include/linux/soc/adi/adi_system_config.h create mode 100644 include/linux/soc/adi/adsp-gpio-port.h create mode 100644 include/linux/soc/adi/rcu.h create mode 100644 include/linux/soc/adi/sc59x.h diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 13173795c43d4f..679576a672be17 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -341,6 +341,19 @@ config ARCH_ROCKCHIP This enables support for the ARMv8 based Rockchip chipsets, like the RK3368. +config ARCH_SC59X_64 + bool "ADI 64-bit SC59X Platforms" + select TIMER_OF + select GPIOLIB + select PINCTRL + select PINCTRL_SC59x + select SERIAL_ADI_UART4 + select SPI_ADI + select COUNTER + help + This enables support for Analog Devices Incorporated's + Family of ARM64 processors + config ARCH_SEATTLE bool "AMD Seattle SoC Family" help diff --git a/include/linux/soc/adi/adi_system_config.h b/include/linux/soc/adi/adi_system_config.h new file mode 100644 index 00000000000000..0fc7904b16f1b3 --- /dev/null +++ b/include/linux/soc/adi/adi_system_config.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SOC_ADI_ADI_SYSTEM_CONFIG_H +#define SOC_ADI_ADI_SYSTEM_CONFIG_H + +#include + +/* + * All possible system register IDs across all platforms supported by this driver + */ +enum adi_system_reg_id { + ADI_SYSTEM_REG_EMAC0_PTPCLK0 = 0, /* PTP Clock Source 0 */ + ADI_SYSTEM_REG_EMAC0_EMACRESET, /* Reset Enable for RGMII */ + ADI_SYSTEM_REG_EMAC0_PHYISEL, /* Select PHY Interface RGMII/RMII/MII */ + ADI_SYSTEM_REG_CNT0UDSEL, /* CNT0 Down Input Select */ + ADI_SYSTEM_REG_CNT0DGSEL, /* CNT0 Up Input Select */ + ADI_SYSTEM_REG_TWI0VSEL, /* TWI2 Voltage Select */ + ADI_SYSTEM_REG_TWI1VSEL, /* TWI1 Voltage Select */ + ADI_SYSTEM_REG_TWI2VSEL, /* TWI0 Voltage Select */ + ADI_SYSTEM_REG_PUMSIDLC, /* Pull-Up Enable for MSI DATA[3:0] bits and CMD Pin */ + ADI_SYSTEM_REG_PUMSIHL, /* Pull-Up Enable for MSI DATA[7:4] bits */ + ADI_SYSTEM_REG_PUTMS, /* Pull-Up Enable for MSI DATA[7:4] bits */ + ADI_SYSTEM_REG_EMAC0_AUXIE, /* Input enable control for PTP_AUXIN pins */ + ADI_SYSTEM_REG_FAULT_DIS, /* FAULT does not exist */ + ADI_SYSTEM_REG_EMAC0_ENDIANNESS, /* EMAC0 DMA transfer endian format */ + ADI_SYSTEM_REG_EMAC1_ENDIANNESS, /* EMAC1 DMA transfer endian format */ + ADI_SYSTEM_REG_MSHC_CCLK_DIV_EN, /* Enable MSHC Card Clock Divider */ + ADI_SYSTEM_REG_DAI0_IE, /* Port input enable for DAI0 */ + ADI_SYSTEM_REG_DAI1_IE, /* Port input enable for DAI1 */ + __ADI_SYSTEM_REG_COUNT +}; + +#endif diff --git a/include/linux/soc/adi/adsp-gpio-port.h b/include/linux/soc/adi/adsp-gpio-port.h new file mode 100644 index 00000000000000..6185c06272f76b --- /dev/null +++ b/include/linux/soc/adi/adsp-gpio-port.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef GPIO_ADI_ADSP_PORT_H +#define GPIO_ADI_ADSP_PORT_H + +#include + +/* Number of GPIOs per port instance */ +#define ADSP_PORT_NGPIO 16 + +/* PORT memory layout */ +#define ADSP_PORT_REG_FER 0x00 +#define ADSP_PORT_REG_FER_SET 0x04 +#define ADSP_PORT_REG_FER_CLEAR 0x08 +#define ADSP_PORT_REG_DATA 0x0c +#define ADSP_PORT_REG_DATA_SET 0x10 +#define ADSP_PORT_REG_DATA_CLEAR 0x14 +#define ADSP_PORT_REG_DIR 0x18 +#define ADSP_PORT_REG_DIR_SET 0x1c +#define ADSP_PORT_REG_DIR_CLEAR 0x20 +#define ADSP_PORT_REG_INEN 0x24 +#define ADSP_PORT_REG_INEN_SET 0x28 +#define ADSP_PORT_REG_INEN_CLEAR 0x2c +#define ADSP_PORT_REG_PORT_MUX 0x30 +#define ADSP_PORT_REG_DATA_TGL 0x34 +#define ADSP_PORT_REG_POLAR 0x38 +#define ADSP_PORT_REG_POLAR_SET 0x3c +#define ADSP_PORT_REG_POLAR_CLEAR 0x40 +#define ADSP_PORT_REG_LOCK 0x44 +#define ADSP_PORT_REG_TRIG_TGL 0x48 + +/* + * One gpio instance per PORT instance in the hardware, provides the per-PORT + * interface to the hardware. Referenced in GPIO and PINCTRL drivers + */ +struct adsp_gpio_port { + struct device *dev; + void __iomem *regs; + struct gpio_chip gpio; + struct irq_domain *irq_domain; + u32 irq_offset; + u32 open_drain; + spinlock_t lock; +}; + +/* may need lock depending on register */ +static inline u32 __adsp_gpio_readl(struct adsp_gpio_port *port, + size_t offset) +{ + return readl(port->regs + offset); +} + +/* may need lock depending on register */ +static inline void __adsp_gpio_writel(struct adsp_gpio_port *port, u32 val, + size_t offset) +{ + writel(val, port->regs + offset); +} + +/* may need lock depending on register */ +static inline u16 __adsp_gpio_readw(struct adsp_gpio_port *port, + size_t offset) +{ + return readw(port->regs + offset); +} + +/* may need lock depending on register */ +static inline void __adsp_gpio_writew(struct adsp_gpio_port *port, u16 val, + size_t offset) +{ + writew(val, port->regs + offset); +} + +static inline struct adsp_gpio_port *to_adsp_gpio_port(struct gpio_chip + *chip) +{ + return container_of(chip, struct adsp_gpio_port, gpio); +} + +int adsp_attach_pint_to_gpio(struct adsp_gpio_port *port); + +#endif diff --git a/include/linux/soc/adi/rcu.h b/include/linux/soc/adi/rcu.h new file mode 100644 index 00000000000000..5dc9b22fe68010 --- /dev/null +++ b/include/linux/soc/adi/rcu.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SOC_ADI_RCU_H +#define SOC_ADI_RCU_H + +#include +#include +#include +#include +#include + +/* Register offsets */ +#define ADI_RCU_REG_CTL 0x00 +#define ADI_RCU_REG_STAT 0x04 +#define ADI_RCU_REG_CRCTL 0x08 +#define ADI_RCU_REG_CRSTAT 0x0c + +#ifdef CONFIG_ARCH_SC58X +#define ADI_RCU_REG_SIDIS 0x10 +#define ADI_RCU_REG_SISTAT 0x14 +#define ADI_RCU_REG_SVECT_LCK 0x18 +#define ADI_RCU_REG_BCODE 0x1c +#define ADI_RCU_REG_SVECT0 0x20 +#define ADI_RCU_REG_SVECT1 0x24 +#define ADI_RCU_REG_SVECT2 0x28 +#define ADI_RCU_REG_MSG 0x60 +#define ADI_RCU_REG_MSG_SET 0x64 +#define ADI_RCU_REG_MSG_CLR 0x68 +#else +#define ADI_RCU_REG_SRRQSTAT 0x18 +#define ADI_RCU_REG_SIDIS 0x1c +#define ADI_RCU_REG_SISTAT 0x20 +#define ADI_RCU_REG_BCODE 0x28 +#define ADI_RCU_REG_SVECT0 0x2c +#define ADI_RCU_REG_SVECT1 0x30 +#define ADI_RCU_REG_SVECT2 0x34 +#define ADI_RCU_REG_MSG 0x6c +#define ADI_RCU_REG_MSG_SET 0x70 +#define ADI_RCU_REG_MSG_CLR 0x74 +#endif + +/* Register bit definitions */ +#define ADI_RCU_CTL_SYSRST BIT(0) + +/* Bit values for the RCU0_MSG register */ +#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */ +#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */ +#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */ +#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */ +#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */ +#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */ +#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */ +#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */ + +struct adi_rcu; +struct adi_sec; + +/* + * Get the RCU instance connected to the given device as a device tree phandle + * in a property named "adi,rcu" + * + * call put_adi_rcu when done with the rcu entirely + */ +struct adi_rcu *get_adi_rcu_from_node(struct device *dev); +void put_adi_rcu(struct adi_rcu *rcu); +void adi_rcu_msg_set(struct adi_rcu *rcu, u32 bits); +void adi_rcu_msg_clear(struct adi_rcu *rcu, u32 bits); + +/* + * These are defined for use with SHARC cores not ARM cores + */ +int adi_rcu_check_coreid_valid(struct adi_rcu *rcu, int coreid); +int adi_rcu_reset_core(struct adi_rcu *rcu, int coreid); +int adi_rcu_start_core(struct adi_rcu *rcu, int coreid); +int adi_rcu_stop_core(struct adi_rcu *rcu, int coreid, int coreirq); +int adi_rcu_is_core_idle(struct adi_rcu *rcu, int coreid); + +void adi_rcu_set_sec(struct adi_rcu *rcu, struct adi_sec *sec); + +u32 adi_rcu_readl(struct adi_rcu *rcu, int offset); +void adi_rcu_writel(u32 val, struct adi_rcu *rcu, int offset); + +#endif diff --git a/include/linux/soc/adi/sc59x.h b/include/linux/soc/adi/sc59x.h new file mode 100644 index 00000000000000..b10bc1426138ad --- /dev/null +++ b/include/linux/soc/adi/sc59x.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SOC_ADI_SC59X_H +#define SOC_ADI_SC59X_H + +#define SC59x_SYSTEM_L2_VIRT_BASE 0xFF020000 +#define SC59x_SYSTEM_L2_SIZE 0x2C0000 + +// General Purpose Timer Block Registers +#define TIMER_GROUP 0x31018004 + +// TIMER0 +#define TIMER0_CONFIG 0x31018060 + +// CGU0 +#define REG_CGU0_CTL 0x3108D000 // CGU0 Control Register +#define REG_CGU0_STAT 0x3108D008 // CGU0 Status Register +#define REG_CGU0_DIV 0x3108D00C // CGU0 Clocks Divisor Register + +// UART0 +#define UART0_REVID 0x31003000 // UART0 Revision ID Register + +// UART1 +#define UART1_REVID 0x31003400 // UART1 Revision ID Register + +// UART2 +#define UART2_REVID 0x31003800 // UART2 Revision ID Register + +// WDOG0 +#define REG_WDOG0_CTL 0x31008000 // WDOG0 Control Register + +// WDOG1 +#define REG_WDOG1_CTL 0x31008800 // WDOG1 Control Register + +// CRC0 MMR +#define REG_CRC0_CTL 0x310A5000 // CRC0 Control Register +#define REG_CRC0_DCNT 0x310A5004 // CRC0 Data Word Count Register +#define REG_CRC0_FILLVAL 0x310A5018 // CRC0 Fill Value Register + +// DMA Channel Registers +#define REG_DMA8_DSCPTR_NXT 0x310A7000 // DMA8 Pointer to Next Initial Descriptor +#define REG_DMA8_CFG 0x310A7008 // DMA8 Configuration Register +#define REG_DMA9_DSCPTR_NXT 0x310A7080 // DMA9 Pointer to Next Initial Descriptor +#define REG_DMA9_CFG 0x310A7088 // DMA9 Configuration Register +#define REG_DMA9_STAT 0x310A70B0 // DMA9 Status Register +#define REG_DMA18_DSCPTR_NXT 0x310A7100 // DMA18 Pointer to Next Initial Descriptor +#define REG_DMA18_CFG 0x310A7108 // DMA18 Configuration Register +#define REG_DMA19_DSCPTR_NXT 0x310A7180 // DMA19 Pointer to Next Initial Descriptor +#define REG_DMA19_CFG 0x310A7188 // DMA19 Configuration Register +#define REG_DMA19_STAT 0x310A71B0 // DMA19 Status Register + +// L2CTL0 +#define L2CTL0_CTL 0x31080000 // L2CTL0 Control Register +#define L2CTL0_STAT 0x31080010 // L2CTL0 Status Register +#define L2CTL0_ERRADDR0 0x31080040 // L2CTL0 ECC Error Address 0 Register +#define L2CTL0_ET0 0x31080080 // L2CTL0 Error Type 0 Register +#define L2CTL0_EADDR0 0x31080084 // L2CTL0 Error Type 0 Address Register +#define L2CTL0_ET1 0x31080088 // L2CTL0 Error Type 1 Register +#define L2CTL0_EADDR1 0x3108008C // L2CTL0 Error Type 1 Address Register + +// SEC Core Interface (SCI) Register Definitions +#define SEC_COMMON_BASE 0x31089000 +#define SEC_SCI_BASE 0x31089440 +#define SEC_SSI_BASE 0x31089800 + +#define SEC_SCI_OFF 0x00000040 +#define SEC_CCTL 0x00000000 // SEC Core Control Register n +#define SEC_CSID 0x0000001C // SEC Core IRQ Source ID Register n + +#define SEC_CCTL_EN 0x00000001 // SEC Core Control Register Enable bit + +// SEC Fault Management Interface (SFI) Register Definitions +#define SEC_FCTL 0x00000010 // SEC Fault Control Register + +// SEC Global Register Definitions +#define SEC_GCTL 0x00000000 // SEC Global Control Register +#define SEC_RAISE 0x00000008 // SEC Global Raise Register +#define SEC_END 0x0000000C // SEC Global End Register + +// SEC_SCTL +#define SEC_SCTL_CTG 0x0F000000 // Core Target Select + +// SEC Source Interface (SSI) Register Definitions +#define SEC_SCTL0 0x00000000 // SEC Source Control Register n + +// SEC_SCTL +#define SEC_SCTL_SRC_EN 0x00000004 // SEN: Enable +#define SEC_SCTL_FAULT_EN 0x00000002 // FEN: Enable +#define SEC_SCTL_INT_EN 0x00000001 // IEN: Enable + +// TRU0 +// 0x3108A000 + (0x4 * n) +#define REG_TRU0_SSR160 0x3108A280 // TRU0 Slave Select Register +#define REG_TRU0_SSR164 0x3108A290 // TRU0 Slave Select Register +#define REG_TRU0_SSR168 0x3108A2A0 // TRU0 Slave Select Register +#define REG_TRU0_MTR 0x3108A7E0 // TRU0 Master Trigger Register +#define REG_TRU0_GCTL 0x3108A7F4 // TRU0 Global Control Register + +// Trigger Master Definitions +#define TRGM_SOFT0 136 // Software-driven Trigger 3 +#define TRGM_SOFT1 137 // Software-driven Trigger 3 +#define TRGM_SOFT2 138 // Software-driven Trigger 4 +#define TRGM_SOFT3 139 // Software-driven Trigger 3 +#define TRGM_SOFT4 140 // Software-driven Trigger 4 +#define TRGM_SOFT5 141 // Software-driven Trigger 5 + +// RCU0 +#define REG_RCU0_CTL 0x3108C000 // RCU0 Control Register +#define REG_RCU0_STAT 0x3108C004 // RCU0 Status Register +#define REG_RCU0_CRCTL 0x3108C008 // RCU0 Core Reset Control Register +#define REG_RCU0_CRSTAT 0x3108C00C // RCU0 Core Reset Status Register +#define REG_RCU0_SIDIS 0x3108C01C // RCU0 System Interface Disable Register +#define REG_RCU0_SISTAT 0x3108C020 // RCU0 System Interface Status Register +#define REG_RCU0_BCODE 0x3108C028 // RCU0 Boot Code Register +#define REG_RCU0_MSG_SET 0x3108C070 // RCU0 Message Set Bits Register +#define REG_RCU0_SVECT1 0x3108C030 // Software Vector Register 1 +#define REG_RCU0_SVECT2 0x3108C034 // Software Vector Register 2 + +// SPU0 +#define REG_SPU0_CTL 0x3108B000 // SPU0 Control Register + +// LP0 +#define LP0_CTL 0x30FFE000 // LP0 Control Register + +// LP1 +#define LP1_CTL 0x30FFE100 // LP1 Control Register + +// PADS0 +#define REG_PADS0_BASE 0x31004600 // PADS Base Register +#define REG_PADS0_PCFG0 0x31004604 // PADS0 Peripheral Configuration0 Register +#define REG_PADS0_DAI0_IE 0x31004690 // PADS DAI0 IE Register +#define REG_PADS0_DAI1_IE 0x31004694 // PADS DAI1 IE Register +#define BITM_PADS_PCFG0_EMACRESET 0x00000004 // Reset Enable for RGMII +#define ENUM_PADS_PCFG0_EMACPHY_MII 0x00000000 // EMACPHYISEL: MII Interface +#define ENUM_PADS_PCFG0_EMACPHY_RGMII 0x00000008 // EMACPHYISEL: RGMII Interface +#define ENUM_PADS_PCFG0_EMACPHY_RMII 0x00000010 // EMACPHYISEL: RMII Interface +#define ENUM_PADS_PCFG0_EMAC0_RMII_CLK 0x00000000 // EMAC0: EMAC0_RMII CLK +#define ENUM_PADS_PCFG0_EMAC0_SCLK1 0x00000001 // EMAC0: SCLK +#define ENUM_PADS_PCFG0_EMAC0_EXT_CLK 0x00000002 // EMAC0: External Clock +#define ENUM_PADS_PCFG0_EMAC0_SCLK3 0x00000003 // EMAC0: SCLK + +#endif From add9ecc9f819cf0ece75c41d1411422839364ace Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:17:26 +0200 Subject: [PATCH 02/85] clock: adi: Add PLL driver for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/clk/adi/clk-adi-pll.c | 191 ++++++++++++++++++++++++++++++++++ drivers/clk/adi/clk.h | 133 +++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 drivers/clk/adi/clk-adi-pll.c create mode 100644 drivers/clk/adi/clk.h diff --git a/drivers/clk/adi/clk-adi-pll.c b/drivers/clk/adi/clk-adi-pll.c new file mode 100644 index 00000000000000..7c7ce644d7c050 --- /dev/null +++ b/drivers/clk/adi/clk-adi-pll.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CGU PLL driver for ADI SC59X processors + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include + +#include "clk.h" + +struct clk_sc5xx_cgu_pll { + struct clk_hw hw; + void __iomem *base; + spinlock_t *lock; + int prepared; + u32 mask; + u32 max; + u32 m_offset; + u8 shift; + bool half_m; +}; + +struct clk_sc5xx_cgu_pll *to_clk_sc5xx_cgu_pll(struct clk_hw *hw) +{ + return container_of(hw, struct clk_sc5xx_cgu_pll, hw); +} + +/* + * For now, prepare/unprepare do nothing because we want to leave the PLLs running + * but eventually this could be used to bypass or disable the PLLs if desired + */ +static int sc5xx_cgu_pll_prepare(struct clk_hw *hw) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + + pll->prepared = 1; + return 0; +} + +static int sc5xx_cgu_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + + return pll->prepared; +} + +static void sc5xx_cgu_pll_unprepare(struct clk_hw *hw) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + + pll->prepared = 0; +} + +static long sc5xx_cgu_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + struct clk_hw *parent_hw; + unsigned long prate = *parent_rate; + int parent_inc; + unsigned long m, m2, new_rate, nr2, prate2; + + parent_hw = clk_hw_get_parent(hw); + + if (pll->half_m) + m = rate / prate / 2; + else + m = rate / prate; + + if (m > pll->max) { + // cannot scale this far, need bigger input + parent_inc = m / pll->max; + prate = + clk_hw_round_rate(parent_hw, prate * (parent_inc + 1)); + } else if (m == 0) { + pr_err + ("%s: Cannot use VCO to reduce parent clock rate, requested %lu, clamping to %lu\n", + __func__, rate, prate); + return prate; + } + + new_rate = prate * m; + + if (new_rate != rate) { + // Check if we could get an integer match by halving parent rate since we + // know at least about the DF bit before the VCO, although we don't know + // if we're already using it or not + prate2 = clk_hw_round_rate(parent_hw, prate / 2); + m2 = rate / prate2; + nr2 = prate * m2; + if (m2 <= pll->max && nr2 == rate) { + m = m2; + new_rate = nr2; + prate = prate2; + } + } + + *parent_rate = prate; + return new_rate; +} + +static unsigned long sc5xx_cgu_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + u32 reg = readl(pll->base); + u32 m = ((reg & pll->mask) >> pll->shift) + pll->m_offset; + + if (m == 0) + m = pll->max; + + if (pll->half_m) + return parent_rate * m * 2; + else + return parent_rate * m; +} + +static int sc5xx_cgu_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + u32 m; + + if (pll->half_m) + m = (rate / parent_rate / 2) - pll->m_offset; + else + m = (rate / parent_rate) - pll->m_offset; + + if (m >= pll->max) + m = 0; + + // reminder for implementation: lock around read/modify to control reg + pr_err + ("%s: set_rate not permitted yet, but we would write %d to m\n", + __func__, m); + return -ENOENT; +} + +static const struct clk_ops clk_sc5xx_cgu_pll_ops = { + .prepare = sc5xx_cgu_pll_prepare, + .unprepare = sc5xx_cgu_pll_unprepare, + .is_prepared = sc5xx_cgu_pll_is_prepared, + .recalc_rate = sc5xx_cgu_pll_recalc_rate, + .round_rate = sc5xx_cgu_pll_round_rate, + .set_rate = sc5xx_cgu_pll_set_rate, +}; + +struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name, + void __iomem *base, u8 shift, u8 width, + u32 m_offset, bool half_m, spinlock_t *lock) +{ + struct clk_sc5xx_cgu_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &clk_sc5xx_cgu_pll_ops; + + pll->base = base; + pll->hw.init = &init; + pll->lock = lock; + pll->shift = shift; + pll->mask = GENMASK(width - 1, 0) << shift; + pll->max = pll->mask + 1; + pll->m_offset = m_offset; + pll->half_m = half_m; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: Failed to register, code %lu\n", __func__, + PTR_ERR(clk)); + } + + return clk; +} diff --git a/drivers/clk/adi/clk.h b/drivers/clk/adi/clk.h new file mode 100644 index 00000000000000..5793c4f907943a --- /dev/null +++ b/drivers/clk/adi/clk.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Clock support for ADI processors + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#ifndef CLK_ADI_CLK_H +#define CLK_ADI_CLK_H + +#include +#include +#include +#include + +#define CGU_CTL 0x00 +#define CGU_PLLCTL 0x04 +#define CGU_STAT 0x08 +#define CGU_DIV 0x0C +#define CGU_CLKOUTSEL 0x10 +#define CGU_OSCWDCTL 0x14 +#define CGU_TSCTL 0x18 +#define CGU_TSVALUE0 0x1C +#define CGU_TSVALUE1 0x20 +#define CGU_TSCOUNT0 0x24 +#define CGU_TSCOUNT1 0x28 +#define CGU_CCBF_DIS 0x2C +#define CGU_CCBF_STAT 0x30 +#define CGU_SCBF_DIS 0x38 +#define CGU_SCBF_STAT 0x3C +#define CGU_DIVEX 0x40 +#define CGU_REVID 0x48 + +#define CDU_CFG0 0x00 +#define CDU_CFG1 0x04 +#define CDU_CFG2 0x08 +#define CDU_CFG3 0x0C +#define CDU_CFG4 0x10 +#define CDU_CFG5 0x14 +#define CDU_CFG6 0x18 +#define CDU_CFG7 0x1C +#define CDU_CFG8 0x20 +#define CDU_CFG9 0x24 +#define CDU_CFG10 0x28 +#define CDU_CFG11 0x2C +#define CDU_CFG12 0x30 +#define CDU_CFG13 0x34 +#define CDU_CFG14 0x38 + +#define PLL3_OFFSET 0x2c + +#define CDU_CLKINSEL 0x44 + +#define CGU_MSEL_SHIFT 8 +#define CGU_MSEL_WIDTH 7 + +#define PLL3_MSEL_SHIFT 4 +#define PLL3_MSEL_WIDTH 7 + +#define CDU_MUX_SIZE 4 +#define CDU_MUX_SHIFT 1 +#define CDU_MUX_WIDTH 2 +#define CDU_EN_BIT 0 + +struct clk_sc5xx_cgu_pll *to_clk_sc5xx_cgu_pll(struct clk_hw *hw); + +struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name, + void __iomem *base, u8 shift, u8 width, + u32 m_offset, bool half_m, spinlock_t *lock); + +/** + * All CDU clock muxes are the same size + */ +static inline struct clk *cdu_mux(const char *name, void __iomem *reg, + const char *const *parents, + spinlock_t *cdu_lock) +{ + return clk_register_mux(NULL, name, parents, CDU_MUX_SIZE, + CLK_SET_RATE_PARENT, reg, CDU_MUX_SHIFT, + CDU_MUX_WIDTH, 0, cdu_lock); +} + +static inline struct clk *cgu_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, + u8 width, u8 extra_flags, + spinlock_t *cdu_lock) +{ + return clk_register_divider(NULL, name, parent, + CLK_SET_RATE_PARENT, reg, shift, width, + CLK_DIVIDER_MAX_AT_ZERO | extra_flags, + cdu_lock); +} + +static inline struct clk *cdu_gate(const char *name, const char *parent, + void __iomem *reg, u32 flags, + spinlock_t *cdu_lock) +{ + return clk_register_gate(NULL, name, parent, + CLK_SET_RATE_PARENT | flags, reg, + CDU_EN_BIT, 0, cdu_lock); +} + +static inline struct clk *cgu_gate(const char *name, const char *parent, + void __iomem *reg, u8 bit, + spinlock_t *cdu_lock) +{ + return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, bit, CLK_GATE_SET_TO_DISABLE, + cdu_lock); +} + +static inline int cdu_check_clocks(struct clk *clks[], size_t count) +{ + size_t i; + + for (i = 0; i < count; ++i) { + if (IS_ERR(clks[i])) { + pr_err("Clock %zu failed to register: %ld\n", i, + PTR_ERR(clks[i])); + return PTR_ERR(clks[i]); + } + } + + return 0; +} + +#endif From f3046722101d74f8adb47161238bc11a30f32f75 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:10:40 +0200 Subject: [PATCH 03/85] clock: Add driver for ADSP-SC5xx Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- drivers/clk/Makefile | 1 + drivers/clk/adi/Makefile | 5 + drivers/clk/adi/clk-adi-sc598.c | 352 ++++++++++++++++++++ include/dt-bindings/clock/adi-sc5xx-clock.h | 278 ++++++++++++++++ 4 files changed, 636 insertions(+) create mode 100644 drivers/clk/adi/Makefile create mode 100644 drivers/clk/adi/clk-adi-sc598.c create mode 100644 include/dt-bindings/clock/adi-sc5xx-clock.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 61ec08404442b4..2d76549e7017f9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o # please keep this section sorted lexicographically by directory path name obj-y += actions/ +obj-y += adi/ obj-y += analogbits/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_ARTPEC) += axis/ diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile new file mode 100644 index 00000000000000..914cc0537d5cb4 --- /dev/null +++ b/drivers/clk/adi/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +obj-$(CONFIG_ARCH_SC59X_64) += clk-adi-pll.o + +obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o diff --git a/drivers/clk/adi/clk-adi-sc598.c b/drivers/clk/adi/clk-adi-sc598.c new file mode 100644 index 00000000000000..4333c6d15b225b --- /dev/null +++ b/drivers/clk/adi/clk-adi-sc598.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Clock support for ADI processor + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static DEFINE_SPINLOCK(cdu_lock); + +static struct clk *clks[ADSP_SC598_CLK_END]; +static struct clk_onecell_data clk_data; + +static const char *const cgu1_in_sels[] = { "sys_clkin0", "sys_clkin1" }; +static const char *const cgu0_s1sels[] = { "cgu0_s1seldiv", "cgu0_s1selexdiv" }; +static const char *const cgu1_s0sels[] = { "cgu1_s0seldiv", "cgu1_s0selexdiv" }; +static const char *const cgu1_s1sels[] = { "cgu1_s1seldiv", "cgu1_s1selexdiv" }; +static const char *const sharc0_sels[] = { "cclk0_0", "dummy", "dummy", "dummy" }; +static const char *const sharc1_sels[] = { "cclk0_0", "dummy", "dummy", "dummy" }; +static const char *const arm_sels[] = { "dummy", "dummy", "cclk2_0", "cclk2_1" }; +static const char *const cdu_ddr_sels[] = { "dclk_0", "dclk_1", "dummy", "dummy" }; +static const char *const can_sels[] = { "dummy", "oclk_1", "dummy", "dummy" }; +static const char *const spdif_sels[] = { "sclk1_0", "dummy", "dummy", "dummy" }; +static const char *const spi_sels[] = { "sclk0_0", "oclk_0", "dummy", "dummy" }; +static const char *const gige_sels[] = { "sclk0_0", "sclk0_1", "dummy", "dummy" }; +static const char *const lp_sels[] = { "oclk_0", "sclk0_0", "cclk0_1", "dummy" }; +static const char *const lp_ddr_sels[] = { "oclk_0", "dclk_0", "sysclk_1", "dummy" }; +static const char *const ospi_refclk_sels[] = { "sysclk_0", "sclk0_0", "sclk1_1", "dummy" }; +static const char *const trace_sels[] = { "sclk0_0", "dummy", "dummy", "dummy" }; +static const char *const emmc_sels[] = { "oclk_0", "sclk0_1", "dclk_0_half", "dclk_1_half" }; +static const char *const emmc_timer_sels[] = { "dummy", "sclk1_1_half", "dummy", "dummy" }; + +static const char *const ddr_sels[] = { "cdu_ddr", "3pll_ddiv" }; + +static void sc598_clock_probe(struct device_node *np) +{ + void __iomem *cgu0; + void __iomem *cgu1; + void __iomem *cdu; + void __iomem *pll3; + int ret; + int i; + + cgu0 = of_iomap(np, 0); + if (!cgu0) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (!cgu1) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (!cdu) { + pr_err("Unable to remap CDU address (resource 2)\n"); + return; + } + + pll3 = of_iomap(np, 3); + if (!pll3) { + pr_err + ("Unable to remap PLL3 control register (resource 3)\n"); + return; + } + // We only access this one register for pll3 + pll3 = pll3 + PLL3_OFFSET; + + // Input clock configuration + clks[ADSP_SC598_CLK_DUMMY] = + clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); + clks[ADSP_SC598_CLK_SYS_CLKIN0] = + of_clk_get_by_name(np, "sys_clkin0"); + clks[ADSP_SC598_CLK_SYS_CLKIN1] = + of_clk_get_by_name(np, "sys_clkin1"); + clks[ADSP_SC598_CLK_CGU1_IN] = + clk_register_mux(NULL, "cgu1_in_sel", cgu1_in_sels, 2, + CLK_SET_RATE_PARENT, cdu + CDU_CLKINSEL, 0, 1, + 0, &cdu_lock); + + // 3rd pll reuses cgu1 clk in selection, feeds directly into 3pll df + // changing the cgu1 in sel mux will affect 3pll so reuse the same clocks + + // CGU configuration and internal clocks + clks[ADSP_SC598_CLK_CGU0_PLL_IN] = + clk_register_divider(NULL, "cgu0_df", "sys_clkin0", + CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 0, 1, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_PLL_IN] = + clk_register_divider(NULL, "cgu1_df", "cgu1_in_sel", + CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 0, 1, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_3PLL_PLL_IN] = + clk_register_divider(NULL, "3pll_df", "cgu1_in_sel", + CLK_SET_RATE_PARENT, pll3, 3, 1, 0, + &cdu_lock); + + // VCO output inside PLL + clks[ADSP_SC598_CLK_CGU0_VCO_OUT] = + sc5xx_cgu_pll("cgu0_vco", "cgu0_df", cgu0 + CGU_CTL, + CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, true, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_VCO_OUT] = + sc5xx_cgu_pll("cgu1_vco", "cgu1_df", cgu1 + CGU_CTL, + CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, true, + &cdu_lock); + clks[ADSP_SC598_CLK_3PLL_VCO_OUT] = + sc5xx_cgu_pll("3pll_vco", "3pll_df", pll3, PLL3_MSEL_SHIFT, + PLL3_MSEL_WIDTH, 1, true, &cdu_lock); + + // Final PLL output + clks[ADSP_SC598_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, + "cgu0_pllclk", + "cgu0_vco", + CLK_SET_RATE_PARENT, + 1, 2); + clks[ADSP_SC598_CLK_CGU1_PLLCLK] = + clk_register_fixed_factor(NULL, "cgu1_pllclk", "cgu1_vco", + CLK_SET_RATE_PARENT, 1, 2); + clks[ADSP_SC598_CLK_3PLL_PLLCLK] = + clk_register_fixed_factor(NULL, "3pll_pllclk", "3pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + // Dividers from pll output + clks[ADSP_SC598_CLK_CGU0_CDIV] = + cgu_divider("cgu0_cdiv", "cgu0_pllclk", cgu0 + CGU_DIV, 0, 5, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_SYSCLK] = + cgu_divider("sysclk_0", "cgu0_pllclk", cgu0 + CGU_DIV, 8, 5, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_DDIV] = + cgu_divider("cgu0_ddiv", "cgu0_pllclk", cgu0 + CGU_DIV, 16, 5, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_ODIV] = + cgu_divider("cgu0_odiv", "cgu0_pllclk", cgu0 + CGU_DIV, 22, 7, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S0SELDIV] = + cgu_divider("cgu0_s0seldiv", "sysclk_0", cgu0 + CGU_DIV, 5, 3, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S1SELDIV] = + cgu_divider("cgu0_s1seldiv", "sysclk_0", cgu0 + CGU_DIV, 13, 3, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S1SELEXDIV] = + cgu_divider("cgu0_s1selexdiv", "cgu0_pllclk", cgu0 + CGU_DIVEX, + 16, 8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S1SEL] = + clk_register_mux(NULL, "cgu0_sclk1sel", cgu0_s1sels, 2, + CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 17, 1, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_CCLK2] = + clk_register_fixed_factor(NULL, "cclk2_0", "cgu0_vco", + CLK_SET_RATE_PARENT, 1, 3); + + clks[ADSP_SC598_CLK_CGU1_CDIV] = + cgu_divider("cgu1_cdiv", "cgu1_pllclk", cgu1 + CGU_DIV, 0, 5, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_SYSCLK] = + cgu_divider("sysclk_1", "cgu1_pllclk", cgu1 + CGU_DIV, 8, 5, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_DDIV] = + cgu_divider("cgu1_ddiv", "cgu1_pllclk", cgu1 + CGU_DIV, 16, 5, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_ODIV] = + cgu_divider("cgu1_odiv", "cgu1_pllclk", cgu1 + CGU_DIV, 22, 7, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S0SELDIV] = + cgu_divider("cgu1_s0seldiv", "sysclk_1", cgu1 + CGU_DIV, 5, 3, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S1SELDIV] = + cgu_divider("cgu1_s1seldiv", "sysclk_1", cgu1 + CGU_DIV, 13, 3, + 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S0SELEXDIV] = + cgu_divider("cgu1_s0selexdiv", "cgu1_pllclk", cgu1 + CGU_DIVEX, + 0, 8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S1SELEXDIV] = + cgu_divider("cgu1_s1selexdiv", "cgu1_pllclk", cgu1 + CGU_DIVEX, + 16, 8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S0SEL] = + clk_register_mux(NULL, "cgu1_sclk0sel", cgu1_s0sels, 2, + CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 16, 1, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S1SEL] = + clk_register_mux(NULL, "cgu1_sclk1sel", cgu1_s1sels, 2, + CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 17, 1, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_CCLK2] = + clk_register_fixed_factor(NULL, "cclk2_1", "cgu1_vco", + CLK_SET_RATE_PARENT, 1, 3); + + clks[ADSP_SC598_CLK_3PLL_DDIV] = + clk_register_divider(NULL, "3pll_ddiv", "3pll_pllclk", + CLK_SET_RATE_PARENT, pll3, 12, 5, 0, + &cdu_lock); + + // Gates to enable CGU outputs + clks[ADSP_SC598_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv", + cgu0 + CGU_CCBF_DIS, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_OCLK] = + cgu_gate("oclk_0", "cgu0_odiv", cgu0 + CGU_SCBF_DIS, 3, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_DCLK] = + cgu_gate("dclk_0", "cgu0_ddiv", cgu0 + CGU_SCBF_DIS, 2, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_SCLK1] = + cgu_gate("sclk1_0", "cgu0_sclk1sel", cgu0 + CGU_SCBF_DIS, 1, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_SCLK0] = + cgu_gate("sclk0_0", "cgu0_s0seldiv", cgu0 + CGU_SCBF_DIS, 0, + &cdu_lock); + + clks[ADSP_SC598_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_OCLK] = + cgu_gate("oclk_1", "cgu1_odiv", cgu1 + CGU_SCBF_DIS, 3, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_DCLK] = + cgu_gate("dclk_1", "cgu1_ddiv", cgu1 + CGU_SCBF_DIS, 2, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_SCLK1] = + cgu_gate("sclk1_1", "cgu1_sclk1sel", cgu1 + CGU_SCBF_DIS, 1, + &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_SCLK0] = + cgu_gate("sclk0_1", "cgu1_sclk0sel", cgu1 + CGU_SCBF_DIS, 0, + &cdu_lock); + + // Extra half rate clocks generated in the CDU + clks[ADSP_SC598_CLK_DCLK0_HALF] = + clk_register_fixed_factor(NULL, "dclk_0_half", "dclk_0", + CLK_SET_RATE_PARENT, 1, 2); + clks[ADSP_SC598_CLK_DCLK1_HALF] = + clk_register_fixed_factor(NULL, "dclk_1_half", "dclk_1", + CLK_SET_RATE_PARENT, 1, 2); + clks[ADSP_SC598_CLK_CGU1_SCLK1_HALF] = + clk_register_fixed_factor(NULL, "sclk1_1_half", "sclk1_1", + CLK_SET_RATE_PARENT, 1, 2); + + // CDU output muxes + clks[ADSP_SC598_CLK_SHARC0_SEL] = + cdu_mux("sharc0_sel", cdu + CDU_CFG0, sharc0_sels, &cdu_lock); + clks[ADSP_SC598_CLK_SHARC1_SEL] = + cdu_mux("sharc1_sel", cdu + CDU_CFG1, sharc1_sels, &cdu_lock); + clks[ADSP_SC598_CLK_ARM_SEL] = + cdu_mux("arm_sel", cdu + CDU_CFG2, arm_sels, &cdu_lock); + clks[ADSP_SC598_CLK_CDU_DDR_SEL] = + cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3, cdu_ddr_sels, + &cdu_lock); + clks[ADSP_SC598_CLK_CAN_SEL] = + cdu_mux("can_sel", cdu + CDU_CFG4, can_sels, &cdu_lock); + clks[ADSP_SC598_CLK_SPDIF_SEL] = + cdu_mux("spdif_sel", cdu + CDU_CFG5, spdif_sels, &cdu_lock); + clks[ADSP_SC598_CLK_SPI_SEL] = + cdu_mux("spi_sel", cdu + CDU_CFG6, spi_sels, &cdu_lock); + clks[ADSP_SC598_CLK_GIGE_SEL] = + cdu_mux("gige_sel", cdu + CDU_CFG7, gige_sels, &cdu_lock); + clks[ADSP_SC598_CLK_LP_SEL] = + cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels, &cdu_lock); + clks[ADSP_SC598_CLK_LP_DDR_SEL] = + cdu_mux("lp_ddr_sel", cdu + CDU_CFG9, lp_ddr_sels, &cdu_lock); + clks[ADSP_SC598_CLK_OSPI_REFCLK_SEL] = + cdu_mux("ospi_refclk_sel", cdu + CDU_CFG10, ospi_refclk_sels, + &cdu_lock); + clks[ADSP_SC598_CLK_TRACE_SEL] = + cdu_mux("trace_sel", cdu + CDU_CFG12, trace_sels, &cdu_lock); + clks[ADSP_SC598_CLK_EMMC_SEL] = + cdu_mux("emmc_sel", cdu + CDU_CFG13, emmc_sels, &cdu_lock); + clks[ADSP_SC598_CLK_EMMC_TIMER_QMC_SEL] = + cdu_mux("emmc_timer_qmc_sel", cdu + CDU_CFG14, emmc_timer_sels, + &cdu_lock); + + // CDU output enable gates + clks[ADSP_SC598_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", + cdu + CDU_CFG0, + CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC598_CLK_SHARC1] = + cdu_gate("sharc1", "sharc1_sel", cdu + CDU_CFG1, + CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC598_CLK_ARM] = + cdu_gate("arm", "arm_sel", cdu + CDU_CFG2, CLK_IS_CRITICAL, + &cdu_lock); + clks[ADSP_SC598_CLK_CDU_DDR] = + cdu_gate("cdu_ddr", "cdu_ddr_sel", cdu + CDU_CFG3, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_CAN] = + cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0, &cdu_lock); + clks[ADSP_SC598_CLK_SPDIF] = + cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, 0, &cdu_lock); + clks[ADSP_SC598_CLK_SPI] = + cdu_gate("spi", "spi_sel", cdu + CDU_CFG6, 0, &cdu_lock); + clks[ADSP_SC598_CLK_GIGE] = + cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0, &cdu_lock); + clks[ADSP_SC598_CLK_LP] = + cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_LP_DDR] = + cdu_gate("lp_ddr", "lp_ddr_sel", cdu + CDU_CFG9, 0, &cdu_lock); + clks[ADSP_SC598_CLK_OSPI_REFCLK] = + cdu_gate("ospi_refclk", "ospi_refclk_sel", cdu + CDU_CFG10, 0, + &cdu_lock); + clks[ADSP_SC598_CLK_TRACE] = + cdu_gate("trace", "trace_sel", cdu + CDU_CFG12, 0, &cdu_lock); + clks[ADSP_SC598_CLK_EMMC] = + cdu_gate("emmc", "emmc_sel", cdu + CDU_CFG13, 0, &cdu_lock); + clks[ADSP_SC598_CLK_EMMC_TIMER_QMC] = + cdu_gate("emmc_timer_qmc", "emmc_timer_qmc_sel", + cdu + CDU_CFG14, 0, &cdu_lock); + + // Dedicated DDR output mux + clks[ADSP_SC598_CLK_DDR] = + clk_register_mux(NULL, "ddr", ddr_sels, 2, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, pll3, + 11, 1, 0, &cdu_lock); + + ret = cdu_check_clocks(clks, ARRAY_SIZE(clks)); + if (ret) + goto cleanup; + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + pr_err("Failed to register SoC clock information\n"); + goto cleanup; + } + + return; + +cleanup: + for (i = 0; i < ARRAY_SIZE(clks); i++) + clk_unregister(clks[i]); +} + +CLK_OF_DECLARE(sc598_clocks, "adi,sc598-clocks", sc598_clock_probe); diff --git a/include/dt-bindings/clock/adi-sc5xx-clock.h b/include/dt-bindings/clock/adi-sc5xx-clock.h new file mode 100644 index 00000000000000..d5aa8d2d3646b7 --- /dev/null +++ b/include/dt-bindings/clock/adi-sc5xx-clock.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADSP SC5xx clock device tree bindings + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef DT_BINDINGS_CLOCK_ADI_SC5XX_CLOCK_H +#define DT_BINDINGS_CLOCK_ADI_SC5XX_CLOCK_H + +//ADSP-SC594 +#define ADSP_SC594_CLK_DUMMY 0 +#define ADSP_SC594_CLK_SYS_CLKIN0 1 +#define ADSP_SC594_CLK_SYS_CLKIN1 2 +#define ADSP_SC594_CLK_CGU1_IN 3 +#define ADSP_SC594_CLK_CGU0_PLL_IN 4 +#define ADSP_SC594_CLK_CGU1_PLL_IN 5 +#define ADSP_SC594_CLK_CGU0_VCO_OUT 6 +#define ADSP_SC594_CLK_CGU1_VCO_OUT 7 +#define ADSP_SC594_CLK_CGU0_PLLCLK 8 +#define ADSP_SC594_CLK_CGU1_PLLCLK 9 +#define ADSP_SC594_CLK_CGU0_CDIV 10 +#define ADSP_SC594_CLK_CGU0_SYSCLK 11 +#define ADSP_SC594_CLK_CGU0_DDIV 12 +#define ADSP_SC594_CLK_CGU0_ODIV 13 +#define ADSP_SC594_CLK_CGU0_S0SELDIV 14 +#define ADSP_SC594_CLK_CGU0_S1SELDIV 15 +#define ADSP_SC594_CLK_CGU1_CDIV 16 +#define ADSP_SC594_CLK_CGU1_SYSCLK 17 +#define ADSP_SC594_CLK_CGU1_DDIV 18 +#define ADSP_SC594_CLK_CGU1_ODIV 19 +#define ADSP_SC594_CLK_CGU1_S0SELDIV 20 +#define ADSP_SC594_CLK_CGU1_S1SELDIV 21 +#define ADSP_SC594_CLK_CGU0_CCLK0 22 +#define ADSP_SC594_CLK_CGU0_CCLK1 23 +#define ADSP_SC594_CLK_CGU0_OCLK 24 +#define ADSP_SC594_CLK_CGU0_DCLK 25 +#define ADSP_SC594_CLK_CGU0_SCLK1 26 +#define ADSP_SC594_CLK_CGU0_SCLK0 27 +#define ADSP_SC594_CLK_CGU1_CCLK0 28 +#define ADSP_SC594_CLK_CGU1_CCLK1 29 +#define ADSP_SC594_CLK_CGU1_OCLK 30 +#define ADSP_SC594_CLK_CGU1_DCLK 31 +#define ADSP_SC594_CLK_CGU1_SCLK1 32 +#define ADSP_SC594_CLK_CGU1_SCLK0 33 +#define ADSP_SC594_CLK_SHARC0_SEL 34 +#define ADSP_SC594_CLK_SHARC1_SEL 35 +#define ADSP_SC594_CLK_ARM_SEL 36 +#define ADSP_SC594_CLK_CDU_DDR_SEL 37 +#define ADSP_SC594_CLK_CAN_SEL 38 +#define ADSP_SC594_CLK_SPDIF_SEL 39 +#define ADSP_SC594_CLK_RESERVED_SEL 40 +#define ADSP_SC594_CLK_GIGE_SEL 41 +#define ADSP_SC594_CLK_LP_SEL 42 +#define ADSP_SC594_CLK_LPDDR_SEL 43 +#define ADSP_SC594_CLK_OSPI_SEL 44 +#define ADSP_SC594_CLK_TRACE_SEL 45 +#define ADSP_SC594_CLK_SHARC0 46 +#define ADSP_SC594_CLK_SHARC1 47 +#define ADSP_SC594_CLK_ARM 48 +#define ADSP_SC594_CLK_CDU_DDR 49 +#define ADSP_SC594_CLK_CAN 50 +#define ADSP_SC594_CLK_SPDIF 51 +#define ADSP_SC594_CLK_SPI 52 +#define ADSP_SC594_CLK_GIGE 53 +#define ADSP_SC594_CLK_LP 54 +#define ADSP_SC594_CLK_LPDDR 55 +#define ADSP_SC594_CLK_OSPI 56 +#define ADSP_SC594_CLK_TRACE 57 +#define ADSP_SC594_CLK_END 58 + +//ADSP-SC598 +#define ADSP_SC598_CLK_DUMMY 0 +#define ADSP_SC598_CLK_SYS_CLKIN0 1 +#define ADSP_SC598_CLK_SYS_CLKIN1 2 +#define ADSP_SC598_CLK_CGU0_PLL_IN 3 +#define ADSP_SC598_CLK_CGU0_VCO_OUT 4 +#define ADSP_SC598_CLK_CGU0_PLLCLK 5 +#define ADSP_SC598_CLK_CGU1_IN 6 +#define ADSP_SC598_CLK_CGU1_PLL_IN 7 +#define ADSP_SC598_CLK_CGU1_VCO_OUT 8 +#define ADSP_SC598_CLK_CGU1_PLLCLK 9 +#define ADSP_SC598_CLK_CGU0_CDIV 10 +#define ADSP_SC598_CLK_CGU0_SYSCLK 11 +#define ADSP_SC598_CLK_CGU0_DDIV 12 +#define ADSP_SC598_CLK_CGU0_ODIV 13 +#define ADSP_SC598_CLK_CGU0_S0SELDIV 14 +#define ADSP_SC598_CLK_CGU0_S1SELDIV 15 +#define ADSP_SC598_CLK_CGU0_S1SELEXDIV 16 +#define ADSP_SC598_CLK_CGU0_S1SEL 17 +#define ADSP_SC598_CLK_CGU1_CDIV 18 +#define ADSP_SC598_CLK_CGU1_SYSCLK 19 +#define ADSP_SC598_CLK_CGU1_DDIV 20 +#define ADSP_SC598_CLK_CGU1_ODIV 21 +#define ADSP_SC598_CLK_CGU1_S0SELDIV 22 +#define ADSP_SC598_CLK_CGU1_S1SELDIV 23 +#define ADSP_SC598_CLK_CGU1_S0SELEXDIV 24 +#define ADSP_SC598_CLK_CGU1_S1SELEXDIV 25 +#define ADSP_SC598_CLK_CGU1_S0SEL 26 +#define ADSP_SC598_CLK_CGU1_S1SEL 27 +#define ADSP_SC598_CLK_CGU0_CCLK2 28 +#define ADSP_SC598_CLK_CGU0_CCLK0 29 +#define ADSP_SC598_CLK_CGU0_OCLK 30 +#define ADSP_SC598_CLK_CGU0_DCLK 31 +#define ADSP_SC598_CLK_CGU0_SCLK1 32 +#define ADSP_SC598_CLK_CGU0_SCLK0 33 +#define ADSP_SC598_CLK_CGU1_CCLK0 34 +#define ADSP_SC598_CLK_CGU1_OCLK 35 +#define ADSP_SC598_CLK_CGU1_DCLK 36 +#define ADSP_SC598_CLK_CGU1_SCLK1 37 +#define ADSP_SC598_CLK_CGU1_SCLK0 38 +#define ADSP_SC598_CLK_CGU1_CCLK2 39 +#define ADSP_SC598_CLK_DCLK0_HALF 40 +#define ADSP_SC598_CLK_DCLK1_HALF 41 +#define ADSP_SC598_CLK_CGU1_SCLK1_HALF 42 +#define ADSP_SC598_CLK_SHARC0_SEL 43 +#define ADSP_SC598_CLK_SHARC1_SEL 44 +#define ADSP_SC598_CLK_ARM_SEL 45 +#define ADSP_SC598_CLK_CDU_DDR_SEL 46 +#define ADSP_SC598_CLK_CAN_SEL 47 +#define ADSP_SC598_CLK_SPDIF_SEL 48 +#define ADSP_SC598_CLK_SPI_SEL 49 +#define ADSP_SC598_CLK_GIGE_SEL 50 +#define ADSP_SC598_CLK_LP_SEL 51 +#define ADSP_SC598_CLK_LP_DDR_SEL 52 +#define ADSP_SC598_CLK_OSPI_REFCLK_SEL 53 +#define ADSP_SC598_CLK_TRACE_SEL 54 +#define ADSP_SC598_CLK_EMMC_SEL 55 +#define ADSP_SC598_CLK_EMMC_TIMER_QMC_SEL 56 +#define ADSP_SC598_CLK_SHARC0 57 +#define ADSP_SC598_CLK_SHARC1 58 +#define ADSP_SC598_CLK_ARM 59 +#define ADSP_SC598_CLK_CDU_DDR 60 +#define ADSP_SC598_CLK_CAN 61 +#define ADSP_SC598_CLK_SPDIF 62 +#define ADSP_SC598_CLK_SPI 63 +#define ADSP_SC598_CLK_GIGE 64 +#define ADSP_SC598_CLK_LP 65 +#define ADSP_SC598_CLK_LP_DDR 66 +#define ADSP_SC598_CLK_OSPI_REFCLK 67 +#define ADSP_SC598_CLK_TRACE 68 +#define ADSP_SC598_CLK_EMMC 69 +#define ADSP_SC598_CLK_EMMC_TIMER_QMC 70 +#define ADSP_SC598_CLK_3PLL_PLL_IN 71 +#define ADSP_SC598_CLK_3PLL_VCO_OUT 72 +#define ADSP_SC598_CLK_3PLL_PLLCLK 73 +#define ADSP_SC598_CLK_3PLL_DDIV 74 +#define ADSP_SC598_CLK_DDR_SEL 75 +#define ADSP_SC598_CLK_DDR 76 +#define ADSP_SC598_CLK_END 77 + +//ADSP-SC58X +#define ADSP_SC58X_CLK_DUMMY 0 +#define ADSP_SC58X_CLK_SYS_CLKIN0 1 +#define ADSP_SC58X_CLK_SYS_CLKIN1 2 +#define ADSP_SC58X_CLK_CGU0_PLL_IN 3 +#define ADSP_SC58X_CLK_CGU0_VCO_OUT 4 +#define ADSP_SC58X_CLK_CGU0_PLLCLK 5 +#define ADSP_SC58X_CLK_CGU1_IN 6 +#define ADSP_SC58X_CLK_CGU1_PLL_IN 7 +#define ADSP_SC58X_CLK_CGU1_VCO_OUT 8 +#define ADSP_SC58X_CLK_CGU1_PLLCLK 9 +#define ADSP_SC58X_CLK_CGU0_CDIV 10 +#define ADSP_SC58X_CLK_CGU0_SYSCLK 11 +#define ADSP_SC58X_CLK_CGU0_DDIV 12 +#define ADSP_SC58X_CLK_CGU0_ODIV 13 +#define ADSP_SC58X_CLK_CGU0_S0SELDIV 14 +#define ADSP_SC58X_CLK_CGU0_S1SELDIV 15 +#define ADSP_SC58X_CLK_CGU0_S1SEL 16 +#define ADSP_SC58X_CLK_CGU1_CDIV 17 +#define ADSP_SC58X_CLK_CGU1_SYSCLK 18 +#define ADSP_SC58X_CLK_CGU1_DDIV 19 +#define ADSP_SC58X_CLK_CGU1_ODIV 20 +#define ADSP_SC58X_CLK_CGU1_S0SELDIV 21 +#define ADSP_SC58X_CLK_CGU1_S1SELDIV 22 +#define ADSP_SC58X_CLK_CGU1_S0SEL 23 +#define ADSP_SC58X_CLK_CGU1_S1SEL 24 +#define ADSP_SC58X_CLK_CGU0_CCLK0 25 +#define ADSP_SC58X_CLK_CGU0_CCLK1 26 +#define ADSP_SC58X_CLK_CGU0_OCLK 27 +#define ADSP_SC58X_CLK_CGU0_DCLK 28 +#define ADSP_SC58X_CLK_CGU0_SCLK1 29 +#define ADSP_SC58X_CLK_CGU0_SCLK0 30 +#define ADSP_SC58X_CLK_CGU1_CCLK0 31 +#define ADSP_SC58X_CLK_CGU1_CCLK1 32 +#define ADSP_SC58X_CLK_CGU1_OCLK 33 +#define ADSP_SC58X_CLK_CGU1_DCLK 34 +#define ADSP_SC58X_CLK_CGU1_SCLK1 35 +#define ADSP_SC58X_CLK_CGU1_SCLK0 36 +#define ADSP_SC58X_CLK_OCLK0_HALF 37 +#define ADSP_SC58X_CLK_CCLK1_1_HALF 38 +#define ADSP_SC58X_CLK_SHARC0_SEL 39 +#define ADSP_SC58X_CLK_SHARC1_SEL 40 +#define ADSP_SC58X_CLK_ARM_SEL 41 +#define ADSP_SC58X_CLK_CDU_DDR_SEL 42 +#define ADSP_SC58X_CLK_CAN_SEL 43 +#define ADSP_SC58X_CLK_SPDIF_SEL 44 +#define ADSP_SC58X_CLK_RESERVED_SEL 45 +#define ADSP_SC58X_CLK_GIGE_SEL 46 +#define ADSP_SC58X_CLK_LP_SEL 47 +#define ADSP_SC58X_CLK_SDIO_SEL 48 +#define ADSP_SC58X_CLK_SHARC0 49 +#define ADSP_SC58X_CLK_SHARC1 50 +#define ADSP_SC58X_CLK_ARM 51 +#define ADSP_SC58X_CLK_CDU_DDR 52 +#define ADSP_SC58X_CLK_CAN 53 +#define ADSP_SC58X_CLK_SPDIF 54 +#define ADSP_SC58X_CLK_RESERVED 55 +#define ADSP_SC58X_CLK_GIGE 56 +#define ADSP_SC58X_CLK_LP 57 +#define ADSP_SC58X_CLK_SDIO 58 +#define ADSP_SC58X_CLK_END 59 + +//ADSP-SC57X +#define ADSP_SC57X_CLK_DUMMY 0 +#define ADSP_SC57X_CLK_SYS_CLKIN0 1 +#define ADSP_SC57X_CLK_SYS_CLKIN1 2 +#define ADSP_SC57X_CLK_CGU0_PLL_IN 3 +#define ADSP_SC57X_CLK_CGU0_PLLCLK 4 +#define ADSP_SC57X_CLK_CGU1_IN 5 +#define ADSP_SC57X_CLK_CGU1_PLL_IN 6 +#define ADSP_SC57X_CLK_CGU1_PLLCLK 7 +#define ADSP_SC57X_CLK_CGU0_CDIV 8 +#define ADSP_SC57X_CLK_CGU0_SYSCLK 9 +#define ADSP_SC57X_CLK_CGU0_DDIV 10 +#define ADSP_SC57X_CLK_CGU0_ODIV 11 +#define ADSP_SC57X_CLK_CGU0_S0SELDIV 12 +#define ADSP_SC57X_CLK_CGU0_S1SELDIV 13 +#define ADSP_SC57X_CLK_CGU0_S1SEL 14 +#define ADSP_SC57X_CLK_CGU1_CDIV 15 +#define ADSP_SC57X_CLK_CGU1_SYSCLK 16 +#define ADSP_SC57X_CLK_CGU1_DDIV 17 +#define ADSP_SC57X_CLK_CGU1_ODIV 18 +#define ADSP_SC57X_CLK_CGU1_S0SELDIV 19 +#define ADSP_SC57X_CLK_CGU1_S1SELDIV 20 +#define ADSP_SC57X_CLK_CGU1_S0SEL 21 +#define ADSP_SC57X_CLK_CGU1_S1SEL 22 +#define ADSP_SC57X_CLK_CGU0_CCLK0 23 +#define ADSP_SC57X_CLK_CGU0_CCLK1 24 +#define ADSP_SC57X_CLK_CGU0_OCLK 25 +#define ADSP_SC57X_CLK_CGU0_DCLK 26 +#define ADSP_SC57X_CLK_CGU0_SCLK1 27 +#define ADSP_SC57X_CLK_CGU0_SCLK0 28 +#define ADSP_SC57X_CLK_CGU1_CCLK0 29 +#define ADSP_SC57X_CLK_CGU1_CCLK1 30 +#define ADSP_SC57X_CLK_CGU1_OCLK 31 +#define ADSP_SC57X_CLK_CGU1_DCLK 32 +#define ADSP_SC57X_CLK_CGU1_SCLK1 33 +#define ADSP_SC57X_CLK_CGU1_SCLK0 34 +#define ADSP_SC57X_CLK_OCLK0_HALF 35 +#define ADSP_SC57X_CLK_CCLK1_1_HALF 36 +#define ADSP_SC57X_CLK_SHARC0_SEL 37 +#define ADSP_SC57X_CLK_SHARC1_SEL 38 +#define ADSP_SC57X_CLK_ARM_SEL 39 +#define ADSP_SC57X_CLK_CDU_DDR_SEL 40 +#define ADSP_SC57X_CLK_CAN_SEL 41 +#define ADSP_SC57X_CLK_SPDIF_SEL 42 +#define ADSP_SC57X_CLK_RESERVED_SEL 43 +#define ADSP_SC57X_CLK_GIGE_SEL 44 +#define ADSP_SC57X_CLK_LP_SEL 45 +#define ADSP_SC57X_CLK_SDIO_SEL 46 +#define ADSP_SC57X_CLK_SHARC0 47 +#define ADSP_SC57X_CLK_SHARC1 48 +#define ADSP_SC57X_CLK_ARM 49 +#define ADSP_SC57X_CLK_CDU_DDR 50 +#define ADSP_SC57X_CLK_CAN 51 +#define ADSP_SC57X_CLK_SPDIF 52 +#define ADSP_SC57X_CLK_GIGE 53 +#define ADSP_SC57X_CLK_SDIO 54 +#define ADSP_SC57X_CLK_END 55 + +#endif From be2d19e37be95214842a51f896d9b72901dc03d1 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 17 Sep 2025 12:38:19 +0200 Subject: [PATCH 04/85] soc: Add additional ADSP-SC5xxx SoC headers Signed-off-by: Philip Molloy --- include/linux/soc/adi/cpu.h | 124 +++++++++++++++++++++++++ include/linux/soc/adi/i2c.h | 19 ++++ include/linux/soc/adi/icc.h | 127 ++++++++++++++++++++++++++ include/linux/soc/adi/pads_system.h | 8 ++ include/linux/soc/adi/system_config.h | 76 +++++++++++++++ include/linux/soc/adi/uart4.h | 8 ++ 6 files changed, 362 insertions(+) create mode 100644 include/linux/soc/adi/cpu.h create mode 100644 include/linux/soc/adi/i2c.h create mode 100644 include/linux/soc/adi/icc.h create mode 100644 include/linux/soc/adi/pads_system.h create mode 100644 include/linux/soc/adi/system_config.h create mode 100644 include/linux/soc/adi/uart4.h diff --git a/include/linux/soc/adi/cpu.h b/include/linux/soc/adi/cpu.h new file mode 100644 index 00000000000000..f246dbf84d2051 --- /dev/null +++ b/include/linux/soc/adi/cpu.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef __MACH_CPU_H +#define __MACH_CPU_H + +#ifdef CONFIG_ARCH_SC57X +#define SYS_L2_START 0x20000000 +#define SYS_SRAM_BASE (0x20000000 + SZ_16K) +#else +#define SYS_L2_START 0x20080000 +#define SYS_SRAM_BASE (0x20080000 + SZ_16K) +#endif + +#define SYS_SRAM_SIZE (SZ_16K + SZ_32K * 3) +#define SYS_SRAM_ICC_SIZE SZ_4K +#define SYS_MMR_BASE 0x31000000 +#define SYS_MMR_SIZE SZ_1M +#define SYS_SMC_BANK1 0x44000000 + +#define SC57X_GIC_PORT0 0x310B2000 +#define SC57X_GIC_PORT1 0x310B4000 + +#define SC58X_GIC_PORT0 0x310B2000 +#define SC58X_GIC_PORT1 0x310B4000 + +#define SC59X_GIC_PORT0 0x310B2000 +#define SC59X_GIC_PORT1 0x310B4000 + +/* + * Timer Configuration Register Bits + */ +#define TIMER_EMU_RUN 0x8000 +#define TIMER_BPER_EN 0x4000 +#define TIMER_BWID_EN 0x2000 +#define TIMER_BDLY_EN 0x1000 +#define TIMER_OUT_DIS 0x0800 +#define TIMER_TIN_SEL 0x0400 +#define TIMER_CLK_SEL 0x0300 +#define TIMER_CLK_SCLK 0x0000 +#define TIMER_CLK_ALT_CLK0 0x0100 +#define TIMER_CLK_ALT_CLK1 0x0300 +#define TIMER_PULSE_HI 0x0080 +#define TIMER_SLAVE_TRIG 0x0040 +#define TIMER_IRQ_MODE 0x0030 +#define TIMER_IRQ_ACT_EDGE 0x0000 +#define TIMER_IRQ_DLY 0x0010 +#define TIMER_IRQ_WID_DLY 0x0020 +#define TIMER_IRQ_PER 0x0030 +#define TIMER_MODE 0x000f +#define TIMER_MODE_WDOG_P 0x0008 +#define TIMER_MODE_WDOG_W 0x0009 +#define TIMER_MODE_PWM_CONT 0x000c +#define TIMER_MODE_PWM 0x000d +#define TIMER_MODE_WDTH 0x000a +#define TIMER_MODE_WDTH_D 0x000b +#define TIMER_MODE_EXT_CLK 0x000e +#define TIMER_MODE_PININT 0x000f + +#define __BFP(m) u16 m; u16 __pad_##m + +struct gptimer3 { + __BFP(config); + u32 counter; + u32 period; + u32 width; + u32 delay; +}; + +struct sc5xx_gptimer { + int id; + int irq; + int reserved; + int int_enable; + void __iomem *io_base; + void __iomem *cgu0_ctl; + unsigned long isr_count; + struct platform_device *pdev; + struct list_head node; +}; + +struct gptimer3_group_regs { + __BFP(run); + __BFP(enable); + __BFP(disable); + __BFP(stop_cfg); + __BFP(stop_cfg_set); + __BFP(stop_cfg_clr); + __BFP(data_imsk); + __BFP(stat_imsk); + __BFP(tr_msk); + __BFP(tr_ie); + __BFP(data_ilat); + __BFP(stat_ilat); + __BFP(err_status); + __BFP(bcast_per); + __BFP(bcast_wid); + __BFP(bcast_dly); +}; + +/* The actual gptimer API */ +struct sc5xx_gptimer *gptimer_request(int id); +int gptimer_free(struct sc5xx_gptimer *timer); +void set_gptimer_pwidth(struct sc5xx_gptimer *timer, uint32_t width); +void set_gptimer_period(struct sc5xx_gptimer *timer, uint32_t period); +uint32_t get_gptimer_count(struct sc5xx_gptimer *timer); +void set_gptimer_config(struct sc5xx_gptimer *timer, u16 config); +void enable_gptimers(u16 mask); +void disable_gptimers(u16 mask); +void map_gptimers(void); +u16 get_gptimer_status(void); +void set_gptimer_status(u16 value); +void set_spu_securep_msec(u16 n, bool msec); +void platform_ipi_init(void); + +#endif /* __MACH_CPU_H */ diff --git a/include/linux/soc/adi/i2c.h b/include/linux/soc/adi/i2c.h new file mode 100644 index 00000000000000..3f6916e33d0f4d --- /dev/null +++ b/include/linux/soc/adi/i2c.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef ADI_I2C_H +#define ADI_I2C_H + +int adi_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data, + bool polling); + +int adi_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); + +int adi_twi_smbus_xfer_atomic(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); + +#endif diff --git a/include/linux/soc/adi/icc.h b/include/linux/soc/adi/icc.h new file mode 100644 index 00000000000000..19bde5d392af98 --- /dev/null +++ b/include/linux/soc/adi/icc.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +/* + * @todo next step is to unify the arm/mach-sc5xx headers under linux/soc/adi + * for now we forward to them based on platform + */ + +#ifndef SOC_ADI_ICC_H +#define SOC_ADI_ICC_H + +#include +#include +#include + +#define sm_atomic_read(v) ioread16(v) +#define sm_atomic_write(i, v) iowrite16(v, i) +#define invalidate_dcache_range(start, end) __sync_cache_range_r((void *)start, end - start) +#define flush_dcache_range(start, end) __sync_cache_range_w((void *)start, end - start) +#define arm_core_id() 0 + +#ifdef CONFIG_ARCH_SC59X_64 +#define ICC_CODE_START 0x20080000 +#elif defined(CONFIG_ARCH_SC59X) || defined(CONFIG_ARCH_SC57X) +#define ICC_CODE_START 0x20000000 +/* RCU0 (reset core unit) register structure */ +struct rcu_reg { + /* RCU0 Control Register */ + u32 reg_rcu_ctl; /* 0x00 */ + /* RCU0 Status Register */ + u32 reg_rcu_stat; /* 0x04 */ + /* RCU0 Core Reset Control Register */ + u32 reg_rcu_crctl; /* 0x08 */ + /* RCU0 Core Reset Status Register */ + u32 reg_rcu_crstat; /* 0x0c */ + /* reg pad from 0x10 to 0x17 */ + u8 pad_0x10_0x17[0x18 - 0x10]; /* 0x10 ~ 0x17 */ + /* RCU0 System Reset Status Register */ + u32 reg_rcu_srrqstat; /* 0x18 */ + /* RCU0 System Interface Disable Register */ + u32 reg_rcu_sidis; /* 0x1c */ + /* RCU0 System Interface Status Register */ + u32 reg_rcu_sistat; /* 0x20 */ + /* reg pad from 0x24 to 0x27 */ + u8 pad_0x24_0x27[0x28 - 0x24]; /* 0x24 ~ 0x27 */ + /* RCU0 Boot Code Register */ + u32 reg_rcu_bcode; /* 0x28 */ + /* Software Vector Register 0 to 2 */ + u32 reg_rcu_svect0; /* 0x2c */ + u32 reg_rcu_svect1; /* 0x30 */ + u32 reg_rcu_svect2; /* 0x34 */ + /* reg pad from 0x38 to 0x6b */ + u8 pad_0x38_0x6b[0x6C - 0x38]; /* 0x38 ~ 0x6b */ + /* RCU0 Message Register */ + u32 reg_rcu_msg; /* 0x6C */ + /* RCU0 Message Set Bits Register */ + u32 reg_rcu_msg_set; /* 0x70 */ + /* RCU0 Message Clear Bits Register */ + u32 reg_rcu_msg_clr; /* 0x74 */ +}; +#elif defined(CONFIG_ARCH_SC58X) +#define ICC_CODE_START 0x20080000 +/* RCU0 (reset core unit) register structure */ +struct rcu_reg { + /* RCU0 Control Register */ + u32 reg_rcu_ctl; /* 0x00 */ + /* RCU0 Status Register */ + u32 reg_rcu_stat; /* 0x04 */ + /* RCU0 Core Reset Control Register */ + u32 reg_rcu_crctl; /* 0x08 */ + /* RCU0 Core Reset Status Register */ + u32 reg_rcu_crstat; /* 0x0c */ + /* RCU0 System Interface Disable Register */ + u32 reg_rcu_sidis; /* 0x10 */ + /* RCU0 System Interface Status Register */ + u32 reg_rcu_sistat; /* 0x14 */ + /* RCU0 SVECT Lock Register */ + u32 reg_rcu_svect_lck; /* 0x18 */ + /* RCU0 Boot Code Register */ + u32 reg_rcu_bcode; /* 0x1c */ + /* Software Vector Register 0 to 2 */ + u32 reg_rcu_svect0; /* 0x20 */ + u32 reg_rcu_svect1; /* 0x24 */ + u32 reg_rcu_svect2; /* 0x28 */ + /* reg pad from 0x2c to 0x63 */ + u8 pad_0x2c_0x59[0x60 - 0x2c]; /* 0x2c ~ 0x59 */ + /* RCU0 Message Register */ + u32 reg_rcu_msg; /* 0x60 */ + /* RCU0 Message Set Bits Register */ + u32 reg_rcu_msg_set; /* 0x64 */ + /* RCU0 Message Clear Bits Register */ + u32 reg_rcu_msg_clr; /* 0x68 */ +}; +#endif + +#define ADI_RESOURCE_TABLE_TAG "AD-RESOURCE-TBL" +#define ADI_RSC_TABLE_INIT_MAGIC (0xADE0AD0E) +#define ADI_RESOURCE_TABLE_VERSION (1) +struct adi_resource_table_hdr { + u8 tag[16]; + u32 version; + u32 initialized; + u32 reserved[8]; +} __packed; + +struct adi_tru; + +struct adi_tru *get_adi_tru_from_node(struct device *dev); +void put_adi_tru(struct adi_tru *tru); +int adi_tru_trigger_device(struct adi_tru *tru, struct device *dev); +int adi_tru_trigger(struct adi_tru *tru, u32 master); +int adi_tru_set_trigger_by_id(struct adi_tru *tru, u32 master, u32 slave); +extern int adi_tru_probe(struct platform_device *pdev); +extern void adi_tru_remove(struct platform_device *pdev); +extern int adi_tru_set_trigger(struct adi_tru *tru, + struct device_node *master, + struct device_node *slave); + +#endif diff --git a/include/linux/soc/adi/pads_system.h b/include/linux/soc/adi/pads_system.h new file mode 100644 index 00000000000000..a9cc01cef9d729 --- /dev/null +++ b/include/linux/soc/adi/pads_system.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef ADI_PADS_SYSTEM_H +#define ADI_PADS_SYSTEM_H + +int adi_pads_probe(struct platform_device *pdev); +void adi_pads_remove(struct platform_device *pdev); + +#endif diff --git a/include/linux/soc/adi/system_config.h b/include/linux/soc/adi/system_config.h new file mode 100644 index 00000000000000..dbb06819767d4f --- /dev/null +++ b/include/linux/soc/adi/system_config.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Implementation of system_config, potential replacement for syscon that generalizes + * it to support arbitrary regmap registration and requires the driver to be initialized + * first + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SOC_ADI_SYSTEM_CONFIG_H +#define SOC_ADI_SYSTEM_CONFIG_H + +#include +#include +#include +#include +#include +#include + +struct system_register { + u32 id; + u32 offset; + u32 mask; + u8 shift; + bool is_bits; +}; + +struct system_config { + /* User configured */ + struct system_register *registers; + unsigned int max_register; + size_t len; + + /* Internal data populated during usage */ + struct regmap_config config; + struct regmap *mmio_regmap; + struct device_node *np; + struct list_head list; + struct regmap *system_regmap; +}; + +struct regmap *__regmap_init_system_config(struct device *dev, + struct system_config *config, + struct lock_class_key *lock_key, + const char *lock_name); + +struct regmap *__devm_regmap_init_system_config(struct device *dev, + struct system_config + *config, + struct lock_class_key + *lock_key, + const char *lock_name); + +#define regmap_init_system_config(dev, config) \ + __regmap_lockdep_wrapper(__regmap_init_system_config, #config, dev, config) + +#define devm_regmap_init_system_config(dev, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_system_config, #config, \ + dev, config) + +struct regmap *system_config_regmap_lookup_by_phandle(struct device_node + *np, + const char + *property); + +int system_config_probe(struct platform_device *pdev, + struct system_config *config); +int system_config_remove(struct platform_device *pdev); + +#endif diff --git a/include/linux/soc/adi/uart4.h b/include/linux/soc/adi/uart4.h new file mode 100644 index 00000000000000..a54496aa3482fc --- /dev/null +++ b/include/linux/soc/adi/uart4.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef ADI_UART4_H +#define ADI_UART4_H + +void adi_uart4_serial_rx_dma_timeout(struct timer_list *list); +struct adi_uart4_serial_port *to_adi_serial_port(struct uart_port *port); + +#endif From fc6a64690f68ffc065e02e0395ba90571c8bac77 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 16:57:41 +0200 Subject: [PATCH 05/85] dmaengine: Add support for ADSP-SC5xx DMA channels Signed-off-by: Philip Molloy --- drivers/dma/Kconfig | 8 +- drivers/dma/Makefile | 1 + drivers/dma/adi-dma.c | 1264 +++++++++++++++++++++++++++++++++++++++++ drivers/dma/adi-dma.h | 185 ++++++ 4 files changed, 1457 insertions(+), 1 deletion(-) create mode 100644 drivers/dma/adi-dma.c create mode 100644 drivers/dma/adi-dma.h diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index b8a74b1798ba1d..0b0079f6204bd7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -57,6 +57,12 @@ config DMA_OF select DMA_ENGINE #devices +config ADI_DMA + bool "ADI SC5xx DMA Controller" + depends on DMA_ENGINE && (ARCH_SC59X || ARCH_SC59X_64 || ARCH_SC58X || ARCH_SC57X) && DMA_OF + help + Enable DMA controller for ADI SC5xx SoCs + config ALTERA_MSGDMA tristate "Altera / Intel mSGDMA Engine" depends on HAS_IOMEM @@ -471,7 +477,7 @@ config MOXART_DMA select DMA_VIRTUAL_CHANNELS help Enable support for the MOXA ART SoC DMA controller. - + Say Y here if you enabled MMP ADMA, otherwise say N. config MPC512X_DMA diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a54d7688392b1a..6e70dce70a131a 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_DMA_OF) += of-dma.o obj-$(CONFIG_DMATEST) += dmatest.o #devices +obj-$(CONFIG_ADI_DMA) += adi-dma.o obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ diff --git a/drivers/dma/adi-dma.c b/drivers/dma/adi-dma.c new file mode 100644 index 00000000000000..b631b5e0aaa2b1 --- /dev/null +++ b/drivers/dma/adi-dma.c @@ -0,0 +1,1264 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DMA Controller driver for sc5xx hardware + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Author: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "adi-dma.h" + +#define ADI_MEMSET_SIZE (4 * sizeof(uint64_t)) + +struct adi_dma_hw { + int has_mdma; +}; + +struct adi_dma_filter_data { + u32 id; +}; + +struct adi_dma_descriptor { + // hardware fields in order; if we wanted to use hw descriptor mode instead of + // register mode these should be most of the required implementation + u32 next; + u32 start; + u32 cfg; + u32 xcnt; + u32 xmod; + u32 ycnt; + u32 ymod; + + // additional bookkeeping + struct dma_async_tx_descriptor tx; + struct dmaengine_result result; + struct list_head node; + struct list_head cb_node; + + enum dma_transfer_direction direction; + + // a cyclic descriptor will reuse itself, triggering callbacks as expected, + // and will not free itself when it finishes + int cyclic; + + // physical address of source location, in case of peripheral<->mem, the + // mem address is ALWAYS here and dest is unused. + dma_addr_t src; + + // physical address of destination, only used for MDMA + dma_addr_t dest; + + // virtual address of memset buffer, used only with memset + u64 *memset; + + // for scatter-gather only, sg is the original scatter gather list in + // case we need to do a cyclic sg operation, and sg_next is the next + // specific entry to load from; it will be null when we're done + struct scatterlist *sg; + struct scatterlist *sg_next; +}; + +struct adi_dma_channel { + int id; + struct adi_dma *dma; + void __iomem *iosrc; + void __iomem *iodest; + int running; + int use_interrupts; + int src_irq; + int src_err_irq; + int dest_irq; + int dest_err_irq; + // descriptor in flight + struct adi_dma_descriptor *current_desc; + // descriptors to process + struct list_head pending; + // descriptors to call callbacks on + struct list_head cb_pending; + struct dma_chan chan; + struct dma_slave_config config; + spinlock_t lock; +}; + +struct adi_dma { + struct device *dev; + struct dma_device dma_device; + void __iomem *ioaddr; + const struct adi_dma_hw *hw_cfg; + spinlock_t lock; +}; + +static struct adi_dma_hw adi_peripheral_dma_data = { + .has_mdma = 0, +}; + +static struct adi_dma_hw adi_mdma_data = { + .has_mdma = 1, +}; + +static const struct of_device_id dma_dt_ids[] = { + { .compatible = "adi,dma-controller", .data = &adi_peripheral_dma_data }, + { .compatible = "adi,mdma-controller", .data = &adi_mdma_data }, + { } +}; +MODULE_DEVICE_TABLE(of, dma_dt_ids); + +static void __adi_dma_enable_irqs(struct adi_dma_channel *); +static void __adi_dma_disable_irqs(struct adi_dma_channel *); +static void __clear_and_reset(struct adi_dma_channel *channel); + +static irqreturn_t adi_dma_handler(int irq, void *id); +static irqreturn_t adi_dma_error_handler(int irq, void *id); +static irqreturn_t adi_dma_thread_handler(int irq, void *id); +static void __process_descriptor(struct adi_dma_descriptor *desc); + +static int init_channel_interrupts(struct adi_dma *dma, struct device_node *node, + struct adi_dma_channel *channel) +{ + int irq; + int ret; + + irq = of_irq_get_byname(node, "complete"); + if (irq <= 0) { + dev_err(dma->dev, "Missing complete IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->src_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_handler, + adi_dma_thread_handler, 0, "dma controller irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + irq = of_irq_get_byname(node, "error"); + if (irq <= 0) { + dev_err(dma->dev, "Missing error IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->src_err_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_error_handler, + adi_dma_thread_handler, 0, "dma controller error irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + if (dma->hw_cfg->has_mdma) { + irq = of_irq_get_byname(node, "complete2"); + if (irq <= 0) { + dev_err(dma->dev, "Missing complete2 IRQ for channel %s\n", + node->full_name); + return irq ? irq : -ENOENT; + } + + channel->dest_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_handler, + adi_dma_thread_handler, 0, "dma controller irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + + irq = of_irq_get_byname(node, "error2"); + if (irq <= 0) { + dev_err(dma->dev, "Missing error2 IRQ for channel %s\n", node->full_name); + return irq ? irq : -ENOENT; + } + + channel->dest_err_irq = irq; + + ret = devm_request_threaded_irq(dma->dev, irq, adi_dma_error_handler, + adi_dma_thread_handler, 0, "dma controller error irq", channel); + if (ret) { + dev_err(dma->dev, "Failed to request IRQ %d\n", ret); + return ret; + } + } + + return 0; +} + +static int init_channel(struct adi_dma *dma, struct device_node *node) +{ + struct adi_dma_channel *channel; + int ret; + u32 offset; + u32 skip_int = 0; + + channel = devm_kzalloc(dma->dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + if (of_property_read_u32(node, "adi,id", &channel->id)) { + dev_err(dma->dev, "Missing adi,id for channel %s\n", node->full_name); + return -ENOENT; + } + + if (of_property_read_u32(node, "adi,src-offset", &offset)) { + dev_err(dma->dev, "Missing adi,src-offset for channel %s\n", + node->full_name); + return -ENOENT; + } + channel->iosrc = dma->ioaddr + offset; + + channel->dma = dma; + channel->current_desc = NULL; + spin_lock_init(&channel->lock); + INIT_LIST_HEAD(&channel->pending); + INIT_LIST_HEAD(&channel->cb_pending); + + channel->config = (struct dma_slave_config) { + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = 1, + .dst_maxburst = 1, + }; + + if (dma->hw_cfg->has_mdma) { + if (of_property_read_u32(node, "adi,dest-offset", &offset)) { + dev_err(dma->dev, "Missing adi,dest-offset for channel %s\n", + node->full_name); + return -ENOENT; + } + channel->iodest = dma->ioaddr + offset; + } + + of_property_read_u32(node, "adi,skip-interrupts", &skip_int); + channel->use_interrupts = !skip_int; + + if (channel->use_interrupts) { + ret = init_channel_interrupts(dma, node, channel); + if (ret) + return ret; + } + + // start with interrupts disabled, enable them when transactions appear + channel->running = 1; + __adi_dma_disable_irqs(channel); + __clear_and_reset(channel); + + dma_cookie_init(&channel->chan); + channel->chan.device = &dma->dma_device; + list_add_tail(&channel->chan.device_node, &dma->dma_device.channels); + return 0; +} + +static struct adi_dma_descriptor *to_adi_desc(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct adi_dma_descriptor, tx); +} + +static struct adi_dma_channel *to_adi_channel(struct dma_chan *chan) +{ + return container_of(chan, struct adi_dma_channel, chan); +} + +static struct adi_dma_descriptor *adi_dma_alloc_descriptor(struct adi_dma *dma) +{ + struct adi_dma_descriptor *ret = NULL; + + ret = devm_kzalloc(dma->dev, sizeof(*ret), GFP_NOWAIT); + dev_dbg(dma->dev, "%s: new desc allocated %p\n", __func__, ret); + + return ret; +} + +static int adi_dma_desc_free(struct dma_async_tx_descriptor *tx) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(tx->chan); + struct adi_dma_descriptor *desc = to_adi_desc(tx); + struct adi_dma *dma = adi_chan->dma; + + dev_dbg(dma->dev, "%s: free desc %p\n", __func__, desc); + + if (desc->memset) + dmam_free_coherent(dma->dev, ADI_MEMSET_SIZE, desc->memset, desc->src); + + devm_kfree(dma->dev, desc); + return 0; +} + +/* + * Only used by MDMA for determining access sizes, don't use with dma that is + * attached directly to a peripheral + */ +static void get_txn_align(dma_addr_t src, dma_addr_t dst, size_t size, + u32 *conf, u32 *shift) +{ + if (dst % 32 == 0 && src % 32 == 0 && size % 32 == 0) { + *conf = WDSIZE_256; + *shift = 5; + } else if (dst % 16 == 0 && src % 16 == 0 && size % 16 == 0) { + *conf = WDSIZE_128; + *shift = 4; + } else if (dst % 8 == 0 && src % 8 == 0 && size % 8 == 0) { + *conf = WDSIZE_64; + *shift = 3; + } else if (dst % 4 == 0 && src % 4 == 0 && size % 4 == 0) { + *conf = WDSIZE_32; + *shift = 2; + } else if (dst % 2 == 0 && src % 2 == 0 && size % 2 == 0) { + *conf = WDSIZE_16; + *shift = 1; + } else { + *conf = WDSIZE_8; + *shift = 0; + } +} + +/** + * Retrieve the peripheral dma transfer sizes based on the burst settings. + * This relies a lot on getting the src/dest max burst configuration correct + * which is generally peripheral specific. An invalid result will cause an error + * like 0x6002 in dma stat, while too small of burst settings will degrade system + * performance + */ +static void get_periph_align(struct adi_dma_channel *adi_chan, + enum dma_transfer_direction direction, dma_addr_t mem, size_t len, + u32 *conf, u32 *shift) +{ + struct dma_slave_config *cfg = &adi_chan->config; + u32 mburst, pburst; + u32 lconf = 0; + u64 tmp_dma_addr; + + if (direction == DMA_DEV_TO_MEM) { + pburst = cfg->src_maxburst * cfg->src_addr_width; + mburst = cfg->dst_maxburst * cfg->dst_addr_width; + } else { + pburst = cfg->dst_maxburst * cfg->dst_addr_width; + mburst = cfg->src_maxburst * cfg->src_addr_width; + } + + // HW limits on maximum burst size in bytes + if (pburst > 8) + pburst = 8; + + if (mburst > 32) + mburst = 32; + + tmp_dma_addr = (uint64_t)mem; + // Find the max bursts that divide the transfer length and align correctly + while (len % pburst || do_div(tmp_dma_addr, pburst)) + pburst = pburst / 2; + + tmp_dma_addr = (uint64_t)mem; + while (len % mburst || do_div(tmp_dma_addr, mburst)) + mburst = mburst / 2; + + switch (mburst) { + case 32: + lconf = WDSIZE_256; + *shift = 5; + break; + case 16: + lconf = WDSIZE_128; + *shift = 4; + break; + case 8: + lconf = WDSIZE_64; + *shift = 3; + break; + case 4: + lconf = WDSIZE_32; + *shift = 2; + break; + case 2: + lconf = WDSIZE_16; + *shift = 1; + break; + default: + if (mburst != 1) + dev_err(adi_chan->dma->dev, + "%s: invalid mem-side burst config %u, \ + defaulting to 1 byte\n", + __func__, mburst); + + lconf = WDSIZE_8; + *shift = 0; + break; + } + + switch (pburst) { + case 8: + lconf |= PSIZE_64; + break; + case 4: + lconf |= PSIZE_32; + break; + case 2: + lconf |= PSIZE_16; + break; + default: + if (pburst != 1) + dev_err(adi_chan->dma->dev, + "%s: invalid burst length %u, defaulting to 1 \ + byte\n", + __func__, pburst); + lconf |= PSIZE_8; + break; + } + + *conf = lconf; +} + +static dma_cookie_t adi_submit(struct dma_async_tx_descriptor *tx) +{ + struct adi_dma_descriptor *adi_desc = to_adi_desc(tx); + struct adi_dma_channel *adi_chan = to_adi_channel(tx->chan); + unsigned long flags; + dma_cookie_t cookie; + + dev_dbg(adi_chan->dma->dev, "%s: submit desc %p\n", __func__, adi_desc); + + spin_lock_irqsave(&adi_chan->lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&adi_desc->node, &adi_chan->pending); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + dev_dbg(adi_chan->dma->dev, "%s: produced cookie %d\n", __func__, cookie); + return cookie; +} + +/* + * Populate the descriptor with entries from its current location in a scatterlist + */ +static void __process_sg_entry(struct adi_dma_descriptor *desc) +{ + struct dma_chan *chan = desc->tx.chan; + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + size_t txlen; + dma_addr_t src; + u32 conf, shift; + + txlen = sg_dma_len(desc->sg_next); + src = sg_dma_address(desc->sg_next); + + get_periph_align(adi_chan, desc->direction, src, txlen, &conf, &shift); + + if (desc->direction == DMA_DEV_TO_MEM) + conf |= WNR; + + conf |= DI_EN_X; + + desc->xcnt = txlen >> shift; + desc->xmod = 1 << shift; + desc->src = src; + desc->cfg = conf | DMARESTART | DMAEN; + + desc->sg_next = sg_next(desc->sg_next); +} + +/* + * Load descriptor information into hardware registers + * must be holding channel lock here + */ +static void __process_descriptor(struct adi_dma_descriptor *desc) +{ + struct adi_dma_channel *channel = to_adi_channel(desc->tx.chan); + struct adi_dma *dma = channel->dma; + + dev_dbg(dma->dev, "%s: process desc at %p\n", __func__, desc); + + if (get_dma_curr_irqstat(channel->iosrc) & DMA_RUN) + dev_err(dma->dev, "processing a new descriptor while running\n"); + + // In sg mode we have to load the descriptor with new data from the scatterlist + // first + if (desc->sg) + __process_sg_entry(desc); + + channel->current_desc = desc; + + dev_dbg(dma->dev, "dma config: src = 0x%llx, dst = 0x%llx\n", desc->src, desc->dest); + + dev_dbg(dma->dev, " xcount = %d, xmod = %d, cfg = 0x%x\n", + desc->xcnt, desc->xmod, desc->cfg); + + if (desc->cfg & DMA2D) { + dev_dbg(dma->dev, " ycount = %d, ymod = %d\n", desc->ycnt, desc->ymod); + set_dma_y_count(channel->iosrc, desc->ycnt); + set_dma_y_modify(channel->iosrc, desc->ymod); + } + + set_dma_start_addr(channel->iosrc, desc->src); + set_dma_x_count(channel->iosrc, desc->xcnt); + + // In memset mode, we use xmod = 0 to copy from the same address repeatedly, + // and xmod only applies to the destination location + if (desc->memset) + set_dma_x_modify(channel->iosrc, 0); + else + set_dma_x_modify(channel->iosrc, desc->xmod); + + clear_dma_irqstat(channel->iosrc); + set_dma_config(channel->iosrc, desc->cfg); + + if (desc->direction == DMA_MEM_TO_MEM) { + u32 extra_config = WNR; + + if (desc->cfg & DMA2D) { + set_dma_y_count(channel->iodest, desc->ycnt); + set_dma_y_modify(channel->iodest, desc->ymod); + extra_config |= DI_EN_Y; + } else { + extra_config |= DI_EN_X; + } + + dev_dbg(dma->dev, " extracfg = 0x%x\n", desc->cfg | extra_config); + + set_dma_start_addr(channel->iodest, desc->dest); + set_dma_x_count(channel->iodest, desc->xcnt); + set_dma_x_modify(channel->iodest, desc->xmod); + clear_dma_irqstat(channel->iodest); + set_dma_config(channel->iodest, desc->cfg | extra_config); + } + + // For first descriptor enable IRQs again + __adi_dma_enable_irqs(channel); +} + +/* + * must be holding channel lock here + */ +static void __issue_pending(struct adi_dma_channel *adi_chan) +{ + struct adi_dma_descriptor *desc; + + if (!adi_chan->current_desc) { + if (!list_empty(&adi_chan->pending)) { + desc = list_first_entry(&adi_chan->pending, struct adi_dma_descriptor, + node); + list_del(&desc->node); + __process_descriptor(desc); + } else { + // Final descriptor ended, disable things + __adi_dma_disable_irqs(adi_chan); + } + } +} + +static void adi_dma_issue_pending(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + unsigned long flags; + + dev_dbg(adi_chan->dma->dev, "%s: run\n", __func__); + + spin_lock_irqsave(&adi_chan->lock, flags); + __issue_pending(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); +} + +static enum dma_status adi_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma_descriptor *desc = adi_chan->current_desc; + size_t done, bytes; + enum dma_status ret; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + if (!desc) + return DMA_COMPLETE; + + if (desc->result.result != DMA_TRANS_NOERROR) + return DMA_ERROR; + + // @todo this assumes ymod is one element, which is currently true in the only + // 2D case we support + // @todo this is incorrect for scatterlists length > 1, instead it only computes + // the residue of the current scatterlist item, but there are also no users + // that require that currently + done = get_dma_curr_addr(adi_chan->iosrc) - desc->src; + bytes = (desc->xcnt * desc->xmod); + if (desc->cfg & DMA2D) + bytes = bytes * desc->ycnt; + + txstate->residue = (u32) (bytes - done); + return DMA_IN_PROGRESS; +} + +static void __adi_dma_enable_irqs(struct adi_dma_channel *adi_chan) +{ + if (adi_chan->running) + return; + + adi_chan->running = 1; + + if (adi_chan->use_interrupts) { + enable_irq(adi_chan->src_irq); + enable_irq(adi_chan->src_err_irq); + + if (adi_chan->iodest) { + enable_irq(adi_chan->dest_irq); + enable_irq(adi_chan->dest_err_irq); + } + } +} + +static void __adi_dma_disable_irqs(struct adi_dma_channel *adi_chan) +{ + if (!adi_chan->running) + return; + + adi_chan->running = 0; + + if (adi_chan->use_interrupts) { + disable_irq_nosync(adi_chan->src_irq); + disable_irq_nosync(adi_chan->src_err_irq); + + if (adi_chan->iodest) { + disable_irq_nosync(adi_chan->dest_irq); + disable_irq_nosync(adi_chan->dest_err_irq); + } + } +} + +static void __adi_dma_enable(struct adi_dma_channel *adi_chan) +{ + u32 cfg; + + cfg = get_dma_config(adi_chan->iosrc); + set_dma_config(adi_chan->iosrc, cfg | DMAEN); + + if (adi_chan->iodest) { + cfg = get_dma_config(adi_chan->iodest); + set_dma_config(adi_chan->iodest, cfg | DMAEN); + } + + __adi_dma_enable_irqs(adi_chan); +} + +static void __adi_dma_disable(struct adi_dma_channel *adi_chan) +{ + u32 cfg; + + cfg = get_dma_config(adi_chan->iosrc); + set_dma_config(adi_chan->iosrc, cfg & ~DMAEN); + + if (adi_chan->iodest) { + cfg = get_dma_config(adi_chan->iodest); + set_dma_config(adi_chan->iodest, cfg & ~DMAEN); + } + + __adi_dma_disable_irqs(adi_chan); +} + +static int adi_dma_pause(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + unsigned long flags; + + spin_lock_irqsave(&adi_chan->lock, flags); + if (adi_chan->current_desc) + __adi_dma_disable(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static int adi_dma_resume(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + unsigned long flags; + + spin_lock_irqsave(&adi_chan->lock, flags); + if (adi_chan->current_desc) + __adi_dma_enable(adi_chan); + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static int adi_dma_terminate_all(struct dma_chan *chan) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma_descriptor *desc; + unsigned long flags; + struct list_head *curr; + struct list_head *tmp; + + spin_lock_irqsave(&adi_chan->lock, flags); + + // Disable regardless of status to clear config that may have been modified + // externally (for example in bootrom during resume) + __adi_dma_disable(adi_chan); + + if (adi_chan->current_desc) { + desc = adi_chan->current_desc; + desc->tx.desc_free(&desc->tx); + adi_chan->current_desc = NULL; + } + + list_for_each_safe(curr, tmp, &adi_chan->pending) { + desc = list_entry(curr, struct adi_dma_descriptor, node); + list_del(curr); + desc->tx.desc_free(&desc->tx); + } + + list_for_each_safe(curr, tmp, &adi_chan->cb_pending) { + desc = list_entry(curr, struct adi_dma_descriptor, cb_node); + list_del(curr); + desc->tx.desc_free(&desc->tx); + } + + spin_unlock_irqrestore(&adi_chan->lock, flags); + + return 0; +} + +static void adi_dma_synchronize(struct dma_chan *chan) +{ + // terminate all doesn't sleep and also has nothing asynchronous to wait on +} + +static int adi_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + + adi_chan->config = *config; + return 0; +} + +static void __clear_only(struct adi_dma_channel *channel) +{ + clear_dma_irqstat(channel->iosrc); + + if (channel->iodest) + clear_dma_irqstat(channel->iodest); +} + +static void __clear_and_reset(struct adi_dma_channel *channel) +{ + set_dma_config(channel->iosrc, 0); + clear_dma_irqstat(channel->iosrc); + + if (channel->iodest) { + set_dma_config(channel->iodest, 0); + clear_dma_irqstat(channel->iodest); + } +} + +static irqreturn_t __adi_dma_handler(struct adi_dma_channel *channel, + enum dmaengine_tx_result result) +{ + struct adi_dma_descriptor *desc; + u32 stat = 0; + u32 stat2 = 0; + irqreturn_t ret = IRQ_WAKE_THREAD; + + spin_lock(&channel->lock); + + stat = get_dma_curr_irqstat(channel->iosrc); + dev_dbg(channel->dma->dev, "%s: got irqstat = 0x%x\n", __func__, stat); + + if (channel->iodest) { + stat2 = get_dma_curr_irqstat(channel->iodest); + dev_dbg(channel->dma->dev, "%s: got dest irqstat = 0x%x\n", __func__, stat); + } + + // If we're not running, clear interrupt status + if (!channel->running) { + __clear_and_reset(channel); + dev_err(channel->dma->dev, + "channel %d: received interrupt while not runnig\n", channel->id); + ret = IRQ_HANDLED; + goto done; + } + + // DMA transaction still running, some peripherals will do this + // before the transaction is finished because they signal the DMA channel + // for more data on the same interrupt line + if (!(stat & DMA_DONE) && !(stat2 & DMA_DONE)) { + dev_err(channel->dma->dev, "channel %d: dma with not-done status 0x%x\n", + channel->id, stat); + ret = IRQ_HANDLED; + goto done; + } + + if (!channel->current_desc) { + dev_err(channel->dma->dev, "channel %d: interrupt with no active desc\n", + channel->id); + ret = IRQ_HANDLED; + goto done; + } + + desc = channel->current_desc; + dev_dbg(channel->dma->dev, "%s: current descriptor %p\n", __func__, desc); + + if (desc->cyclic) + __clear_only(channel); + else + __clear_and_reset(channel); + + // For scatter-gather, do not finish or mark the descriptor for callback pending + // until we have processed every entry in the scatterlist + if (desc->sg_next) { + __process_descriptor(desc); + ret = IRQ_HANDLED; + goto done; + } + + desc->result.result = result; + desc->result.residue = 0; + + // Cyclic interrupts do not use the pending list to avoid complications + // during dma termination + if (!desc->cyclic) { + list_add_tail(&desc->cb_node, &channel->cb_pending); + channel->current_desc = NULL; + __issue_pending(channel); + } + +done: + spin_unlock(&channel->lock); + return ret; +} + +static irqreturn_t adi_dma_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + + return __adi_dma_handler(channel, DMA_TRANS_NOERROR); +} + +static irqreturn_t adi_dma_error_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + enum dmaengine_tx_result result; + u32 stat; + + // This is only meaningful for memcpy, as peripherals should be interpreted + // based on dev-to-mem or mem-to-dev direction + if (irq == channel->src_err_irq) + result = DMA_TRANS_READ_FAILED; + else + result = DMA_TRANS_WRITE_FAILED; + + spin_lock(&channel->lock); + + // stop on this descriptor, user needs to read out the status and see what is + // wrong, then terminate, and then queue new descriptors + if (channel->current_desc) { + stat = get_dma_curr_irqstat(channel->iosrc); + dev_err(channel->dma->dev, "DMA error on channel %d, stat = 0x%x\n", + channel->id, stat); + channel->current_desc->result.result = result; + __adi_dma_disable_irqs(channel); + } + + __clear_and_reset(channel); + + spin_unlock(&channel->lock); + return IRQ_HANDLED; +} + +static irqreturn_t adi_dma_thread_handler(int irq, void *id) +{ + struct adi_dma_channel *channel = id; + struct adi_dma_descriptor *desc; + struct dmaengine_desc_callback cb; + unsigned long flags; + + spin_lock_irqsave(&channel->lock, flags); + + if (channel->current_desc && channel->current_desc->cyclic) { + dmaengine_desc_get_callback(&channel->current_desc->tx, &cb); + + spin_unlock_irqrestore(&channel->lock, flags); + dmaengine_desc_callback_invoke(&cb, &channel->current_desc->result); + return IRQ_HANDLED; + } + + while (!list_empty(&channel->cb_pending)) { + desc = list_first_entry(&channel->cb_pending, struct adi_dma_descriptor, + cb_node); + list_del(&desc->cb_node); + + dma_cookie_complete(&desc->tx); + dmaengine_desc_get_callback(&desc->tx, &cb); + + spin_unlock_irqrestore(&channel->lock, flags); + dmaengine_desc_callback_invoke(&cb, &desc->result); + + desc->tx.desc_free(&desc->tx); + spin_lock_irqsave(&channel->lock, flags); + } + + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +/* + * This never generates 2D memcpy but can handle up to 4 GB anyway + */ +static void adi_dma_memcpy_config(struct adi_dma_descriptor *desc, dma_addr_t dst, + dma_addr_t src, size_t size) +{ + u32 conf, shift; + s16 mod; + + get_txn_align(src, dst, size, &conf, &shift); + + // Run memcpy backwards if the two regions might overlap + mod = 1 << shift; + if (src < dst) { + mod *= -1; + dst += size + mod; + src += size + mod; + } + size >>= shift; + + desc->xcnt = size; + desc->xmod = mod; + desc->cfg = conf | DMAEN; + desc->src = src; + desc->dest = dst; +} + +static struct dma_async_tx_descriptor *adi_prep_memcpy(struct dma_chan *chan, + dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + + if (!dma->hw_cfg->has_mdma) + return NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %p\n", __func__, desc); + + adi_dma_memcpy_config(desc, dst, src, len); + desc->direction = DMA_MEM_TO_MEM; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static struct dma_async_tx_descriptor *adi_prep_memset(struct dma_chan *chan, + dma_addr_t dest, int value, size_t len, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + u8 byte = (u8)value; + u64 bigword = byte * 0x01010101010101ull; + u32 conf, shift; + s16 mod; + + if (!dma->hw_cfg->has_mdma) + return NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %p\n", __func__, desc); + + desc->memset = dmam_alloc_coherent(dma->dev, ADI_MEMSET_SIZE, &desc->src, + GFP_NOWAIT); + if (!desc->memset) { + dev_err(dma->dev, "%s, dmam_alloc_coherent failed\n", __func__); + devm_kfree(dma->dev, desc); + return NULL; + } + + get_txn_align(desc->src, dest, len, &conf, &shift); + + desc->memset[0] = bigword; + desc->memset[1] = bigword; + desc->memset[2] = bigword; + desc->memset[3] = bigword; + + mod = 1 << shift; + len >>= shift; + + desc->xcnt = len; + desc->xmod = mod; + desc->cfg = conf | DMAEN; + // desc->src set above when memset buf is allocated + desc->dest = dest; + desc->direction = DMA_MEM_TO_MEM; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static struct dma_async_tx_descriptor *adi_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags, void *context) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc = NULL; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %p\n", __func__, desc); + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_submit; + desc->tx.desc_free = adi_dma_desc_free; + desc->sg = sgl; + desc->sg_next = sgl; + desc->direction = direction; + + return &desc->tx; +} + +static struct dma_async_tx_descriptor *adi_prep_cyclic(struct dma_chan *chan, + dma_addr_t buf, size_t len, size_t period_len, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma *dma = adi_chan->dma; + struct adi_dma_descriptor *desc; + u32 conf; + u32 shift; + + desc = adi_dma_alloc_descriptor(dma); + if (!desc) + return NULL; + + dev_dbg(dma->dev, "%s: using desc at %p\n", __func__, desc); + + get_periph_align(adi_chan, direction, buf, period_len, &conf, &shift); + + if (len != ((len / period_len) * period_len)) { + dev_warn(dma->dev, + "%s: period length %zu does not divide total length %zu\n", __func__, + period_len, len); + } + + desc->xcnt = period_len >> shift; + desc->xmod = 1 << shift; + desc->ycnt = len / period_len; + desc->ymod = desc->xmod; + + // Interpret prep interrupt to mean interrupt between each period, + // without it only interrupt after all periods for bookkeeping + // @todo find a way to specify that the user wants the Y interrupt + if (flags & DMA_PREP_INTERRUPT) + conf |= DI_EN_X; + + if (direction == DMA_DEV_TO_MEM) + conf |= WNR; + + // autoflow mode, 2D mode, restart on synchronize, enable dma channel, + desc->cfg = conf | DMAFLOW_AUTO | DMA2D | DMARESTART | DMAEN; + desc->src = buf; + desc->direction = direction; + desc->cyclic = 1; + + dma_async_tx_descriptor_init(&desc->tx, chan); + desc->tx.flags = flags; + desc->tx.tx_submit = adi_submit; + desc->tx.desc_free = adi_dma_desc_free; + + return &desc->tx; +} + +static bool adi_dma_filter(struct dma_chan *chan, void *data) +{ + struct adi_dma_channel *adi_chan = to_adi_channel(chan); + struct adi_dma_filter_data *adi_data = data; + + if (adi_chan->id == adi_data->id) + return true; + + return false; +} + +static struct dma_chan *adi_dma_translate(struct of_phandle_args *args, + struct of_dma *ofdma) +{ + dma_cap_mask_t mask; + struct adi_dma_filter_data data; + + if (args->args_count != 1) + return NULL; + + data.id = (u32)args->args[0]; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + return __dma_request_channel(&mask, adi_dma_filter, &data, ofdma->of_node); +} + +static int adi_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + const struct of_device_id *of_id; + struct adi_dma *dma; + struct resource *res; + void __iomem *base = NULL; + int ret; + u32 buswidths; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + spin_lock_init(&dma->lock); + dma->dev = dev; + + of_id = of_match_device(dma_dt_ids, dev); + if (!of_id) { + dev_err(dev, "No matching device data found...?\n"); + return -ENOENT; + } + + dma->hw_cfg = of_id->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + dma->ioaddr = base; + + INIT_LIST_HEAD(&dma->dma_device.channels); + + dma->dma_device.device_issue_pending = adi_dma_issue_pending; + dma->dma_device.device_tx_status = adi_dma_tx_status; + dma->dma_device.device_pause = adi_dma_pause; + dma->dma_device.device_resume = adi_dma_resume; + dma->dma_device.device_terminate_all = adi_dma_terminate_all; + dma->dma_device.device_synchronize = adi_dma_synchronize; + + buswidths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + + if (dma->hw_cfg->has_mdma) { + dev_info(dev, "Creating new MDMA controller instance\n"); + dma_cap_set(DMA_MEMCPY, dma->dma_device.cap_mask); + dma_cap_set(DMA_MEMSET, dma->dma_device.cap_mask); + + buswidths |= BIT(DMA_SLAVE_BUSWIDTH_16_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_32_BYTES); + + dma->dma_device.directions = BIT(DMA_MEM_TO_MEM); + dma->dma_device.src_addr_widths = buswidths; + dma->dma_device.dst_addr_widths = buswidths; + dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dma->dma_device.copy_align = 0; + dma->dma_device.fill_align = 0; + + dma->dma_device.device_prep_dma_memcpy = adi_prep_memcpy; + dma->dma_device.device_prep_dma_memset = adi_prep_memset; + } else { + dev_info(dev, "Creating new peripheral DMA controller instance\n"); + dma_cap_set(DMA_SLAVE, dma->dma_device.cap_mask); + dma_cap_set(DMA_CYCLIC, dma->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, dma->dma_device.cap_mask); + + dma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dma->dma_device.src_addr_widths = buswidths; + dma->dma_device.dst_addr_widths = buswidths; + dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + dma->dma_device.device_config = adi_dma_slave_config; + dma->dma_device.device_prep_slave_sg = adi_prep_slave_sg; + dma->dma_device.device_prep_dma_cyclic = adi_prep_cyclic; + } + + child = NULL; + while ((child = of_get_next_child(np, child))) { + ret = init_channel(dma, child); + if (ret) { + of_node_put(child); + return ret; + } + } + + platform_set_drvdata(pdev, dma); + + dma->dma_device.dev = dev; + ret = dmaenginem_async_device_register(&dma->dma_device); + if (ret) { + dev_err(dev, "Unable to register async transaction DMA engine\n"); + return ret; + } + + ret = of_dma_controller_register(np, adi_dma_translate, dma); + if (ret) { + dev_err(&pdev->dev, "failed to register controller\n"); + return ret; + } + + return 0; +} + +static void adi_dma_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + // everything else allocated with devm, we don't have to free anything + + of_dma_controller_free(np); +} + +static struct platform_driver dma_driver = { + .driver = { + .name = "adi-dma", + .of_match_table = dma_dt_ids, + }, + .probe = adi_dma_probe, + .remove = adi_dma_remove, +}; +module_platform_driver(dma_driver); + +MODULE_AUTHOR("Greg Malysa "); +MODULE_DESCRIPTION("SC5xx DMA Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/adi-dma.h b/drivers/dma/adi-dma.h new file mode 100644 index 00000000000000..7f1a5c046b58be --- /dev/null +++ b/drivers/dma/adi-dma.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * SC598 DMA definitions + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef __ASM_DMA_H__ +#define __ASM_DMA_H__ + +#include +#include +#include +#include +#include +#include + +/* DMA_CONFIG Masks */ +#define DMAEN 0x00000001 /* DMA Channel Enable */ +#define WNR 0x00000002 /* Channel Direction (W/R*) */ + +#define PSIZE_8 0x00000000 /* Transfer Word Size = 16 */ +#define PSIZE_16 0x00000010 /* Transfer Word Size = 16 */ +#define PSIZE_32 0x00000020 /* Transfer Word Size = 32 */ +#define PSIZE_64 0x00000030 /* Transfer Word Size = 32 */ + +#define WDSIZE_8 0x00000000 /* Transfer Word Size = 8 */ +#define WDSIZE_16 0x00000100 /* Transfer Word Size = 16 */ +#define WDSIZE_32 0x00000200 /* Transfer Word Size = 32 */ +#define WDSIZE_64 0x00000300 /* Transfer Word Size = 32 */ +#define WDSIZE_128 0x00000400 /* Transfer Word Size = 32 */ +#define WDSIZE_256 0x00000500 /* Transfer Word Size = 32 */ + +#define DMA2D 0x04000000 /* DMA Mode (2D/1D*) */ +#define DMARESTART 0x00000004 /* DMA Buffer Clear SYNC */ + +#define DI_EN_X 0x00100000 /* Data Interrupt Enable in X count */ +#define DI_EN_Y 0x00200000 /* Data Interrupt Enable in Y count */ +#define DI_EN_P 0x00300000 /* Data Interrupt Enable in Peripheral */ +#define DI_EN DI_EN_X /* Data Interrupt Enable */ + +#define NDSIZE_0 0x00000000 /* Next Descriptor Size = 1 */ +#define NDSIZE_1 0x00010000 /* Next Descriptor Size = 2 */ +#define NDSIZE_2 0x00020000 /* Next Descriptor Size = 3 */ +#define NDSIZE_3 0x00030000 /* Next Descriptor Size = 4 */ +#define NDSIZE_4 0x00040000 /* Next Descriptor Size = 5 */ +#define NDSIZE_5 0x00050000 /* Next Descriptor Size = 6 */ +#define NDSIZE_6 0x00060000 /* Next Descriptor Size = 7 */ +#define NDSIZE 0x00070000 /* Next Descriptor Size */ +#define NDSIZE_OFFSET 16 /* Next Descriptor Size Offset */ + +#define DMAFLOW_LIST 0x00004000 /* Descriptor List Mode */ +#define DMAFLOW_LARGE DMAFLOW_LIST +#define DMAFLOW_ARRAY 0x00005000 /* Descriptor Array Mode */ +#define DMAFLOW_LIST_DEMAND 0x00006000 /* Descriptor Demand List Mode */ +#define DMAFLOW_ARRAY_DEMAND 0x00007000 /* Descriptor Demand Array Mode */ + +#define DMA_RUN_DFETCH 0x00000100 /* DMA Running Fetch */ +#define DMA_RUN 0x00000200 /* DMA Running Trans */ +#define DMA_RUN_WAIT_TRIG 0x00000300 /* DMA Running WAIT TRIG */ +#define DMA_RUN_WAIT_ACK 0x00000400 /* DMA Running WAIT ACK */ +#define DMA_RUN_MASK 0x00000700 /* DMA Running Bits Mask */ + +#define DMAFLOW 0x000007000 /* Flow Control */ +#define DMAFLOW_STOP 0x000000000 /* Stop Mode */ +#define DMAFLOW_AUTO 0x000001000 /* Autobuffer Mode */ + +/* DMA_IRQ_STATUS Masks */ +#define DMA_DONE 0x1 /* DMA Completion Interrupt Status */ +#define DMA_ERR 0x2 /* DMA Error Interrupt Status */ +#define DMA_PIRQ 0x4 /* DMA Peripheral Error Interrupt Status */ + +#define ADI_DMA_NEXT_DESC 0x00 +#define ADI_DMA_ADDRSTART 0x04 +#define ADI_DMA_CFG 0x08 +#define ADI_DMA_XCNT 0x0c +#define ADI_DMA_XMOD 0x10 +#define ADI_DMA_YCNT 0x14 +#define ADI_DMA_YMOD 0x18 +#define ADI_DMA_DSCPTR_CUR 0x24 +#define ADI_DMA_DSCPTR_PRV 0x28 +#define ADI_DMA_ADDR_CUR 0x2c +#define ADI_DMA_STAT 0x30 +#define ADI_DMA_XCNT_CUR 0x34 +#define ADI_DMA_YCNT_CUR 0x38 +#define ADI_DMA_BWLCNT 0x40 +#define ADI_DMA_BWLCNT_CUR 0x44 +#define ADI_DMA_BWMCNT 0x48 +#define ADI_DMA_BWMCNT_CUR 0x4c + +/******************************************************************************* + * DMA API's + *******************************************************************************/ +static inline void set_dma_start_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + writel(lower_32_bits(addr), ioaddr + ADI_DMA_ADDRSTART); +} + +static inline void set_dma_next_desc_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + writel(lower_32_bits(addr), ioaddr + ADI_DMA_NEXT_DESC); +} + +static inline void set_dma_curr_desc_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + writel(lower_32_bits(addr), ioaddr + ADI_DMA_DSCPTR_CUR); +} + +static inline void set_dma_x_count(void __iomem *ioaddr, unsigned long x_count) +{ + writel(x_count, ioaddr + ADI_DMA_XCNT); +} + +static inline void set_dma_y_count(void __iomem *ioaddr, unsigned long y_count) +{ + writel(y_count, ioaddr + ADI_DMA_YCNT); +} + +static inline void set_dma_x_modify(void __iomem *ioaddr, long x_modify) +{ + writel(x_modify, ioaddr + ADI_DMA_XMOD); +} + +static inline void set_dma_y_modify(void __iomem *ioaddr, long y_modify) +{ + writel(y_modify, ioaddr + ADI_DMA_YMOD); +} + +static inline void set_dma_config(void __iomem *ioaddr, unsigned long config) +{ + writel(config, ioaddr + ADI_DMA_CFG); +} + +static inline void set_dma_curr_addr(void __iomem *ioaddr, dma_addr_t addr) +{ + writel(lower_32_bits(addr), ioaddr + ADI_DMA_ADDR_CUR); +} + +static inline unsigned long get_dma_curr_irqstat(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_STAT); +} + +static inline unsigned long get_dma_curr_xcount(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_XCNT_CUR); +} + +static inline unsigned long get_dma_curr_ycount(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_YCNT_CUR); +} + +static inline dma_addr_t get_dma_next_desc_ptr(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_NEXT_DESC); +} + +static inline dma_addr_t get_dma_curr_desc_ptr(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_DSCPTR_CUR); +} + +static inline unsigned long get_dma_config(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_CFG); +} + +static inline unsigned long get_dma_curr_addr(void __iomem *ioaddr) +{ + return readl(ioaddr + ADI_DMA_ADDR_CUR); +} + +static inline void clear_dma_irqstat(void __iomem *ioaddr) +{ + writel(DMA_DONE | DMA_ERR | DMA_PIRQ, ioaddr + ADI_DMA_STAT); +} + +#endif From 07ea154910fb132ab83860db61aaa31ca4b83d5b Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 17:55:15 +0200 Subject: [PATCH 06/85] clocksource: Add support for ADSP-SC5xx generic timer Signed-off-by: Philip Molloy --- drivers/clocksource/Makefile | 2 + drivers/clocksource/timer-adi-adsp-sc5xx.c | 547 +++++++++++++++++++++ 2 files changed, 549 insertions(+) create mode 100644 drivers/clocksource/timer-adi-adsp-sc5xx.c diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ec4452ee958f1a..9f53ff78650398 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o obj-$(CONFIG_GOLDFISH_TIMER) += timer-goldfish.o obj-$(CONFIG_GXP_TIMER) += timer-gxp.o +obj-$(CONFIG_ARCH_SC5XX) += timer-adi-adsp-sc5xx.o +obj-$(CONFIG_ARCH_SC59X_64) += timer-adi-adsp-sc5xx.o obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o diff --git a/drivers/clocksource/timer-adi-adsp-sc5xx.c b/drivers/clocksource/timer-adi-adsp-sc5xx.c new file mode 100644 index 00000000000000..bb2e0164b10251 --- /dev/null +++ b/drivers/clocksource/timer-adi-adsp-sc5xx.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * gptimer driver for providing system clock source, clock event source, + * and generic counters for use in userspace + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Shared gptimers registers + */ +#define GPTIMER_RUN 0x04 +#define GPTIMER_RUN_SET 0x08 +#define GPTIMER_RUN_CLR 0x0C +#define GPTIMER_STOP_CFG 0x10 +#define GPTIMER_STOP_CFG_SET 0x14 +#define GPTIMER_STOP_CFG_CLR 0x18 +#define GPTIMER_DATA_IMSK 0x1C +#define GPTIMER_STAT_IMSK 0x20 +#define GPTIMER_TRG_MSK 0x24 +#define GPTIMER_TRG_IE 0x28 +#define GPTIMER_DATA_ILAT 0x2C +#define GPTIMER_STAT_ILAT 0x30 +#define GPTIMER_ERR_TYPE 0x34 +#define GPTIMER_BCAST_PER 0x38 +#define GPTIMER_BCAST_WID 0x3C +#define GPTIMER_BCAST_DLY 0x40 + +/** + * Per-timer registers starting at offset + */ +#define GPTIMER_CFG_OFF 0x00 +#define GPTIMER_CNT_OFF 0x04 +#define GPTIMER_PER_OFF 0x08 +#define GPTIMER_WID_OFF 0x0C +#define GPTIMER_DLY_OFF 0x10 + +/* + * Timer Configuration Register Bits + */ +#define TIMER_EMU_RUN 0x8000 +#define TIMER_BPER_EN 0x4000 +#define TIMER_BWID_EN 0x2000 +#define TIMER_BDLY_EN 0x1000 +#define TIMER_OUT_DIS 0x0800 +#define TIMER_TIN_SEL 0x0400 +#define TIMER_CLK_SEL 0x0300 +#define TIMER_CLK_SCLK 0x0000 +#define TIMER_CLK_ALT_CLK0 0x0100 +#define TIMER_CLK_ALT_CLK1 0x0300 +#define TIMER_PULSE_HI 0x0080 +#define TIMER_SLAVE_TRIG 0x0040 +#define TIMER_IRQ_MODE 0x0030 +#define TIMER_IRQ_ACT_EDGE 0x0000 +#define TIMER_IRQ_DLY 0x0010 +#define TIMER_IRQ_WID_DLY 0x0020 +#define TIMER_IRQ_PER 0x0030 +#define TIMER_MODE 0x000f +#define TIMER_MODE_WDOG_P 0x0008 +#define TIMER_MODE_WDOG_W 0x0009 +#define TIMER_MODE_PWM_CONT 0x000c +#define TIMER_MODE_PWM 0x000d +#define TIMER_MODE_WDTH 0x000a +#define TIMER_MODE_WDTH_D 0x000b +#define TIMER_MODE_EXT_CLK 0x000e +#define TIMER_MODE_PININT 0x000f + +struct sc5xx_gptimer { + int id; + int irq; + void __iomem *io_base; +}; + +struct gptimer_counter { + struct counter_device counter; +}; + +struct clocksource_gptimer { + struct clocksource cs; + struct sc5xx_gptimer *timer; +}; + +struct clockevent_gptimer { + struct clock_event_device evt; + struct sc5xx_gptimer *timer; +}; + +struct sc5xx_gptimer_controller { + void __iomem *base; + struct clk *clk; + struct clocksource_gptimer *cs; + struct clockevent_gptimer *cevt; + struct sc5xx_gptimer *timers; + size_t num_timers; +}; + +static struct sc5xx_gptimer_controller gptimer_controller = { 0x00 }; + +static struct clockevent_gptimer *to_clockevent_gptimer(struct + clock_event_device + *evt) +{ + return container_of(evt, struct clockevent_gptimer, evt); +} + +/** + * Per gptimer accessors + */ +static void set_gptimer_period(struct sc5xx_gptimer *timer, + uint32_t period) +{ + writel(period, timer->io_base + GPTIMER_PER_OFF); +} + +static void set_gptimer_pwidth(struct sc5xx_gptimer *timer, uint32_t value) +{ + writel(value, timer->io_base + GPTIMER_WID_OFF); +} + +static void set_gptimer_delay(struct sc5xx_gptimer *timer, uint32_t value) +{ + writel(value, timer->io_base + GPTIMER_DLY_OFF); +} + +static void set_gptimer_config(struct sc5xx_gptimer *timer, + uint16_t config) +{ + writew(config, timer->io_base + GPTIMER_CFG_OFF); +} + +static uint32_t get_gptimer_count(struct sc5xx_gptimer *timer) +{ + return readl(timer->io_base + GPTIMER_CNT_OFF); +} + +/** + * Accessors that redirect to the shared registers + */ +static void gptimer_enable(struct sc5xx_gptimer *timer) +{ + writew(1 << timer->id, gptimer_controller.base + GPTIMER_RUN_SET); +} + +static void gptimer_disable(struct sc5xx_gptimer *timer) +{ + writew(1 << timer->id, + gptimer_controller.base + GPTIMER_STOP_CFG_SET); + writew(1 << timer->id, gptimer_controller.base + GPTIMER_RUN_CLR); +} + +static void gptimer_clear_interrupt(struct sc5xx_gptimer *timer) +{ + writew(1 << timer->id, + gptimer_controller.base + GPTIMER_DATA_ILAT); +} + +static bool gptimer_is_running(struct sc5xx_gptimer *timer) +{ + u32 stat = readw(gptimer_controller.base + GPTIMER_RUN); + u32 check = 1 << timer->id; + + return (stat & check) == check; +} + +/** + * Scheduler/clocksource functions + */ +static u64 read_cs_gptimer(struct clocksource *cs) +{ + struct clocksource_gptimer *gp = + container_of(cs, struct clocksource_gptimer, cs); + + return (u64)get_gptimer_count(gp->timer); +} + +static u64 notrace read_sched_gptimer(void) +{ + return (u64)get_gptimer_count(gptimer_controller.cs->timer); +} + +/** + * Clockevent functions + */ +static int gptimer_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct clockevent_gptimer *cevt = to_clockevent_gptimer(evt); + + /* it starts counting three SCLK cycles after the TIMENx bit is set */ + set_gptimer_pwidth(cevt->timer, 1); + set_gptimer_delay(cevt->timer, cycles - 3); + + gptimer_enable(cevt->timer); + return 0; +} + +static int gptimer_set_state_periodic(struct clock_event_device *evt) +{ + struct clockevent_gptimer *cevt = to_clockevent_gptimer(evt); + unsigned long rate = clk_get_rate(gptimer_controller.clk); + + gptimer_disable(cevt->timer); + set_gptimer_config(cevt->timer, + TIMER_OUT_DIS | TIMER_MODE_PWM_CONT | + TIMER_PULSE_HI | TIMER_IRQ_PER); + + set_gptimer_period(cevt->timer, rate / HZ); + set_gptimer_pwidth(cevt->timer, rate / HZ - 1); + + gptimer_enable(cevt->timer); + return 0; +} + +static int gptimer_set_state_oneshot(struct clock_event_device *evt) +{ + struct clockevent_gptimer *cevt = to_clockevent_gptimer(evt); + + gptimer_disable(cevt->timer); + set_gptimer_config(cevt->timer, TIMER_OUT_DIS | TIMER_MODE_PWM | + TIMER_PULSE_HI | TIMER_IRQ_DLY); + + /* gptimer_set_next_event will configure the period and delay */ + return 0; +} + +static int gptimer_set_state_shutdown(struct clock_event_device *evt) +{ + struct clockevent_gptimer *cevt = to_clockevent_gptimer(evt); + + gptimer_disable(cevt->timer); + return 0; +} + +static irqreturn_t cevt_gptimer_handler(int irq, void *dev) +{ + struct clockevent_gptimer *cevt = dev; + + gptimer_clear_interrupt(cevt->timer); + cevt->evt.event_handler(&cevt->evt); + return IRQ_HANDLED; +} + +static int gptimer_counter_count_read(struct counter_device *counter, + struct counter_count *count, + u64 *val) +{ + u32 id = count->id; + struct sc5xx_gptimer *timer = &gptimer_controller.timers[id]; + u64 timer_count = get_gptimer_count(timer); + + *val = timer_count; + return 0; +} + +/** + * Counter implementation + */ +static struct counter_ops gptimer_counter_ops = { + .count_read = gptimer_counter_count_read, +}; + +/** + * Initializes an individual gptimer belonging to the controller + * @todo resource cleanup in error paths + */ +static int __init sc5xx_gptimer_init(struct device_node *np, + struct sc5xx_gptimer *timer) +{ + int irq, id; + u32 offset; + int ret; + struct clk *clk = gptimer_controller.clk; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + pr_err("%s: Unable to find irq for gptimer %pOFn\n", + __func__, np); + return -ENODEV; + } + + ret = of_property_read_s32(np, "reg", &id); + if (ret) { + pr_err + ("%s: Missing reg property containing timer id for gptimer %pOFn\n", + __func__, np); + return -ENODEV; + } + + ret = of_property_read_u32(np, "adi,offset", &offset); + if (ret) { + pr_err("%s: Missing adi,offset for gptimer %pOFn\n", + __func__, np); + return -ENODEV; + } + + timer->id = id; + timer->irq = irq; + timer->io_base = gptimer_controller.base + offset; + + /* + * @todo add period or other timing options to dts? + */ + if (!gptimer_is_running(timer) || + of_property_read_bool(np, "adi,reset-timer")) { + gptimer_disable(timer); + + set_gptimer_config(timer, + TIMER_OUT_DIS | TIMER_MODE_PWM_CONT | + TIMER_PULSE_HI | TIMER_IRQ_PER); + set_gptimer_period(timer, 0xFFFFFFFF); + set_gptimer_pwidth(timer, 0xFFFFFFFE); + + gptimer_enable(timer); + } + + if (of_property_read_bool(np, "adi,is-clocksource")) { + struct clocksource_gptimer *cs; + + cs = kzalloc(sizeof(*cs), GFP_KERNEL); + if (!cs) + return -ENOMEM; + + cs->cs.name = "cs_adi_gptimer"; + cs->cs.rating = 350; + cs->cs.read = read_cs_gptimer; + cs->cs.mask = CLOCKSOURCE_MASK(32); + cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + cs->timer = timer; + gptimer_controller.cs = cs; + + ret = clocksource_register_hz(&cs->cs, clk_get_rate(clk)); + if (ret) { + pr_err("%s: failed to register clocksource = %d\n", + __func__, ret); + return ret; + } + + sched_clock_register(read_sched_gptimer, 32, + clk_get_rate(clk)); + + // optionally, register_current_timer_delay if we also want to + // use this gptimer for timer-based delays instead of while loops, but + // this may not be a good idea on slower platforms + } + + if (of_property_read_bool(np, "adi,is-clockevent")) { + struct clockevent_gptimer *cevt; + u16 imsk; + + cevt = kzalloc(sizeof(*cevt), GFP_KERNEL); + if (!cevt) + return -ENOMEM; + + cevt->evt = (struct clock_event_device) { + .name = "cevt_adi_gptimer", + .features = + CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .rating = 300, + .shift = 32, + .cpumask = cpumask_of(0), + .set_next_event = gptimer_set_next_event, + .set_state_periodic = gptimer_set_state_periodic, + .set_state_oneshot = gptimer_set_state_oneshot, + .set_state_shutdown = gptimer_set_state_shutdown, + }; + + cevt->timer = timer; + gptimer_controller.cevt = cevt; + + imsk = readw(gptimer_controller.base + GPTIMER_DATA_IMSK); + imsk &= ~(1 << timer->id); + writew(imsk, gptimer_controller.base + GPTIMER_DATA_IMSK); + + ret = + request_irq(irq, cevt_gptimer_handler, + IRQF_TIMER | IRQF_IRQPOLL, + "sc5xx gptimer clockevent", cevt); + if (ret) { + pr_err + ("%s: Could not register clockevent handler\n", + __func__); + return ret; + } + + clockevents_config_and_register(&cevt->evt, + clk_get_rate(clk), 100, + -1); + } + + return 0; +} + +/** + * Map master gptimers module, which finds a clocksource and clockevent child + * and registers them as such with the system. The other gptimers are registers + * as counters with the counter framework + * + * @todo clear up resource leaks in exit paths + */ +static int __init sc5xx_gptimer_controller_init(struct device_node *np) +{ + struct device_node *timer_np; + void __iomem *base; + struct clk *clk; + int ret; + int i, n; + + if (gptimer_controller.base) { + pr_err + ("%s: Tried to initialize a second gptimer controller; check your device tree\n", + __func__); + return -EINVAL; + } + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: Unable to map gptimer registers\n", __func__); + return -ENODEV; + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("%s: could not find sclk0_0 = %ld\n", __func__, + PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("%s: sclk0_0 clock enable failed = %d\n", __func__, + ret); + return ret; + } + + gptimer_controller.clk = clk; + gptimer_controller.base = base; + + n = of_get_child_count(np); + gptimer_controller.num_timers = n; + gptimer_controller.timers = + kcalloc(n, sizeof(*gptimer_controller.timers), GFP_KERNEL); + + if (!gptimer_controller.timers) { + pr_err("%s: Unable to allocate memory for timers\n", + __func__); + return -ENOMEM; + } + + i = 0; + for_each_child_of_node(np, timer_np) { + ret = + sc5xx_gptimer_init(timer_np, + &gptimer_controller.timers[i]); + if (ret) { + of_node_put(timer_np); + return ret; + } + + i += 1; + } + + return 0; +} + +TIMER_OF_DECLARE(sc5xx_gptimers, "adi,sc5xx-gptimers", + sc5xx_gptimer_controller_init); +static int gptimer_counter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct counter_count *adi_counts; + struct sc5xx_gptimer_controller *priv; + struct counter_device *counter; + u32 i; + int ret; + + adi_counts = + devm_kcalloc(dev, gptimer_controller.num_timers, + sizeof(*adi_counts), GFP_KERNEL); + if (!adi_counts) + return -ENOMEM; + + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) + return -ENOMEM; + + priv = counter_priv(counter); + + for (i = 0; i < gptimer_controller.num_timers; ++i) { + adi_counts[i].name = + kasprintf(GFP_KERNEL, "gptimer_counter%d", i); + } + + priv->clk = gptimer_controller.clk; + + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &gptimer_counter_ops; + counter->counts = adi_counts; + counter->num_counts = gptimer_controller.num_timers; + + /* Register Counter device */ + ret = devm_counter_add(dev, counter); + if (ret < 0) + dev_err_probe(dev, ret, "Failed to add counter\n"); + + return ret; +} + +static const struct of_device_id adsp_gptimer_counter_match[] = { + {.compatible = "adi,gptimer-counter" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, adsp_gptimer_counter_match); + +static struct platform_driver gptimer_counter_driver = { + .probe = gptimer_counter_probe, + .driver = { + .name = "adsp-gptimer-counter", + .of_match_table = adsp_gptimer_counter_match, + }, +}; + +module_platform_driver(gptimer_counter_driver); From bd61800df852b1b3dd897d5a370d997f8d1e7419 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:00:40 +0200 Subject: [PATCH 07/85] spi: Add v3 SPI controller support for ADSP-SC5xx Co-developed-by: Arturs Artamonovs Signed-off-by: Arturs Artamonovs Signed-off-by: Philip Molloy --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-adi.c | 876 ++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spidev.c | 2 + 4 files changed, 885 insertions(+) create mode 100644 drivers/spi/spi-adi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1872f9d54a5cc9..012e6aff0fc278 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -226,6 +226,12 @@ config SPI_BCM2835AUX "universal SPI master", and the regular SPI controller. This driver is for the universal/auxiliary SPI controller. +config SPI_ADI + tristate "SPI controller v3 for ADI" + help + This is the SPI controller v3 master driver + found on ADI SC5xx processor. + config SPI_BCM63XX tristate "Broadcom BCM63xx SPI controller" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 1f7c06a3091d92..33721f55bf708d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -142,6 +142,7 @@ obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SG2044_NOR) += spi-sg2044-nor.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o +obj-$(CONFIG_SPI_ADI) += spi-adi.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o obj-$(CONFIG_SPI_STM32_OSPI) += spi-stm32-ospi.o obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o diff --git a/drivers/spi/spi-adi.c b/drivers/spi/spi-adi.c new file mode 100644 index 00000000000000..f30b56fdc0b5cf --- /dev/null +++ b/drivers/spi/spi-adi.c @@ -0,0 +1,876 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SPI3 controller driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI_CONTROL */ +#define SPI_CTL_EN 0x00000001 /* Enable */ +#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ +#define SPI_CTL_PSSE 0x00000004 /* controls modf error in controller mode */ +#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ +#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ +#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ +#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ +#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ +#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ +#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ +#define SPI_CTL_LSBF 0x00001000 /* LSB First */ +#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ +#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ +#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ +#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ +#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ +#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO >= 75% empty, RFIFO >= 75% full */ +#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO >= 50% empty, RFIFO >= 50% full */ +#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ +#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ +/* SPI_RX_CONTROL */ +#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ +#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ +#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ +#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ +#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ +#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ +/* SPI_TX_CONTROL */ +#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ +#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ +#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ +#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ +#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ +#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ +/* SPI_CLOCK */ +#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ +/* SPI_DELAY */ +#define SPI_DLY_STOP 0x000000FF /* Transfer delay time */ +#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ +#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ +/* SPI_SSEL */ +#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ +#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ +#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ +#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ +/* SPI_RWC */ +#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ +/* SPI_RWCR */ +#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ +/* SPI_TWC */ +#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ +/* SPI_TWCR */ +#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ +/* SPI_IMASK */ +#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKCL */ +#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKST */ +#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_STATUS */ +#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ +#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ +#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ +#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ +#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ +#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ +#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ +#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ +#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ +#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ +#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ +#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ +#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ +/* SPI_ILAT */ +#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ +/* SPI_ILATCL */ +#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ + +/* + * adi spi3 registers layout + */ +struct adi_spi_regs { + u32 revid; + u32 control; + u32 rx_control; + u32 tx_control; + u32 clock; + u32 delay; + u32 ssel; + u32 rwc; + u32 rwcr; + u32 twc; + u32 twcr; + u32 reserved0; + u32 emask; + u32 emaskcl; + u32 emaskst; + u32 reserved1; + u32 status; + u32 elat; + u32 elatcl; + u32 reserved2; + u32 rfifo; + u32 reserved3; + u32 tfifo; +}; + +struct adi_spi_controller; + +struct adi_spi_transfer_ops { + void (*write)(struct adi_spi_controller *controller, struct spi_transfer *xfer); + void (*read)(struct adi_spi_controller *controller, struct spi_transfer *xfer); + void (*duplex)(struct adi_spi_controller *controller, struct spi_transfer *xfer); +}; + +/* runtime info for spi controller */ +struct adi_spi_controller { + /* SPI framework hookup */ + struct spi_controller *controller; + struct device *dev; + + /* Regs base of SPI controller */ + struct adi_spi_regs __iomem *regs; + + /* Current message transfer state info */ + struct spi_transfer *cur_transfer; + const struct adi_spi_transfer_ops *ops; + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + struct clk *sclk; + unsigned long sclk_rate; +}; + +struct adi_spi_device { + bool dma; + u32 control; +}; + +static void adi_spi_disable(struct adi_spi_controller *drv_data) +{ + u32 ctl; + + ctl = ioread32(&drv_data->regs->control); + ctl &= ~SPI_CTL_EN; + iowrite32(ctl, &drv_data->regs->control); +} + +static void adi_spi_dma_terminate(struct adi_spi_controller *drv_data) +{ + dmaengine_terminate_sync(drv_data->controller->dma_tx); + dmaengine_terminate_sync(drv_data->controller->dma_rx); +} + +/* Calculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = sclk / speed_hz; + + if (spi_clock) + spi_clock--; + return spi_clock; +} + +static void adi_spi_u8_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u8 *)(xfer->tx_buf + i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u16_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u16 *)(xfer->tx_buf + 2*i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u32_write(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u32 *)(xfer->tx_buf + 4*i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u8_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(xfer->rx_buf + i) = ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u16_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)(xfer->rx_buf + 2 * i) = ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u32_read(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)(xfer->rx_buf + 4 * i) = ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u8_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u8 *)(xfer->tx_buf + i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(xfer->rx_buf + i) = ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u16_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u16 *)(xfer->tx_buf + 2 * i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)(xfer->rx_buf + 2 * i) = ioread32(&drv->regs->rfifo); + } +} + +static void adi_spi_u32_duplex(struct adi_spi_controller *drv, + struct spi_transfer *xfer) +{ + size_t i; + + for (i = 0; i < xfer->len; ++i) { + iowrite32(*(u32 *)(xfer->tx_buf + 4 * i), &drv->regs->tfifo); + while (ioread32(&drv->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)(xfer->rx_buf + 4 * i) = ioread32(&drv->regs->rfifo); + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u8 = { + .write = adi_spi_u8_write, + .read = adi_spi_u8_read, + .duplex = adi_spi_u8_duplex, +}; + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u16 = { + .write = adi_spi_u16_write, + .read = adi_spi_u16_read, + .duplex = adi_spi_u16_duplex, +}; + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u32 = { + .write = adi_spi_u32_write, + .read = adi_spi_u32_read, + .duplex = adi_spi_u32_duplex, +}; + +static int adi_spi_pio_xfer(struct spi_controller *controller, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(controller); + + if (!xfer->rx_buf) { + iowrite32(SPI_RXCTL_REN, &drv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &drv->regs->tx_control); + drv->ops->write(drv, xfer); + } else if (!xfer->tx_buf) { + iowrite32(0, &drv->regs->tx_control); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, &drv->regs->rx_control); + drv->ops->read(drv, xfer); + } else { + iowrite32(SPI_RXCTL_REN, &drv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &drv->regs->tx_control); + drv->ops->duplex(drv, xfer); + } + + iowrite32(0, &drv->regs->tx_control); + iowrite32(0, &drv->regs->rx_control); + return 0; +} + +/* + * Disable both paths and alert spi core that this transfer is done + */ +static void adi_spi_rx_dma_isr(void *data) +{ + struct adi_spi_controller *drv_data = data; + + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(drv_data->controller->dma_rx, drv_data->rx_cookie, &state); + if (status == DMA_ERROR) + dev_err(&drv_data->controller->dev, "spi rx dma error\n"); + + iowrite32(0, &drv_data->regs->tx_control); + iowrite32(0, &drv_data->regs->rx_control); + spi_finalize_current_transfer(drv_data->controller); +} + +/* + * Disable tx path and enable rx path for dual/quad modes + */ +static void adi_spi_tx_dma_isr(void *data) +{ + struct adi_spi_controller *drv = data; + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(drv->controller->dma_tx, drv->tx_cookie, &state); + if (status == DMA_ERROR) + dev_err(&drv->controller->dev, "spi tx dma error\n"); + + iowrite32(0, &drv->regs->tx_control); + + if (drv->cur_transfer->rx_buf) { + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI | SPI_RXCTL_RDR_NE, + &drv->regs->rx_control); + dma_async_issue_pending(drv->controller->dma_rx); + } else { + spi_finalize_current_transfer(drv->controller); + } +} + +static int adi_spi_dma_xfer(struct spi_controller *controller, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(controller); + struct dma_async_tx_descriptor *tx_desc; + struct dma_async_tx_descriptor *rx_desc; + + if (xfer->tx_buf) { + tx_desc = dmaengine_prep_slave_sg(controller->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, 0); + if (!tx_desc) { + dev_err(drv->dev, "Unable to allocate TX DMA descriptor\n"); + goto error; + } + + if (!xfer->rx_buf) { + tx_desc->callback = adi_spi_tx_dma_isr; + tx_desc->callback_param = drv; + } + drv->tx_cookie = dmaengine_submit(tx_desc); + + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF, + &drv->regs->tx_control); + dma_async_issue_pending(controller->dma_tx); + } + + if (xfer->rx_buf) { + rx_desc = dmaengine_prep_slave_sg(controller->dma_rx, xfer->rx_sg.sgl, + xfer->rx_sg.nents, DMA_DEV_TO_MEM, 0); + if (!rx_desc) { + dev_err(drv->dev, "Unable to allocate RX DMA descriptor\n"); + goto error; + } + + rx_desc->callback = adi_spi_rx_dma_isr; + rx_desc->callback_param = drv; + drv->rx_cookie = dmaengine_submit(rx_desc); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI | SPI_RXCTL_RDR_NE, + &drv->regs->rx_control); + dma_async_issue_pending(controller->dma_rx); + } + + return 1; + +error: + adi_spi_dma_terminate(drv); + return -ENOENT; +} + +static bool adi_spi_can_dma(struct spi_controller *controller, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + + if (chip->dma) + return true; + return false; +} + +static int adi_spi_transfer_one(struct spi_controller *controller, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(controller); + u32 cr; + + drv->cur_transfer = xfer; + + cr = ioread32(&drv->regs->control) & ~SPI_CTL_MIOM; + + if (xfer->rx_nbits == SPI_NBITS_QUAD || xfer->tx_nbits == SPI_NBITS_QUAD) + cr |= SPI_CTL_MIO_QUAD; + else if (xfer->rx_nbits == SPI_NBITS_DUAL || xfer->tx_nbits == SPI_NBITS_DUAL) + cr |= SPI_CTL_MIO_DUAL; + + iowrite32(cr, &drv->regs->control); + + if (adi_spi_can_dma(controller, spi, xfer)) + return adi_spi_dma_xfer(controller, spi, xfer); + return adi_spi_pio_xfer(controller, spi, xfer); +} + +/* + * Settings like clock speed and bits per word are assumed to be the same for all + * transfers in a message. tx_nbits and rx_nbits can change, however + */ +static int adi_spi_prepare_message(struct spi_controller *controller, struct spi_message *msg) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(controller); + struct adi_spi_device *chip = spi_get_ctldata(msg->spi); + struct dma_slave_config dma_config = {0}; + struct spi_transfer *xfer; + int ret; + u32 cr, cr_width; + u32 words; + + xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); + words = DIV_ROUND_UP(xfer->bits_per_word, 8); + iowrite32(hz_to_spi_clock(drv->sclk_rate, xfer->speed_hz), &drv->regs->clock); + + switch (words) { + case 1: + cr_width = SPI_CTL_SIZE08; + drv->ops = &adi_spi_transfer_ops_u8; + break; + case 2: + cr_width = SPI_CTL_SIZE16; + drv->ops = &adi_spi_transfer_ops_u16; + break; + case 4: + cr_width = SPI_CTL_SIZE32; + drv->ops = &adi_spi_transfer_ops_u32; + break; + default: + dev_err(&controller->dev, "invalid word size in incoming message\n"); + return -EINVAL; + } + + cr = chip->control; + cr |= cr_width | SPI_CTL_EN; + cr &= ~SPI_CTL_SOSI; + iowrite32(cr, &drv->regs->control); + + dma_config.direction = DMA_MEM_TO_DEV; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = words; + dma_config.dst_maxburst = words; + ret = dmaengine_slave_config(controller->dma_tx, &dma_config); + if (ret) { + dev_err(drv->dev, "tx dma slave config failed: %d\n", ret); + return ret; + } + + dma_config.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(controller->dma_rx, &dma_config); + if (ret) { + dev_err(drv->dev, "rx dma slave config failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int adi_spi_unprepare_message(struct spi_controller *controller, struct spi_message *msg) +{ + struct adi_spi_controller *drv = spi_controller_get_devdata(controller); + + adi_spi_disable(drv); + return 0; +} + +static int adi_spi_setup(struct spi_device *spi) +{ + struct adi_spi_device *chip; + struct device_node *np = spi->dev.of_node; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + spi_set_ctldata(spi, chip); + + chip->dma = false; + if (of_property_read_bool(np, "adi,enable-dma")) + chip->dma = true; + + chip->control = 0; + if (of_property_read_bool(np, "adi,open-drain-mode")) + chip->control |= SPI_CTL_ODM; + + if (of_property_read_bool(np, "adi,psse")) + chip->control |= SPI_CTL_PSSE; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + chip->control &= ~SPI_CTL_ASSEL; + + return 0; +} + +static void adi_spi_cleanup(struct spi_device *spi) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + + if (!chip) + return; + + spi_set_ctldata(spi, NULL); + kfree(chip); +} + +static irqreturn_t spi_irq_err(int irq, void *dev_id) +{ + struct adi_spi_controller *drv_data = dev_id; + u32 status; + + status = ioread32(&drv_data->regs->status); + dev_err(drv_data->dev, "spi error irq, status = 0x%x\n", status); + iowrite32(status, &drv_data->regs->status); + + iowrite32(0, &drv_data->regs->tx_control); + iowrite32(0, &drv_data->regs->rx_control); + adi_spi_disable(drv_data); + adi_spi_dma_terminate(drv_data); + + return IRQ_HANDLED; +} + +static const struct of_device_id adi_spi_of_match[] = { + { + .compatible = "adi,spi3", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_spi_of_match); + +static int adi_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *controller; + struct adi_spi_controller *drv_data; + struct resource *mem; + struct clk *sclk; + int ret, err_irq; + + sclk = devm_clk_get(dev, "spi"); + if (IS_ERR(sclk)) { + dev_err(dev, "can not get spi clock\n"); + return PTR_ERR(sclk); + } + + controller = devm_spi_alloc_host(dev, sizeof(*drv_data)); + if (!controller) { + dev_err(dev, "can not alloc spi_controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, controller); + + /* the mode bits supported by this driver */ + controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | + SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD; + + controller->dev.of_node = dev->of_node; + controller->bus_num = -1; + controller->num_chipselect = 4; + controller->use_gpio_descriptors = true; + controller->cleanup = adi_spi_cleanup; + controller->setup = adi_spi_setup; + controller->prepare_message = adi_spi_prepare_message; + controller->unprepare_message = adi_spi_unprepare_message; + controller->transfer_one = adi_spi_transfer_one; + controller->can_dma = adi_spi_can_dma; + controller->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + + drv_data = spi_controller_get_devdata(controller); + drv_data->controller = controller; + drv_data->sclk = sclk; + drv_data->sclk_rate = clk_get_rate(sclk); + drv_data->dev = dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + dev_err(dev, "Could not map spi memory, check device tree\n"); + return PTR_ERR(drv_data->regs); + } + + err_irq = platform_get_irq(pdev, 0); + if (!err_irq) { + dev_err(dev, "No SPI error irq resource found\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, err_irq, spi_irq_err, 0, "SPI ERROR", drv_data); + if (ret) { + dev_err(dev, "could not request spi error irq\n"); + return ret; + } + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control); + iowrite32(0x0000FE00, &drv_data->regs->ssel); + iowrite32(0x0, &drv_data->regs->delay); + iowrite32(SPI_IMSK_SET_ROM, &drv_data->regs->emaskst); + + controller->dma_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(controller->dma_tx)) { + dev_err(dev, "Could not get TX DMA channel\n"); + return PTR_ERR(controller->dma_tx); + } + + controller->dma_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(controller->dma_rx)) { + dev_err(dev, "Could not get RX DMA channel\n"); + ret = PTR_ERR(controller->dma_rx); + goto err_free_tx_dma; + } + + ret = clk_prepare_enable(drv_data->sclk); + if (ret) { + dev_err(dev, "Could not enable SPI clock\n"); + goto err_free_rx_dma; + } + + ret = devm_spi_register_controller(dev, controller); + if (ret) { + dev_err(dev, "can not register spi controller\n"); + goto err_free_rx_dma; + } + + dev_info(dev, "registered ADI SPI controller %s\n", + dev_name(&controller->dev)); + return ret; + +err_free_rx_dma: + dma_release_channel(controller->dma_rx); + +err_free_tx_dma: + dma_release_channel(controller->dma_tx); + + return ret; +} + +static void adi_spi_remove(struct platform_device *pdev) +{ + struct spi_controller *controller = platform_get_drvdata(pdev); + struct adi_spi_controller *drv_data = spi_controller_get_devdata(controller); + + adi_spi_disable(drv_data); + clk_disable_unprepare(drv_data->sclk); + dma_release_channel(controller->dma_tx); + dma_release_channel(controller->dma_rx); +} + +static int __maybe_unused adi_spi_suspend(struct device *dev) +{ + struct spi_controller *controller = dev_get_drvdata(dev); + + return spi_controller_suspend(controller); +} + +static int __maybe_unused adi_spi_resume(struct device *dev) +{ + struct spi_controller *controller = dev_get_drvdata(dev); + + return spi_controller_resume(controller); +} + +static const struct dev_pm_ops adi_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(adi_spi_suspend, adi_spi_resume) +}; + +MODULE_ALIAS("platform:adi-spi3"); +static struct platform_driver adi_spi_driver = { + .driver = { + .name = "adi-spi3", + .pm = &adi_spi_pm_ops, + .of_match_table = adi_spi_of_match, + }, + .probe = adi_spi_probe, + .remove = adi_spi_remove, +}; + +module_platform_driver(adi_spi_driver); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 4d53c394101a44..367023678a7924 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -686,6 +686,7 @@ static const struct class spidev_class = { */ static const struct spi_device_id spidev_spi_ids[] = { { .name = /* abb */ "spi-sensor" }, + { .name = /* adi */ "generic-spidev" }, { .name = /* cisco */ "spi-petra" }, { .name = /* dh */ "dhcom-board" }, { .name = /* elgin */ "jg10309-01" }, @@ -733,6 +734,7 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "semtech,sx1301", .data = &spidev_of_check }, { .compatible = "silabs,em3581", .data = &spidev_of_check }, { .compatible = "silabs,si3210", .data = &spidev_of_check }, + { .compatible = "adi,generic-spidev", .data = &spidev_of_check }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); From 40485a4757b7af7504c900a251d26703a7e26ed5 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:06:41 +0200 Subject: [PATCH 08/85] gpio: Add GPIO port driver for ADSP-SC5xxx SoCs Signed-off-by: Philip Molloy --- drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-adi-adsp-port.c | 160 ++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 drivers/gpio/gpio-adi-adsp-port.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4d644dcecad930..849fe157cbf896 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -157,6 +157,14 @@ config GPIO_74XX_MMIO 8 bits: 74244 (Input), 74273 (Output) 16 bits: 741624 (Input), 7416374 (Output) +config GPIO_ADI_ADSP_PORT + bool "ADI ADSP PORT GPIO driver" + depends on OF_GPIO + select GPIO_GENERIC + help + Say Y to enable the ADSP PORT-based GPIO driver for Analog Devices + ADSP chips. + config GPIO_ALTERA tristate "Altera GPIO" select GPIOLIB_IRQCHIP diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ec296fa14bfdb3..56471f4c71d470 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_ADI_ADSP_PORT) += gpio-adi-adsp-port.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o diff --git a/drivers/gpio/gpio-adi-adsp-port.c b/drivers/gpio/gpio-adi-adsp-port.c new file mode 100644 index 00000000000000..2a6957b7a3897f --- /dev/null +++ b/drivers/gpio/gpio-adi-adsp-port.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADSP PORT gpio driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include "gpiolib.h" + +static int adsp_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(chip); + + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_SET); + return 0; +} + +static int adsp_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(chip); + + /* + * For open drain ports, they've already been configured by pinctrl and + * we should not modify their output characteristics + */ + if (port->open_drain & BIT(offset)) + return 0; + + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_CLEAR); + + if (value) + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_SET); + else + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_CLEAR); + + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_SET); + return 0; +} + +static void adsp_gpio_set_value(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(chip); + + /* + * For open drain ports, set as input if driving a 1, set as output + * if driving a 0 + */ + if (port->open_drain & BIT(offset)) { + if (value) { + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_SET); + } else { + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_SET); + } + } else { + if (value) + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_SET); + else + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_CLEAR); + } +} + +static int adsp_gpio_get_value(struct gpio_chip *chip, unsigned int offset) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(chip); + + return !!(__adsp_gpio_readw(port, ADSP_PORT_REG_DATA) & BIT(offset)); +} + +static int adsp_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(chip); + irq_hw_number_t irq = offset + port->irq_offset; + int map = irq_find_mapping(port->irq_domain, irq); + + if (!map) + map = irq_create_mapping(port->irq_domain, irq); + + return map; +} + +static int adsp_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adsp_gpio_port *gpio; + struct resource *res; + int ret; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(gpio->regs)) + return PTR_ERR(gpio->regs); + + gpio->dev = dev; + platform_set_drvdata(pdev, gpio); + + ret = adsp_attach_pint_to_gpio(gpio); + if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_err(dev, "Failed to attach to pint node!\n"); + return ret; + } + + spin_lock_init(&gpio->lock); + + gpio->gpio.label = "adsp-gpio"; + gpio->gpio.direction_input = adsp_gpio_direction_input; + gpio->gpio.direction_output = adsp_gpio_direction_output; + gpio->gpio.get = adsp_gpio_get_value; + gpio->gpio.set = adsp_gpio_set_value; + gpio->gpio.to_irq = adsp_gpio_to_irq; + gpio->gpio.request = gpiochip_generic_request; + gpio->gpio.free = gpiochip_generic_free; + gpio->gpio.ngpio = ADSP_PORT_NGPIO; + gpio->gpio.parent = dev; + gpio->gpio.base = -1; + ret = devm_gpiochip_add_data(dev, &gpio->gpio, gpio); + return ret; +} + +static const struct of_device_id adsp_gpio_of_match[] = { + { .compatible = "adi,adsp-port-gpio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adsp_gpio_of_match); + +static struct platform_driver adsp_gpio_driver = { + .driver = { + .name = "adsp-port-gpio", + .of_match_table = adsp_gpio_of_match, + }, + .probe = adsp_gpio_probe, +}; + +static int __init adsp_gpio_init(void) +{ + return platform_driver_register(&adsp_gpio_driver); +} + +arch_initcall(adsp_gpio_init); From 64221e73e1cccd38702f1e048d6efdb594bd8328 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:08:50 +0200 Subject: [PATCH 09/85] i2c: Add TWI I2C support for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/i2c/busses/Kconfig | 20 +- drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-adi-twi.c | 957 +++++++++++++++++++++++++++++++ 3 files changed, 977 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/busses/i2c-adi-twi.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4515ded4338c56..75f5b9b137bdd7 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -463,7 +463,7 @@ config I2C_AXXIA Say yes if you want to support the I2C bus on Axxia platforms. Please note that this controller is limited to transfers of maximum - 255 bytes in length. Any attempt to to a larger transfer will return + 255 bytes in length. Any attempt to a larger transfer will return an error. config I2C_BCM2835 @@ -511,6 +511,24 @@ config I2C_BRCMSTB If you do not need I2C interface, say N. +config I2C_ADI_TWI + tristate "ADI TWI I2C support" + depends on BLACKFIN || ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X + depends on !BF561 && !BF531 && !BF532 && !BF533 + help + This is the I2C bus driver for ADI on-chip TWI interface. + + This driver can also be built as a module. If so, the module + will be called i2c-adi-twi. + +config I2C_ADI_TWI_CLK_KHZ + int "ADI TWI I2C clock (kHz)" + depends on I2C_ADI_TWI + range 21 400 + default 50 + help + The unit of the TWI clock is kHz. + config I2C_CADENCE tristate "Cadence I2C Controller" depends on ARCH_ZYNQ || ARM64 || XTENSA || RISCV || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index fb985769f5ff78..0afcad63ef8ea5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o +obj-$(CONFIG_I2C_ADI_TWI) += i2c-adi-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CGBC) += i2c-cgbc.o diff --git a/drivers/i2c/busses/i2c-adi-twi.c b/drivers/i2c/busses/i2c-adi-twi.c new file mode 100644 index 00000000000000..8ac33f5fcc16f5 --- /dev/null +++ b/drivers/i2c/busses/i2c-adi-twi.c @@ -0,0 +1,957 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADI On-Chip Two Wire Interface Driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* TWI_PRESCALE Masks */ +#define TWI_ENA 0x0080 /* TWI Enable */ + +/* TWI_MASTER_CTL Masks */ +#define MEN 0x0001 /* Master Mode Enable */ +#define MDIR 0x0004 /* Master Transmit Direction (RX/TX*) */ +#define FAST 0x0008 /* Use Fast Mode Timing Specs */ +#define STOP 0x0010 /* Issue Stop Condition */ +#define RSTART 0x0020 /* Repeat Start or Stop* At End Of Transfer */ +#define SDAOVR 0x4000 /* Serial Data Override */ +#define SCLOVR 0x8000 /* Serial Clock Override */ + +/* TWI_MASTER_STAT Masks */ +#define LOSTARB 0x0002 /* Lost Arbitration Indicator (Xfer Aborted) */ +#define ANAK 0x0004 /* Address Not Acknowledged */ +#define DNAK 0x0008 /* Data Not Acknowledged */ +#define BUFRDERR 0x0010 /* Buffer Read Error */ +#define BUFWRERR 0x0020 /* Buffer Write Error */ +#define SDASEN 0x0040 /* Serial Data Sense */ +#define BUSBUSY 0x0100 /* Bus Busy Indicator */ + +/* TWI_INT_SRC and TWI_INT_ENABLE Masks */ +#define MCOMP 0x0010 /* Master Transfer Complete */ +#define MERR 0x0020 /* Master Transfer Error */ +#define XMTSERV 0x0040 /* Transmit FIFO Service */ +#define RCVSERV 0x0080 /* Receive FIFO Service */ + +/* TWI_FIFO_STAT Masks */ +#define XMTSTAT 0x0003 /* Transmit FIFO Status */ +#define XMT_FULL 0x0003 /* Transmit FIFO Full (2 Bytes To Write) */ +#define RCVSTAT 0x000C /* Receive FIFO Status */ + +/* SMBus mode*/ +#define TWI_I2C_MODE_STANDARD 1 +#define TWI_I2C_MODE_STANDARDSUB 2 +#define TWI_I2C_MODE_COMBINED 3 +#define TWI_I2C_MODE_REPEAT 4 + +/* + * ADI twi registers layout + */ +struct adi_twi_regs { + u16 clkdiv; + u16 dummy1; + u16 control; + u16 dummy2; + u16 slave_ctl; + u16 dummy3; + u16 slave_stat; + u16 dummy4; + u16 slave_addr; + u16 dummy5; + u16 master_ctl; + u16 dummy6; + u16 master_stat; + u16 dummy7; + u16 master_addr; + u16 dummy8; + u16 int_stat; + u16 dummy9; + u16 int_mask; + u16 dummy10; + u16 fifo_ctl; + u16 dummy11; + u16 fifo_stat; + u16 dummy12; + u32 __pad[20]; + u16 xmt_data8; + u16 dummy13; + u16 xmt_data16; + u16 dummy14; + u16 rcv_data8; + u16 dummy15; + u16 rcv_data16; + u16 dummy16; +}; + +struct adi_twi_iface { + int irq; + spinlock_t lock; + char read_write; + u8 command; + u8 *transPtr; + int readNum; + int writeNum; + int cur_mode; + int manual_stop; + int result; + unsigned int twi_clk; + struct i2c_adapter adap; + struct completion complete; + struct i2c_msg *pmsg; + int msg_num; + int cur_msg; + u16 saved_clkdiv; + u16 saved_control; + struct adi_twi_regs __iomem *regs_base; + struct clk *sclk; +}; + +static void adi_twi_handle_interrupt(struct adi_twi_iface *iface, + unsigned short twi_int_status, + bool polling) +{ + u16 writeValue; + unsigned short mast_stat = ioread16(&iface->regs_base->master_stat); + + if (twi_int_status & XMTSERV) { + if (iface->writeNum <= 0) { + /* start receive immediately after complete sending in + * combine mode. + */ + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + writeValue = ioread16(&iface->regs_base->master_ctl) | MDIR; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else if (iface->manual_stop) { + writeValue = ioread16(&iface->regs_base->master_ctl) | STOP; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) { + writeValue = ioread16(&iface->regs_base->master_ctl) + | MDIR; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else { + writeValue = ioread16(&iface->regs_base->master_ctl) + & ~MDIR; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } + } + } + /* Transmit next data */ + while (iface->writeNum > 0 && + (ioread16(&iface->regs_base->fifo_stat) & XMTSTAT) != XMT_FULL) { + iowrite16(*(iface->transPtr++), &iface->regs_base->xmt_data8); + iface->writeNum--; + } + } + if (twi_int_status & RCVSERV) { + while (iface->readNum > 0 && + (ioread16(&iface->regs_base->fifo_stat) & RCVSTAT)) { + /* Receive next data */ + *iface->transPtr = ioread16(&iface->regs_base->rcv_data8); + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + /* Change combine mode into sub mode after + * read first data. + */ + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + /* Get read number from first byte in block + * combine mode. + */ + if (iface->readNum == 1 && iface->manual_stop) + iface->readNum = *iface->transPtr + 1; + } + iface->transPtr++; + iface->readNum--; + } + + if (iface->readNum == 0) { + if (iface->manual_stop) { + /* Temporary workaround to avoid possible bus stall - + * Flush FIFO before issuing the STOP condition + */ + ioread16(&iface->regs_base->rcv_data16); + writeValue = ioread16(&iface->regs_base->master_ctl) | STOP; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) { + writeValue = ioread16(&iface->regs_base->master_ctl) | + MDIR; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else { + writeValue = ioread16(&iface->regs_base->master_ctl) & + ~MDIR; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } + } + } + } + if (twi_int_status & MERR) { + iowrite16(0, &iface->regs_base->int_mask); + iowrite16(0x3e, &iface->regs_base->master_stat); + iowrite16(0, &iface->regs_base->master_ctl); + iface->result = -EIO; + + if (mast_stat & LOSTARB) + dev_dbg(&iface->adap.dev, "Lost Arbitration\n"); + if (mast_stat & ANAK) + dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n"); + if (mast_stat & DNAK) + dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n"); + if (mast_stat & BUFRDERR) + dev_dbg(&iface->adap.dev, "Buffer Read Error\n"); + if (mast_stat & BUFWRERR) + dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); + + /* Faulty slave devices, may drive SDA low after a transfer + * finishes. To release the bus this code generates up to 9 + * extra clocks until SDA is released. + */ + + if (ioread16(&iface->regs_base->master_stat) & SDASEN) { + int cnt = 9; + + do { + iowrite16(SCLOVR, &iface->regs_base->master_ctl); + udelay(6); + iowrite16(0, &iface->regs_base->master_ctl); + udelay(6); + } while ((ioread16(&iface->regs_base->master_stat) & SDASEN) && cnt--); + + iowrite16(SDAOVR | SCLOVR, &iface->regs_base->master_ctl); + udelay(6); + iowrite16(SDAOVR, &iface->regs_base->master_ctl); + udelay(6); + iowrite16(0, &iface->regs_base->master_ctl); + } + + /* If it is a quick transfer, only address without data, + * not an err, return 1. + */ + if (iface->cur_mode == TWI_I2C_MODE_STANDARD && + !iface->transPtr && + (twi_int_status & MCOMP) && (mast_stat & DNAK)) + iface->result = 1; + + if (!polling) + complete(&iface->complete); + return; + } + if (twi_int_status & MCOMP) { + if (twi_int_status & (XMTSERV | RCVSERV) && + (ioread16(&iface->regs_base->master_ctl) & MEN) == 0 && + (iface->cur_mode == TWI_I2C_MODE_REPEAT || + iface->cur_mode == TWI_I2C_MODE_COMBINED)) { + iface->result = -1; + iowrite16(0, &iface->regs_base->int_mask); + iowrite16(0, &iface->regs_base->master_ctl); + } else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + if (iface->readNum == 0) { + /* set the read number to 1 and ask for manual + * stop in block combine mode + */ + iface->readNum = 1; + iface->manual_stop = 1; + writeValue = ioread16(&iface->regs_base->master_ctl) | (0xff << 6); + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else { + /* set the readd number in other + * combine mode. + */ + writeValue = (ioread16(&iface->regs_base->master_ctl) + & (~(0xff << 6))) | (iface->readNum << 6); + iowrite16(writeValue, &iface->regs_base->master_ctl); + } + /* remove restart bit and enable master receive */ + writeValue = ioread16(&iface->regs_base->master_ctl) & ~RSTART; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + iface->cur_msg++; + iface->transPtr = iface->pmsg[iface->cur_msg].buf; + iface->writeNum = iface->readNum = + iface->pmsg[iface->cur_msg].len; + /* Set Transmit device address */ + iowrite16(iface->pmsg[iface->cur_msg].addr, &iface->regs_base->master_addr); + if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD) + iface->read_write = I2C_SMBUS_READ; + else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), + &iface->regs_base->xmt_data8); + iface->writeNum--; + } + } + + if (iface->pmsg[iface->cur_msg].len <= 255) { + writeValue = (ioread16(&iface->regs_base->master_ctl) + & (~(0xff << 6))) + | (iface->pmsg[iface->cur_msg].len << 6); + iowrite16(writeValue, &iface->regs_base->master_ctl); + iface->manual_stop = 0; + } else { + writeValue = (ioread16(&iface->regs_base->master_ctl) + | (0xff << 6)); + iowrite16(writeValue, &iface->regs_base->master_ctl); + iface->manual_stop = 1; + } + + /* remove restart bit before last message */ + if (iface->cur_msg + 1 == iface->msg_num) { + writeValue = ioread16(&iface->regs_base->master_ctl) & ~RSTART; + iowrite16(writeValue, &iface->regs_base->master_ctl); + } + + } else { + iface->result = 1; + iowrite16(0, &iface->regs_base->int_mask); + iowrite16(0, &iface->regs_base->master_ctl); + } + if (!polling) + complete(&iface->complete); + } +} + +/* Interrupt handler */ +static irqreturn_t adi_twi_handle_all_interrupts(struct adi_twi_iface *iface, + bool polling) +{ + irqreturn_t handled = IRQ_NONE; + unsigned short twi_int_status; + + while (1) { + twi_int_status = ioread16(&iface->regs_base->int_stat); + if (!twi_int_status) + return handled; + /* Clear interrupt status */ + iowrite16(twi_int_status, &iface->regs_base->int_stat); + adi_twi_handle_interrupt(iface, twi_int_status, polling); + handled = IRQ_HANDLED; + } +} + +static irqreturn_t adi_twi_interrupt_entry(int irq, void *dev_id) +{ + struct adi_twi_iface *iface = dev_id; + unsigned long flags; + irqreturn_t handled; + + spin_lock_irqsave(&iface->lock, flags); + handled = adi_twi_handle_all_interrupts(iface, false); + spin_unlock_irqrestore(&iface->lock, flags); + return handled; +} + +/* + * One i2c master transfer + */ +static int adi_twi_do_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num, bool polling) +{ + struct adi_twi_iface *iface = adap->algo_data; + struct i2c_msg *pmsg; + int rc = 0; + u16 writeValue; + + if (!(ioread16(&iface->regs_base->control) & TWI_ENA)) + return -ENXIO; + + if (ioread16(&iface->regs_base->master_stat) & BUSBUSY) + return -EAGAIN; + + iface->pmsg = msgs; + iface->msg_num = num; + iface->cur_msg = 0; + + pmsg = &msgs[0]; + if (pmsg->flags & I2C_M_TEN) { + dev_err(&adap->dev, "10 bits addr not supported!\n"); + return -EINVAL; + } + + if (iface->msg_num > 1) + iface->cur_mode = TWI_I2C_MODE_REPEAT; + iface->manual_stop = 0; + iface->transPtr = pmsg->buf; + iface->writeNum = iface->readNum = pmsg->len; + iface->result = 0; + if (!polling) + init_completion(&(iface->complete)); + /* Set Transmit device address */ + iowrite16(pmsg->addr, &iface->regs_base->master_addr); + + /* FIFO Initiation. Data in FIFO should be + * discarded before start a new operation. + */ + iowrite16(0x3, &iface->regs_base->fifo_ctl); + iowrite16(0, &iface->regs_base->fifo_ctl); + + if (pmsg->flags & I2C_M_RD) + iface->read_write = I2C_SMBUS_READ; + else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), &iface->regs_base->xmt_data8); + iface->writeNum--; + } + } + + /* clear int stat */ + iowrite16(MERR | MCOMP | XMTSERV | RCVSERV, &iface->regs_base->int_stat); + + /* Interrupt mask . Enable XMT, RCV interrupt */ + iowrite16(MCOMP | MERR | RCVSERV | XMTSERV, &iface->regs_base->int_mask); + + if (pmsg->len <= 255) + iowrite16(pmsg->len << 6, &iface->regs_base->master_ctl); + else { + iowrite16(0xff << 6, &iface->regs_base->master_ctl); + iface->manual_stop = 1; + } + + /* Master enable */ + writeValue = ioread16(&iface->regs_base->master_ctl) | + MEN | + ((iface->msg_num > 1) ? RSTART : 0) | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((iface->twi_clk > 100) ? FAST : 0); + + iowrite16(writeValue, &iface->regs_base->master_ctl); + + if (polling) { + int timeout = 50000; + + for (;;) { + irqreturn_t handled = adi_twi_handle_all_interrupts( + iface, true); + if (handled == IRQ_HANDLED && iface->result) + break; + if (--timeout == 0) { + iface->result = -1; + dev_err(&adap->dev, "master polling timeout"); + break; + } + } + } else { /* interrupt driven */ + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "master transfer timeout"); + } + } + } + + if (iface->result == 1) + rc = iface->cur_msg + 1; + else + rc = iface->result; + + return rc; +} + +/* + * Generic i2c master transfer entrypoint + */ +static int adi_twi_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return adi_twi_do_master_xfer(adap, msgs, num, false); +} + +static int adi_twi_master_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return adi_twi_do_master_xfer(adap, msgs, num, true); +} + +/* + * One I2C SMBus transfer + */ +int adi_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data, + bool polling) +{ + struct adi_twi_iface *iface = adap->algo_data; + int rc = 0; + u16 writeValue; + + if (!(ioread16(&iface->regs_base->control) & TWI_ENA)) + return -ENXIO; + + if (ioread16(&iface->regs_base->master_stat) & BUSBUSY) + return -EAGAIN; + + iface->writeNum = 0; + iface->readNum = 0; + + /* Prepare datas & select mode */ + switch (size) { + case I2C_SMBUS_QUICK: + iface->transPtr = NULL; + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE: + if (data == NULL) + iface->transPtr = NULL; + else { + if (read_write == I2C_SMBUS_READ) + iface->readNum = 1; + else + iface->writeNum = 1; + iface->transPtr = &data->byte; + } + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 1; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = &data->byte; + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 2; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_PROC_CALL: + iface->writeNum = 2; + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 0; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0] + 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = data->block; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->block[1]; + break; + default: + return -1; + } + + iface->result = 0; + iface->manual_stop = 0; + iface->read_write = read_write; + iface->command = command; + if (!polling) + init_completion(&(iface->complete)); + + /* FIFO Initiation. Data in FIFO should be discarded before + * start a new operation. + */ + iowrite16(0x3, &iface->regs_base->fifo_ctl); + iowrite16(0, &iface->regs_base->fifo_ctl); + + /* clear int stat */ + iowrite16(MERR | MCOMP | XMTSERV | RCVSERV, &iface->regs_base->int_stat); + + /* Set Transmit device address */ + iowrite16(addr, &iface->regs_base->master_addr); + + switch (iface->cur_mode) { + case TWI_I2C_MODE_STANDARDSUB: + iowrite16(iface->command, &iface->regs_base->xmt_data8); + + writeValue = MCOMP | MERR; + if (iface->read_write == I2C_SMBUS_READ) + writeValue |= RCVSERV; + else + writeValue |= XMTSERV; + + iowrite16(writeValue, &iface->regs_base->int_mask); + + if (iface->writeNum + 1 <= 255) + iowrite16((iface->writeNum + 1) << 6, &iface->regs_base->master_ctl); + else { + iowrite16(0xff << 6, &iface->regs_base->master_ctl); + iface->manual_stop = 1; + } + /* Master enable */ + writeValue = ioread16(&iface->regs_base->master_ctl) | MEN; + if (iface->twi_clk > 100) + writeValue |= FAST; + iowrite16(writeValue, &iface->regs_base->master_ctl); + break; + case TWI_I2C_MODE_COMBINED: + iowrite16(iface->command, &iface->regs_base->xmt_data8); + iowrite16(MCOMP | MERR | RCVSERV | XMTSERV, &iface->regs_base->int_mask); + + if (iface->writeNum > 0) + iowrite16((iface->writeNum + 1) << 6, &iface->regs_base->master_ctl); + else + iowrite16(0x1 << 6, &iface->regs_base->master_ctl); + /* Master enable */ + writeValue = ioread16(&iface->regs_base->master_ctl) | MEN | RSTART; + if (iface->twi_clk > 100) + writeValue |= FAST; + iowrite16(writeValue, &iface->regs_base->master_ctl); + break; + default: + iowrite16(0, &iface->regs_base->master_ctl); + if (size != I2C_SMBUS_QUICK) { + /* Don't access xmit data register when this is a + * read operation. + */ + if (iface->read_write != I2C_SMBUS_READ) { + if (iface->writeNum > 0) { + iowrite16(*(iface->transPtr++), + &iface->regs_base->xmt_data8); + if (iface->writeNum <= 255) + iowrite16(iface->writeNum << 6, + &iface->regs_base->master_ctl); + else { + iowrite16(0xff << 6, &iface->regs_base->master_ctl); + iface->manual_stop = 1; + } + iface->writeNum--; + } else { + iowrite16(iface->command, &iface->regs_base->xmt_data8); + iowrite16(1 << 6, &iface->regs_base->master_ctl); + } + } else { + if (iface->readNum > 0 && iface->readNum <= 255) + iowrite16(iface->readNum << 6, + &iface->regs_base->master_ctl); + else if (iface->readNum > 255) { + iowrite16(0xff << 6, &iface->regs_base->master_ctl); + iface->manual_stop = 1; + } else + break; + } + } + writeValue = MCOMP | MERR; + if (iface->read_write == I2C_SMBUS_READ) + writeValue |= RCVSERV; + else + writeValue |= XMTSERV; + iowrite16(writeValue, &iface->regs_base->int_mask); + + /* Master enable */ + writeValue = ioread16(&iface->regs_base->master_ctl) | MEN | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((iface->twi_clk > 100) ? FAST : 0); + iowrite16(writeValue, &iface->regs_base->master_ctl); + break; + } + + if (polling) { + int timeout = 50000; + + for (;;) { + irqreturn_t handled = adi_twi_handle_all_interrupts( + iface, true); + if (handled == IRQ_HANDLED && iface->result) + break; + if (--timeout == 0) { + iface->result = -1; + dev_err(&adap->dev, "smbus polling timeout"); + break; + } + } + } else { /* interrupt driven */ + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "smbus transfer timeout"); + } + } + } + + rc = (iface->result >= 0) ? 0 : -1; + + return rc; +} + +/* + * Generic I2C SMBus transfer entrypoint + */ +int adi_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return adi_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data, false); +} + +int adi_twi_smbus_xfer_atomic(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return adi_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data, true); +} + +/* + * Return what the adapter supports + */ +static u32 adi_twi_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm adi_twi_algorithm = { + .master_xfer = adi_twi_master_xfer, + .master_xfer_atomic = adi_twi_master_xfer_atomic, + .smbus_xfer = adi_twi_smbus_xfer, + .smbus_xfer_atomic = adi_twi_smbus_xfer_atomic, + .functionality = adi_twi_functionality, +}; + +#ifdef CONFIG_PM_SLEEP +static int i2c_adi_twi_suspend(struct device *dev) +{ + struct adi_twi_iface *iface = dev_get_drvdata(dev); + + iface->saved_clkdiv = ioread16(&iface->regs_base->clkdiv); + iface->saved_control = ioread16(&iface->regs_base->control); + + free_irq(iface->irq, iface); + + /* Disable TWI */ + iowrite16(iface->saved_control & ~TWI_ENA, &iface->regs_base->control); + + return 0; +} + +static int i2c_adi_twi_resume(struct device *dev) +{ + struct adi_twi_iface *iface = dev_get_drvdata(dev); + + int rc = request_irq(iface->irq, adi_twi_interrupt_entry, + 0, to_platform_device(dev)->name, iface); + if (rc) { + dev_err(dev, "Can't get IRQ %d !\n", iface->irq); + return -ENODEV; + } + + /* Resume TWI interface clock as specified */ + iowrite16(iface->saved_clkdiv, &iface->regs_base->clkdiv); + + /* Resume TWI */ + iowrite16(iface->saved_control, &iface->regs_base->control); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(i2c_adi_twi_pm, + i2c_adi_twi_suspend, i2c_adi_twi_resume); +#define I2C_ADI_TWI_PM_OPS (&i2c_adi_twi_pm) +#else +#define I2C_ADI_TWI_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id adi_twi_of_match[] = { + { + .compatible = "adi,twi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_twi_of_match); +#endif + +static int i2c_adi_twi_probe(struct platform_device *pdev) +{ + struct adi_twi_iface *iface; + struct i2c_adapter *p_adap; + struct resource *res; + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + int rc; + unsigned int clkhilow; + u16 writeValue; + + iface = devm_kzalloc(&pdev->dev, sizeof(*iface), GFP_KERNEL); + if (!iface) + return -ENOMEM; + + spin_lock_init(&(iface->lock)); + + match = of_match_device(of_match_ptr(adi_twi_of_match), &pdev->dev); + if (match) { + if (of_property_read_u32(node, "clock-khz", + &iface->twi_clk)) + iface->twi_clk = 50; + } else + iface->twi_clk = CONFIG_I2C_ADI_TWI_CLK_KHZ; + + iface->sclk = devm_clk_get(&pdev->dev, "sclk0"); + if (IS_ERR(iface->sclk)) { + if (PTR_ERR(iface->sclk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Missing i2c clock\n"); + return PTR_ERR(iface->sclk); + } + + /* Find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + return -ENOENT; + } + + iface->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iface->regs_base)) { + dev_err(&pdev->dev, "Cannot map IO\n"); + return PTR_ERR(iface->regs_base); + } + + iface->irq = platform_get_irq(pdev, 0); + if (iface->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + return -ENOENT; + } + + p_adap = &iface->adap; + p_adap->nr = pdev->id; + strscpy(p_adap->name, pdev->name, sizeof(p_adap->name)); + p_adap->algo = &adi_twi_algorithm; + p_adap->algo_data = iface; + p_adap->class = I2C_CLASS_DEPRECATED; + p_adap->dev.parent = &pdev->dev; + p_adap->dev.of_node = node; + p_adap->timeout = 5 * HZ; + p_adap->retries = 3; + + rc = devm_request_irq(&pdev->dev, iface->irq, adi_twi_interrupt_entry, + 0, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + rc = -ENODEV; + goto out_error; + } + + /* Set TWI internal clock as 10MHz */ + clk_prepare_enable(iface->sclk); + if (rc) { + dev_err(&pdev->dev, "Could not enable sclk\n"); + goto out_error; + } + + writeValue = ((clk_get_rate(iface->sclk) / 1000 / 1000 + 5) / 10) & 0x7F; + iowrite16(writeValue, &iface->regs_base->control); + + /* + * We will not end up with a CLKDIV=0 because no one will specify + * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250) + */ + clkhilow = ((10 * 1000 / iface->twi_clk) + 1) / 2; + + /* Set Twi interface clock as specified */ + writeValue = (clkhilow << 8) | clkhilow; + iowrite16(writeValue, &iface->regs_base->clkdiv); + + /* Enable TWI */ + writeValue = ioread16(&iface->regs_base->control) | TWI_ENA; + iowrite16(writeValue, &iface->regs_base->control); + + rc = i2c_add_numbered_adapter(p_adap); + if (rc < 0) + goto disable_clk; + + platform_set_drvdata(pdev, iface); + + dev_info(&pdev->dev, "ADI on-chip I2C TWI Controller, regs_base@%p\n", + iface->regs_base); + + return 0; + +disable_clk: + clk_disable_unprepare(iface->sclk); + +out_error: + return rc; +} + +static void i2c_adi_twi_remove(struct platform_device *pdev) +{ + struct adi_twi_iface *iface = platform_get_drvdata(pdev); + + clk_disable_unprepare(iface->sclk); + i2c_del_adapter(&(iface->adap)); +} + +static struct platform_driver i2c_adi_twi_driver = { + .probe = i2c_adi_twi_probe, + .remove = i2c_adi_twi_remove, + .driver = { + .name = "i2c-adi-twi", + .pm = I2C_ADI_TWI_PM_OPS, + .of_match_table = of_match_ptr(adi_twi_of_match), + }, +}; + +static int __init i2c_adi_twi_init(void) +{ + return platform_driver_register(&i2c_adi_twi_driver); +} + +static void __exit i2c_adi_twi_exit(void) +{ + platform_driver_unregister(&i2c_adi_twi_driver); +} + +subsys_initcall(i2c_adi_twi_init); +module_exit(i2c_adi_twi_exit); + +MODULE_AUTHOR("Bryan Wu, Sonic Zhang"); +MODULE_DESCRIPTION("ADI on-chip I2C TWI Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-adi-twi"); From 96494b730a3b73582ecd2fcb27af64a74f5c5fc1 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:12:03 +0200 Subject: [PATCH 10/85] serial: Add UART driver for SC5xx SoCs Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- drivers/tty/serial/Kconfig | 17 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/adi_uart4.c | 1377 ++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1398 insertions(+) create mode 100644 drivers/tty/serial/adi_uart4.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2b9c8b39d68f55..57ae138ffa9504 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -475,6 +475,23 @@ config SERIAL_SA1100_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_ADI_UART4 + tristate "ADI uart4 serial port support" + depends on ARCH_SC59X = y || ARCH_SC58X = y || ARCH_SC57X = y || ARCH_SC59X_64 = y + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + help + Add support for the built-in adi uart4 driver. + +config SERIAL_ADI_UART4_CONSOLE + bool "Console on ADI uart4 serial port" + depends on SERIAL_ADI_UART4 + default y + select SERIAL_CORE_CONSOLE + help + If you have enabled the ADI UART4 serial port, you can + make it the console by answering Y to this option. + config SERIAL_IMX tristate "IMX serial port support" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index a2ccbc508ec57f..632da1224481d3 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o +obj-$(CONFIG_SERIAL_ADI_UART4) += adi_uart4.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_MAX310X) += max310x.o obj-$(CONFIG_SERIAL_MCF) += mcf.o diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c new file mode 100644 index 00000000000000..adb56ad8631a13 --- /dev/null +++ b/drivers/tty/serial/adi_uart4.c @@ -0,0 +1,1377 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADI UART4 Serial Driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_SERIAL_ADI_UART4_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#define DRIVER_NAME "adi-uart4" + +struct adi_uart4_serial_port { + struct uart_port port; + struct device *dev; + unsigned int old_status; + int tx_irq; + int rx_irq; + int status_irq; + unsigned int lsr; + /* DMA specific fields */ + int tx_done; + int tx_count; + dma_addr_t tx_dma_phy; + struct scatterlist tx_sgl; + struct circ_buf rx_dma_buf; + dma_addr_t rx_dma_phy; + dma_cookie_t rx_cookie; + struct timer_list rx_dma_timer; + spinlock_t rx_lock; + struct dma_chan *tx_dma_channel; + struct dma_chan *rx_dma_channel; + /* Hardware flow control specific fields */ + unsigned int hwflow_mode; + struct gpio_desc *hwflow_en_pin; + bool hwflow_en; + /* Use enable-divide-by-one in divisor? */ + bool edbo; + struct clk *clk; +}; + +struct adi_uart4_serial_port *to_adi_serial_port(struct uart_port *port) +{ + return container_of(port, struct adi_uart4_serial_port, port); +} + +#define ADI_UART_NO_HWFLOW 0 +#define ADI_UART_HWFLOW_PERI 1 + +#define ADI_UART_NR_PORTS 4 +static struct adi_uart4_serial_port *adi_uart4_serial_ports[ADI_UART_NR_PORTS]; + +/* UART_CTL Masks */ +#define UCEN 0x1 /* Enable UARTx Clocks */ +#define LOOP_ENA 0x2 /* Loopback Mode Enable */ +#define UMOD_MDB 0x10 /* Enable MDB Mode */ +#define UMOD_IRDA 0x20 /* Enable IrDA Mode */ +#define UMOD_MASK 0x30 /* Uart Mode Mask */ +#define WLS(x) (((x-5) & 0x03) << 8) /* Word Length Select */ +#define WLS_MASK 0x300 /* Word length Select Mask */ +#define WLS_OFFSET 8 /* Word length Select Offset */ +#define STB 0x1000 /* Stop Bits */ +#define STBH 0x2000 /* Half Stop Bits */ +#define PEN 0x4000 /* Parity Enable */ +#define EPS 0x8000 /* Even Parity Select */ +#define STP 0x10000 /* Stick Parity */ +#define FPE 0x20000 /* Force Parity Error On Transmit */ +#define FFE 0x40000 /* Force Framing Error On Transmit */ +#define SB 0x80000 /* Set Break */ +#define LCR_MASK (SB | STP | EPS | PEN | STB | WLS_MASK) +#define FCPOL 0x400000 /* Flow Control Pin Polarity */ +#define RPOLC 0x800000 /* IrDA RX Polarity Change */ +#define TPOLC 0x1000000 /* IrDA TX Polarity Change */ +#define MRTS 0x2000000 /* Manual Request To Send */ +#define XOFF 0x4000000 /* Transmitter Off */ +#define ARTS 0x8000000 /* Automatic Request To Send */ +#define ACTS 0x10000000 /* Automatic Clear To Send */ +#define RFIT 0x20000000 /* Receive FIFO IRQ Threshold */ +#define RFRT 0x40000000 /* Receive FIFO RTS Threshold */ + +/* UART_STAT Masks */ +#define DR 0x01 /* Data Ready */ +#define OE 0x02 /* Overrun Error */ +#define PE 0x04 /* Parity Error */ +#define FE 0x08 /* Framing Error */ +#define BI 0x10 /* Break Interrupt */ +#define THRE 0x20 /* THR Empty */ +#define TEMT 0x80 /* TSR and UART_THR Empty */ +#define TFI 0x100 /* Transmission Finished Indicator */ + +#define ASTKY 0x200 /* Address Sticky */ +#define ADDR 0x400 /* Address bit status */ +#define RO 0x800 /* Reception Ongoing */ +#define SCTS 0x1000 /* Sticky CTS */ +#define CTS 0x10000 /* Clear To Send */ +#define RFCS 0x20000 /* Receive FIFO Count Status */ + +/* UART_CLOCK Masks */ +#define EDBO 0x80000000 /* Enable Devide by One */ + +/* UART_IER Masks */ +#define ERBFI 0x01 /* Enable Receive Buffer Full Interrupt */ +#define ETBEI 0x02 /* Enable Transmit Buffer Empty Interrupt */ +#define ELSI 0x04 /* Enable RX Status Interrupt */ +#define EDSSI 0x08 /* Enable Modem Status Interrupt */ +#define EDTPTI 0x10 /* Enable DMA Transmit PIRQ Interrupt */ +#define ETFI 0x20 /* Enable Transmission Finished Interrupt */ +#define ERFCI 0x40 /* Enable Receive FIFO Count Interrupt */ + +# define OFFSET_REDIV 0x00 /* Version ID Register */ +# define OFFSET_CTL 0x04 /* Control Register */ +# define OFFSET_STAT 0x08 /* Status Register */ +# define OFFSET_SCR 0x0C /* SCR Scratch Register */ +# define OFFSET_CLK 0x10 /* Clock Rate Register */ +# define OFFSET_IER 0x14 /* Interrupt Enable Register */ +# define OFFSET_IER_SET 0x18 /* Set Interrupt Enable Register */ +# define OFFSET_IER_CLEAR 0x1C /* Clear Interrupt Enable Register */ +# define OFFSET_RBR 0x20 /* Receive Buffer register */ +# define OFFSET_THR 0x24 /* Transmit Holding register */ + +#define UART_GET_CHAR(p) readl(p->port.membase + OFFSET_RBR) +#define UART_GET_CLK(p) readl(p->port.membase + OFFSET_CLK) +#define UART_GET_CTL(p) readl(p->port.membase + OFFSET_CTL) +#define UART_GET_GCTL(p) UART_GET_CTL(p) +#define UART_GET_LCR(p) UART_GET_CTL(p) +#define UART_GET_MCR(p) UART_GET_CTL(p) +#define UART_GET_STAT(p) readl(p->port.membase + OFFSET_STAT) +#define UART_GET_MSR(p) UART_GET_STAT(p) + +#define UART_PUT_CHAR(p, v) writel(v, p->port.membase + OFFSET_THR) +#define UART_PUT_CLK(p, v) writel(v, p->port.membase + OFFSET_CLK) +#define UART_PUT_CTL(p, v) writel(v, p->port.membase + OFFSET_CTL) +#define UART_PUT_GCTL(p, v) UART_PUT_CTL(p, v) +#define UART_PUT_LCR(p, v) UART_PUT_CTL(p, v) +#define UART_PUT_MCR(p, v) UART_PUT_CTL(p, v) +#define UART_PUT_STAT(p, v) writel(v, p->port.membase + OFFSET_STAT) + +#define UART_CLEAR_IER(p, v) writel(v, p->port.membase + OFFSET_IER_CLEAR) +#define UART_GET_IER(p) readl(p->port.membase + OFFSET_IER) +#define UART_SET_IER(p, v) writel(v, p->port.membase + OFFSET_IER_SET) + +#define UART_CLEAR_LSR(p) UART_PUT_STAT(p, -1) +#define UART_GET_LSR(p) UART_GET_STAT(p) +#define UART_PUT_LSR(p, v) UART_PUT_STAT(p, v) + +/* This handles hard CTS/RTS */ +#define UART_CLEAR_SCTS(p) UART_PUT_STAT(p, SCTS) +#define UART_GET_CTS(x) (UART_GET_MSR(x) & CTS) +#define UART_DISABLE_RTS(x) UART_PUT_MCR(x, UART_GET_MCR(x) & ~(ARTS | MRTS)) +#define UART_ENABLE_RTS(x) UART_PUT_MCR(x, UART_GET_MCR(x) | MRTS | ARTS) +#define UART_ENABLE_INTS(x, v) UART_SET_IER(x, v) +#define UART_DISABLE_INTS(x) UART_CLEAR_IER(x, 0xF) + +#define DMA_RX_XCOUNT 512 +#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) + +#define DMA_RX_FLUSH_JIFFIES (msecs_to_jiffies(50)) + +static void adi_uart4_serial_dma_tx_chars(struct adi_uart4_serial_port *uart); +static void adi_uart4_serial_dma_tx(void *data); +static void adi_uart4_serial_tx_chars(struct adi_uart4_serial_port *uart); +static void adi_uart4_serial_reset_irda(struct uart_port *port); + +static unsigned int adi_uart4_serial_get_mctrl(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + if (!uart->hwflow_mode || !uart->hwflow_en) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (UART_GET_CTS(uart)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void adi_uart4_serial_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + if (!uart->hwflow_mode || !uart->hwflow_en) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + UART_ENABLE_RTS(uart); + else + UART_DISABLE_RTS(uart); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t adi_uart4_serial_mctrl_cts_int(int irq, void *dev_id) +{ + struct adi_uart4_serial_port *uart = dev_id; + unsigned int status = adi_uart4_serial_get_mctrl(&uart->port); + struct tty_struct *tty = uart->port.state->port.tty; + + if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) { + UART_CLEAR_SCTS(uart); + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + uart_write_wakeup(&uart->port); + } + } else { + if (!status) + tty->hw_stopped = 1; + } + } + + uart_handle_cts_change(&uart->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} + +/* + * interrupts are disabled on entry + */ +static void adi_uart4_serial_stop_tx(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + while (!(UART_GET_LSR(uart) & TEMT)) + cpu_relax(); + + if (IS_ERR(uart->tx_dma_channel)) { + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); + UART_CLEAR_IER(uart, ETBEI); + } else { + if (!uart->tx_done) + dma_unmap_sg(uart->dev, &uart->tx_sgl, 1, DMA_TO_DEVICE); + + dmaengine_terminate_sync(uart->tx_dma_channel); + uart->port.icount.tx += uart->tx_count; + uart->tx_count = 0; + uart->tx_done = 1; + + } +} + +/* + * port is locked and interrupts are disabled + */ +static void adi_uart4_serial_start_tx(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + struct tty_struct *tty = uart->port.state->port.tty; + + /* + * To avoid losting RX interrupt, we reset IR function + * before sending data. + */ + if (tty->termios.c_line == N_IRDA) + adi_uart4_serial_reset_irda(port); + + if (IS_ERR(uart->tx_dma_channel)) { + UART_SET_IER(uart, ETBEI); + adi_uart4_serial_tx_chars(uart); + } else { + if (uart->tx_done) + adi_uart4_serial_dma_tx_chars(uart); + } +} + +/* + * Interrupts are enabled + */ +static void adi_uart4_serial_stop_rx(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + UART_CLEAR_IER(uart, ERBFI); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void adi_uart4_serial_enable_ms(struct uart_port *port) +{ +} + +static void adi_uart4_serial_rx_chars(struct adi_uart4_serial_port *uart) +{ + unsigned int status, ch, flg; + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + ch = UART_GET_CHAR(uart); + uart->port.icount.rx++; + + if (status & BI) { + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + if (uart_handle_sysrq_char(&uart->port, ch)) + goto ignore_char; + + uart_insert_char(&uart->port, status, OE, ch, flg); + + ignore_char: + tty_flip_buffer_push(&uart->port.state->port); +} + +static void adi_uart4_serial_tx_chars(struct adi_uart4_serial_port *uart) +{ + struct tty_port *tport = &uart->port.state->port; + unsigned char c; + + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uart->port)) { + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + if (UART_GET_LSR(uart) & THRE) { + /* pop data from fifo */ + if (!kfifo_get(&tport->xmit_fifo, &c)) + return; + + UART_PUT_CHAR(uart, c); + uart->port.icount.tx++; + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + } + +} + +static irqreturn_t adi_uart4_serial_rx_int(int irq, void *dev_id) +{ + struct adi_uart4_serial_port *uart = dev_id; + + while (UART_GET_LSR(uart) & DR) + adi_uart4_serial_rx_chars(uart); + + return IRQ_HANDLED; +} + +static irqreturn_t adi_uart4_serial_tx_int(int irq, void *dev_id) +{ + struct adi_uart4_serial_port *uart = dev_id; + + spin_lock(&uart->port.lock); + if (UART_GET_LSR(uart) & THRE) + adi_uart4_serial_tx_chars(uart); + spin_unlock(&uart->port.lock); + + return IRQ_HANDLED; +} + +static void adi_uart4_serial_dma_tx_chars(struct adi_uart4_serial_port *uart) +{ + struct dma_async_tx_descriptor *desc; + struct tty_port *tport = &uart->port.state->port; + int ret; + + uart->tx_done = 0; + + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uart->port)) { + uart->tx_count = 0; + uart->tx_done = 1; + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + /* Setup the scatterlist for DMA output with fifo */ + sg_init_table(&uart->tx_sgl, 1); + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &uart->tx_sgl, 1, + UART_XMIT_SIZE, uart->tx_dma_phy); + if (ret != 1) + return; + + desc = dmaengine_prep_slave_sg(uart->tx_dma_channel, &uart->tx_sgl, 1, + DMA_MEM_TO_DEV, 0); + if (!desc) { + dma_unmap_sg(uart->dev, &uart->tx_sgl, 1, DMA_TO_DEVICE); + return; + } + + uart->tx_count = sg_dma_len(&uart->tx_sgl); + + desc->callback = adi_uart4_serial_dma_tx; + desc->callback_param = uart; + dmaengine_submit(desc); + dma_sync_single_for_device(uart->dev, uart->tx_dma_phy, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_async_issue_pending(uart->tx_dma_channel); + UART_SET_IER(uart, ETBEI); +} + +static void adi_uart4_serial_dma_rx_chars(struct adi_uart4_serial_port *uart) +{ + int i, flg, status; + + mod_timer(&uart->rx_dma_timer, jiffies + DMA_RX_FLUSH_JIFFIES); + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + uart->port.icount.rx += + CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, + UART_XMIT_SIZE); + + if (status & BI) { + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto dma_ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + for (i = uart->rx_dma_buf.tail; ; i++) { + if (i >= UART_XMIT_SIZE) + i = 0; + if (i == uart->rx_dma_buf.head) + break; + if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) + uart_insert_char(&uart->port, status, OE, + uart->rx_dma_buf.buf[i], flg); + } + + dma_ignore_char: + tty_flip_buffer_push(&uart->port.state->port); +} + +void adi_uart4_serial_rx_dma_timeout(struct timer_list *list) +{ + struct adi_uart4_serial_port *uart = + container_of(list, struct adi_uart4_serial_port, rx_dma_timer); + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + + dmaengine_pause(uart->rx_dma_channel); + spin_lock_irqsave(&uart->rx_lock, flags); + + status = dmaengine_tx_status(uart->rx_dma_channel, uart->rx_cookie, + &state); + + if (status == DMA_ERROR) { + dev_err(uart->dev, "Error in RX DMA\n"); + goto exit; + } + + /* Because resume will reset us to the start of the buffer, + * reset the tail pointer to 0 after, but use the previous tail for an + * offset buffer slice that timed out + */ + uart->rx_dma_buf.head = UART_XMIT_SIZE - state.residue; + adi_uart4_serial_dma_rx_chars(uart); + uart->rx_dma_buf.tail = 0; + +exit: + spin_unlock_irqrestore(&uart->rx_lock, flags); + dmaengine_resume(uart->rx_dma_channel); +} + +static void adi_uart4_serial_dma_tx(void *data) +{ + struct adi_uart4_serial_port *uart = data; + struct tty_port *tport = &uart->port.state->port; + unsigned long flags; + + dma_unmap_sg(uart->dev, &uart->tx_sgl, 1, DMA_TO_DEVICE); + + spin_lock_irqsave(&uart->port.lock, flags); + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + + /* + * Advance fifo to match the DMA write + */ + uart_xmit_advance(&uart->port, uart->tx_count); + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + adi_uart4_serial_dma_tx_chars(uart); + spin_unlock_irqrestore(&uart->port.lock, flags); +} + +static void adi_uart4_serial_dma_rx(void *data) +{ + struct adi_uart4_serial_port *uart = data; + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + int pos; + + spin_lock_irqsave(&uart->rx_lock, flags); + + status = dmaengine_tx_status(uart->rx_dma_channel, uart->rx_cookie, + &state); + + if (status == DMA_ERROR) { + dev_err(uart->dev, "Error in RX DMA\n"); + spin_unlock_irqrestore(&uart->rx_lock, flags); + return; + } + + /* Update tail to start of the current block, so that we can receive + * multiple full blocks or a partial block not at the start of the + * buffer in event of a timeout + */ + uart->rx_dma_buf.head = UART_XMIT_SIZE - state.residue; + pos = (uart->rx_dma_buf.head - 1) & (UART_XMIT_SIZE - 1); + pos = (pos / DMA_RX_XCOUNT) * DMA_RX_XCOUNT; + uart->rx_dma_buf.tail = pos; + adi_uart4_serial_dma_rx_chars(uart); + + spin_unlock_irqrestore(&uart->rx_lock, flags); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int adi_uart4_serial_tx_empty(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + unsigned int lsr; + + lsr = UART_GET_LSR(uart); + if (lsr & TEMT) + return TIOCSER_TEMT; + else + return 0; +} + +static void adi_uart4_serial_break_ctl(struct uart_port *port, int break_state) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + u32 lcr = UART_GET_LCR(uart); + + if (break_state) + lcr |= SB; + else + lcr &= ~SB; + UART_PUT_LCR(uart, lcr); +} + +static int adi_uart4_serial_startup(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + struct tty_port *tport = &uart->port.state->port; + struct dma_slave_config dma_config = {0}; + struct dma_async_tx_descriptor *desc; + int ret; + + uart->tx_done = 1; + + ret = clk_prepare_enable(uart->clk); + if (ret) + return ret; + + if (!IS_ERR(uart->tx_dma_channel)) { + /* RX channel: + * - src_addr is not configured because we're attached to + * peripheral + */ + uart->rx_dma_buf.buf = dmam_alloc_coherent(uart->dev, UART_XMIT_SIZE, + &uart->rx_dma_phy, GFP_KERNEL); + if (!uart->rx_dma_buf.buf) + return -ENOMEM; + + uart->rx_dma_buf.head = 0; + uart->rx_dma_buf.tail = 0; + + dma_config.direction = DMA_DEV_TO_MEM; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = 1; + dma_config.dst_maxburst = 1; + + ret = dmaengine_slave_config(uart->rx_dma_channel, &dma_config); + if (ret) { + dev_err(uart->dev, "Error configuring RX DMA channel\n"); + return -EINVAL; + } + + desc = dmaengine_prep_dma_cyclic(uart->rx_dma_channel, + uart->rx_dma_phy, UART_XMIT_SIZE, DMA_RX_XCOUNT, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + desc->callback = adi_uart4_serial_dma_rx; + desc->callback_param = uart; + + uart->rx_cookie = dmaengine_submit(desc); + dma_async_issue_pending(uart->rx_dma_channel); + + timer_setup(&uart->rx_dma_timer, + adi_uart4_serial_rx_dma_timeout, 0); + mod_timer(&uart->rx_dma_timer, jiffies + DMA_RX_FLUSH_JIFFIES); + /* TX channel: + * - Use the port's buffer directly for dma + * - dst_addr is not configured because we're attached to + * peripheral + */ + uart->tx_dma_phy = dma_map_single(uart->dev, + tport->xmit_buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(uart->dev, uart->tx_dma_phy)) + return -ENOMEM; + + uart->port.fifosize = UART_XMIT_SIZE; + + dma_config = (struct dma_slave_config) {0}; + dma_config.direction = DMA_MEM_TO_DEV; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_maxburst = 1; + dma_config.src_maxburst = 1; + + ret = dmaengine_slave_config(uart->tx_dma_channel, &dma_config); + if (ret) { + dev_err(uart->dev, "Error configuring TX DMA channel\n"); + return -EINVAL; + } + } + + if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) { + /* CTS RTS PINs are negative assertive. */ + UART_PUT_MCR(uart, UART_GET_MCR(uart) | ACTS); + UART_SET_IER(uart, EDSSI); + } + + UART_SET_IER(uart, ERBFI); + return 0; +} + +static void adi_uart4_serial_shutdown(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + dev_dbg(uart->dev, "in serial_shutdown\n"); + + if (!IS_ERR(uart->tx_dma_channel)) { + dmaengine_terminate_sync(uart->tx_dma_channel); + dmaengine_terminate_sync(uart->rx_dma_channel); + dma_unmap_single(uart->dev, uart->tx_dma_phy, UART_XMIT_SIZE, + DMA_TO_DEVICE); + dmam_free_coherent(uart->dev, UART_XMIT_SIZE, + uart->rx_dma_buf.buf, uart->rx_dma_phy); + timer_delete(&uart->rx_dma_timer); + } + + clk_disable_unprepare(uart->clk); +} + +static void adi_uart4_serial_set_termios(struct uart_port *port, + struct ktermios *termios, const struct ktermios *old) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + unsigned long flags; + unsigned int baud, quot; + unsigned int ier, lcr = 0; + unsigned long timeout; + + if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) + termios->c_cflag |= CRTSCTS; + + switch (termios->c_cflag & CSIZE) { + case CS8: + lcr = WLS(8); + break; + case CS7: + lcr = WLS(7); + break; + case CS6: + lcr = WLS(6); + break; + case CS5: + lcr = WLS(5); + break; + default: + dev_err(port->dev, "%s: word length not supported\n", + __func__); + } + + if (termios->c_cflag & CSTOPB) + lcr |= STB; + if (termios->c_cflag & PARENB) + lcr |= PEN; + if (!(termios->c_cflag & PARODD)) + lcr |= EPS; + if (termios->c_cflag & CMSPAR) + lcr |= STP; + if (termios->c_cflag & CRTSCTS) + uart->hwflow_en = true; + else + uart->hwflow_en = false; + + spin_lock_irqsave(&uart->port.lock, flags); + + port->read_status_mask = OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (FE | PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= FE | PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= OE; + } + + /* + * uart_get_divisor has a hardcoded /16 factor that will cause integer + * round off errors if we're in divide-by-one mode + */ + if (uart->edbo) { + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk); + quot = EDBO | DIV_ROUND_CLOSEST(port->uartclk, baud); + } else { + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk/16); + quot = uart_get_divisor(port, baud); + } + + /* Wait till the transfer buffer is empty */ + timeout = jiffies + msecs_to_jiffies(10); + while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) + if (time_after(jiffies, timeout)) { + dev_warn(port->dev, + "timeout waiting for TX buffer empty\n"); + break; + } + + /* Wait till the transfer buffer is empty */ + timeout = jiffies + msecs_to_jiffies(10); + while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) + if (time_after(jiffies, timeout)) { + dev_warn(port->dev, + "timeout waiting for TX buffer empty\n"); + break; + } + + /* Disable UART */ + ier = UART_GET_IER(uart); + UART_PUT_GCTL(uart, UART_GET_GCTL(uart) & ~UCEN); + UART_DISABLE_INTS(uart); + + UART_PUT_CLK(uart, quot); + + UART_PUT_LCR(uart, (UART_GET_LCR(uart) & ~LCR_MASK) | lcr); + + /* Enable UART */ + UART_ENABLE_INTS(uart, ier); + UART_PUT_GCTL(uart, UART_GET_GCTL(uart) | UCEN); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&uart->port.lock, flags); +} + +static const char *adi_uart4_serial_type(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + return uart->port.type == PORT_BFIN ? "ADI-UART4" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void adi_uart4_serial_release_port(struct uart_port *port) +{ +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int adi_uart4_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void adi_uart4_serial_config_port(struct uart_port *port, int flags) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + if (flags & UART_CONFIG_TYPE && + adi_uart4_serial_request_port(&uart->port) == 0) + uart->port.type = PORT_BFIN; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_BFIN and PORT_UNKNOWN + */ +static int +adi_uart4_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +/* + * Enable the IrDA function if tty->ldisc.num is N_IRDA. + * In other cases, disable IrDA function. + */ +static void adi_uart4_serial_set_ldisc(struct uart_port *port, + struct ktermios *termios) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + unsigned int val; + + switch (termios->c_line) { + case N_IRDA: + val = UART_GET_GCTL(uart); + val |= (UMOD_IRDA | RPOLC); + UART_PUT_GCTL(uart, val); + break; + default: + val = UART_GET_GCTL(uart); + val &= ~(UMOD_MASK | RPOLC); + UART_PUT_GCTL(uart, val); + } +} + +static void adi_uart4_serial_reset_irda(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + unsigned int val; + + val = UART_GET_GCTL(uart); + val &= ~(UMOD_MASK | RPOLC); + UART_PUT_GCTL(uart, val); + val |= (UMOD_IRDA | RPOLC); + UART_PUT_GCTL(uart, val); +} + +#ifdef CONFIG_CONSOLE_POLL +static void adi_uart4_serial_poll_put_char(struct uart_port *port, + unsigned char chr) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + while (!(UART_GET_LSR(uart) & THRE)) + cpu_relax(); + + UART_PUT_CHAR(uart, (unsigned char)chr); +} + +static int adi_uart4_serial_poll_get_char(struct uart_port *port) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + unsigned char chr; + + while (!(UART_GET_LSR(uart) & DR)) + cpu_relax(); + + chr = UART_GET_CHAR(uart); + + return chr; +} +#endif + + +static const struct uart_ops adi_uart4_serial_pops = { + .tx_empty = adi_uart4_serial_tx_empty, + .set_mctrl = adi_uart4_serial_set_mctrl, + .get_mctrl = adi_uart4_serial_get_mctrl, + .stop_tx = adi_uart4_serial_stop_tx, + .start_tx = adi_uart4_serial_start_tx, + .stop_rx = adi_uart4_serial_stop_rx, + .enable_ms = adi_uart4_serial_enable_ms, + .break_ctl = adi_uart4_serial_break_ctl, + .startup = adi_uart4_serial_startup, + .shutdown = adi_uart4_serial_shutdown, + .set_termios = adi_uart4_serial_set_termios, + .set_ldisc = adi_uart4_serial_set_ldisc, + .type = adi_uart4_serial_type, + .release_port = adi_uart4_serial_release_port, + .request_port = adi_uart4_serial_request_port, + .config_port = adi_uart4_serial_config_port, + .verify_port = adi_uart4_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = adi_uart4_serial_poll_put_char, + .poll_get_char = adi_uart4_serial_poll_get_char, +#endif +}; + +#ifdef CONFIG_SERIAL_ADI_UART4_CONSOLE +static void adi_uart4_serial_console_putchar(struct uart_port *port, + unsigned char ch) +{ + struct adi_uart4_serial_port *uart = to_adi_serial_port(port); + + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); +} + +static void __init +adi_uart4_serial_console_get_options(struct adi_uart4_serial_port *uart, + int *baud, int *parity, int *bits) +{ + unsigned int status; + + status = UART_GET_IER(uart) & (ERBFI | ETBEI); + if (status == (ERBFI | ETBEI)) { + /* ok, the port was enabled */ + u32 lcr, clk; + + lcr = UART_GET_LCR(uart); + + *parity = 'n'; + if (lcr & PEN) { + if (lcr & EPS) + *parity = 'e'; + else + *parity = 'o'; + } + *bits = ((lcr & WLS_MASK) >> WLS_OFFSET) + 5; + + clk = UART_GET_CLK(uart); + + /* Only the lowest 16 bits are the divisor */ + if (clk & EDBO) + *baud = uart->port.uartclk / (clk & 0xffff); + else + *baud = uart->port.uartclk / (16*clk); + } + pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, + *baud, *parity, *bits); +} +static void +adi_uart4_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct adi_uart4_serial_port *uart = adi_uart4_serial_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, + adi_uart4_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + +} + +static int __init +adi_uart4_serial_console_setup(struct console *co, char *options) +{ + struct adi_uart4_serial_port *uart; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index < 0 || co->index >= ADI_UART_NR_PORTS) + return -ENODEV; + + uart = adi_uart4_serial_ports[co->index]; + if (!uart) + return -ENODEV; + + if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) + flow = 'r'; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + adi_uart4_serial_console_get_options(uart, &baud, &parity, + &bits); + + return uart_set_options(&uart->port, co, baud, parity, bits, flow); +} + +static struct uart_driver adi_uart4_serial_reg; + +static struct console adi_uart4_serial_console = { + .name = "ttySC", + .write = adi_uart4_serial_console_write, + .device = uart_console_device, + .setup = adi_uart4_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &adi_uart4_serial_reg, +}; + +#define ADI_SERIAL_UART4_CONSOLE (&adi_uart4_serial_console) +#else +#define ADI_SERIAL_UART4_CONSOLE NULL +#endif + +static struct uart_driver adi_uart4_serial_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = "ttySC", + .major = TTY_MAJOR, + // Other serial drivers are using 64 -- + // Can probably disable in the future and set this back to 64 + .minor = 74, + .nr = ADI_UART_NR_PORTS, + .cons = ADI_SERIAL_UART4_CONSOLE, +}; + +static int adi_uart4_serial_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct adi_uart4_serial_port *uart = platform_get_drvdata(pdev); + + clk_disable(uart->clk); + return uart_suspend_port(&adi_uart4_serial_reg, &uart->port); +} + +static int adi_uart4_serial_resume(struct platform_device *pdev) +{ + struct adi_uart4_serial_port *uart = platform_get_drvdata(pdev); + int ret; + + ret = clk_enable(uart->clk); + if (ret) + return ret; + + return uart_resume_port(&adi_uart4_serial_reg, &uart->port); +} + +static int adi_uart4_serial_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *res; + struct adi_uart4_serial_port *uart = NULL; + int ret = 0; + int uartid; + struct dma_chan *tx_dma_channel = NULL; + struct dma_chan *rx_dma_channel = NULL; + + dev_info(dev, "Serial probe\n"); + + uartid = of_alias_get_id(np, "serial"); + tx_dma_channel = dma_request_chan(dev, "tx"); + rx_dma_channel = dma_request_chan(dev, "rx"); + + if (uartid < 0) { + dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", + uartid); + ret = -ENODEV; + return ret; + } + + if (adi_uart4_serial_ports[uartid] == NULL) { + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + adi_uart4_serial_ports[uartid] = uart; + uart->dev = &pdev->dev; + + uart->clk = devm_clk_get(dev, "sclk0"); + if (IS_ERR(uart->clk)) + return -ENODEV; + + spin_lock_init(&uart->port.lock); + uart->port.uartclk = clk_get_rate(uart->clk); + uart->port.fifosize = 8; + uart->port.ops = &adi_uart4_serial_pops; + uart->port.line = uartid; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + uart->port.membase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!uart->port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + return -ENXIO; + } + + uart->tx_dma_channel = tx_dma_channel; + uart->rx_dma_channel = rx_dma_channel; + spin_lock_init(&uart->rx_lock); + uart->tx_done = 1; + uart->tx_count = 0; + + if (IS_ERR(tx_dma_channel)) { + uart->tx_irq = platform_get_irq_byname(pdev, "tx"); + uart->rx_irq = platform_get_irq_byname(pdev, "rx"); + uart->status_irq = + platform_get_irq_byname(pdev, "status"); + uart->port.irq = uart->rx_irq; + + ret = devm_request_threaded_irq(dev, uart->rx_irq, + adi_uart4_serial_rx_int, NULL, 0, "ADI UART RX", + uart); + if (ret) { + dev_err(dev, "Unable to attach UART RX int\n"); + return ret; + } + + ret = devm_request_threaded_irq(dev, uart->tx_irq, + adi_uart4_serial_tx_int, NULL, 0, "ADI UART TX", + uart); + if (ret) { + dev_err(dev, "Unable to attach UART TX int\n"); + return ret; + } + } + + /* adi,uart-has-rtscts is deprecated */ + if (of_property_read_bool(np, "uart-has-rtscts") || + of_property_read_bool(np, "adi,uart-has-rtscts")) { + uart->hwflow_mode = ADI_UART_HWFLOW_PERI; + ret = devm_request_threaded_irq(dev, uart->status_irq, + adi_uart4_serial_mctrl_cts_int, NULL, 0, + "ADI UART Modem Status", + uart); + if (ret) { + uart->hwflow_mode = ADI_UART_NO_HWFLOW; + dev_info(dev, + "Unable to attach UART Modem Status int.\n"); + } + } else + uart->hwflow_mode = ADI_UART_NO_HWFLOW; + + uart->edbo = false; + if (of_property_read_bool(np, "adi,use-edbo")) + uart->edbo = true; + + if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) { + uart->hwflow_en_pin = devm_gpiod_get(dev, "hwflow-en", + GPIOD_OUT_HIGH); + if (IS_ERR(uart->hwflow_en_pin)) { + dev_err(dev, + "hwflow-en required in peripheral hwflow mode\n"); + return PTR_ERR(uart->hwflow_en_pin); + } + } + } + + uart = adi_uart4_serial_ports[uartid]; + uart->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, uart); + + ret = uart_add_one_port(&adi_uart4_serial_reg, &uart->port); + if (!ret) + return 0; + + if (uart) { +out_error_unmap: + adi_uart4_serial_ports[uartid] = NULL; + kfree(uart); + } + + return ret; +} + +static void adi_uart4_serial_remove(struct platform_device *pdev) +{ + struct adi_uart4_serial_port *uart = platform_get_drvdata(pdev); + + dev_set_drvdata(&pdev->dev, NULL); + if (uart) { + uart_remove_one_port(&adi_uart4_serial_reg, &uart->port); + adi_uart4_serial_ports[uart->port.line] = NULL; + + if (!IS_ERR(uart->tx_dma_channel)) { + dma_release_channel(uart->tx_dma_channel); + dma_release_channel(uart->rx_dma_channel); + } + kfree(uart); + } +} + +static const struct of_device_id adi_uart_dt_match[] = { + { .compatible = "adi,uart4"}, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_uart_dt_match); + +static struct platform_driver adi_uart4_serial_driver = { + .probe = adi_uart4_serial_probe, + .remove = adi_uart4_serial_remove, + .suspend = adi_uart4_serial_suspend, + .resume = adi_uart4_serial_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = adi_uart_dt_match, + }, +}; + +static int __init adi_uart4_serial_init(void) +{ + int ret; + + pr_info("ADI serial driver\n"); + + ret = uart_register_driver(&adi_uart4_serial_reg); + if (ret) { + pr_err("failed to register %s:%d\n", + adi_uart4_serial_reg.driver_name, ret); + } + + ret = platform_driver_register(&adi_uart4_serial_driver); + if (ret) { + pr_err("fail to register ADI uart\n"); + uart_unregister_driver(&adi_uart4_serial_reg); + } + + return ret; +} + +static void __exit adi_uart4_serial_exit(void) +{ + platform_driver_unregister(&adi_uart4_serial_driver); + uart_unregister_driver(&adi_uart4_serial_reg); +} + +module_init(adi_uart4_serial_init); +module_exit(adi_uart4_serial_exit); + +/* Early Console Support */ +static inline u32 adi_uart_read(struct uart_port *port, u32 off) +{ + return readl(port->membase + off); +} + +static inline void adi_uart_write(struct uart_port *port, u32 val, + u32 off) +{ + writel(val, port->membase + off); +} + +static void adi_uart_wait_bit_set(struct uart_port *port, unsigned int offset, + u32 bit) +{ + while (!(adi_uart_read(port, offset) & bit)) + cpu_relax(); +} + +static void adi_uart_console_putchar(struct uart_port *port, unsigned char ch) +{ + /* wait for the hardware fifo to clear up */ + adi_uart_wait_bit_set(port, OFFSET_STAT, THRE); + + /* queue the character for transmission */ + adi_uart_write(port, ch, OFFSET_THR); +} + +static void adi_uart_early_write(struct console *con, const char *s, + unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, adi_uart_console_putchar); +} + +static int __init adi_uart_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = adi_uart_early_write; + return 0; +} + +EARLYCON_DECLARE(adi_uart, adi_uart_early_console_setup); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 9c007a106330b9..ce3c50dfa5cacd 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -109,6 +109,9 @@ /* Xilinx uartlite */ #define PORT_UARTLITE 74 +/* Blackfin */ +#define PORT_BFIN 75 + /* Broadcom BCM7271 UART */ #define PORT_BCM7271 76 From 7f6e9ec826a34a7e631449f8b97270a867313f1a Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 11:54:23 +0200 Subject: [PATCH 11/85] irqchip: Add PINT PORT driver for ADSP-SC5xx SoCs Signed-off-by: Philip Molloy --- drivers/irqchip/Kconfig | 9 + drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-adi-adsp.c | 316 +++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/irqchip/irq-adi-adsp.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a61c6dc63c29cf..9c3d6f09c5e2b6 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -102,6 +102,15 @@ config ALPINE_MSI select IRQ_MSI_LIB select GENERIC_IRQ_CHIP +config ADI_ADSP_IRQ + bool "ADI PORT PINT Driver" + depends on OF + depends on (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X) + select IRQ_DOMAIN + help + Say Y to enable the PORT-based PINT interrupt controller for + Analog Devices ADSP devices. + config AL_FIC bool "Amazon's Annapurna Labs Fabric Interrupt Controller" depends on OF diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 3de083f5484cc0..039225ed49582c 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IRQCHIP) += irqchip.o + +obj-$(CONFIG_ADI_ADSP_IRQ) += irq-adi-adsp.o obj-$(CONFIG_AL_FIC) += irq-al-fic.o obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o obj-$(CONFIG_ATH79) += irq-ath79-cpu.o diff --git a/drivers/irqchip/irq-adi-adsp.c b/drivers/irqchip/irq-adi-adsp.c new file mode 100644 index 00000000000000..a617002133424c --- /dev/null +++ b/drivers/irqchip/irq-adi-adsp.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * ADSP PINT PORT driver. + * + * The default mapping is used for all PINTs, refer to the HRM to identify + * PORT mapping to PINTs. For example, PINT0 has PORT B (0-15) and PORT A + * (16-31). + * + * Copyright (C) 2022, Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADSP_PINT_IRQS 32 + +/* Register offsets in a single PINT */ +#define ADSP_PINT_REG_MASK_SET 0x00 +#define ADSP_PINT_REG_MASK_CLEAR 0x04 +#define ADSP_PINT_REG_REQUEST 0x08 +#define ADSP_PINT_REG_ASSIGN 0x0c +#define ADSP_PINT_REG_EDGE_SET 0x10 +#define ADSP_PINT_REG_EDGE_CLEAR 0x14 +#define ADSP_PINT_REG_INVERT_SET 0x18 +#define ADSP_PINT_REG_INVERT_CLEAR 0x1c +#define ADSP_PINT_REG_PINSTATE 0x20 +#define ADSP_PINT_REG_LATCH 0x24 + +struct adsp_pint { + struct irq_chip chip; + void __iomem *regs; + struct irq_domain *domain; + unsigned int irq; +}; + +static struct adsp_pint *to_adsp_pint(struct irq_chip *chip) +{ + return container_of(chip, struct adsp_pint, chip); +} + +/** + * Each gpio device should be connected to one of the two valid pints with an + * indicator of which half it is connected to: + * + * pint0 { + * ... + * }; + * gpa { + * adi,pint = <&pint0 1>; + * }; + * gpb { + * adi,pint = <&pint0 0>; + * }; + * + * This relies on the default configuration of the hardware, which we do not + * expose an interface to change. + */ +int adsp_attach_pint_to_gpio(struct adsp_gpio_port *port) +{ + struct platform_device *pint_pdev; + struct device_node *pint_node; + struct adsp_pint *pint; + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_fixed_args(port->dev->of_node, "adi,pint", 1, 0, + &args); + if (ret) { + dev_err(port->dev, "Missing or invalid adi,pint connection for %pOFn; " + "attach a pint instance with one argument for port assignment\n", + port->dev->of_node); + return ret; + } + + pint_node = args.np; + + pint_pdev = of_find_device_by_node(pint_node); + if (!pint_pdev) { + ret = -EPROBE_DEFER; + goto cleanup; + } + + pint = dev_get_drvdata(&pint_pdev->dev); + if (!pint) { + ret = -EPROBE_DEFER; + goto cleanup; + } + + port->irq_domain = pint->domain; + + if (args.args[0]) + port->irq_offset = 16; + else + port->irq_offset = 0; + +cleanup: + of_node_put(pint_node); + return ret; +} + +static void adsp_pint_dispatch_irq(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct adsp_pint *pint = to_adsp_pint(chip); + unsigned int type = irqd_get_trigger_type(&desc->irq_data); + u32 pos = BIT(desc->irq_data.hwirq); + + /* for both edge interrupt, toggle invert bit to catch next edge */ + if (type == IRQ_TYPE_EDGE_BOTH) { + u32 invert = readl(pint->regs + ADSP_PINT_REG_INVERT_SET) & pos; + + if (invert) + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_CLEAR); + else + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_SET); + } + + writel(pos, pint->regs + ADSP_PINT_REG_REQUEST); + + /* either edge is set */ + if (type & IRQ_TYPE_EDGE_BOTH) + handle_edge_irq(desc); + else + handle_level_irq(desc); +} + +static int adsp_pint_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct adsp_pint *pint = domain->host_data; + + irq_set_chip_data(irq, pint); + irq_set_chip_and_handler(irq, &pint->chip, adsp_pint_dispatch_irq); + return 0; +} + +static const struct irq_domain_ops adsp_irq_domain_ops = { + .map = adsp_pint_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +/** + * This handles the GIC interrupt associated with this PINT being activated. + * It chains the interrupt associated with a particular pin + */ +static void adsp_pint_irq_handler(struct irq_desc *desc) +{ + struct adsp_pint *pint = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long req; + int pos; + + chained_irq_enter(chip, desc); + + req = readl(pint->regs + ADSP_PINT_REG_REQUEST); + + for_each_set_bit(pos, &req, 32) { + unsigned int virq = irq_find_mapping(pint->domain, pos); + + if (virq) + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static void adsp_pint_irq_ack(struct irq_data *d) +{ + /* this is required for edge type irqs unconditionally */ +} + +static void adsp_pint_irq_mask(struct irq_data *d) +{ + struct adsp_pint *pint = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), pint->regs + ADSP_PINT_REG_MASK_CLEAR); +} + +static void adsp_pint_irq_unmask(struct irq_data *d) +{ + struct adsp_pint *pint = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), pint->regs + ADSP_PINT_REG_MASK_SET); +} + +static int adsp_pint_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct adsp_pint *pint = irq_data_get_irq_chip_data(d); + unsigned int pos = BIT(d->hwirq); + + switch (type) { + case IRQ_TYPE_PROBE: + type = IRQ_TYPE_EDGE_BOTH; + fallthrough; + case IRQ_TYPE_EDGE_BOTH: + /* start by looking for rising edge */ + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_CLEAR); + writel(pos, pint->regs + ADSP_PINT_REG_EDGE_SET); + break; + + case IRQ_TYPE_EDGE_FALLING: + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_SET); + writel(pos, pint->regs + ADSP_PINT_REG_EDGE_SET); + break; + + case IRQ_TYPE_EDGE_RISING: + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_CLEAR); + writel(pos, pint->regs + ADSP_PINT_REG_EDGE_SET); + break; + + case IRQ_TYPE_LEVEL_HIGH: + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_CLEAR); + writel(pos, pint->regs + ADSP_PINT_REG_EDGE_CLEAR); + break; + + case IRQ_TYPE_LEVEL_LOW: + writel(pos, pint->regs + ADSP_PINT_REG_INVERT_SET); + writel(pos, pint->regs + ADSP_PINT_REG_EDGE_CLEAR); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int adsp_pint_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adsp_pint *pint; + struct resource *res; + + pint = devm_kzalloc(dev, sizeof(*pint), GFP_KERNEL); + if (!pint) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pint->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pint->regs)) { + dev_err(dev, "Could not find address range for interrupt controller\n"); + return PTR_ERR(pint->regs); + } + + pint->chip.name = "adsp-pint"; + pint->chip.irq_ack = adsp_pint_irq_ack; + pint->chip.irq_mask = adsp_pint_irq_mask; + pint->chip.irq_unmask = adsp_pint_irq_unmask; + pint->chip.irq_set_type = adsp_pint_irq_set_type; + // @todo potentially only SEC supports wake options, not gic + + // @todo determine if we actually need a raw spinlock + + pint->domain = irq_domain_add_linear(np, ADSP_PINT_IRQS, + &adsp_irq_domain_ops, pint); + if (!pint->domain) { + dev_err(dev, "Could not create irq domain\n"); + return -EINVAL; + } + + pint->irq = platform_get_irq(pdev, 0); + if (!pint->irq) { + dev_err(dev, "Could not find parent interrupt for port\n"); + return -EINVAL; + } + + irq_set_chained_handler_and_data(pint->irq, adsp_pint_irq_handler, pint); + platform_set_drvdata(pdev, pint); + + return 0; +} + +static void adsp_pint_remove(struct platform_device *pdev) +{ + struct adsp_pint *pint = platform_get_drvdata(pdev); + + irq_set_chained_handler_and_data(pint->irq, NULL, NULL); + irq_domain_remove(pint->domain); +} + +static const struct of_device_id adsp_pint_of_match[] = { + { .compatible = "adi,adsp-pint" }, + { } +}; +MODULE_DEVICE_TABLE(of, adsp_pint_of_match); + +static struct platform_driver adsp_pint_driver = { + .driver = { + .name = "adsp-port-pint", + .of_match_table = adsp_pint_of_match, + }, + .probe = adsp_pint_probe, + .remove = adsp_pint_remove, +}; + +static int __init adsp_pint_init(void) +{ + return platform_driver_register(&adsp_pint_driver); +} + +arch_initcall(adsp_pint_init); From 62e9f23dab366b323f8b28723a556fddc02cd38a Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:56:22 +0200 Subject: [PATCH 12/85] dt-bindings: clock: Add ADSP-SC5xx clock bindings Signed-off-by: Philip Molloy --- .../bindings/clock/adi,sc5xx-clocks.yaml | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml diff --git a/Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml b/Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml new file mode 100644 index 00000000000000..89d01700913940 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/adi,sc5xx-clocks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Clock Tree Drivers for Analog Devices SC5XX Processors + +maintainers: + - Nathan Barrett-Morrison + - Greg Malysa + +description: | + These drivers read in the processors CDU (clock distribution unit) + and CGU (clock generation unit) values to determine various clock + rates + +properties: + compatible: + enum: + - adi,sc57x-clocks # 32-Bit SC573 processor + - adi,sc58x-clocks # 32-Bit SC584, SC589 processors + - adi,sc594-clocks # 32-Bit SC594 processor + - adi,sc598-clocks # 64-Bit SC598 processor + + '#clock-cells': + const: 1 + + reg: + minItems: 3 + maxItems: 4 + + clocks: + description: + Specifies the CLKIN0 and CLKIN1 reference clock(s) from which the + output frequencies are derived via CDU+CGU + minItems: 2 + maxItems: 2 + + clock-names: + description: + String reference names for CLKIN0 and CLKIN1 + minItems: 2 + maxItems: 2 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + - clock-names + +additionalProperties: false + +examples: + - | + clocks@3108d000 { + compatible = "adi,sc57x-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + - | + clocks@3108d000 { + compatible = "adi,sc58x-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + - | + clocks@3108d000 { + compatible = "adi,sc594-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + - | + clocks@3108d000 { + compatible = "adi,sc598-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>, + <0x310a9000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; From 5361698b99437a7c869466e21c279e9432b29879 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:19:21 +0200 Subject: [PATCH 13/85] mtd: spi-nor: issi: Add support for is25lp512 Signed-off-by: Philip Molloy --- drivers/mtd/spi-nor/issi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 18d9a00aa22eb2..be79de52c3f16d 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -105,6 +105,13 @@ static const struct flash_info issi_nor_parts[] = { .name = "is25lp256", .fixups = &is25lp256_fixups, .fixup_flags = SPI_NOR_4B_OPCODES, + }, { + .id = SNOR_ID(0x9d, 0x60, 0x1a), + .name = "is25lp512", + .size = SZ_64M, + .fixups = &is25lp256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { .id = SNOR_ID(0x9d, 0x70, 0x16), .name = "is25wp032", From 054c2def2d88caf4b802429656facc1ee5bf45ff Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:26:15 +0200 Subject: [PATCH 14/85] pinctrl: Add support for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 2 + drivers/pinctrl/adi/Kconfig | 24 + drivers/pinctrl/adi/Makefile | 3 + drivers/pinctrl/adi/pinctrl-adsp.c | 915 +++++++++++++++++++++++++ include/dt-bindings/pinctrl/adi-adsp.h | 25 + 6 files changed, 970 insertions(+) create mode 100644 drivers/pinctrl/adi/Kconfig create mode 100644 drivers/pinctrl/adi/Makefile create mode 100644 drivers/pinctrl/adi/pinctrl-adsp.c create mode 100644 include/dt-bindings/pinctrl/adi-adsp.h diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4f8507ebbdacdd..bb272f23c9b76f 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -681,6 +681,7 @@ config PINCTRL_RP1 Enable the gpio and pinctrl/mux driver for RaspberryPi RP1 multi function device. +source "drivers/pinctrl/adi/Kconfig" source "drivers/pinctrl/actions/Kconfig" source "drivers/pinctrl/aspeed/Kconfig" source "drivers/pinctrl/bcm/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index e0cfb9b7c99bae..b14ef5efde9289 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -64,6 +64,8 @@ obj-$(CONFIG_PINCTRL_UPBOARD) += pinctrl-upboard.o obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o + +obj-y += adi/ obj-y += actions/ obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-y += bcm/ diff --git a/drivers/pinctrl/adi/Kconfig b/drivers/pinctrl/adi/Kconfig new file mode 100644 index 00000000000000..2bdde2a63f2656 --- /dev/null +++ b/drivers/pinctrl/adi/Kconfig @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-only + +config PINCTRL_ADSP + bool + depends on OF + select PINMUX + select GENERIC_PINCONF + +config PINCTRL_ADSP_SC5XX + bool "ADSP-SC5XX pinctrl driver" + depends on (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X) + select PINCTRL_ADSP + help + Say Y here to enable the ADSP-SC5XX pinctrl driver. This is required for + correct peripheral functionality on the SoC. + correct peripheral functionality on the SoC. + +config SRUCTRL_ADSP_SC5XX + bool "ADSP-SC5XX SRU control driver" + depends on (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X) + select SRUCTRL_ADSP + help + Say Y here to enable the ADSP-SC5XX SRU control driver. This is required for + SRU muxing functionaility on the SoC. diff --git a/drivers/pinctrl/adi/Makefile b/drivers/pinctrl/adi/Makefile new file mode 100644 index 00000000000000..6bdd3a212a593b --- /dev/null +++ b/drivers/pinctrl/adi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +obj-$(CONFIG_PINCTRL_ADSP) += pinctrl-adsp.o \ No newline at end of file diff --git a/drivers/pinctrl/adi/pinctrl-adsp.c b/drivers/pinctrl/adi/pinctrl-adsp.c new file mode 100644 index 00000000000000..d54aac0d4de54a --- /dev/null +++ b/drivers/pinctrl/adi/pinctrl-adsp.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices ADSP family pinctrl driver. + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Author: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinctrl-utils.h" + +/* Convert from pinmux constants in device tree to actual settings */ +#define ADSP_PINMUX_PIN(p) ((p & 0xffffff00) >> 8) +#define ADSP_PINMUX_FUNC(p) (p & 0xff) + +/* Details of the PORT_MUX register */ +#define ADSP_PORT_PORT_MUX_BITS 2 +#define ADSP_PORT_PORT_MUX_MASK GENMASK(1, 0) + +/* Number of pin alternate functions, see pin_functions array */ +#define ADSP_NUMBER_OF_PIN_FUNCTIONS ARRAY_SIZE(pin_functions) + +/* Information for drive strength registers */ +#define ADSP_PADS_DS_BITS 3 +#define ADSP_PADS_DS_PINS_PER_REG 8 +#define ADSP_PADS_DS_HIGH 2 +#define ADSP_PADS_DS_LOW 1 + +/* Information for pull up/pull down enable registers */ +#define ADSP_PADS_PUD_PINS_PER_REG 16 + +#define ADSP_PADS_REG_PCFG0 0x04 +#define ADSP_PADS_REG_PCFG1 0x08 +/* Convert from pin number (e.g. 0-143) to drive strength register offset */ +#define ADSP_PADS_PORTx_DS(p) (0x0c + 0x04*(p/ADSP_PADS_DS_PINS_PER_REG)) +#define ADSP_PADS_NONPORTS_DS 0x50 +/* Convert from pin number to pull up enable register offset */ +#define ADSP_PADS_PORTx_PUE(p) (0x98 + 0x04*(p/ADSP_PADS_PUD_PINS_PER_REG)) +/* Convert from pin number to pull down enable register offset */ +#define ADSP_PADS_PORTx_PDE(p) (0xc4 + 0x04*(p/ADSP_PADS_PUD_PINS_PER_REG)) + +/* Non GPIO PORT drive strength settings */ +#define ADSP_NONPORTS_DS_CKOUT 0 +#define ADSP_NONPORTS_DS_RESOUTB 1 +#define ADSP_NONPORTS_DS_FAULTB 2 +#define ADSP_NONPORTS_DS_LP1CK 3 +#define ADSP_NONPORTS_DS_LP0CK 4 +#define ADSP_NONPORTS_DS_OSPI 5 + +/* DAI pad configuration offsets */ +#define ADSP_PADS_REG_DAI0_0_DS 0x78 +#define ADSP_PADS_REG_DAI0_1_DS 0x7c +#define ADSP_PADS_REG_DAI1_0_DS 0x80 +#define ADSP_PADS_REG_DAI1_1_DS 0x84 + +#define ADSP_PADS_REG_DAI0_PUE 0xbc +#define ADSP_PADS_REG_DAI1_PUE 0xc0 +#define ADSP_PADS_REG_DAI0_PDE 0xfc +#define ADSP_PADS_REG_DAI1_PDE 0x100 + +/* + * Represents a function setting for pins, controls the mux modes essentially + */ +struct adsp_pin_function { + const char *name; + /* 0 for gpio, 1-4 for alt functions 0-3 */ + u8 mode; +}; + +/* + * Available pin function settings in the pin mux for GPIO-associated pins + */ +static const struct adsp_pin_function pin_functions[] = { + { + .name = "gpio", + .mode = 0, + }, { + .name = "alt0", + .mode = 1, + }, { + .name = "alt1", + .mode = 2, + }, { + .name = "alt2", + .mode = 3, + }, { + .name = "alt3", + .mode = 4, + } +}; + +/* + * One pinctrl instance per chip, unifies the interface to the port mux and pad + * conf registers in the PORT instances + * @todo pads registers should be routed through system configuration abstraction + * to remove the need for feature testing/listing "missing" registers here + */ +struct adsp_pinctrl { + struct device *dev; + struct pinctrl_dev *pin_dev; + void __iomem *regs; + const char **group_names; + unsigned int *pins; + spinlock_t lock; + size_t num_ports; + u32 *pin_counts; + u32 total_pins; + + /* Are the drive strength registers missing on this part? */ + bool ds_missing; + + /* Are the pull up/down enable registers missing on this part? */ + bool pude_missing; +}; + +/* + * Custom pinconf properties + */ +#define ADSP_PIN_CONFIG_TRU_TOGGLE (PIN_CONFIG_END+1) + +static const struct pinconf_generic_params adsp_custom_bindings[] = { + /* Configure this pin as a toggle pin which flip each time a trigger event + * is received by the pin controller from the TRU + */ + {"adi,tru-toggle", ADSP_PIN_CONFIG_TRU_TOGGLE, 0} +}; + +static const struct pin_config_item adsp_conf_items[] = { + PCONFDUMP(ADSP_PIN_CONFIG_TRU_TOGGLE, "tru-toggle", NULL, false), +}; + +/* does not need lock */ +static void adsp_set_pin_gpio(struct adsp_gpio_port *port, unsigned int offset, bool gpio) +{ + if (gpio) + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_FER_CLEAR); + else + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_FER_SET); +} + +/* + * Configure a pin either for gpio or an alternate function + */ +static void adsp_portmux_setup(struct adsp_gpio_port *port, unsigned int offset, + const struct adsp_pin_function *func) +{ + if (func->mode == 0) { + adsp_set_pin_gpio(port, offset, true); + } else { + unsigned long flags; + u32 val; + u32 f = (func->mode - 1) & ADSP_PORT_PORT_MUX_MASK; + + spin_lock_irqsave(&port->lock, flags); + + val = __adsp_gpio_readl(port, ADSP_PORT_REG_PORT_MUX); + val &= ~(ADSP_PORT_PORT_MUX_MASK << (ADSP_PORT_PORT_MUX_BITS * offset)); + val |= f << (ADSP_PORT_PORT_MUX_BITS * offset); + __adsp_gpio_writel(port, val, ADSP_PORT_REG_PORT_MUX); + + spin_unlock_irqrestore(&port->lock, flags); + + adsp_set_pin_gpio(port, offset, false); + } +} + +/* pin control operations */ +static int adsp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return adsp_pinctrl->total_pins; +} + +static const char *adsp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return adsp_pinctrl->group_names[selector]; +} + +static int adsp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, + const unsigned int **pins, unsigned int *num_pins) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + *pins = &adsp_pinctrl->pins[selector]; + *num_pins = 1; + return 0; +} + +static int adsp_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + const char *group; + unsigned long *configs; + unsigned int num_configs, num_pins; + unsigned int reserve = 0; + u32 pinmux; + int ret; + + num_pins = of_property_count_u32_elems(np, "pinmux"); + if (num_pins <= 0) { + dev_err(adsp_pinctrl->dev, "Must have at least one `pinmux` entry in %pOFn.\n", + np); + return -EINVAL; + } + + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); + if (ret) + return ret; + + /* One configuration for the whole group, potentially */ + reserve = num_pins; + if (num_configs) + reserve = reserve * 2; + + ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, reserve); + if (ret) + goto exit; + + of_property_for_each_u32(np, "pinmux", pinmux) { + u32 pin = ADSP_PINMUX_PIN(pinmux); + u32 func = ADSP_PINMUX_FUNC(pinmux); + + if (func >= ADSP_NUMBER_OF_PIN_FUNCTIONS) { + dev_err(adsp_pinctrl->dev, + "Function number %d is not available for pin %d in %pOFn.n\n", + func, pin, np); + goto exit; + } + + group = adsp_pinctrl->group_names[pin]; + ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps, + group, pin_functions[func].name); + if (ret) + goto exit; + + if (num_configs) { + ret = pinctrl_utils_add_map_configs(pctldev, map, reserved_maps, num_maps, + group, configs, num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); + if (ret) + goto exit; + } + } + +exit: + kfree(configs); + return ret; +} + +/** + * Handle device tree structures like: + * + * pinctrl_uart0_hwflow: uart0_hwflow_pins { + * pins_rxtx_ { + * pinmux = <1>, <2>; + * some-padconf-flag; + * }; + * pins_hwflow { + * pinmux = <3>, <4>; + * some-other-padconf-flag; + * }; + * }; + * + * where &pinctrl_uart0_hwflow is passed as an entry in pinctrl-0 on uart driver and + * enables all sub-pins at once + */ +static int adsp_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, struct pinctrl_map **map, unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *child_np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np, child_np) { + ret = adsp_pinctrl_dt_subnode_to_map(pctldev, child_np, map, + &reserved_maps, num_maps); + if (ret < 0) + goto exit; + } + return 0; + +exit: + pinctrl_utils_free_map(pctldev, *map, *num_maps); + return ret; +} + +static const struct pinctrl_ops adsp_pctlops = { + .get_groups_count = adsp_pinctrl_get_groups_count, + .get_group_name = adsp_pinctrl_get_group_name, + .get_group_pins = adsp_pinctrl_get_group_pins, + .dt_node_to_map = adsp_pinctrl_dt_node_to_map, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +/* pin mux operations */ +static int adsp_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ADSP_NUMBER_OF_PIN_FUNCTIONS; +} + +static const char *adsp_pinmux_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return pin_functions[selector].name; +} + +static int adsp_pinmux_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, const char * const **groups, unsigned * const num_groups) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = adsp_pinctrl->group_names; + *num_groups = adsp_pinctrl->total_pins; + return 0; +} + +/* Each group is exactly 1 pin and group id == pin id */ +static int adsp_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int func, + unsigned int group) +{ + struct adsp_gpio_port *port; + struct pinctrl_gpio_range *range; + u32 offset; + + range = pinctrl_find_gpio_range_from_pin(pctldev, group); + if (!range || !range->gc) + return -EPROBE_DEFER; + + offset = group - range->pin_base; + + port = to_adsp_gpio_port(range->gc); + adsp_portmux_setup(port, offset, &pin_functions[func]); + + return 0; +} + +static int adsp_pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int pin) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(range->gc); + u32 offset = pin - range->pin_base; + + adsp_set_pin_gpio(port, offset, true); + return 0; +} + +static void adsp_pinmux_release_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int pin) +{ + struct adsp_gpio_port *port = to_adsp_gpio_port(range->gc); + u32 offset = pin - range->pin_base; + + adsp_set_pin_gpio(port, offset, false); +} + +static const struct pinmux_ops adsp_pmxops = { + .get_functions_count = adsp_pinmux_get_functions_count, + .get_function_name = adsp_pinmux_get_function_name, + .get_function_groups = adsp_pinmux_get_function_groups, + .set_mux = adsp_pinmux_set_mux, + .gpio_request_enable = adsp_pinmux_request_gpio, + .gpio_disable_free = adsp_pinmux_release_gpio, +}; + +/* pin configuration operations */ +static bool __adsp_pinconf_is_pue(struct adsp_pinctrl *p, unsigned int pin) +{ + u32 offset = ADSP_PADS_PORTx_PUE(pin); + u32 val, bit; + + if (p->pude_missing) + return 0; + + val = readl(p->regs + offset); + bit = BIT(pin & (ADSP_PADS_PUD_PINS_PER_REG-1)); + return !!(val & bit); +} + +static bool __adsp_pinconf_is_pde(struct adsp_pinctrl *p, unsigned int pin) +{ + u32 offset = ADSP_PADS_PORTx_PDE(pin); + u32 val, bit; + + if (p->pude_missing) + return 0; + + val = readl(p->regs + offset); + bit = BIT(pin & (ADSP_PADS_PUD_PINS_PER_REG-1)); + return !!(val & bit); +} + +static u32 __adsp_pinconf_get_ds(struct adsp_pinctrl *p, unsigned int pin) +{ + u32 offset = ADSP_PADS_PORTx_DS(pin); + u32 val, shift, mask; + + if (p->ds_missing) + return 0; + + val = readl(p->regs + offset); + shift = (pin & (ADSP_PADS_DS_PINS_PER_REG-1)) * ADSP_PADS_DS_BITS; + mask = GENMASK(ADSP_PADS_DS_BITS-1, 0) << shift; + val = val & mask; + + if (val == ADSP_PADS_DS_HIGH) + return 1; + return 0; +} + +/* seems we return -EINVAL for disabled static option, -ENOTSUPP for not supported, + * and otherwise the argument is included in config + */ +static int adsp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + struct pinctrl_gpio_range *range; + struct adsp_gpio_port *port; + u32 offset, val; + u32 param = pinconf_to_config_param(*config); + u32 arg = 0; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (__adsp_pinconf_is_pue(adsp_pinctrl, pin) || + __adsp_pinconf_is_pde(adsp_pinctrl, pin)) + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!__adsp_pinconf_is_pde(adsp_pinctrl, pin)) + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (!__adsp_pinconf_is_pue(adsp_pinctrl, pin)) + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + arg = __adsp_pinconf_get_ds(adsp_pinctrl, pin); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + if (!(port->open_drain & BIT(offset))) + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + if (port->open_drain & BIT(offset)) + return -EINVAL; + break; + case ADSP_PIN_CONFIG_TRU_TOGGLE: + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + val = __adsp_gpio_readl(port, ADSP_PORT_REG_TRIG_TGL); + if (!(val & BIT(offset))) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static void __adsp_pinconf_pue(struct adsp_pinctrl *p, unsigned int pin, bool state) +{ + u32 offset = ADSP_PADS_PORTx_PUE(pin); + u32 val, bit; + + if (p->pude_missing) { + dev_warn(p->dev, + "Pull Up Enable is not supported by this PADS HW (tried to set PUE for pin %d)\n", + pin); + return; + } + + val = readl(p->regs + offset); + bit = BIT(pin & (ADSP_PADS_PUD_PINS_PER_REG-1)); + + if (state) + writel(val | bit, p->regs + offset); + else + writel(val & ~bit, p->regs + offset); +} + +static void __adsp_pinconf_pde(struct adsp_pinctrl *p, unsigned int pin, bool state) +{ + u32 offset = ADSP_PADS_PORTx_PDE(pin); + u32 val, bit; + + if (p->pude_missing) { + dev_warn(p->dev, + "Pull Down Enable is not supported by this PADS HW (tried to set PDE for pin %d)\n", + pin); + return; + } + + val = readl(p->regs + offset); + bit = BIT(pin & (ADSP_PADS_PUD_PINS_PER_REG-1)); + + if (state) + writel(val | bit, p->regs + offset); + else + writel(val & ~bit, p->regs + offset); +} + +static void __adsp_pinconf_ds(struct adsp_pinctrl *p, unsigned int pin, bool high) +{ + u32 offset = ADSP_PADS_PORTx_DS(pin); + u32 val, shift, mask; + + if (p->ds_missing) { + dev_warn(p->dev, + "Drive strength is not supported by this PADS HW (tried to set drive strength for pin %d)\n", + pin); + return; + } + + val = readl(p->regs + offset); + shift = (pin & (ADSP_PADS_DS_PINS_PER_REG-1)) * ADSP_PADS_DS_BITS; + mask = GENMASK(ADSP_PADS_DS_BITS-1, 0) << shift; + val = val & ~mask; + + if (high) + writel(val | (ADSP_PADS_DS_HIGH << shift), p->regs + offset); + else + writel(val | (ADSP_PADS_DS_LOW << shift), p->regs + offset); +} + +static int adsp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config, unsigned int num_configs) +{ + struct adsp_pinctrl *adsp_pinctrl = pinctrl_dev_get_drvdata(pctldev); + struct pinctrl_gpio_range *range; + struct adsp_gpio_port *port; + u32 param, arg, val; + u32 offset; + int cfg; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&adsp_pinctrl->lock, flags); + + for (cfg = 0; cfg < num_configs; ++cfg) { + param = pinconf_to_config_param(config[cfg]); + arg = pinconf_to_config_argument(config[cfg]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + __adsp_pinconf_pue(adsp_pinctrl, pin, false); + __adsp_pinconf_pde(adsp_pinctrl, pin, false); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + __adsp_pinconf_pde(adsp_pinctrl, pin, !!arg); + break; + case PIN_CONFIG_BIAS_PULL_UP: + __adsp_pinconf_pue(adsp_pinctrl, pin, !!arg); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* This only supports high/low-speed drive strength (see HRM) + * so assume any positive value means we would like high-speed strength + */ + __adsp_pinconf_ds(adsp_pinctrl, pin, !!arg); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + range = pinctrl_find_gpio_range_from_pin(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + spin_lock(&port->lock); + val = __adsp_gpio_readw(port, ADSP_PORT_REG_DATA); + val &= BIT(offset); + + if (val) { + /* open drain with value of 1 => configure as input */ + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_SET); + } else { + /* open drain with value of 0 => configure as output, drive 0 */ + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DATA_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_SET); + } + + port->open_drain |= BIT(offset); + spin_unlock(&port->lock); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + range = pinctrl_find_gpio_range_from_pin(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + spin_lock(&port->lock); + + /* + * by default make the pin an input when exiting open drain mode; + * user can correct later with GPIO in/out configuration + */ + if (port->open_drain & BIT(offset)) { + port->open_drain &= ~BIT(offset); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_DIR_CLEAR); + __adsp_gpio_writew(port, BIT(offset), ADSP_PORT_REG_INEN_SET); + } + + spin_unlock(&port->lock); + break; + case ADSP_PIN_CONFIG_TRU_TOGGLE: + range = pinctrl_find_gpio_range_from_pin(pctldev, pin); + offset = pin - range->pin_base; + port = to_adsp_gpio_port(range->gc); + + spin_lock(&port->lock); + val = __adsp_gpio_readl(port, ADSP_PORT_REG_TRIG_TGL); + val |= BIT(offset); + __adsp_gpio_writel(port, val, ADSP_PORT_REG_TRIG_TGL); + spin_unlock(&port->lock); + break; + default: + ret = -EOPNOTSUPP; + goto end; + } + } + +end: + spin_unlock_irqrestore(&adsp_pinctrl->lock, flags); + return ret; +} + +/* Config for all pins must match or we have an error regarding group structure */ +static int adsp_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins, i; + unsigned long first; + int ret; + + ret = adsp_pinctrl_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; ++i) { + ret = adsp_pinconf_get(pctldev, pins[i], config); + if (ret) + return ret; + + if (i == 0) + first = *config; + + if (first != *config) + return -EOPNOTSUPP; + } + + return 0; +} + +static int adsp_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int group, + unsigned long *configs, unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins, i; + int ret; + + ret = adsp_pinctrl_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; ++i) { + ret = adsp_pinconf_set(pctldev, pins[i], configs, num_configs); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinconf_ops adsp_confops = { + .is_generic = true, + .pin_config_get = adsp_pinconf_get, + .pin_config_set = adsp_pinconf_set, + .pin_config_group_get = adsp_pinconf_group_get, + .pin_config_group_set = adsp_pinconf_group_set, +#ifdef CONFIG_DEBUG_FS + .pin_config_config_dbg_show = pinconf_generic_dump_config, +#endif +}; + +/* + * We want to make one group per pin so that we can refer to the pins by group + * later on when mux assignments are made + */ +static int adsp_pinctrl_init_groups(struct adsp_pinctrl *adsp_pinctrl, + struct pinctrl_desc *desc) +{ + struct device *dev = adsp_pinctrl->dev; + struct pinctrl_pin_desc *all_pins; + size_t port, pin; + unsigned int i, pin_total; + int num_ports; + int ret; + + num_ports = of_property_count_u32_elems(dev->of_node, "adi,port-sizes"); + + if (num_ports < 0) + return num_ports; + + if (num_ports == 0) { + dev_err(dev, "pinctrl missing `adi,port-sizes` port size definition\n"); + return -ENOENT; + } + + adsp_pinctrl->num_ports = num_ports; + + adsp_pinctrl->pin_counts = devm_kcalloc(dev, sizeof(*adsp_pinctrl->pin_counts), + num_ports, GFP_KERNEL); + if (!adsp_pinctrl->pin_counts) + return -ENOMEM; + + ret = of_property_read_u32_array(dev->of_node, "adi,port-sizes", + adsp_pinctrl->pin_counts, num_ports); + if (ret) + return ret; + + pin_total = 0; + + for (i = 0; i < num_ports; ++i) + pin_total += adsp_pinctrl->pin_counts[i]; + + adsp_pinctrl->total_pins = pin_total; + + all_pins = devm_kcalloc(dev, sizeof(*all_pins), adsp_pinctrl->total_pins, + GFP_KERNEL); + + adsp_pinctrl->pins = devm_kcalloc(dev, sizeof(adsp_pinctrl->pins), + adsp_pinctrl->total_pins, GFP_KERNEL); + if (!adsp_pinctrl->pins) + return -ENOMEM; + + adsp_pinctrl->group_names = devm_kcalloc(dev, sizeof(*adsp_pinctrl->group_names), + adsp_pinctrl->total_pins, GFP_KERNEL); + if (!adsp_pinctrl->group_names) + return -ENOMEM; + + i = 0; + for (port = 0; port < adsp_pinctrl->num_ports; ++port) { + for (pin = 0; pin < adsp_pinctrl->pin_counts[port]; ++pin) { + adsp_pinctrl->group_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "p%c%zu", (char) ('A' + port), pin); + adsp_pinctrl->pins[i] = i; + + all_pins[i].name = adsp_pinctrl->group_names[i]; + all_pins[i].number = i; + i += 1; + } + } + + desc->pins = all_pins; + desc->npins = adsp_pinctrl->total_pins; + + return 0; +} + +static void adsp_set_nongpio_ds(struct adsp_pinctrl *p, int type, bool high) +{ + u32 val = readl(p->regs + ADSP_PADS_NONPORTS_DS); + u32 shift = ADSP_PADS_DS_BITS * type; + u32 mask = GENMASK(ADSP_PADS_DS_BITS-1, 0) << shift; + + val = val & ~mask; + + if (high) + writel(val | (ADSP_PADS_DS_HIGH << shift), p->regs + ADSP_PADS_NONPORTS_DS); + else + writel(val | (ADSP_PADS_DS_LOW << shift), p->regs + ADSP_PADS_NONPORTS_DS); +} + +static int adsp_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adsp_pinctrl *adsp_pinctrl; + struct pinctrl_desc *pnctrl_desc; + struct resource *res; + u32 val; + int ret; + + adsp_pinctrl = devm_kzalloc(dev, sizeof(*adsp_pinctrl), GFP_KERNEL); + if (!adsp_pinctrl) + return -ENOMEM; + + adsp_pinctrl->dev = dev; + pnctrl_desc = devm_kzalloc(dev, sizeof(*pnctrl_desc), GFP_KERNEL); + if (!pnctrl_desc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adsp_pinctrl->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(adsp_pinctrl->regs)) + return PTR_ERR(adsp_pinctrl->regs); + + /* Different features are available in different hw revisions; no way to read this + * from an ID register so the missing features need to be specified in dts + */ + adsp_pinctrl->ds_missing = of_property_read_bool(np, "adi,no-drive-strength"); + adsp_pinctrl->pude_missing = of_property_read_bool(np, "adi,no-pull-up-down"); + + /* Only if requested, adjust non-port drive strengths */ + ret = of_property_read_u32(np, "adi,clkout-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_CKOUT, !!val); + + ret = of_property_read_u32(np, "adi,resoutb-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_RESOUTB, !!val); + + ret = of_property_read_u32(np, "adi,faultb-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_FAULTB, !!val); + + ret = of_property_read_u32(np, "adi,lp1ck-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_LP1CK, !!val); + + ret = of_property_read_u32(np, "adi,lp0ck-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_LP0CK, !!val); + + ret = of_property_read_u32(np, "adi,ospi-drive-strength", &val); + if (!ret) + adsp_set_nongpio_ds(adsp_pinctrl, ADSP_NONPORTS_DS_OSPI, !!val); + + pnctrl_desc->name = dev_name(dev); + pnctrl_desc->pctlops = &adsp_pctlops; + pnctrl_desc->confops = &adsp_confops; + pnctrl_desc->pmxops = &adsp_pmxops; + pnctrl_desc->owner = THIS_MODULE; + pnctrl_desc->num_custom_params = ARRAY_SIZE(adsp_custom_bindings); + pnctrl_desc->custom_params = adsp_custom_bindings; + pnctrl_desc->custom_conf_items = adsp_conf_items; + + spin_lock_init(&adsp_pinctrl->lock); + ret = adsp_pinctrl_init_groups(adsp_pinctrl, pnctrl_desc); + if (ret) + return ret; + + ret = devm_pinctrl_register_and_init(dev, pnctrl_desc, adsp_pinctrl, + &adsp_pinctrl->pin_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, adsp_pinctrl); + ret = pinctrl_enable(adsp_pinctrl->pin_dev); + return ret; +} + +static const struct of_device_id adsp_pinctrl_of_match[] = { + { .compatible = "adi,adsp-pinctrl", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adsp_pinctrl_of_match); + +static struct platform_driver adsp_pinctrl_driver = { + .driver = { + .name = "adsp-pinctrl", + .of_match_table = adsp_pinctrl_of_match, + .suppress_bind_attrs = true, + }, + .probe = adsp_pinctrl_probe, +}; + +static int __init adsp_pinctrl_init(void) +{ + return platform_driver_register(&adsp_pinctrl_driver); +} + +/* + * We want the pinctrl driver to be available at arch init time not at the + * later device init time + */ +arch_initcall(adsp_pinctrl_init); diff --git a/include/dt-bindings/pinctrl/adi-adsp.h b/include/dt-bindings/pinctrl/adi-adsp.h new file mode 100644 index 00000000000000..2f7c84b1828b94 --- /dev/null +++ b/include/dt-bindings/pinctrl/adi-adsp.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Macros for populating pinmux properties on the pincontroller + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef DT_BINDINGS_PINCTRL_ADI_ADSP_H +#define DT_BINDINGS_PINCTRL_ADI_ADSP_H + +#define ADI_ADSP_PINFUNC_GPIO 0 +#define ADI_ADSP_PINFUNC_ALT0 1 +#define ADI_ADSP_PINFUNC_ALT1 2 +#define ADI_ADSP_PINFUNC_ALT2 3 +#define ADI_ADSP_PINFUNC_ALT3 4 + +#define ADI_ADSP_PINMUX(port, pin, func) ((((port - 'A')*16 + pin) << 8) + func) + +#endif From c8bfe4fd90b879c71864868147f6a379b36d030c Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:51:10 +0200 Subject: [PATCH 15/85] pinctrl: Add SRU control driver for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/pinctrl/adi/Kconfig | 7 +- drivers/pinctrl/adi/Makefile | 3 +- drivers/pinctrl/adi/sru-ctrl-adsp.c | 1632 ++++++++++++++++++++ drivers/pinctrl/adi/sru-ctrl-adsp.h | 140 ++ include/dt-bindings/pinctrl/adi-adsp-sru.h | 701 +++++++++ 5 files changed, 2481 insertions(+), 2 deletions(-) create mode 100644 drivers/pinctrl/adi/sru-ctrl-adsp.c create mode 100644 drivers/pinctrl/adi/sru-ctrl-adsp.h create mode 100644 include/dt-bindings/pinctrl/adi-adsp-sru.h diff --git a/drivers/pinctrl/adi/Kconfig b/drivers/pinctrl/adi/Kconfig index 2bdde2a63f2656..adccc51728fc51 100644 --- a/drivers/pinctrl/adi/Kconfig +++ b/drivers/pinctrl/adi/Kconfig @@ -6,6 +6,12 @@ config PINCTRL_ADSP select PINMUX select GENERIC_PINCONF +config SRUCTRL_ADSP + bool + depends on OF + select PINMUX + select GENERIC_PINCONF + config PINCTRL_ADSP_SC5XX bool "ADSP-SC5XX pinctrl driver" depends on (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X) @@ -13,7 +19,6 @@ config PINCTRL_ADSP_SC5XX help Say Y here to enable the ADSP-SC5XX pinctrl driver. This is required for correct peripheral functionality on the SoC. - correct peripheral functionality on the SoC. config SRUCTRL_ADSP_SC5XX bool "ADSP-SC5XX SRU control driver" diff --git a/drivers/pinctrl/adi/Makefile b/drivers/pinctrl/adi/Makefile index 6bdd3a212a593b..86e914c0f232bf 100644 --- a/drivers/pinctrl/adi/Makefile +++ b/drivers/pinctrl/adi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -obj-$(CONFIG_PINCTRL_ADSP) += pinctrl-adsp.o \ No newline at end of file +obj-$(CONFIG_PINCTRL_ADSP) += pinctrl-adsp.o +obj-$(CONFIG_SRUCTRL_ADSP) += sru-ctrl-adsp.o \ No newline at end of file diff --git a/drivers/pinctrl/adi/sru-ctrl-adsp.c b/drivers/pinctrl/adi/sru-ctrl-adsp.c new file mode 100644 index 00000000000000..68d11092adcee2 --- /dev/null +++ b/drivers/pinctrl/adi/sru-ctrl-adsp.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices ADSP family SRU control driver. + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinctrl-utils.h" +#include "sru-ctrl-adsp.h" + +static const struct dai_destination dai0_destinations[] = { + { "SPT0_ACLK_I", 0x5, 0x00, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT0_BCLK_I", 0x5, 0x01, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT1_ACLK_I", 0x5, 0x02, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT1_BCLK_I", 0x5, 0x03, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT2_ACLK_I", 0x5, 0x04, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT2_BCLK_I", 0x5, 0x05, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SRC0_CLK_IP_I", 0x5, 0x00, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC0_CLK_OP_I", 0x5, 0x01, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC1_CLK_IP_I", 0x5, 0x02, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC1_CLK_OP_I", 0x5, 0x03, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC2_CLK_IP_I", 0x5, 0x04, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC2_CLK_OP_I", 0x5, 0x05, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC3_CLK_IP_I", 0x5, 0x00, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "SRC3_CLK_OP_I", 0x5, 0x01, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "SPDIF0_TX_CLK_I", 0x5, 0x02, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "PDM0_CLK0_I", 0x5, 0x03, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, GROUP_A }, //59x + { "PDM0_BCLK_I", 0x5, 0x04, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, GROUP_A }, //59x + { "SPDIF0_TX_HFCLK_I", 0x5, 0x05, REG_DAI_CLK3, REG_DAI_EXTD_CLK3, + GROUP_A }, + { "PCG0_EXTCLKA_I", 0x5, 0x00, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "PCG0_EXTCLKB_I", 0x5, 0x01, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "SPDIF0_TX_EXT_SYNC_I", 0x5, 0x03, REG_DAI_CLK4, + REG_DAI_EXTD_CLK4, GROUP_A }, + { "PCG0_SYNC_CLKA_I", 0x5, 0x04, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "PCG0_SYNC_CLKB_I", 0x5, 0x05, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "SPT3_ACLK_I", 0x5, 0x00, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "SPT3_BCLK_I", 0x5, 0x01, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "PCG0_SYNC_CLKE_I", 0x5, 0x02, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, GROUP_A }, //59x + { "PCG0_SYNC_CLKF_I", 0x5, 0x03, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, GROUP_A }, //59x + { "PCG0_EXTCLKE_I", 0x5, 0x04, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, GROUP_A }, //59x + { "PCG0_EXTCLKF_I", 0x5, 0x05, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, GROUP_A }, //59x + { "SPT0_AD0_I", 0x6, 0x00, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT0_AD1_I", 0x6, 0x01, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT0_BD0_I", 0x6, 0x02, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT0_BD1_I", 0x6, 0x03, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT1_AD0_I", 0x6, 0x04, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT1_AD1_I", 0x6, 0x00, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT1_BD0_I", 0x6, 0x01, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT1_BD1_I", 0x6, 0x02, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT2_AD0_I", 0x6, 0x03, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT2_AD1_I", 0x6, 0x04, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT2_BD0_I", 0x6, 0x00, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SPT2_BD1_I", 0x6, 0x01, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC0_DAT_IP_I", 0x6, 0x02, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC1_DAT_IP_I", 0x6, 0x03, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC2_DAT_IP_I", 0x6, 0x04, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC3_DAT_IP_I", 0x6, 0x00, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC0_TDM_OP_I", 0x6, 0x01, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC1_TDM_OP_I", 0x6, 0x02, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC2_TDM_OP_I", 0x6, 0x03, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC3_TDM_OP_I", 0x6, 0x04, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SPDIF0_TX_DAT_I", 0x6, 0x00, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "PDM0_DAT0_I", 0x6, 0x01, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "PDM0_DAT1_I", 0x6, 0x02, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "SPDIF0_RX_I", 0x6, 0x04, REG_DAI_DAT5, REG_DAI_EXTD_DAT5, + GROUP_B }, + { "SPT3_AD0_I", 0x6, 0x00, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT3_AD1_I", 0x6, 0x01, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT3_BD0_I", 0x6, 0x02, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT3_BD1_I", 0x6, 0x03, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT0_AFS_I", 0x5, 0x00, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT0_BFS_I", 0x5, 0x01, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT1_AFS_I", 0x5, 0x02, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT1_BFS_I", 0x5, 0x03, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT2_AFS_I", 0x5, 0x04, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT2_BFS_I", 0x5, 0x05, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SRC0_FS_IP_I", 0x5, 0x00, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC0_FS_OP_I", 0x5, 0x01, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC1_FS_IP_I", 0x5, 0x02, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC1_FS_OP_I", 0x5, 0x03, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC2_FS_IP_I", 0x5, 0x04, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC2_FS_OP_I", 0x5, 0x05, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC3_FS_IP_I", 0x5, 0x00, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SRC3_FS_OP_I", 0x5, 0x01, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SPDIF0_TX_FS_I", 0x5, 0x02, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SPT3_AFS_I", 0x5, 0x00, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "SPT3_BFS_I", 0x5, 0x01, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "TM0_ACI14_I", 0x5, 0x02, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "PDM0_LRCLK_I", 0x5, 0x03, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "DAI0_PB01_I", 0x7, 0x00, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI0_PB02_I", 0x7, 0x01, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI0_PB03_I", 0x7, 0x02, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI0_PB04_I", 0x7, 0x03, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI0_PB05_I", 0x7, 0x00, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI0_PB06_I", 0x7, 0x01, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI0_PB07_I", 0x7, 0x02, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI0_PB08_I", 0x7, 0x03, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI0_PB09_I", 0x7, 0x00, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI0_PB10_I", 0x7, 0x01, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI0_PB11_I", 0x7, 0x02, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI0_PB12_I", 0x7, 0x03, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI0_PB13_I", 0x7, 0x00, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI0_PB14_I", 0x7, 0x01, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI0_PB15_I", 0x7, 0x02, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI0_PB16_I", 0x7, 0x03, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI0_PB17_I", 0x7, 0x00, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI0_PB18_I", 0x7, 0x01, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI0_PB19_I", 0x7, 0x02, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI0_PB20_I", 0x7, 0x03, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "INV_DAI0_PB19_I", 0x1, 0x28, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, GROUP_D }, //58x + { "INV_DAI0_PB20_I", 0x1, 0x29, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, GROUP_D }, //58x + { "DAI0_MISCA0_I", 0x5, 0x00, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_INT_6_I", 0x5, 0x00, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_MISCA1_I", 0x5, 0x01, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_INT_7_I", 0x5, 0x01, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_MISCA2_I", 0x5, 0x02, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_INT_8_I", 0x5, 0x02, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_MISCA3_I", 0x5, 0x03, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_INT_9_I", 0x5, 0x03, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_MISCA4_I", 0x5, 0x04, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_MISCA5_I", 0x5, 0x05, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI0_INV_MISCA4_I", 0x1, 0x30, REG_DAI_MISC0, + REG_DAI_EXTD_MISC0, GROUP_E }, + { "DAI0_INV_MISCA5_I", 0x1, 0x31, REG_DAI_MISC0, + REG_DAI_EXTD_MISC0, GROUP_E }, + { "DAI0_INT_0_I", 0x5, 0x00, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI0_INT_1_I", 0x5, 0x01, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI0_INT_2_I", 0x5, 0x02, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI0_INT_3_I", 0x5, 0x03, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI0_INT_4_I", 0x5, 0x04, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI0_INT_5_I", 0x5, 0x05, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "PCG_HWA_TRIG_I", 0x5, 0x00, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWB_TRIG_I", 0x5, 0x01, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWE_TRIG_I", 0x5, 0x02, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWF_TRIG_I", 0x5, 0x03, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "DAI0_PBEN01_I", 0x6, 0x00, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI0_PBEN02_I", 0x6, 0x01, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI0_PBEN03_I", 0x6, 0x02, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI0_PBEN04_I", 0x6, 0x03, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI0_PBEN05_I", 0x6, 0x04, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI0_PBEN06_I", 0x6, 0x00, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI0_PBEN07_I", 0x6, 0x01, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI0_PBEN08_I", 0x6, 0x02, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI0_PBEN09_I", 0x6, 0x03, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI0_PBEN10_I", 0x6, 0x04, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI0_PBEN11_I", 0x6, 0x00, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI0_PBEN12_I", 0x6, 0x01, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI0_PBEN13_I", 0x6, 0x02, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI0_PBEN14_I", 0x6, 0x03, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI0_PBEN15_I", 0x6, 0x04, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI0_PBEN16_I", 0x6, 0x00, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI0_PBEN17_I", 0x6, 0x01, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI0_PBEN18_I", 0x6, 0x02, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI0_PBEN19_I", 0x6, 0x03, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI0_PBEN20_I", 0x6, 0x04, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F } +}; + +static const struct dai_destination dai1_destinations[] = { + { "SPT4_ACLK_I", 0x5, 0x00, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT4_BCLK_I", 0x5, 0x01, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT5_ACLK_I", 0x5, 0x02, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT5_BCLK_I", 0x5, 0x03, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT6_ACLK_I", 0x5, 0x04, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SPT6_BCLK_I", 0x5, 0x05, REG_DAI_CLK0, REG_DAI_EXTD_CLK0, + GROUP_A }, + { "SRC4_CLK_IP_I", 0x5, 0x00, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC4_CLK_OP_I", 0x5, 0x01, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC5_CLK_IP_I", 0x5, 0x02, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC5_CLK_OP_I", 0x5, 0x03, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC6_CLK_IP_I", 0x5, 0x04, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC6_CLK_OP_I", 0x5, 0x05, REG_DAI_CLK1, REG_DAI_EXTD_CLK1, + GROUP_A }, + { "SRC7_CLK_IP_I", 0x5, 0x00, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "SRC7_CLK_OP_I", 0x5, 0x01, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "SPDIF1_TX_CLK_I", 0x5, 0x02, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "PDM1_CLK0_I", 0x5, 0x03, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "PDM1_BCLK_I", 0x5, 0x04, REG_DAI_CLK2, REG_DAI_EXTD_CLK2, + GROUP_A }, + { "SPDIF1_TX_HFCLK_I", 0x5, 0x05, REG_DAI_CLK3, REG_DAI_EXTD_CLK3, + GROUP_A }, + { "PCG0_EXTCLKC_I", 0x5, 0x00, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "PCG0_EXTCLKD_I", 0x5, 0x01, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "SPDIF1_TX_EXT_SYNC_I", 0x5, 0x03, REG_DAI_CLK4, + REG_DAI_EXTD_CLK4, GROUP_A }, + { "PCG0_SYNC_CLKC_I", 0x5, 0x04, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "PCG0_SYNC_CLKD_I", 0x5, 0x05, REG_DAI_CLK4, REG_DAI_EXTD_CLK4, + GROUP_A }, + { "SPT7_ACLK_I", 0x5, 0x00, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "SPT7_BCLK_I", 0x5, 0x01, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "PCG0_SYNC_CLKG_I", 0x5, 0x02, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "PCG0_SYNC_CLKH_I", 0x5, 0x03, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "PCG0_EXTCLKG_I", 0x5, 0x04, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "PCG0_EXTCLKH_I", 0x5, 0x05, REG_DAI_CLK5, REG_DAI_EXTD_CLK5, + GROUP_A }, + { "SPT4_AD0_I", 0x6, 0x00, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT4_AD1_I", 0x6, 0x01, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT4_BD0_I", 0x6, 0x02, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT4_BD1_I", 0x6, 0x03, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT5_AD0_I", 0x6, 0x04, REG_DAI_DAT0, REG_DAI_EXTD_DAT0, + GROUP_B }, + { "SPT5_AD1_I", 0x6, 0x00, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT5_BD0_I", 0x6, 0x01, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT5_BD1_I", 0x6, 0x02, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT6_AD0_I", 0x6, 0x03, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT6_AD1_I", 0x6, 0x04, REG_DAI_DAT1, REG_DAI_EXTD_DAT1, + GROUP_B }, + { "SPT6_BD0_I", 0x6, 0x00, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SPT6_BD1_I", 0x6, 0x01, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC4_DAT_IP_I", 0x6, 0x02, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC5_DAT_IP_I", 0x6, 0x03, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC6_DAT_IP_I", 0x6, 0x04, REG_DAI_DAT2, REG_DAI_EXTD_DAT2, + GROUP_B }, + { "SRC7_DAT_IP_I", 0x6, 0x00, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC4_TDM_OP_I", 0x6, 0x01, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC5_TDM_OP_I", 0x6, 0x02, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC6_TDM_OP_I", 0x6, 0x03, REG_DAI_DAT3, REG_DAI_EXTD_DAT3, + GROUP_B }, + { "SRC7_TDM_OP_I", 0x6, 0x04, REG_DAI_DAT3, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "SPDIF1_TX_DAT_I", 0x6, 0x00, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "PDM1_DAT0_I", 0x6, 0x01, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "PDM1_DAT1_I", 0x6, 0x02, REG_DAI_DAT4, REG_DAI_EXTD_DAT4, + GROUP_B }, + { "SPDIF1_RX_I", 0x6, 0x04, REG_DAI_DAT5, REG_DAI_EXTD_DAT5, + GROUP_B }, + { "SPT7_AD0_I", 0x6, 0x00, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT7_AD1_I", 0x6, 0x01, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT7_BD0_I", 0x6, 0x02, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT7_BD1_I", 0x6, 0x03, REG_DAI_DAT6, REG_DAI_EXTD_DAT6, + GROUP_B }, + { "SPT4_AFS_I", 0x5, 0x00, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT4_BFS_I", 0x5, 0x01, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT5_AFS_I", 0x5, 0x02, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT5_BFS_I", 0x5, 0x03, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT6_AFS_I", 0x5, 0x04, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SPT6_BFS_I", 0x5, 0x05, REG_DAI_FS0, REG_DAI_EXTD_FS0, + GROUP_C }, + { "SRC4_FS_IP_I", 0x5, 0x00, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC4_FS_OP_I", 0x5, 0x01, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC5_FS_IP_I", 0x5, 0x02, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC5_FS_OP_I", 0x5, 0x03, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC6_FS_IP_I", 0x5, 0x04, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC6_FS_OP_I", 0x5, 0x05, REG_DAI_FS1, REG_DAI_EXTD_FS1, + GROUP_C }, + { "SRC7_FS_IP_I", 0x5, 0x00, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SRC7_FS_OP_I", 0x5, 0x01, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SPDIF1_TX_FS_I", 0x5, 0x02, REG_DAI_FS2, REG_DAI_EXTD_FS2, + GROUP_C }, + { "SPT7_AFS_I", 0x5, 0x00, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "SPT7_BFS_I", 0x5, 0x01, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "TM0_ACI15_I", 0x5, 0x02, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "PDM1_LRCLK_I", 0x5, 0x03, REG_DAI_FS4, REG_DAI_EXTD_FS4, + GROUP_C }, + { "DAI1_PB01_I", 0x7, 0x00, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI1_PB02_I", 0x7, 0x01, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI1_PB03_I", 0x7, 0x02, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI1_PB04_I", 0x7, 0x03, REG_DAI_PIN0, REG_DAI_EXTD_PIN0, + GROUP_D }, + { "DAI1_PB05_I", 0x7, 0x00, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI1_PB06_I", 0x7, 0x01, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI1_PB07_I", 0x7, 0x02, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI1_PB08_I", 0x7, 0x03, REG_DAI_PIN1, REG_DAI_EXTD_PIN1, + GROUP_D }, + { "DAI1_PB09_I", 0x7, 0x00, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI1_PB10_I", 0x7, 0x01, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI1_PB11_I", 0x7, 0x02, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI1_PB12_I", 0x7, 0x03, REG_DAI_PIN2, REG_DAI_EXTD_PIN2, + GROUP_D }, + { "DAI1_PB13_I", 0x7, 0x00, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI1_PB14_I", 0x7, 0x01, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI1_PB15_I", 0x7, 0x02, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI1_PB16_I", 0x7, 0x03, REG_DAI_PIN3, REG_DAI_EXTD_PIN3, + GROUP_D }, + { "DAI1_PB17_I", 0x7, 0x00, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI1_PB18_I", 0x7, 0x01, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI1_PB19_I", 0x7, 0x02, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI1_PB20_I", 0x7, 0x03, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "INV_DAI1_PB19_I", 0x1, 0x28, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "INV_DAI1_PB20_I", 0x1, 0x29, REG_DAI_PIN4, REG_DAI_EXTD_PIN4, + GROUP_D }, + { "DAI1_MISCA0_I", 0x5, 0x00, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_INT_6_I", 0x5, 0x00, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_MISCA1_I", 0x5, 0x01, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_INT_7_I", 0x5, 0x01, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_MISCA2_I", 0x5, 0x02, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_INT_8_I", 0x5, 0x02, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_MISCA3_I", 0x5, 0x03, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_INT_9_I", 0x5, 0x03, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_MISCA4_I", 0x5, 0x04, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_MISCA5_I", 0x5, 0x05, REG_DAI_MISC0, REG_DAI_EXTD_MISC0, + GROUP_E }, + { "DAI1_INV_MISCA4_I", 0x1, 0x30, REG_DAI_MISC0, + REG_DAI_EXTD_MISC0, GROUP_E }, + { "DAI1_INV_MISCA5_I", 0x1, 0x31, REG_DAI_MISC0, + REG_DAI_EXTD_MISC0, GROUP_E }, + { "DAI1_INT_0_I", 0x5, 0x00, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI1_INT_1_I", 0x5, 0x01, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI1_INT_2_I", 0x5, 0x02, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI1_INT_3_I", 0x5, 0x03, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI1_INT_4_I", 0x5, 0x04, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "DAI1_INT_5_I", 0x5, 0x05, REG_DAI_MISC1, REG_DAI_EXTD_MISC1, + GROUP_E }, + { "PCG_HWC_TRIG_I", 0x5, 0x00, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWD_TRIG_I", 0x5, 0x01, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWG_TRIG_I", 0x5, 0x02, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "PCG_HWH_TRIG_I", 0x5, 0x03, REG_DAI_MISC2, REG_DAI_EXTD_MISC2, + GROUP_E }, + { "DAI1_PBEN01_I", 0x6, 0x00, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI1_PBEN02_I", 0x6, 0x01, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI1_PBEN03_I", 0x6, 0x02, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI1_PBEN04_I", 0x6, 0x03, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI1_PBEN05_I", 0x6, 0x04, REG_DAI_PBEN0, REG_DAI_EXTD_PBEN0, + GROUP_F }, + { "DAI1_PBEN06_I", 0x6, 0x00, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI1_PBEN07_I", 0x6, 0x01, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI1_PBEN08_I", 0x6, 0x02, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI1_PBEN09_I", 0x6, 0x03, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI1_PBEN10_I", 0x6, 0x04, REG_DAI_PBEN1, REG_DAI_EXTD_PBEN1, + GROUP_F }, + { "DAI1_PBEN11_I", 0x6, 0x00, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI1_PBEN12_I", 0x6, 0x01, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI1_PBEN13_I", 0x6, 0x02, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI1_PBEN14_I", 0x6, 0x03, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI1_PBEN15_I", 0x6, 0x04, REG_DAI_PBEN2, REG_DAI_EXTD_PBEN2, + GROUP_F }, + { "DAI1_PBEN16_I", 0x6, 0x00, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI1_PBEN17_I", 0x6, 0x01, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI1_PBEN18_I", 0x6, 0x02, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI1_PBEN19_I", 0x6, 0x03, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, + { "DAI1_PBEN20_I", 0x6, 0x04, REG_DAI_PBEN3, REG_DAI_EXTD_PBEN3, + GROUP_F }, +}; + +static const struct dai_source dai0_sources[] = { + { "DAI0_PB01_O_ABCDE", 0x00, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB02_O_ABCDE", 0x01, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB03_O_ABCDE", 0x02, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB04_O_ABCDE", 0x03, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB05_O_ABCDE", 0x04, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB06_O_ABCDE", 0x05, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB07_O_ABCDE", 0x06, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB08_O_ABCDE", 0x07, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB09_O_ABCDE", 0x08, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB10_O_ABCDE", 0x09, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB11_O_ABCDE", 0x0a, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB12_O_ABCDE", 0x0b, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB13_O_ABCDE", 0x0c, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB14_O_ABCDE", 0x0d, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB15_O_ABCDE", 0x0e, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB16_O_ABCDE", 0x0f, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB17_O_ABCDE", 0x10, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB18_O_ABCDE", 0x11, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB19_O_ABCDE", 0x12, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_PB20_O_ABCDE", 0x13, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI0_LOW_ACE", 0x1e, GROUP_A | GROUP_C | GROUP_E }, + { "DAI0_HIGH_ACE", 0x1f, GROUP_A | GROUP_C | GROUP_E }, + { "SPT0_AD0_O_BD", 0x14, GROUP_B | GROUP_D }, + { "SPT0_AD1_O_BD", 0x15, GROUP_B | GROUP_D }, + { "SPT0_BD0_O_BD", 0x16, GROUP_B | GROUP_D }, + { "SPT0_BD1_O_BD", 0x17, GROUP_B | GROUP_D }, + { "SPT1_AD0_O_BD", 0x18, GROUP_B | GROUP_D }, + { "SPT1_AD1_O_BD", 0x19, GROUP_B | GROUP_D }, + { "SPT1_BD0_O_BD", 0x1a, GROUP_B | GROUP_D }, + { "SPT1_BD1_O_BD", 0x1b, GROUP_B | GROUP_D }, + { "SPT2_AD0_O_BD", 0x1c, GROUP_B | GROUP_D }, + { "SPT2_AD1_O_BD", 0x1d, GROUP_B | GROUP_D }, + { "SPT2_BD0_O_BD", 0x1e, GROUP_B | GROUP_D }, + { "SPT2_BD1_O_BD", 0x1f, GROUP_B | GROUP_D }, + { "DAI0_CRS_PB03_O_A", 0x00, GROUP_A }, + { "SPT0_ACLK_O_A", 0x14, GROUP_A }, + { "SPT0_BCLK_O_A", 0x15, GROUP_A }, + { "SPT1_ACLK_O_A", 0x16, GROUP_A }, + { "SPT1_BCLK_O_A", 0x17, GROUP_A }, + { "SPT2_ACLK_O_A", 0x18, GROUP_A }, + { "SPT2_BCLK_O_A", 0x19, GROUP_A }, + { "SPDIF0_RX_CLK_O_A", 0x1a, GROUP_A }, + { "SPDIF0_RX_TDMCLK_O_A", 0x1b, GROUP_A }, + { "PCG0_CLKA_O_A", 0x1c, GROUP_A }, + { "PCG0_CLKB_O_A", 0x1d, GROUP_A }, + { "SRC0_DAT_OP_O_B", 0x20, GROUP_B }, + { "SRC1_DAT_OP_O_B", 0x21, GROUP_B }, + { "SRC2_DAT_OP_O_B", 0x22, GROUP_B }, + { "SRC3_DAT_OP_O_B", 0x23, GROUP_B }, + { "SRC0_TDM_IP_O_B", 0x24, GROUP_B }, + { "SRC1_TDM_IP_O_B", 0x25, GROUP_B }, + { "SRC2_TDM_IP_O_B", 0x26, GROUP_B }, + { "SRC3_TDM_IP_O_B", 0x27, GROUP_B }, + { "SPDIF0_RX_DAT_O_B", 0x28, GROUP_B }, + { "SPT3_AD0_O_B", 0x2c, GROUP_B }, + { "SPT3_AD1_O_B", 0x2d, GROUP_B }, + { "SPT3_BD0_O_B", 0x2e, GROUP_B }, + { "SPT3_BD1_O_B", 0x2f, GROUP_B }, + { "SPDIF0_TX_O_B", 0x30, GROUP_B }, + { "SRC7_CRS_DAT_OP_O_B", 0x31, GROUP_B }, + { "SRC7_CRS_TDM_IP_O_B", 0x32, GROUP_B }, + { "PDM0_SDATA_O_B", 0x33, GROUP_B }, + { "DAI0_LOW_B", 0x3e, GROUP_B }, + { "DAI0_HIGH_B", 0x3f, GROUP_B }, + { "DAI0_CRS_PB04_O_C", 0x00, GROUP_C }, + { "SPT0_AFS_O_C", 0x14, GROUP_C }, + { "SPT0_BFS_O_C", 0x15, GROUP_C }, + { "SPT1_AFS_O_C", 0x16, GROUP_C }, + { "SPT1_BFS_O_C", 0x17, GROUP_C }, + { "SPT2_AFS_O_C", 0x18, GROUP_C }, + { "SPT2_BFS_O_C", 0x19, GROUP_C }, + { "SPDIF0_FS_O_C", 0x1a, GROUP_C }, + { "PCG0_FSA_O_C", 0x1c, GROUP_C }, + { "PCG0_FSB_O_C", 0x1d, GROUP_C }, + { "SPT0_ACLK_O_D", 0x20, GROUP_D }, + { "SPT0_BCLK_O_D", 0x21, GROUP_D }, + { "SPT1_ACLK_O_D", 0x22, GROUP_D }, + { "SPT1_BCLK_O_D", 0x23, GROUP_D }, + { "SPT2_ACLK_O_D", 0x24, GROUP_D }, + { "SPT2_BCLK_O_D", 0x25, GROUP_D }, + { "SPT0_AFS_O_D", 0x26, GROUP_D }, + { "SPT0_BFS_O_D", 0x27, GROUP_D }, + { "SPT1_AFS_O_D", 0x28, GROUP_D }, + { "SPT1_BFS_O_D", 0x29, GROUP_D }, + { "SPT2_AFS_O_D", 0x2a, GROUP_D }, + { "SPT2_BFS_O_D", 0x2b, GROUP_D }, + { "SPT3_AD0_O_D", 0x2c, GROUP_D }, + { "SPT3_AD1_O_D", 0x2d, GROUP_D }, + { "SPT3_BD0_O_D", 0x2e, GROUP_D }, + { "SPT3_BD1_O_D", 0x2f, GROUP_D }, + { "MLB0_CLKOUT_O_D", 0x30, GROUP_D }, + { "SPDIF0_TX_BLKSTART_O_D", 0x31, GROUP_D }, + { "SPT3_ACLK_O_D", 0x34, GROUP_D }, + { "SPT3_BCLK_O_D", 0x35, GROUP_D }, + { "SPT3_AFS_O_D", 0x36, GROUP_D }, + { "SPT3_BFS_O_D", 0x37, GROUP_D }, + { "PCG0_CLKA_O_D", 0x38, GROUP_D }, + { "PCG0_CLKB_O_D", 0x39, GROUP_D }, + { "PCG0_FSA_O_D", 0x3a, GROUP_D }, + { "PCG0_FSB_O_D", 0x3b, GROUP_D }, + { "SRC0_DAT_OP_O_D", 0x3d, GROUP_D }, + { "SRC1_DAT_OP_O_D", 0x3e, GROUP_D }, + { "SRC2_DAT_OP_O_D", 0x3f, GROUP_D }, + { "SRC3_DAT_OP_O_D", 0x40, GROUP_D }, + { "SPDIF0_RX_DAT_O_D", 0x41, GROUP_D }, + { "SPDIF0_RX_FS_O_D", 0x42, GROUP_D }, + { "SPDIF0_RX_CLK_O_D", 0x43, GROUP_D }, + { "SPDIF0_RX_TDMCLK_O_D", 0x44, GROUP_D }, + { "SPDIF0_TX_O_D", 0x45, GROUP_D }, + { "SPT0_ATDV_O_D", 0x46, GROUP_D }, + { "SPT0_BTDV_O_D", 0x47, GROUP_D }, + { "SPT1_ATDV_O_D", 0x48, GROUP_D }, + { "SPT1_BTDV_O_D", 0x49, GROUP_D }, + { "SPT2_ATDV_O_D", 0x4a, GROUP_D }, + { "SPT2_BTDV_O_D", 0x4b, GROUP_D }, + { "SPT3_ATDV_O_D", 0x4c, GROUP_D }, + { "SPT3_BTDV_O_D", 0x4d, GROUP_D }, + { "SPDIF0_RX_LRCLK_REF_O_D", 0x4e, GROUP_D }, + { "SPDIF0_RX_LRCLK_FB_O_D", 0x4f, GROUP_D }, + { "PCG0_CRS_CLKC_O_D", 0x50, GROUP_D }, + { "PCG0_CRS_CLKD_O_D", 0x51, GROUP_D }, + { "PCG0_CRS_FSC_O_D", 0x52, GROUP_D }, + { "PCG0_CRS_FSD_O_D", 0x53, GROUP_D }, + { "DAI0_CRS_PB03_O_D", 0x54, GROUP_D }, + { "DAI0_CRS_PB04_O_D", 0x55, GROUP_D }, + { "PCG_CLKE_O_D", 0x56, GROUP_D }, + { "PCG_CLKF_O_D", 0x57, GROUP_D }, + { "PCG_FSE_O_D", 0x58, GROUP_D }, + { "PCG_FSF_O_D", 0x59, GROUP_D }, + { "PCG_CLKA_INV_O_D", 0x5a, GROUP_D }, + { "PCG_CLKB_INV_O_D", 0x5b, GROUP_D }, + { "PCG_CLKE_INV_O_D", 0x5c, GROUP_D }, + { "PCG_CLKF_INV_O_D", 0x5d, GROUP_D }, + { "PCG_FSA_INV_O_D", 0x5e, GROUP_D }, + { "PCG_FSB_INV_O_D", 0x5f, GROUP_D }, + { "PCG_FSE_INV_O_D", 0x60, GROUP_D }, + { "PCG_FSF_INV_O_D", 0x61, GROUP_D }, + { "PDM0_CLK0_O_D", 0x62, GROUP_D }, + { "PDM0_SDATA_O_D", 0x63, GROUP_D }, + { "DAI0_LOW_D", 0x7e, GROUP_D }, + { "DAI0_HIGH_D", 0x7f, GROUP_D }, + { "SPT0A_FS_O_E", 0x14, GROUP_E }, + { "SPT0B_FS_O_E", 0x15, GROUP_E }, + { "SPT1A_FS_O_E", 0x16, GROUP_E }, + { "SPT1B_FS_O_E", 0x17, GROUP_E }, + { "SPT2A_FS_O_E", 0x18, GROUP_E }, + { "SPT2B_FS_O_E", 0x19, GROUP_E }, + { "SPDIF0_TX_BLKSTART_O_E", 0x1a, GROUP_E }, + { "PCG0_FSA_O_E", 0x1b, GROUP_E }, + { "PCG0_CLKB_O_E", 0x1c, GROUP_E }, + { "PCG0_FSB_O_E", 0x1d, GROUP_E }, + { "DAI0_LOW_F", 0x00, GROUP_F }, + { "DAI0_HIGH_F", 0x01, GROUP_F }, + { "DAI0_MISCA0_O_F", 0x02, GROUP_F }, + { "DAI0_MISCA1_O_F", 0x03, GROUP_F }, + { "DAI0_MISCA2_O_F", 0x04, GROUP_F }, + { "DAI0_MISCA3_O_F", 0x05, GROUP_F }, + { "DAI0_MISCA4_O_F", 0x06, GROUP_F }, + { "DAI0_MISCA5_O_F", 0x07, GROUP_F }, + { "SPT0_ACLK_PBEN_O_F", 0x08, GROUP_F }, + { "SPT0_AFS_PBEN_O_F", 0x09, GROUP_F }, + { "SPT0_AD0_PBEN_O_F", 0x0a, GROUP_F }, + { "SPT0_AD1_PBEN_O_F", 0x0b, GROUP_F }, + { "SPT0_BCLK_PBEN_O_F", 0x0c, GROUP_F }, + { "SPT0_BFS_PBEN_O_F", 0x0d, GROUP_F }, + { "SPT0_BD0_PBEN_O_F", 0x0e, GROUP_F }, + { "SPT0_BD1_PBEN_O_F", 0x0f, GROUP_F }, + { "SPT1_ACLK_PBEN_O_F", 0x10, GROUP_F }, + { "SPT1_AFS_PBEN_O_F", 0x11, GROUP_F }, + { "SPT1_AD0_PBEN_O_F", 0x12, GROUP_F }, + { "SPT1_AD1_PBEN_O_F", 0x13, GROUP_F }, + { "SPT1_BCLK_PBEN_O_F", 0x14, GROUP_F }, + { "SPT1_BFS_PBEN_O_F", 0x15, GROUP_F }, + { "SPT1_BD0_PBEN_O_F", 0x16, GROUP_F }, + { "SPT1_BD1_PBEN_O_F", 0x17, GROUP_F }, + { "SPT2_ACLK_PBEN_O_F", 0x18, GROUP_F }, + { "SPT2_AFS_PBEN_O_F", 0x19, GROUP_F }, + { "SPT2_AD0_PBEN_O_F", 0x1a, GROUP_F }, + { "SPT2_AD1_PBEN_O_F", 0x1b, GROUP_F }, + { "SPT2_BCLK_PBEN_O_F", 0x1c, GROUP_F }, + { "SPT2_BFS_PBEN_O_F", 0x1d, GROUP_F }, + { "SPT2_BD0_PBEN_O_F", 0x1e, GROUP_F }, + { "SPT2_BD1_PBEN_O_F", 0x1f, GROUP_F }, + { "SPT3_ACLK_PBEN_O_F", 0x20, GROUP_F }, + { "SPT3_AFS_PBEN_O_F", 0x21, GROUP_F }, + { "SPT3_AD0_PBEN_O_F", 0x22, GROUP_F }, + { "SPT3_AD1_PBEN_O_F", 0x23, GROUP_F }, + { "SPT3_BCLK_PBEN_O_F", 0x24, GROUP_F }, + { "SPT3_BFS_PBEN_O_F", 0x25, GROUP_F }, + { "SPT3_BD0_PBEN_O_F", 0x26, GROUP_F }, + { "SPT3_BD1_PBEN_O_F", 0x27, GROUP_F }, + { "SPT0_ATDV_PBEN_O_F", 0x28, GROUP_F }, + { "SPT0_BTDV_PBEN_O_F", 0x29, GROUP_F }, + { "SPT1_ATDV_PBEN_O_F", 0x2a, GROUP_F }, + { "SPT1_BTDV_PBEN_O_F", 0x2b, GROUP_F }, + { "SPT2_ATDV_PBEN_O_F", 0x2c, GROUP_F }, + { "SPT2_BTDV_PBEN_O_F", 0x2d, GROUP_F }, + { "SPT3_ATDV_PBEN_O_F", 0x2e, GROUP_F }, + { "SPT3_BTDV_PBEN_O_F", 0x2f, GROUP_F }, + { "PDM0_CLK0_OE_O_F", 0x30, GROUP_F }, + { "PDM0_SDATA_OE_O_F", 0x31, GROUP_F }, +}; + +static const struct dai_source dai1_sources[] = { + { "DAI1_PB01_O_ABCDE", 0x00, GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB02_O_ABCDE", 0x01, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB03_O_ABCDE", 0x02, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB04_O_ABCDE", 0x03, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB05_O_ABCDE", 0x04, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB06_O_ABCDE", 0x05, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB07_O_ABCDE", 0x06, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB08_O_ABCDE", 0x07, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB09_O_ABCDE", 0x08, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB10_O_ABCDE", 0x09, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB11_O_ABCDE", 0x0a, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB12_O_ABCDE", 0x0b, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB13_O_ABCDE", 0x0c, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB14_O_ABCDE", 0x0d, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB15_O_ABCDE", 0x0e, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB16_O_ABCDE", 0x0f, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB17_O_ABCDE", 0x10, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB18_O_ABCDE", 0x11, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB19_O_ABCDE", 0x12, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_PB20_O_ABCDE", 0x13, + GROUP_A | GROUP_B | GROUP_C | GROUP_D | GROUP_E }, + { "DAI1_LOW_ACE", 0x1e, GROUP_A | GROUP_C | GROUP_E }, + { "DAI1_HIGH_ACE", 0x1f, GROUP_A | GROUP_C | GROUP_E }, + { "SPT4_AD0_O_BD", 0x14, GROUP_B | GROUP_D }, + { "SPT4_AD1_O_BD", 0x15, GROUP_B | GROUP_D }, + { "SPT4_BD0_O_BD", 0x16, GROUP_B | GROUP_D }, + { "SPT4_BD1_O_BD", 0x17, GROUP_B | GROUP_D }, + { "SPT5_AD0_O_BD", 0x18, GROUP_B | GROUP_D }, + { "SPT5_AD1_O_BD", 0x19, GROUP_B | GROUP_D }, + { "SPT5_BD0_O_BD", 0x1a, GROUP_B | GROUP_D }, + { "SPT5_BD1_O_BD", 0x1b, GROUP_B | GROUP_D }, + { "SPT6_AD0_O_BD", 0x1c, GROUP_B | GROUP_D }, + { "SPT6_AD1_O_BD", 0x1d, GROUP_B | GROUP_D }, + { "SPT6_BD0_O_BD", 0x1e, GROUP_B | GROUP_D }, + { "SPT6_BD1_O_BD", 0x1f, GROUP_B | GROUP_D }, + { "DAI1_CRS_PB03_O_A", 0x00, GROUP_A }, + { "SPT4_ACLK_O_A", 0x14, GROUP_A }, + { "SPT4_BCLK_O_A", 0x15, GROUP_A }, + { "SPT5_ACLK_O_A", 0x16, GROUP_A }, + { "SPT5_BCLK_O_A", 0x17, GROUP_A }, + { "SPT6_ACLK_O_A", 0x18, GROUP_A }, + { "SPT6_BCLK_O_A", 0x19, GROUP_A }, + { "SPDIF1_RX_CLK_O_A", 0x1a, GROUP_A }, + { "SPDIF1_RX_TDMCLK_O_A", 0x1b, GROUP_A }, + { "PCG0_CLKC_O_A", 0x1c, GROUP_A }, + { "PCG0_CLKD_O_A", 0x1d, GROUP_A }, + { "SRC4_DAT_OP_O_B", 0x20, GROUP_B }, + { "SRC5_DAT_OP_O_B", 0x21, GROUP_B }, + { "SRC6_DAT_OP_O_B", 0x22, GROUP_B }, + { "SRC7_DAT_OP_O_B", 0x23, GROUP_B }, + { "SRC4_TDM_IP_O_B", 0x24, GROUP_B }, + { "SRC5_TDM_IP_O_B", 0x25, GROUP_B }, + { "SRC6_TDM_IP_O_B", 0x26, GROUP_B }, + { "SRC7_TDM_IP_O_B", 0x27, GROUP_B }, + { "SPDIF1_RX_DAT_O_B", 0x28, GROUP_B }, + { "SPT7_AD0_O_B", 0x2c, GROUP_B }, + { "SPT7_AD1_O_B", 0x2d, GROUP_B }, + { "SPT7_BD0_O_B", 0x2e, GROUP_B }, + { "SPT7_BD1_O_B", 0x2f, GROUP_B }, + { "SPDIF1_TX_O_B", 0x30, GROUP_B }, + { "SRC3_CRS_DAT_OP_O_B", 0x31, GROUP_B }, + { "SRC3_CRS_TDM_IP_O_B", 0x32, GROUP_B }, + { "PDM1_SDATA_O_B", 0x33, GROUP_B }, + { "DAI1_LOW_B", 0x3e, GROUP_B }, + { "DAI1_HIGH_B", 0x3f, GROUP_B }, + { "DAI1_CRS_PB04_O_C", 0x00, GROUP_C }, + { "SPT4_AFS_O_C", 0x14, GROUP_C }, + { "SPT4_BFS_O_C", 0x15, GROUP_C }, + { "SPT5_AFS_O_C", 0x16, GROUP_C }, + { "SPT5_BFS_O_C", 0x17, GROUP_C }, + { "SPT6_AFS_O_C", 0x18, GROUP_C }, + { "SPT6_BFS_O_C", 0x19, GROUP_C }, + { "SPDIF1_FS_O_C", 0x1a, GROUP_C }, + { "PCG0_FSC_O_C", 0x1c, GROUP_C }, + { "PCG0_FSD_O_C", 0x1d, GROUP_C }, + { "SPT4_ACLK_O_D", 0x20, GROUP_D }, + { "SPT4_BCLK_O_D", 0x21, GROUP_D }, + { "SPT5_ACLK_O_D", 0x22, GROUP_D }, + { "SPT5_BCLK_O_D", 0x23, GROUP_D }, + { "SPT6_ACLK_O_D", 0x24, GROUP_D }, + { "SPT6_BCLK_O_D", 0x25, GROUP_D }, + { "SPT4_AFS_O_D", 0x26, GROUP_D }, + { "SPT4_BFS_O_D", 0x27, GROUP_D }, + { "SPT5_AFS_O_D", 0x28, GROUP_D }, + { "SPT5_BFS_O_D", 0x29, GROUP_D }, + { "SPT6_AFS_O_D", 0x2a, GROUP_D }, + { "SPT6_BFS_O_D", 0x2b, GROUP_D }, + { "SPT7_AD0_O_D", 0x2c, GROUP_D }, + { "SPT7_AD1_O_D", 0x2d, GROUP_D }, + { "SPT7_BD0_O_D", 0x2e, GROUP_D }, + { "SPT7_BD1_O_D", 0x2f, GROUP_D }, + { "MLB0_CLKOUT_O_D", 0x30, GROUP_D }, + { "SPDIF1_TX_BLKSTART_O_D", 0x31, GROUP_D }, + { "SPT7_ACLK_O_D", 0x34, GROUP_D }, + { "SPT7_BCLK_O_D", 0x35, GROUP_D }, + { "SPT7_AFS_O_D", 0x36, GROUP_D }, + { "SPT7_BFS_O_D", 0x37, GROUP_D }, + { "PCG0_CLKC_O_D", 0x38, GROUP_D }, + { "PCG0_CLKD_O_D", 0x39, GROUP_D }, + { "PCG0_FSC_O_D", 0x3a, GROUP_D }, + { "PCG0_FSD_O_D", 0x3b, GROUP_D }, + { "SRC4_DAT_OP_O_D", 0x3d, GROUP_D }, + { "SRC5_DAT_OP_O_D", 0x3e, GROUP_D }, + { "SRC6_DAT_OP_O_D", 0x3f, GROUP_D }, + { "SRC7_DAT_OP_O_D", 0x40, GROUP_D }, + { "SPDIF1_RX_DAT_O_D", 0x41, GROUP_D }, + { "SPDIF1_RX_FS_O_D", 0x42, GROUP_D }, + { "SPDIF1_RX_CLK_O_D", 0x43, GROUP_D }, + { "SPDIF1_RX_TDMCLK_O_D", 0x44, GROUP_D }, + { "SPDIF1_TX_O_D", 0x45, GROUP_D }, + { "SPT4_ATDV_O_D", 0x46, GROUP_D }, + { "SPT4_BTDV_O_D", 0x47, GROUP_D }, + { "SPT5_ATDV_O_D", 0x48, GROUP_D }, + { "SPT5_BTDV_O_D", 0x49, GROUP_D }, + { "SPT6_ATDV_O_D", 0x4a, GROUP_D }, + { "SPT6_BTDV_O_D", 0x4b, GROUP_D }, + { "SPT7_ATDV_O_D", 0x4c, GROUP_D }, + { "SPT7_BTDV_O_D", 0x4d, GROUP_D }, + { "SPDIF1_RX_LRCLK_REF_O_D", 0x4e, GROUP_D }, + { "SPDIF1_RX_LRCLK_FB_O_D", 0x4f, GROUP_D }, + { "PCG0_CRS_CLKA_O_D", 0x50, GROUP_D }, + { "PCG0_CRS_CLKB_O_D", 0x51, GROUP_D }, + { "PCG0_CRS_FSA_O_D", 0x52, GROUP_D }, + { "PCG0_CRS_FSB_O_D", 0x53, GROUP_D }, + { "DAI1_CRS_PB03_O_D", 0x54, GROUP_D }, + { "DAI1_CRS_PB04_O_D", 0x55, GROUP_D }, + { "PCG_CLKG_O_D", 0x56, GROUP_D }, + { "PCG_CLKH_O_D", 0x57, GROUP_D }, + { "PCG_FSG_O_D", 0x58, GROUP_D }, + { "PCG_FSH_O_D", 0x59, GROUP_D }, + { "PCG_CLKC_INV_O_D", 0x5a, GROUP_D }, + { "PCG_CLKG_INV_O_D", 0x5b, GROUP_D }, + { "PCG_CLKD_INV_O_D", 0x5c, GROUP_D }, + { "PCG_CLKH_INV_O_D", 0x5d, GROUP_D }, + { "PCG_FSC_INV_O_D", 0x5e, GROUP_D }, + { "PCG_FSD_INV_O_D", 0x5f, GROUP_D }, + { "PCG_FSG_INV_O_D", 0x60, GROUP_D }, + { "PCG_FSH_INV_O_D", 0x61, GROUP_D }, + { "PDM1_CLK0_O_D", 0x62, GROUP_D }, + { "PDM1_SDATA_O_D", 0x63, GROUP_D }, + { "DAI1_LOW_D", 0x7e, GROUP_D }, + { "DAI1_HIGH_D", 0x7f, GROUP_D }, + { "SPT4A_FS_O_E", 0x14, GROUP_E }, + { "SPT4B_FS_O_E", 0x15, GROUP_E }, + { "SPT5A_FS_O_E", 0x16, GROUP_E }, + { "SPT5B_FS_O_E", 0x17, GROUP_E }, + { "SPT6A_FS_O_E", 0x18, GROUP_E }, + { "SPT6B_FS_O_E", 0x19, GROUP_E }, + { "SPDIF1_TX_BLKSTART_O_E", 0x1a, GROUP_E }, + { "PCG0_FSC_O_E", 0x1b, GROUP_E }, + { "PCG0_CLKD_O_E", 0x1c, GROUP_E }, + { "PCG0_FSD_O_E", 0x1d, GROUP_E }, + { "DAI1_LOW_F", 0x00, GROUP_F }, + { "DAI1_HIGH_F", 0x01, GROUP_F }, + { "DAI1_MISCA0_O_F", 0x02, GROUP_F }, + { "DAI1_MISCA1_O_F", 0x03, GROUP_F }, + { "DAI1_MISCA2_O_F", 0x04, GROUP_F }, + { "DAI1_MISCA3_O_F", 0x05, GROUP_F }, + { "DAI1_MISCA4_O_F", 0x06, GROUP_F }, + { "DAI1_MISCA5_O_F", 0x07, GROUP_F }, + { "SPT4_ACLK_PBEN_O_F", 0x08, GROUP_F }, + { "SPT4_AFS_PBEN_O_F", 0x09, GROUP_F }, + { "SPT4_AD0_PBEN_O_F", 0x0a, GROUP_F }, + { "SPT4_AD1_PBEN_O_F", 0x0b, GROUP_F }, + { "SPT4_BCLK_PBEN_O_F", 0x0c, GROUP_F }, + { "SPT4_BFS_PBEN_O_F", 0x0d, GROUP_F }, + { "SPT4_BD0_PBEN_O_F", 0x0e, GROUP_F }, + { "SPT4_BD1_PBEN_O_F", 0x0f, GROUP_F }, + { "SPT5_ACLK_PBEN_O_F", 0x10, GROUP_F }, + { "SPT5_AFS_PBEN_O_F", 0x11, GROUP_F }, + { "SPT5_AD0_PBEN_O_F", 0x12, GROUP_F }, + { "SPT5_AD1_PBEN_O_F", 0x13, GROUP_F }, + { "SPT5_BCLK_PBEN_O_F", 0x14, GROUP_F }, + { "SPT5_BFS_PBEN_O_F", 0x15, GROUP_F }, + { "SPT5_BD0_PBEN_O_F", 0x16, GROUP_F }, + { "SPT5_BD1_PBEN_O_F", 0x17, GROUP_F }, + { "SPT6_ACLK_PBEN_O_F", 0x18, GROUP_F }, + { "SPT6_AFS_PBEN_O_F", 0x19, GROUP_F }, + { "SPT6_AD0_PBEN_O_F", 0x1a, GROUP_F }, + { "SPT6_AD1_PBEN_O_F", 0x1b, GROUP_F }, + { "SPT6_BCLK_PBEN_O_F", 0x1c, GROUP_F }, + { "SPT6_BFS_PBEN_O_F", 0x1d, GROUP_F }, + { "SPT6_BD0_PBEN_O_F", 0x1e, GROUP_F }, + { "SPT6_BD1_PBEN_O_F", 0x1f, GROUP_F }, + { "SPT7_ACLK_PBEN_O_F", 0x20, GROUP_F }, + { "SPT7_AFS_PBEN_O_F", 0x21, GROUP_F }, + { "SPT7_AD0_PBEN_O_F", 0x22, GROUP_F }, + { "SPT7_AD1_PBEN_O_F", 0x23, GROUP_F }, + { "SPT7_BCLK_PBEN_O_F", 0x24, GROUP_F }, + { "SPT7_BFS_PBEN_O_F", 0x25, GROUP_F }, + { "SPT7_BD0_PBEN_O_F", 0x26, GROUP_F }, + { "SPT7_BD1_PBEN_O_F", 0x27, GROUP_F }, + { "SPT4_ATDV_PBEN_O_F", 0x28, GROUP_F }, + { "SPT4_BTDV_PBEN_O_F", 0x29, GROUP_F }, + { "SPT5_ATDV_PBEN_O_F", 0x2a, GROUP_F }, + { "SPT5_BTDV_PBEN_O_F", 0x2b, GROUP_F }, + { "SPT6_ATDV_PBEN_O_F", 0x2c, GROUP_F }, + { "SPT6_BTDV_PBEN_O_F", 0x2d, GROUP_F }, + { "SPT7_ATDV_PBEN_O_F", 0x2e, GROUP_F }, + { "SPT7_BTDV_PBEN_O_F", 0x2f, GROUP_F }, + { "PDM1_CLK0_OE_O_F", 0x30, GROUP_F }, + { "PDM1_SDATA_OE_O_F", 0x31, GROUP_F }, +}; + +static int32_t adsp_populate_group_combinations(struct adsp_sru_ctrl + *adsp_sru_ctrl) +{ + struct device *dev = adsp_sru_ctrl->dev; + + struct group_combination *grp_combs = adsp_sru_ctrl->grp_combs; + u32 *grp_combs_cnt = &adsp_sru_ctrl->grp_combs_cnt; + + u32 *poss_grp_combs = + (u32 *) kmalloc_array(1024, sizeof(*poss_grp_combs), + GFP_KERNEL); + u32 grps_found = 0; + u32 found = 0; + u32 i, j, index; + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 0) { + for (i = 0; i < DAI0_SOURCE_COUNT; i++) { + for (found = 0, j = 0; j < grps_found; j++) { + if (poss_grp_combs[j] == + dai0_sources[i].group) { + found = 1; + break; + } + } + if (!found) + poss_grp_combs[grps_found++] = + dai0_sources[i].group; + } + } + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 1) { + for (i = 0; i < DAI1_SOURCE_COUNT; i++) { + for (found = 0, j = 0; j < grps_found; j++) { + if (poss_grp_combs[j] == + dai1_sources[i].group) { + found = 1; + break; + } + } + if (!found) + poss_grp_combs[grps_found++] = + dai1_sources[i].group; + } + } + + for (i = 0; i < grps_found; i++) { + grp_combs[*grp_combs_cnt].associated_groups = + poss_grp_combs[i]; + grp_combs[*grp_combs_cnt].dest_names = + devm_kcalloc(dev, sizeof(*adsp_sru_ctrl->group_names), + DAI0_DESTINATION_COUNT + + DAI1_DESTINATION_COUNT, GFP_KERNEL); + + if (!grp_combs[*grp_combs_cnt].dest_names) { + dev_err(dev, + "Insufficient memory available to allocate group associations\n"); + return -ENOMEM; + } + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 0) { + for (j = 0; j < DAI0_DESTINATION_COUNT; j++) { + if (dai0_destinations[j].group & + poss_grp_combs[i]) { + index = + grp_combs + [*grp_combs_cnt].destination_count; + grp_combs + [*grp_combs_cnt].dest_names + [index] = + dai0_destinations + [j].dest_signal; + grp_combs + [*grp_combs_cnt].destination_count++; + } + } + } + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 1) { + for (j = 0; j < DAI1_DESTINATION_COUNT; j++) { + if (dai1_destinations[j].group & + poss_grp_combs[i]) { + index = + grp_combs + [*grp_combs_cnt].destination_count; + grp_combs + [*grp_combs_cnt].dest_names + [index] = + dai1_destinations + [j].dest_signal; + grp_combs + [*grp_combs_cnt].destination_count++; + } + } + } + + (*grp_combs_cnt)++; + } + + kfree(poss_grp_combs); + + return 0; +} + +static int adsp_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + u32 count = 0; + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 0) + count += DAI0_SOURCE_COUNT; + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 1) + count += DAI1_SOURCE_COUNT; + + return count; +} + +static const struct dai_source *adsp_find_source(struct adsp_sru_ctrl + *adsp_sru_ctrl, + unsigned int selector) +{ + + if (adsp_sru_ctrl->has_extended || adsp_sru_ctrl->dai == 0) { + if (selector < DAI0_SOURCE_COUNT) + return &dai0_sources[selector]; + } + + if (adsp_sru_ctrl->has_extended) { + if (selector >= DAI0_SOURCE_COUNT && + selector <= + (DAI0_SOURCE_COUNT + DAI1_SOURCE_COUNT - 1)) + return &dai1_sources[selector - DAI0_SOURCE_COUNT]; + } else if (adsp_sru_ctrl->dai == 1) { + if (selector < DAI1_SOURCE_COUNT) + return &dai1_sources[selector]; + } + + dev_err(adsp_sru_ctrl->dev, "Invalid selector %d\n", selector); + return NULL; +} + +static const char *adsp_pinmux_get_function_name(struct pinctrl_dev + *pctldev, + unsigned int selector) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + + return adsp_find_source(adsp_sru_ctrl, selector)->source_signal; +} + +static int adsp_pinmux_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char *const **groups, + unsigned *const num_groups) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + struct group_combination *grp_combs = adsp_sru_ctrl->grp_combs; + u32 *grp_combs_cnt = &adsp_sru_ctrl->grp_combs_cnt; + u32 i, associated_groups; + + associated_groups = + adsp_find_source(adsp_sru_ctrl, selector)->group; + + for (i = 0; i < *grp_combs_cnt; i++) { + if (grp_combs[i].associated_groups == associated_groups) { + *groups = grp_combs[i].dest_names; + *num_groups = grp_combs[i].destination_count; + return 0; + } + } + + dev_err(adsp_sru_ctrl->dev, + "Unable to find associated function group\n"); + return -ENODEV; +} + +/* Each group is exactly 1 pin and group id == pin id */ +static int adsp_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int func, unsigned int group) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + + u32 selection_code, register_val; + u32 offset, offset_ext, width, input; + u8 ext_bits; + + selection_code = + adsp_find_source(adsp_sru_ctrl, func)->selection_code; + + offset = adsp_sru_ctrl->dest[group].offset; + offset_ext = adsp_sru_ctrl->dest[group].offset_ext; + width = adsp_sru_ctrl->dest[group].width; + input = adsp_sru_ctrl->dest[group].input; + + register_val = readl(adsp_sru_ctrl->regs + offset); + register_val &= ~GENMASK((width * input) + width - 1, (width * input)); //Clear bits + register_val |= selection_code << width * input; //Set bits + writel(register_val, adsp_sru_ctrl->regs + offset); + + /* + * If we're using the DAI1 source table, we also need to set the corresponding + * extended selection bits + */ + if (adsp_sru_ctrl->has_extended) { + if (func >= DAI0_SOURCE_COUNT + && func <= + (DAI0_SOURCE_COUNT + DAI1_SOURCE_COUNT - 1)) { + switch (width) { + case 0x5: + ext_bits = 0x3; + break; + case 0x6: + case 0x7: + ext_bits = 0x2; + break; + } + + register_val = + readl(adsp_sru_ctrl->regs + offset_ext); + register_val |= ext_bits << 4 * input; + writel(register_val, + adsp_sru_ctrl->regs + offset_ext); + } + } + + return 0; +} + +static const struct pinmux_ops adsp_pmxops = { + .get_functions_count = adsp_pinmux_get_functions_count, + .get_function_name = adsp_pinmux_get_function_name, + .get_function_groups = adsp_pinmux_get_function_groups, + .set_mux = adsp_pinmux_set_mux, + .strict = true, +}; + +/* + * We want to make one group per pin so that we can refer to the pins by group + * later on when mux assignments are made + */ +static int adsp_sru_ctrl_init_groups(struct adsp_sru_ctrl *adsp_sru_ctrl, + struct pinctrl_desc *desc) +{ + struct device *dev = adsp_sru_ctrl->dev; + struct pinctrl_pin_desc *all_pins; + size_t pin; + + all_pins = devm_kcalloc(dev, sizeof(*all_pins), + adsp_sru_ctrl->dest_count, GFP_KERNEL); + + adsp_sru_ctrl->pins = + devm_kcalloc(dev, sizeof(*adsp_sru_ctrl->pins), + adsp_sru_ctrl->dest_count, GFP_KERNEL); + if (!adsp_sru_ctrl->pins) + return -ENOMEM; + + adsp_sru_ctrl->group_names = + devm_kcalloc(dev, sizeof(*adsp_sru_ctrl->group_names), + adsp_sru_ctrl->dest_count, GFP_KERNEL); + if (!adsp_sru_ctrl->group_names) + return -ENOMEM; + + for (pin = 0; pin < adsp_sru_ctrl->dest_count; ++pin) { + adsp_sru_ctrl->group_names[pin] = + devm_kasprintf(dev, GFP_KERNEL, "%s", + adsp_sru_ctrl->dest[pin].dest_signal); + adsp_sru_ctrl->pins[pin] = (unsigned int) pin; + + all_pins[pin].name = adsp_sru_ctrl->group_names[pin]; + all_pins[pin].number = (unsigned int) pin; + } + + desc->pins = all_pins; + desc->npins = adsp_sru_ctrl->dest_count; + + return 0; +} + +static int adsp_sru_ctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + + return adsp_sru_ctrl->dest_count; +} + +static const char *adsp_sru_ctrl_get_group_name(struct pinctrl_dev + *pctldev, + unsigned int selector) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + + return adsp_sru_ctrl->group_names[selector]; +} + +static int adsp_sru_ctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *reserved_maps, + unsigned int *num_maps) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + struct property *prop; + const char *dest_name; + const char *source_name; + unsigned long *configs; + unsigned int num_pins, pos; + unsigned int reserve = 0; + const __be32 *p1, *p2; + u32 source, destination, num_entries; + int ret; + + num_pins = of_property_count_u32_elems(np, "sru-routing"); + if (num_pins <= 0) { + dev_err(adsp_sru_ctrl->dev, + "Must have at least one `sru-routing` entry in %pOFn.\n", + np); + return -EINVAL; + } + + reserve = num_pins / 2; + + ret = + pinctrl_utils_reserve_map(pctldev, map, reserved_maps, + num_maps, reserve); + if (ret) + goto exit; + + prop = of_find_property(np, "sru-routing", NULL); + p2 = NULL; + num_entries = 0; + do { + p1 = of_prop_next_u32(prop, p2, &source); + if (p1) + p2 = of_prop_next_u32(prop, p1, &destination); + + if (p1 && p2) { + + num_entries += 2; + + if (adsp_sru_ctrl->dai == 0) { + if (destination >= DAI0_DST_DTS_BINDING && + destination <= + (DAI1_DST_DTS_BINDING - 1)) { + pos = + destination - + DAI0_DST_DTS_BINDING; + dest_name = + adsp_sru_ctrl-> + dest[pos].dest_signal; + } else { + dev_err(adsp_sru_ctrl->dev, + "%d is not a valid destination for DAI0\n", + destination); + return -EINVAL; + } + } + + if (adsp_sru_ctrl->dai == 1) { + if (destination >= DAI1_DST_DTS_BINDING && + destination <= + (DAI0_SRC_DTS_BINDING - 1)) { + pos = + destination - + DAI1_DST_DTS_BINDING; + dest_name = + adsp_sru_ctrl-> + dest[pos].dest_signal; + } else { + dev_err(adsp_sru_ctrl->dev, + "%d is not a valid destination for DAI1\n", + destination); + return -EINVAL; + } + } + + if (adsp_sru_ctrl->has_extended + || adsp_sru_ctrl->dai == 0) { + if (source >= DAI0_SRC_DTS_BINDING + && source <= + (DAI1_SRC_DTS_BINDING - 1)) { + pos = + source - DAI0_SRC_DTS_BINDING; + source_name = + dai0_sources + [pos].source_signal; + } + } + + if (adsp_sru_ctrl->has_extended + || adsp_sru_ctrl->dai == 1) { + if (source >= DAI1_SRC_DTS_BINDING + && source <= + (DAI1_SRC_DTS_BINDING_END - 1)) { + pos = + source - DAI1_SRC_DTS_BINDING; + source_name = + dai1_sources + [pos].source_signal; + } + } + + if (source_name == NULL) { + dev_err(adsp_sru_ctrl->dev, + "%d is not a valid source\n", + source); + return -EINVAL; + } + + ret = + pinctrl_utils_add_map_mux(pctldev, map, + reserved_maps, + num_maps, dest_name, + source_name); + + if (ret) + goto exit; + } + } while (p1 && p2); + + if ((num_entries <= 0) || (num_entries % 2)) { + dev_err(adsp_sru_ctrl->dev, + "`sru-routing` entry in %pOFn must have source/destination pairs\n", + np); + return -EINVAL; + } + +exit: + kfree(configs); + return ret; +} + +static int adsp_sru_ctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *child_np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np, child_np) { + ret = + adsp_sru_ctrl_dt_subnode_to_map(pctldev, child_np, map, + &reserved_maps, + num_maps); + if (ret < 0) + goto exit; + } + return 0; + +exit: + pinctrl_utils_free_map(pctldev, *map, *num_maps); + return ret; +} + +static int adsp_sru_ctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned const **pins, + unsigned *const num_pins) +{ + struct adsp_sru_ctrl *adsp_sru_ctrl = + pinctrl_dev_get_drvdata(pctldev); + + *pins = &adsp_sru_ctrl->pins[selector]; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops adsp_pctlops = { + .get_groups_count = adsp_sru_ctrl_get_groups_count, + .get_group_name = adsp_sru_ctrl_get_group_name, + .get_group_pins = adsp_sru_ctrl_get_group_pins, + .dt_node_to_map = adsp_sru_ctrl_dt_node_to_map, +}; + +static const int adsp_sru_ctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adsp_sru_ctrl *adsp_sru_ctrl; + struct pinctrl_desc *pnctrl_desc; + struct resource *res; + struct regmap *regmap; + int ret; + + adsp_sru_ctrl = + devm_kzalloc(dev, sizeof(*adsp_sru_ctrl), GFP_KERNEL); + if (!adsp_sru_ctrl) + return -ENOMEM; + + adsp_sru_ctrl->dev = dev; + + pnctrl_desc = devm_kzalloc(dev, sizeof(*pnctrl_desc), GFP_KERNEL); + if (!pnctrl_desc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adsp_sru_ctrl->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(adsp_sru_ctrl->regs)) + return PTR_ERR(adsp_sru_ctrl->regs); + + pnctrl_desc->name = dev_name(dev); + pnctrl_desc->pmxops = &adsp_pmxops; + pnctrl_desc->pctlops = &adsp_pctlops; + pnctrl_desc->owner = THIS_MODULE; + + regmap = + system_config_regmap_lookup_by_phandle(np, + "adi,system-config"); + if (IS_ERR(regmap)) { + if (PTR_ERR(regmap) == -EPROBE_DEFER) + return PTR_ERR(regmap); + + dev_err(&pdev->dev, + "adi,system-config regmap not connected\n"); + return PTR_ERR(regmap); + } + + adsp_sru_ctrl->dai = of_alias_get_id(np, "sru"); + if (adsp_sru_ctrl->dai == 0) { + adsp_sru_ctrl->dest = dai0_destinations; + adsp_sru_ctrl->dest_count = DAI0_DESTINATION_COUNT; + regmap_write(regmap, ADI_SYSTEM_REG_DAI0_IE, 0xffffffff); + } else if (adsp_sru_ctrl->dai == 1) { + adsp_sru_ctrl->dest = dai1_destinations; + adsp_sru_ctrl->dest_count = DAI1_DESTINATION_COUNT; + regmap_write(regmap, ADI_SYSTEM_REG_DAI1_IE, 0xffffffff); + } else { + dev_err(adsp_sru_ctrl->dev, "Unknown DAI instance (%d)\n", + adsp_sru_ctrl->dai); + return -EINVAL; + } + + if (of_property_read_bool(np, "has-extended")) { + adsp_sru_ctrl->has_extended = 1; + dev_info(adsp_sru_ctrl->dev, + "Started with extended selection codes (SC594, SC598)\n"); + } else { + adsp_sru_ctrl->has_extended = 0; + dev_info(adsp_sru_ctrl->dev, + "Started without extended selection codes (SC573, SC584, SC589)\n"); + } + + ret = adsp_sru_ctrl_init_groups(adsp_sru_ctrl, pnctrl_desc); + if (ret) + return ret; + + ret = adsp_populate_group_combinations(adsp_sru_ctrl); + if (ret) + return ret; + + ret = + devm_pinctrl_register_and_init(dev, pnctrl_desc, adsp_sru_ctrl, + &adsp_sru_ctrl->pin_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, adsp_sru_ctrl); + + return pinctrl_enable(adsp_sru_ctrl->pin_dev); +} + +static const struct of_device_id adsp_sru_ctrl_of_match[] = { + {.compatible = "adi,adsp-sru-ctrl", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, adsp_sru_ctrl_of_match); + +static struct platform_driver adsp_sru_ctrl_driver = { + .driver = { + .name = "adsp-sru-ctrl", + .of_match_table = adsp_sru_ctrl_of_match, + .suppress_bind_attrs = true, + }, + .probe = adsp_sru_ctrl_probe, +}; + +static int __init adsp_sru_ctrl_init(void) +{ + return platform_driver_register(&adsp_sru_ctrl_driver); +} + +arch_initcall(adsp_sru_ctrl_init); diff --git a/drivers/pinctrl/adi/sru-ctrl-adsp.h b/drivers/pinctrl/adi/sru-ctrl-adsp.h new file mode 100644 index 00000000000000..7ca814e62be264 --- /dev/null +++ b/drivers/pinctrl/adi/sru-ctrl-adsp.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Analog Devices ADSP family SRU control driver. + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SRU_CTRL_ADSP_H +#define SRU_CTRL_ADSP_H + +#include + +#define REG_DAI_EXTD_CLK0 0x000 // DAI0 Extended Clock Routing Control Register 0 +#define REG_DAI_EXTD_CLK1 0x004 // DAI0 Extended Clock Routing Control Register 1 +#define REG_DAI_EXTD_CLK2 0x008 // DAI0 Extended Clock Routing Control Register 2 +#define REG_DAI_EXTD_CLK3 0x00C // DAI0 Extended Clock Routing Control Register 3 +#define REG_DAI_EXTD_CLK4 0x010 // DAI0 Extended Clock Routing Control Register 4 +#define REG_DAI_EXTD_CLK5 0x014 // DAI0 Extended Clock Routing Control Register 5 +#define REG_DAI_EXTD_DAT0 0x018 // DAI0 Extended Serial Data Routing Control Register 0 +#define REG_DAI_EXTD_DAT1 0x01C // DAI0 Extended Serial Data Routing Control Register 1 +#define REG_DAI_EXTD_DAT2 0x020 // DAI0 Extended Serial Data Routing Control Register 2 +#define REG_DAI_EXTD_DAT3 0x024 // DAI0 Extended Serial Data Routing Control Register 3 +#define REG_DAI_EXTD_DAT4 0x028 // DAI0 Extended Serial Data Routing Control Register 4 +#define REG_DAI_EXTD_DAT5 0x02C // DAI0 Extended Serial Data Routing Control Register 5 +#define REG_DAI_EXTD_DAT6 0x030 // DAI0 Extended Serial Data Routing Control Register 6 +#define REG_DAI_EXTD_FS0 0x034 // DAI0 Extended Frame Sync Routing Control Register 0 +#define REG_DAI_EXTD_FS1 0x038 // DAI0 Extended Frame Sync Routing Control Register 1 +#define REG_DAI_EXTD_FS2 0x03C // DAI0 Extended Frame Sync Routing Control Register 2 +#define REG_DAI_EXTD_FS4 0x044 // DAI0 Extended Frame Sync Routing Control Register 4 +#define REG_DAI_EXTD_PIN0 0x048 // DAI0 Extended Pin Buffer Assignment Register 0 +#define REG_DAI_EXTD_PIN1 0x04C // DAI0 Extended Pin Buffer Assignment Register 1 +#define REG_DAI_EXTD_PIN2 0x050 // DAI0 Extended Pin Buffer Assignment Register 2 +#define REG_DAI_EXTD_PIN3 0x054 // DAI0 Extended Pin Buffer Assignment Register 3 +#define REG_DAI_EXTD_PIN4 0x058 // DAI0 Extended Pin Buffer Assignment Register 4 +#define REG_DAI_EXTD_MISC0 0x05C // DAI0 Extended Miscellaneous Control Register 0 +#define REG_DAI_EXTD_MISC1 0x060 // DAI0 Extended Miscellaneous Control Register 1 +#define REG_DAI_EXTD_MISC2 0x064 // DAI0 Extended Miscellaneous Control Register 2 +#define REG_DAI_EXTD_PBEN0 0x068 // DAI0 Extended Pin Buffer Enable Register 0 +#define REG_DAI_EXTD_PBEN1 0x06C // DAI0 Extended Pin Buffer Enable Register 1 +#define REG_DAI_EXTD_PBEN2 0x070 // DAI0 Extended Pin Buffer Enable Register 2 +#define REG_DAI_EXTD_PBEN3 0x074 // DAI0 Extended Pin Buffer Enable Register 3 +#define REG_DAI_CLK0 0x0C0 // DAI0 Clock Routing Control Register 0 +#define REG_DAI_CLK1 0x0C4 // DAI0 Clock Routing Control Register 1 +#define REG_DAI_CLK2 0x0C8 // DAI0 Clock Routing Control Register 2 +#define REG_DAI_CLK3 0x0CC // DAI0 Clock Routing Control Register 3 +#define REG_DAI_CLK4 0x0D0 // DAI0 Clock Routing Control Register 4 +#define REG_DAI_CLK5 0x0D4 // DAI0 Clock Routing Control Register 5 +#define REG_DAI_DAT0 0x100 // DAI0 Serial Data Routing Control Register 0 +#define REG_DAI_DAT1 0x104 // DAI0 Serial Data Routing Control Register 1 +#define REG_DAI_DAT2 0x108 // DAI0 Serial Data Routing Control Register 2 +#define REG_DAI_DAT3 0x10C // DAI0 Serial Data Routing Control Register 3 +#define REG_DAI_DAT4 0x110 // DAI0 Serial Data Routing Control Register 4 +#define REG_DAI_DAT5 0x114 // DAI0 Serial Data Routing Control Register 5 +#define REG_DAI_DAT6 0x118 // DAI0 Serial Data Routing Control Register 6 +#define REG_DAI_FS0 0x140 // DAI0 Frame Sync Routing Control Register 0 +#define REG_DAI_FS1 0x144 // DAI0 Frame Sync Routing Control Register 1 +#define REG_DAI_FS2 0x148 // DAI0 Frame Sync Routing Control Register 2 +#define REG_DAI_FS4 0x150 // DAI0 Frame Sync Routing Control Register 4 +#define REG_DAI_PIN0 0x180 // DAI0 Pin Buffer Assignment Register 0 +#define REG_DAI_PIN1 0x184 // DAI0 Pin Buffer Assignment Register 1 +#define REG_DAI_PIN2 0x188 // DAI0 Pin Buffer Assignment Register 2 +#define REG_DAI_PIN3 0x18C // DAI0 Pin Buffer Assignment Register 3 +#define REG_DAI_PIN4 0x190 // DAI0 Pin Buffer Assignment Register 4 +#define REG_DAI_MISC0 0x1C0 // DAI0 Miscellaneous Control Register 0 +#define REG_DAI_MISC1 0x1C4 // DAI0 Miscellaneous Control Register 1 +#define REG_DAI_MISC2 0x1C8 // DAI0 Miscellaneous Control Register 1 +#define REG_DAI_PBEN0 0x1E0 // DAI0 Pin Buffer Enable Register 0 +#define REG_DAI_PBEN1 0x1E4 // DAI0 Pin Buffer Enable Register 1 +#define REG_DAI_PBEN2 0x1E8 // DAI0 Pin Buffer Enable Register 2 +#define REG_DAI_PBEN3 0x1EC // DAI0 Pin Buffer Enable Register 3 + +#define NUM_GROUPS 6 +#define GROUP_A (1 << 0) +#define GROUP_B (1 << 1) +#define GROUP_C (1 << 2) +#define GROUP_D (1 << 3) +#define GROUP_E (1 << 4) +#define GROUP_F (1 << 5) + +#define DAI0_DESTINATION_COUNT ARRAY_SIZE(dai0_destinations) +#define DAI0_SOURCE_COUNT ARRAY_SIZE(dai0_sources) + +#define DAI1_DESTINATION_COUNT ARRAY_SIZE(dai1_destinations) +#define DAI1_SOURCE_COUNT ARRAY_SIZE(dai1_sources) + +//These numbers must be adjusted to match the device tree binding (if changed). +//They're used to map the device tree integers to the desired list+offset +#define DAI0_DST_DTS_BINDING 0 +#define DAI1_DST_DTS_BINDING 140 +#define DAI0_SRC_DTS_BINDING 280 +#define DAI1_SRC_DTS_BINDING 481 +#define DAI1_SRC_DTS_BINDING_END 682 + +#define NUM_GROUP_COMBINATIONS 16 //adjust as needed + +struct dai_destination { + char *dest_signal; + u32 width; + u32 input; + u32 offset; + u32 offset_ext; + u32 group; +}; + +struct dai_source { + char *source_signal; + u32 selection_code; + u32 group; +}; + +struct group_combination { + u32 associated_groups; + u32 destination_count; + const char **dest_names; +}; + +struct adsp_sru_ctrl { + struct device *dev; + struct pinctrl_dev *pin_dev; + void __iomem *regs; + const char **group_names; + unsigned int *pins; + + u8 dai; + u8 has_extended; + + const struct dai_destination *dest; + u32 dest_count; + + struct group_combination grp_combs[NUM_GROUP_COMBINATIONS]; + u32 grp_combs_cnt; +}; + +#endif diff --git a/include/dt-bindings/pinctrl/adi-adsp-sru.h b/include/dt-bindings/pinctrl/adi-adsp-sru.h new file mode 100644 index 00000000000000..03e1779a99a027 --- /dev/null +++ b/include/dt-bindings/pinctrl/adi-adsp-sru.h @@ -0,0 +1,701 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Analog Devices ADSP family SRU control driver device tree bindings + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +//DAI0 Destinations +#define SPT0_ACLK_I 0 //GROUP_A +#define SPT0_BCLK_I 1 //GROUP_A +#define SPT1_ACLK_I 2 //GROUP_A +#define SPT1_BCLK_I 3 //GROUP_A +#define SPT2_ACLK_I 4 //GROUP_A +#define SPT2_BCLK_I 5 //GROUP_A +#define SRC0_CLK_IP_I 6 //GROUP_A +#define SRC0_CLK_OP_I 7 //GROUP_A +#define SRC1_CLK_IP_I 8 //GROUP_A +#define SRC1_CLK_OP_I 9 //GROUP_A +#define SRC2_CLK_IP_I 10 //GROUP_A +#define SRC2_CLK_OP_I 11 //GROUP_A +#define SRC3_CLK_IP_I 12 //GROUP_A +#define SRC3_CLK_OP_I 13 //GROUP_A +#define SPDIF0_TX_CLK_I 14 //GROUP_A +#define PDM0_CLK0_I 15 //GROUP_A +#define PDM0_BCLK_I 16 //GROUP_A +#define SPDIF0_TX_HFCLK_I 17 //GROUP_A +#define PCG0_EXTCLKA_I 18 //GROUP_A +#define PCG0_EXTCLKB_I 19 //GROUP_A +#define SPDIF0_TX_EXT_SYNC_I 20 //GROUP_A +#define PCG0_SYNC_CLKA_I 21 //GROUP_A +#define PCG0_SYNC_CLKB_I 22 //GROUP_A +#define SPT3_ACLK_I 23 //GROUP_A +#define SPT3_BCLK_I 24 //GROUP_A +#define PCG0_SYNC_CLKE_I 25 //GROUP_A +#define PCG0_SYNC_CLKF_I 26 //GROUP_A +#define PCG0_EXTCLKE_I 27 //GROUP_A +#define PCG0_EXTCLKF_I 28 //GROUP_A +#define SPT0_AD0_I 29 //GROUP_B +#define SPT0_AD1_I 30 //GROUP_B +#define SPT0_BD0_I 31 //GROUP_B +#define SPT0_BD1_I 32 //GROUP_B +#define SPT1_AD0_I 33 //GROUP_B +#define SPT1_AD1_I 34 //GROUP_B +#define SPT1_BD0_I 35 //GROUP_B +#define SPT1_BD1_I 36 //GROUP_B +#define SPT2_AD0_I 37 //GROUP_B +#define SPT2_AD1_I 38 //GROUP_B +#define SPT2_BD0_I 39 //GROUP_B +#define SPT2_BD1_I 40 //GROUP_B +#define SRC0_DAT_IP_I 41 //GROUP_B +#define SRC1_DAT_IP_I 42 //GROUP_B +#define SRC2_DAT_IP_I 43 //GROUP_B +#define SRC3_DAT_IP_I 44 //GROUP_B +#define SRC0_TDM_OP_I 45 //GROUP_B +#define SRC1_TDM_OP_I 46 //GROUP_B +#define SRC2_TDM_OP_I 47 //GROUP_B +#define SRC3_TDM_OP_I 48 //GROUP_B +#define SPDIF0_TX_DAT_I 49 //GROUP_B +#define PDM0_DAT0_I 50 //GROUP_B +#define PDM0_DAT1_I 51 //GROUP_B +#define SPDIF0_RX_I 52 //GROUP_B +#define SPT3_AD0_I 53 //GROUP_B +#define SPT3_AD1_I 54 //GROUP_B +#define SPT3_BD0_I 55 //GROUP_B +#define SPT3_BD1_I 56 //GROUP_B +#define SPT0_AFS_I 57 //GROUP_C +#define SPT0_BFS_I 58 //GROUP_C +#define SPT1_AFS_I 59 //GROUP_C +#define SPT1_BFS_I 60 //GROUP_C +#define SPT2_AFS_I 61 //GROUP_C +#define SPT2_BFS_I 62 //GROUP_C +#define SRC0_FS_IP_I 63 //GROUP_C +#define SRC0_FS_OP_I 64 //GROUP_C +#define SRC1_FS_IP_I 65 //GROUP_C +#define SRC1_FS_OP_I 66 //GROUP_C +#define SRC2_FS_IP_I 67 //GROUP_C +#define SRC2_FS_OP_I 68 //GROUP_C +#define SRC3_FS_IP_I 69 //GROUP_C +#define SRC3_FS_OP_I 70 //GROUP_C +#define SPDIF0_TX_FS_I 71 //GROUP_C +#define SPT3_AFS_I 72 //GROUP_C +#define SPT3_BFS_I 73 //GROUP_C +#define TM0_ACI14_I 74 //GROUP_C +#define PDM0_LRCLK_I 75 //GROUP_C +#define DAI0_PB01_I 76 //GROUP_D +#define DAI0_PB02_I 77 //GROUP_D +#define DAI0_PB03_I 78 //GROUP_D +#define DAI0_PB04_I 79 //GROUP_D +#define DAI0_PB05_I 80 //GROUP_D +#define DAI0_PB06_I 81 //GROUP_D +#define DAI0_PB07_I 82 //GROUP_D +#define DAI0_PB08_I 83 //GROUP_D +#define DAI0_PB09_I 84 //GROUP_D +#define DAI0_PB10_I 85 //GROUP_D +#define DAI0_PB11_I 86 //GROUP_D +#define DAI0_PB12_I 87 //GROUP_D +#define DAI0_PB13_I 88 //GROUP_D +#define DAI0_PB14_I 89 //GROUP_D +#define DAI0_PB15_I 90 //GROUP_D +#define DAI0_PB16_I 91 //GROUP_D +#define DAI0_PB17_I 92 //GROUP_D +#define DAI0_PB18_I 93 //GROUP_D +#define DAI0_PB19_I 94 //GROUP_D +#define DAI0_PB20_I 95 //GROUP_D +#define INV_DAI0_PB19_I 96 //GROUP_D +#define INV_DAI0_PB20_I 97 //GROUP_D +#define DAI0_MISCA0_I 98 //GROUP_E +#define DAI0_INT_6_I 99 //GROUP_E +#define DAI0_MISCA1_I 100 //GROUP_E +#define DAI0_INT_7_I 101 //GROUP_E +#define DAI0_MISCA2_I 102 //GROUP_E +#define DAI0_INT_8_I 103 //GROUP_E +#define DAI0_MISCA3_I 104 //GROUP_E +#define DAI0_INT_9_I 105 //GROUP_E +#define DAI0_MISCA4_I 106 //GROUP_E +#define DAI0_MISCA5_I 107 //GROUP_E +#define DAI0_INV_MISCA4_I 108 //GROUP_E +#define DAI0_INV_MISCA5_I 109 //GROUP_E +#define DAI0_INT_0_I 110 //GROUP_E +#define DAI0_INT_1_I 111 //GROUP_E +#define DAI0_INT_2_I 112 //GROUP_E +#define DAI0_INT_3_I 113 //GROUP_E +#define DAI0_INT_4_I 114 //GROUP_E +#define DAI0_INT_5_I 115 //GROUP_E +#define PCG_HWA_TRIG_I 116 //GROUP_E +#define PCG_HWB_TRIG_I 117 //GROUP_E +#define PCG_HWE_TRIG_I 118 //GROUP_E +#define PCG_HWF_TRIG_I 119 //GROUP_E +#define DAI0_PBEN01_I 120 //GROUP_F +#define DAI0_PBEN02_I 121 //GROUP_F +#define DAI0_PBEN03_I 122 //GROUP_F +#define DAI0_PBEN04_I 123 //GROUP_F +#define DAI0_PBEN05_I 124 //GROUP_F +#define DAI0_PBEN06_I 125 //GROUP_F +#define DAI0_PBEN07_I 126 //GROUP_F +#define DAI0_PBEN08_I 127 //GROUP_F +#define DAI0_PBEN09_I 128 //GROUP_F +#define DAI0_PBEN10_I 129 //GROUP_F +#define DAI0_PBEN11_I 130 //GROUP_F +#define DAI0_PBEN12_I 131 //GROUP_F +#define DAI0_PBEN13_I 132 //GROUP_F +#define DAI0_PBEN14_I 133 //GROUP_F +#define DAI0_PBEN15_I 134 //GROUP_F +#define DAI0_PBEN16_I 135 //GROUP_F +#define DAI0_PBEN17_I 136 //GROUP_F +#define DAI0_PBEN18_I 137 //GROUP_F +#define DAI0_PBEN19_I 138 //GROUP_F +#define DAI0_PBEN20_I 139 //GROUP_F + +//DAI1 Destinations +#define SPT4_ACLK_I 140 //GROUP_A +#define SPT4_BCLK_I 141 //GROUP_A +#define SPT5_ACLK_I 142 //GROUP_A +#define SPT5_BCLK_I 143 //GROUP_A +#define SPT6_ACLK_I 144 //GROUP_A +#define SPT6_BCLK_I 145 //GROUP_A +#define SRC4_CLK_IP_I 146 //GROUP_A +#define SRC4_CLK_OP_I 147 //GROUP_A +#define SRC5_CLK_IP_I 148 //GROUP_A +#define SRC5_CLK_OP_I 149 //GROUP_A +#define SRC6_CLK_IP_I 150 //GROUP_A +#define SRC6_CLK_OP_I 151 //GROUP_A +#define SRC7_CLK_IP_I 152 //GROUP_A +#define SRC7_CLK_OP_I 153 //GROUP_A +#define SPDIF1_TX_CLK_I 154 //GROUP_A +#define PDM1_CLK0_I 155 //GROUP_A +#define PDM1_BCLK_I 156 //GROUP_A +#define SPDIF1_TX_HFCLK_I 157 //GROUP_A +#define PCG0_EXTCLKC_I 158 //GROUP_A +#define PCG0_EXTCLKD_I 159 //GROUP_A +#define SPDIF1_TX_EXT_SYNC_I 160 //GROUP_A +#define PCG0_SYNC_CLKC_I 161 //GROUP_A +#define PCG0_SYNC_CLKD_I 162 //GROUP_A +#define SPT7_ACLK_I 163 //GROUP_A +#define SPT7_BCLK_I 164 //GROUP_A +#define PCG0_SYNC_CLKG_I 165 //GROUP_A +#define PCG0_SYNC_CLKH_I 166 //GROUP_A +#define PCG0_EXTCLKG_I 167 //GROUP_A +#define PCG0_EXTCLKH_I 168 //GROUP_A +#define SPT4_AD0_I 169 //GROUP_B +#define SPT4_AD1_I 170 //GROUP_B +#define SPT4_BD0_I 171 //GROUP_B +#define SPT4_BD1_I 172 //GROUP_B +#define SPT5_AD0_I 173 //GROUP_B +#define SPT5_AD1_I 174 //GROUP_B +#define SPT5_BD0_I 175 //GROUP_B +#define SPT5_BD1_I 176 //GROUP_B +#define SPT6_AD0_I 177 //GROUP_B +#define SPT6_AD1_I 178 //GROUP_B +#define SPT6_BD0_I 179 //GROUP_B +#define SPT6_BD1_I 180 //GROUP_B +#define SRC4_DAT_IP_I 181 //GROUP_B +#define SRC5_DAT_IP_I 182 //GROUP_B +#define SRC6_DAT_IP_I 183 //GROUP_B +#define SRC7_DAT_IP_I 184 //GROUP_B +#define SRC4_TDM_OP_I 185 //GROUP_B +#define SRC5_TDM_OP_I 186 //GROUP_B +#define SRC6_TDM_OP_I 187 //GROUP_B +#define SRC7_TDM_OP_I 188 //GROUP_B +#define SPDIF1_TX_DAT_I 189 //GROUP_B +#define PDM1_DAT0_I 190 //GROUP_B +#define PDM1_DAT1_I 191 //GROUP_B +#define SPDIF1_RX_I 192 //GROUP_B +#define SPT7_AD0_I 193 //GROUP_B +#define SPT7_AD1_I 194 //GROUP_B +#define SPT7_BD0_I 195 //GROUP_B +#define SPT7_BD1_I 196 //GROUP_B +#define SPT4_AFS_I 197 //GROUP_C +#define SPT4_BFS_I 198 //GROUP_C +#define SPT5_AFS_I 199 //GROUP_C +#define SPT5_BFS_I 200 //GROUP_C +#define SPT6_AFS_I 201 //GROUP_C +#define SPT6_BFS_I 202 //GROUP_C +#define SRC4_FS_IP_I 203 //GROUP_C +#define SRC4_FS_OP_I 204 //GROUP_C +#define SRC5_FS_IP_I 205 //GROUP_C +#define SRC5_FS_OP_I 206 //GROUP_C +#define SRC6_FS_IP_I 207 //GROUP_C +#define SRC6_FS_OP_I 208 //GROUP_C +#define SRC7_FS_IP_I 209 //GROUP_C +#define SRC7_FS_OP_I 210 //GROUP_C +#define SPDIF1_TX_FS_I 211 //GROUP_C +#define SPT7_AFS_I 212 //GROUP_C +#define SPT7_BFS_I 213 //GROUP_C +#define TM0_ACI15_I 214 //GROUP_C +#define PDM1_LRCLK_I 215 //GROUP_C +#define DAI1_PB01_I 216 //GROUP_D +#define DAI1_PB02_I 217 //GROUP_D +#define DAI1_PB03_I 218 //GROUP_D +#define DAI1_PB04_I 219 //GROUP_D +#define DAI1_PB05_I 220 //GROUP_D +#define DAI1_PB06_I 221 //GROUP_D +#define DAI1_PB07_I 222 //GROUP_D +#define DAI1_PB08_I 223 //GROUP_D +#define DAI1_PB09_I 224 //GROUP_D +#define DAI1_PB10_I 225 //GROUP_D +#define DAI1_PB11_I 226 //GROUP_D +#define DAI1_PB12_I 227 //GROUP_D +#define DAI1_PB13_I 228 //GROUP_D +#define DAI1_PB14_I 229 //GROUP_D +#define DAI1_PB15_I 230 //GROUP_D +#define DAI1_PB16_I 231 //GROUP_D +#define DAI1_PB17_I 232 //GROUP_D +#define DAI1_PB18_I 233 //GROUP_D +#define DAI1_PB19_I 234 //GROUP_D +#define DAI1_PB20_I 235 //GROUP_D +#define INV_DAI1_PB19_I 236 //GROUP_D +#define INV_DAI1_PB20_I 237 //GROUP_D +#define DAI1_MISCA0_I 238 //GROUP_E +#define DAI1_INT_6_I 239 //GROUP_E +#define DAI1_MISCA1_I 240 //GROUP_E +#define DAI1_INT_7_I 241 //GROUP_E +#define DAI1_MISCA2_I 242 //GROUP_E +#define DAI1_INT_8_I 243 //GROUP_E +#define DAI1_MISCA3_I 244 //GROUP_E +#define DAI1_INT_9_I 245 //GROUP_E +#define DAI1_MISCA4_I 246 //GROUP_E +#define DAI1_MISCA5_I 247 //GROUP_E +#define DAI1_INV_MISCA4_I 248 //GROUP_E +#define DAI1_INV_MISCA5_I 249 //GROUP_E +#define DAI1_INT_0_I 250 //GROUP_E +#define DAI1_INT_1_I 251 //GROUP_E +#define DAI1_INT_2_I 252 //GROUP_E +#define DAI1_INT_3_I 253 //GROUP_E +#define DAI1_INT_4_I 254 //GROUP_E +#define DAI1_INT_5_I 255 //GROUP_E +#define PCG_HWC_TRIG_I 256 //GROUP_E +#define PCG_HWD_TRIG_I 257 //GROUP_E +#define PCG_HWG_TRIG_I 258 //GROUP_E +#define PCG_HWH_TRIG_I 259 //GROUP_E +#define DAI1_PBEN01_I 260 //GROUP_F +#define DAI1_PBEN02_I 261 //GROUP_F +#define DAI1_PBEN03_I 262 //GROUP_F +#define DAI1_PBEN04_I 263 //GROUP_F +#define DAI1_PBEN05_I 264 //GROUP_F +#define DAI1_PBEN06_I 265 //GROUP_F +#define DAI1_PBEN07_I 266 //GROUP_F +#define DAI1_PBEN08_I 267 //GROUP_F +#define DAI1_PBEN09_I 268 //GROUP_F +#define DAI1_PBEN10_I 269 //GROUP_F +#define DAI1_PBEN11_I 270 //GROUP_F +#define DAI1_PBEN12_I 271 //GROUP_F +#define DAI1_PBEN13_I 272 //GROUP_F +#define DAI1_PBEN14_I 273 //GROUP_F +#define DAI1_PBEN15_I 274 //GROUP_F +#define DAI1_PBEN16_I 275 //GROUP_F +#define DAI1_PBEN17_I 276 //GROUP_F +#define DAI1_PBEN18_I 277 //GROUP_F +#define DAI1_PBEN19_I 278 //GROUP_F +#define DAI1_PBEN20_I 279 //GROUP_F + +//DAI0 Sources +#define DAI0_PB01_O_ABCDE 280 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB02_O_ABCDE 281 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB03_O_ABCDE 282 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB04_O_ABCDE 283 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB05_O_ABCDE 284 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB06_O_ABCDE 285 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB07_O_ABCDE 286 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB08_O_ABCDE 287 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB09_O_ABCDE 288 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB10_O_ABCDE 289 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB11_O_ABCDE 290 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB12_O_ABCDE 291 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB13_O_ABCDE 292 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB14_O_ABCDE 293 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB15_O_ABCDE 294 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB16_O_ABCDE 295 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB17_O_ABCDE 296 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB18_O_ABCDE 297 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB19_O_ABCDE 298 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_PB20_O_ABCDE 299 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI0_LOW_ACE 300 //GROUP_A GROUP_C GROUP_E +#define DAI0_HIGH_ACE 301 //GROUP_A GROUP_C GROUP_E +#define SPT0_AD0_O_BD 302 //GROUP_B GROUP_D +#define SPT0_AD1_O_BD 303 //GROUP_B GROUP_D +#define SPT0_BD0_O_BD 304 //GROUP_B GROUP_D +#define SPT0_BD1_O_BD 305 //GROUP_B GROUP_D +#define SPT1_AD0_O_BD 306 //GROUP_B GROUP_D +#define SPT1_AD1_O_BD 307 //GROUP_B GROUP_D +#define SPT1_BD0_O_BD 308 //GROUP_B GROUP_D +#define SPT1_BD1_O_BD 309 //GROUP_B GROUP_D +#define SPT2_AD0_O_BD 310 //GROUP_B GROUP_D +#define SPT2_AD1_O_BD 311 //GROUP_B GROUP_D +#define SPT2_BD0_O_BD 312 //GROUP_B GROUP_D +#define SPT2_BD1_O_BD 313 //GROUP_B GROUP_D +#define DAI0_CRS_PB03_O_A 314 //GROUP_A +#define SPT0_ACLK_O_A 315 //GROUP_A +#define SPT0_BCLK_O_A 316 //GROUP_A +#define SPT1_ACLK_O_A 317 //GROUP_A +#define SPT1_BCLK_O_A 318 //GROUP_A +#define SPT2_ACLK_O_A 319 //GROUP_A +#define SPT2_BCLK_O_A 320 //GROUP_A +#define SPDIF0_RX_CLK_O_A 321 //GROUP_A +#define SPDIF0_RX_TDMCLK_O_A 322 //GROUP_A +#define PCG0_CLKA_O_A 323 //GROUP_A +#define PCG0_CLKB_O_A 324 //GROUP_A +#define SRC0_DAT_OP_O_B 325 //GROUP_B +#define SRC1_DAT_OP_O_B 326 //GROUP_B +#define SRC2_DAT_OP_O_B 327 //GROUP_B +#define SRC3_DAT_OP_O_B 328 //GROUP_B +#define SRC0_TDM_IP_O_B 329 //GROUP_B +#define SRC1_TDM_IP_O_B 330 //GROUP_B +#define SRC2_TDM_IP_O_B 331 //GROUP_B +#define SRC3_TDM_IP_O_B 332 //GROUP_B +#define SPDIF0_RX_DAT_O_B 333 //GROUP_B +#define SPT3_AD0_O_B 334 //GROUP_B +#define SPT3_AD1_O_B 335 //GROUP_B +#define SPT3_BD0_O_B 336 //GROUP_B +#define SPT3_BD1_O_B 337 //GROUP_B +#define SPDIF0_TX_O_B 338 //GROUP_B +#define SRC7_CRS_DAT_OP_O_B 339 //GROUP_B +#define SRC7_CRS_TDM_IP_O_B 340 //GROUP_B +#define PDM0_SDATA_O_B 341 //GROUP_B +#define DAI0_LOW_B 342 //GROUP_B +#define DAI0_HIGH_B 343 //GROUP_B +#define DAI0_CRS_PB04_O_C 344 //GROUP_C +#define SPT0_AFS_O_C 345 //GROUP_C +#define SPT0_BFS_O_C 346 //GROUP_C +#define SPT1_AFS_O_C 347 //GROUP_C +#define SPT1_BFS_O_C 348 //GROUP_C +#define SPT2_AFS_O_C 349 //GROUP_C +#define SPT2_BFS_O_C 350 //GROUP_C +#define SPDIF0_FS_O_C 351 //GROUP_C +#define PCG0_FSA_O_C 352 //GROUP_C +#define PCG0_FSB_O_C 353 //GROUP_C +#define SPT0_ACLK_O_D 354 //GROUP_D +#define SPT0_BCLK_O_D 355 //GROUP_D +#define SPT1_ACLK_O_D 356 //GROUP_D +#define SPT1_BCLK_O_D 357 //GROUP_D +#define SPT2_ACLK_O_D 358 //GROUP_D +#define SPT2_BCLK_O_D 359 //GROUP_D +#define SPT0_AFS_O_D 360 //GROUP_D +#define SPT0_BFS_O_D 361 //GROUP_D +#define SPT1_AFS_O_D 362 //GROUP_D +#define SPT1_BFS_O_D 363 //GROUP_D +#define SPT2_AFS_O_D 364 //GROUP_D +#define SPT2_BFS_O_D 365 //GROUP_D +#define SPT3_AD0_O_D 366 //GROUP_D +#define SPT3_AD1_O_D 367 //GROUP_D +#define SPT3_BD0_O_D 368 //GROUP_D +#define SPT3_BD1_O_D 369 //GROUP_D +#define MLB0_CLKOUT_O_D 370 //GROUP_D +#define SPDIF0_TX_BLKSTART_O_D 371 //GROUP_D +#define SPT3_ACLK_O_D 372 //GROUP_D +#define SPT3_BCLK_O_D 373 //GROUP_D +#define SPT3_AFS_O_D 374 //GROUP_D +#define SPT3_BFS_O_D 375 //GROUP_D +#define PCG0_CLKA_O_D 376 //GROUP_D +#define PCG0_CLKB_O_D 377 //GROUP_D +#define PCG0_FSA_O_D 378 //GROUP_D +#define PCG0_FSB_O_D 379 //GROUP_D +#define SRC0_DAT_OP_O_D 380 //GROUP_D +#define SRC1_DAT_OP_O_D 381 //GROUP_D +#define SRC2_DAT_OP_O_D 382 //GROUP_D +#define SRC3_DAT_OP_O_D 383 //GROUP_D +#define SPDIF0_RX_DAT_O_D 384 //GROUP_D +#define SPDIF0_RX_FS_O_D 385 //GROUP_D +#define SPDIF0_RX_CLK_O_D 386 //GROUP_D +#define SPDIF0_RX_TDMCLK_O_D 387 //GROUP_D +#define SPDIF0_TX_O_D 388 //GROUP_D +#define SPT0_ATDV_O_D 389 //GROUP_D +#define SPT0_BTDV_O_D 390 //GROUP_D +#define SPT1_ATDV_O_D 391 //GROUP_D +#define SPT1_BTDV_O_D 392 //GROUP_D +#define SPT2_ATDV_O_D 393 //GROUP_D +#define SPT2_BTDV_O_D 394 //GROUP_D +#define SPT3_ATDV_O_D 395 //GROUP_D +#define SPT3_BTDV_O_D 396 //GROUP_D +#define SPDIF0_RX_LRCLK_REF_O_D 397 //GROUP_D +#define SPDIF0_RX_LRCLK_FB_O_D 398 //GROUP_D +#define PCG0_CRS_CLKC_O_D 399 //GROUP_D +#define PCG0_CRS_CLKD_O_D 400 //GROUP_D +#define PCG0_CRS_FSC_O_D 401 //GROUP_D +#define PCG0_CRS_FSD_O_D 402 //GROUP_D +#define DAI0_CRS_PB03_O_D 403 //GROUP_D +#define DAI0_CRS_PB04_O_D 404 //GROUP_D +#define PCG_CLKE_O_D 405 //GROUP_D +#define PCG_CLKF_O_D 406 //GROUP_D +#define PCG_FSE_O_D 407 //GROUP_D +#define PCG_FSF_O_D 408 //GROUP_D +#define PCG_CLKA_INV_O_D 409 //GROUP_D +#define PCG_CLKB_INV_O_D 410 //GROUP_D +#define PCG_CLKE_INV_O_D 411 //GROUP_D +#define PCG_CLKF_INV_O_D 412 //GROUP_D +#define PCG_FSA_INV_O_D 413 //GROUP_D +#define PCG_FSB_INV_O_D 414 //GROUP_D +#define PCG_FSE_INV_O_D 415 //GROUP_D +#define PCG_FSF_INV_O_D 416 //GROUP_D +#define PDM0_CLK0_O_D 417 //GROUP_D +#define PDM0_SDATA_O_D 418 //GROUP_D +#define DAI0_LOW_D 419 //GROUP_D +#define DAI0_HIGH_D 420 //GROUP_D +#define SPT0A_FS_O_E 421 //GROUP_E +#define SPT0B_FS_O_E 422 //GROUP_E +#define SPT1A_FS_O_E 423 //GROUP_E +#define SPT1B_FS_O_E 424 //GROUP_E +#define SPT2A_FS_O_E 425 //GROUP_E +#define SPT2B_FS_O_E 426 //GROUP_E +#define SPDIF0_TX_BLKSTART_O_E 427 //GROUP_E +#define PCG0_FSA_O_E 428 //GROUP_E +#define PCG0_CLKB_O_E 429 //GROUP_E +#define PCG0_FSB_O_E 430 //GROUP_E +#define DAI0_LOW_F 431 //GROUP_F +#define DAI0_HIGH_F 432 //GROUP_F +#define DAI0_MISCA0_O_F 433 //GROUP_F +#define DAI0_MISCA1_O_F 434 //GROUP_F +#define DAI0_MISCA2_O_F 435 //GROUP_F +#define DAI0_MISCA3_O_F 436 //GROUP_F +#define DAI0_MISCA4_O_F 437 //GROUP_F +#define DAI0_MISCA5_O_F 438 //GROUP_F +#define SPT0_ACLK_PBEN_O_F 439 //GROUP_F +#define SPT0_AFS_PBEN_O_F 440 //GROUP_F +#define SPT0_AD0_PBEN_O_F 441 //GROUP_F +#define SPT0_AD1_PBEN_O_F 442 //GROUP_F +#define SPT0_BCLK_PBEN_O_F 443 //GROUP_F +#define SPT0_BFS_PBEN_O_F 444 //GROUP_F +#define SPT0_BD0_PBEN_O_F 445 //GROUP_F +#define SPT0_BD1_PBEN_O_F 446 //GROUP_F +#define SPT1_ACLK_PBEN_O_F 447 //GROUP_F +#define SPT1_AFS_PBEN_O_F 448 //GROUP_F +#define SPT1_AD0_PBEN_O_F 449 //GROUP_F +#define SPT1_AD1_PBEN_O_F 450 //GROUP_F +#define SPT1_BCLK_PBEN_O_F 451 //GROUP_F +#define SPT1_BFS_PBEN_O_F 452 //GROUP_F +#define SPT1_BD0_PBEN_O_F 453 //GROUP_F +#define SPT1_BD1_PBEN_O_F 454 //GROUP_F +#define SPT2_ACLK_PBEN_O_F 455 //GROUP_F +#define SPT2_AFS_PBEN_O_F 456 //GROUP_F +#define SPT2_AD0_PBEN_O_F 457 //GROUP_F +#define SPT2_AD1_PBEN_O_F 458 //GROUP_F +#define SPT2_BCLK_PBEN_O_F 459 //GROUP_F +#define SPT2_BFS_PBEN_O_F 460 //GROUP_F +#define SPT2_BD0_PBEN_O_F 461 //GROUP_F +#define SPT2_BD1_PBEN_O_F 462 //GROUP_F +#define SPT3_ACLK_PBEN_O_F 463 //GROUP_F +#define SPT3_AFS_PBEN_O_F 464 //GROUP_F +#define SPT3_AD0_PBEN_O_F 465 //GROUP_F +#define SPT3_AD1_PBEN_O_F 466 //GROUP_F +#define SPT3_BCLK_PBEN_O_F 467 //GROUP_F +#define SPT3_BFS_PBEN_O_F 468 //GROUP_F +#define SPT3_BD0_PBEN_O_F 469 //GROUP_F +#define SPT3_BD1_PBEN_O_F 470 //GROUP_F +#define SPT0_ATDV_PBEN_O_F 471 //GROUP_F +#define SPT0_BTDV_PBEN_O_F 472 //GROUP_F +#define SPT1_ATDV_PBEN_O_F 473 //GROUP_F +#define SPT1_BTDV_PBEN_O_F 474 //GROUP_F +#define SPT2_ATDV_PBEN_O_F 475 //GROUP_F +#define SPT2_BTDV_PBEN_O_F 476 //GROUP_F +#define SPT3_ATDV_PBEN_O_F 477 //GROUP_F +#define SPT3_BTDV_PBEN_O_F 478 //GROUP_F +#define PDM0_CLK0_OE_O_F 479 //GROUP_F +#define PDM0_SDATA_OE_O_F 480 //GROUP_F + +//DAI1 Sources +#define DAI1_PB01_O_ABCDE 481 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB02_O_ABCDE 482 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB03_O_ABCDE 483 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB04_O_ABCDE 484 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB05_O_ABCDE 485 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB06_O_ABCDE 486 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB07_O_ABCDE 487 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB08_O_ABCDE 488 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB09_O_ABCDE 489 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB10_O_ABCDE 490 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB11_O_ABCDE 491 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB12_O_ABCDE 492 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB13_O_ABCDE 493 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB14_O_ABCDE 494 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB15_O_ABCDE 495 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB16_O_ABCDE 496 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB17_O_ABCDE 497 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB18_O_ABCDE 498 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB19_O_ABCDE 499 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_PB20_O_ABCDE 500 //GROUP_A GROUP_B GROUP_C GROUP_D GROUP_E +#define DAI1_LOW_ACE 501 //GROUP_A GROUP_C GROUP_E +#define DAI1_HIGH_ACE 502 //GROUP_A GROUP_C GROUP_E +#define SPT4_AD0_O_BD 503 //GROUP_B GROUP_D +#define SPT4_AD1_O_BD 504 //GROUP_B GROUP_D +#define SPT4_BD0_O_BD 505 //GROUP_B GROUP_D +#define SPT4_BD1_O_BD 506 //GROUP_B GROUP_D +#define SPT5_AD0_O_BD 507 //GROUP_B GROUP_D +#define SPT5_AD1_O_BD 508 //GROUP_B GROUP_D +#define SPT5_BD0_O_BD 509 //GROUP_B GROUP_D +#define SPT5_BD1_O_BD 510 //GROUP_B GROUP_D +#define SPT6_AD0_O_BD 511 //GROUP_B GROUP_D +#define SPT6_AD1_O_BD 512 //GROUP_B GROUP_D +#define SPT6_BD0_O_BD 513 //GROUP_B GROUP_D +#define SPT6_BD1_O_BD 514 //GROUP_B GROUP_D +#define DAI1_CRS_PB03_O_A 515 //GROUP_A +#define SPT4_ACLK_O_A 516 //GROUP_A +#define SPT4_BCLK_O_A 517 //GROUP_A +#define SPT5_ACLK_O_A 518 //GROUP_A +#define SPT5_BCLK_O_A 519 //GROUP_A +#define SPT6_ACLK_O_A 520 //GROUP_A +#define SPT6_BCLK_O_A 521 //GROUP_A +#define SPDIF1_RX_CLK_O_A 522 //GROUP_A +#define SPDIF1_RX_TDMCLK_O_A 523 //GROUP_A +#define PCG0_CLKC_O_A 524 //GROUP_A +#define PCG0_CLKD_O_A 525 //GROUP_A +#define SRC4_DAT_OP_O_B 526 //GROUP_B +#define SRC5_DAT_OP_O_B 527 //GROUP_B +#define SRC6_DAT_OP_O_B 528 //GROUP_B +#define SRC7_DAT_OP_O_B 529 //GROUP_B +#define SRC4_TDM_IP_O_B 530 //GROUP_B +#define SRC5_TDM_IP_O_B 531 //GROUP_B +#define SRC6_TDM_IP_O_B 532 //GROUP_B +#define SRC7_TDM_IP_O_B 533 //GROUP_B +#define SPDIF1_RX_DAT_O_B 534 //GROUP_B +#define SPT7_AD0_O_B 535 //GROUP_B +#define SPT7_AD1_O_B 536 //GROUP_B +#define SPT7_BD0_O_B 537 //GROUP_B +#define SPT7_BD1_O_B 538 //GROUP_B +#define SPDIF1_TX_O_B 539 //GROUP_B +#define SRC3_CRS_DAT_OP_O_B 540 //GROUP_B +#define SRC3_CRS_TDM_IP_O_B 541 //GROUP_B +#define PDM1_SDATA_O_B 542 //GROUP_B +#define DAI1_LOW_B 543 //GROUP_B +#define DAI1_HIGH_B 544 //GROUP_B +#define DAI1_CRS_PB04_O_C 545 //GROUP_C +#define SPT4_AFS_O_C 546 //GROUP_C +#define SPT4_BFS_O_C 547 //GROUP_C +#define SPT5_AFS_O_C 548 //GROUP_C +#define SPT5_BFS_O_C 549 //GROUP_C +#define SPT6_AFS_O_C 550 //GROUP_C +#define SPT6_BFS_O_C 551 //GROUP_C +#define SPDIF1_FS_O_C 552 //GROUP_C +#define PCG0_FSC_O_C 553 //GROUP_C +#define PCG0_FSD_O_C 554 //GROUP_C +#define SPT4_ACLK_O_D 555 //GROUP_D +#define SPT4_BCLK_O_D 556 //GROUP_D +#define SPT5_ACLK_O_D 557 //GROUP_D +#define SPT5_BCLK_O_D 558 //GROUP_D +#define SPT6_ACLK_O_D 559 //GROUP_D +#define SPT6_BCLK_O_D 560 //GROUP_D +#define SPT4_AFS_O_D 561 //GROUP_D +#define SPT4_BFS_O_D 562 //GROUP_D +#define SPT5_AFS_O_D 563 //GROUP_D +#define SPT5_BFS_O_D 564 //GROUP_D +#define SPT6_AFS_O_D 565 //GROUP_D +#define SPT6_BFS_O_D 566 //GROUP_D +#define SPT7_AD0_O_D 567 //GROUP_D +#define SPT7_AD1_O_D 568 //GROUP_D +#define SPT7_BD0_O_D 569 //GROUP_D +#define SPT7_BD1_O_D 570 //GROUP_D +#define SPDIF1_TX_BLKSTART_O_D 572 //GROUP_D +#define SPT7_ACLK_O_D 573 //GROUP_D +#define SPT7_BCLK_O_D 574 //GROUP_D +#define SPT7_AFS_O_D 575 //GROUP_D +#define SPT7_BFS_O_D 576 //GROUP_D +#define PCG0_CLKC_O_D 577 //GROUP_D +#define PCG0_CLKD_O_D 578 //GROUP_D +#define PCG0_FSC_O_D 579 //GROUP_D +#define PCG0_FSD_O_D 580 //GROUP_D +#define SRC4_DAT_OP_O_D 581 //GROUP_D +#define SRC5_DAT_OP_O_D 582 //GROUP_D +#define SRC6_DAT_OP_O_D 583 //GROUP_D +#define SRC7_DAT_OP_O_D 584 //GROUP_D +#define SPDIF1_RX_DAT_O_D 585 //GROUP_D +#define SPDIF1_RX_FS_O_D 586 //GROUP_D +#define SPDIF1_RX_CLK_O_D 587 //GROUP_D +#define SPDIF1_RX_TDMCLK_O_D 588 //GROUP_D +#define SPDIF1_TX_O_D 589 //GROUP_D +#define SPT4_ATDV_O_D 590 //GROUP_D +#define SPT4_BTDV_O_D 591 //GROUP_D +#define SPT5_ATDV_O_D 592 //GROUP_D +#define SPT5_BTDV_O_D 593 //GROUP_D +#define SPT6_ATDV_O_D 594 //GROUP_D +#define SPT6_BTDV_O_D 595 //GROUP_D +#define SPT7_ATDV_O_D 596 //GROUP_D +#define SPT7_BTDV_O_D 597 //GROUP_D +#define SPDIF1_RX_LRCLK_REF_O_D 598 //GROUP_D +#define SPDIF1_RX_LRCLK_FB_O_D 599 //GROUP_D +#define PCG0_CRS_CLKA_O_D 600 //GROUP_D +#define PCG0_CRS_CLKB_O_D 601 //GROUP_D +#define PCG0_CRS_FSA_O_D 602 //GROUP_D +#define PCG0_CRS_FSB_O_D 603 //GROUP_D +#define DAI1_CRS_PB03_O_D 604 //GROUP_D +#define DAI1_CRS_PB04_O_D 605 //GROUP_D +#define PCG_CLKG_O_D 606 //GROUP_D +#define PCG_CLKH_O_D 607 //GROUP_D +#define PCG_FSG_O_D 608 //GROUP_D +#define PCG_FSH_O_D 609 //GROUP_D +#define PCG_CLKC_INV_O_D 610 //GROUP_D +#define PCG_CLKG_INV_O_D 611 //GROUP_D +#define PCG_CLKD_INV_O_D 612 //GROUP_D +#define PCG_CLKH_INV_O_D 613 //GROUP_D +#define PCG_FSC_INV_O_D 614 //GROUP_D +#define PCG_FSD_INV_O_D 615 //GROUP_D +#define PCG_FSG_INV_O_D 616 //GROUP_D +#define PCG_FSH_INV_O_D 617 //GROUP_D +#define PDM1_CLK0_O_D 618 //GROUP_D +#define PDM1_SDATA_O_D 619 //GROUP_D +#define DAI1_LOW_D 620 //GROUP_D +#define DAI1_HIGH_D 621 //GROUP_D +#define SPT4A_FS_O_E 622 //GROUP_E +#define SPT4B_FS_O_E 623 //GROUP_E +#define SPT5A_FS_O_E 624 //GROUP_E +#define SPT5B_FS_O_E 625 //GROUP_E +#define SPT6A_FS_O_E 626 //GROUP_E +#define SPT6B_FS_O_E 627 //GROUP_E +#define SPDIF1_TX_BLKSTART_O_E 628 //GROUP_E +#define PCG0_FSC_O_E 629 //GROUP_E +#define PCG0_CLKD_O_E 630 //GROUP_E +#define PCG0_FSD_O_E 631 //GROUP_E +#define DAI1_LOW_F 632 //GROUP_F +#define DAI1_HIGH_F 633 //GROUP_F +#define DAI1_MISCA0_O_F 634 //GROUP_F +#define DAI1_MISCA1_O_F 635 //GROUP_F +#define DAI1_MISCA2_O_F 636 //GROUP_F +#define DAI1_MISCA3_O_F 637 //GROUP_F +#define DAI1_MISCA4_O_F 638 //GROUP_F +#define DAI1_MISCA5_O_F 639 //GROUP_F +#define SPT4_ACLK_PBEN_O_F 640 //GROUP_F +#define SPT4_AFS_PBEN_O_F 641 //GROUP_F +#define SPT4_AD0_PBEN_O_F 642 //GROUP_F +#define SPT4_AD1_PBEN_O_F 643 //GROUP_F +#define SPT4_BCLK_PBEN_O_F 644 //GROUP_F +#define SPT4_BFS_PBEN_O_F 645 //GROUP_F +#define SPT4_BD0_PBEN_O_F 646 //GROUP_F +#define SPT4_BD1_PBEN_O_F 647 //GROUP_F +#define SPT5_ACLK_PBEN_O_F 648 //GROUP_F +#define SPT5_AFS_PBEN_O_F 649 //GROUP_F +#define SPT5_AD0_PBEN_O_F 650 //GROUP_F +#define SPT5_AD1_PBEN_O_F 651 //GROUP_F +#define SPT5_BCLK_PBEN_O_F 652 //GROUP_F +#define SPT5_BFS_PBEN_O_F 653 //GROUP_F +#define SPT5_BD0_PBEN_O_F 654 //GROUP_F +#define SPT5_BD1_PBEN_O_F 655 //GROUP_F +#define SPT6_ACLK_PBEN_O_F 656 //GROUP_F +#define SPT6_AFS_PBEN_O_F 657 //GROUP_F +#define SPT6_AD0_PBEN_O_F 658 //GROUP_F +#define SPT6_AD1_PBEN_O_F 659 //GROUP_F +#define SPT6_BCLK_PBEN_O_F 660 //GROUP_F +#define SPT6_BFS_PBEN_O_F 661 //GROUP_F +#define SPT6_BD0_PBEN_O_F 662 //GROUP_F +#define SPT6_BD1_PBEN_O_F 663 //GROUP_F +#define SPT7_ACLK_PBEN_O_F 664 //GROUP_F +#define SPT7_AFS_PBEN_O_F 665 //GROUP_F +#define SPT7_AD0_PBEN_O_F 666 //GROUP_F +#define SPT7_AD1_PBEN_O_F 667 //GROUP_F +#define SPT7_BCLK_PBEN_O_F 668 //GROUP_F +#define SPT7_BFS_PBEN_O_F 669 //GROUP_F +#define SPT7_BD0_PBEN_O_F 670 //GROUP_F +#define SPT7_BD1_PBEN_O_F 671 //GROUP_F +#define SPT4_ATDV_PBEN_O_F 672 //GROUP_F +#define SPT4_BTDV_PBEN_O_F 673 //GROUP_F +#define SPT5_ATDV_PBEN_O_F 674 //GROUP_F +#define SPT5_BTDV_PBEN_O_F 675 //GROUP_F +#define SPT6_ATDV_PBEN_O_F 676 //GROUP_F +#define SPT6_BTDV_PBEN_O_F 677 //GROUP_F +#define SPT7_ATDV_PBEN_O_F 678 //GROUP_F +#define SPT7_BTDV_PBEN_O_F 679 //GROUP_F +#define PDM1_CLK0_OE_O_F 680 //GROUP_F +#define PDM1_SDATA_OE_O_F 681 //GROUP_F From 8e1d041f6793bdf6626c93e4e9fc6c30d4e5d80e Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 17:27:05 +0200 Subject: [PATCH 16/85] ARM: Support 32-bit ADSP-SC5xx SoCs Signed-off-by: Philip Molloy --- arch/arm/Kconfig | 7 ++--- arch/arm/Makefile | 1 + arch/arm/mach-sc5xx/Kconfig | 43 +++++++++++++++++++++++++++++++ arch/arm/mach-sc5xx/Makefile.boot | 26 +++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-sc5xx/Kconfig create mode 100644 arch/arm/mach-sc5xx/Makefile.boot diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 70cd3b5b5a0599..84f4cc2fe07e36 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -443,6 +443,8 @@ source "arch/arm/mach-s5pv210/Kconfig" source "arch/arm/mach-sa1100/Kconfig" +source "arch/arm/mach-sc5xx/Kconfig" + source "arch/arm/mach-shmobile/Kconfig" source "arch/arm/mach-socfpga/Kconfig" @@ -1101,7 +1103,6 @@ config HZ_500 config HZ_1000 bool "1000 Hz" - endchoice config HZ @@ -1486,7 +1487,7 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND bool "Extend with bootloader kernel arguments" help The command-line arguments provided by the boot loader will be - appended to the the device tree bootargs property. + appended to the device tree bootargs property. endchoice @@ -1628,7 +1629,7 @@ config DMI continue to boot on existing non-UEFI platforms. NOTE: This does *NOT* enable or encourage the use of DMI quirks, - i.e., the the practice of identifying the platform via DMI to + i.e., the practice of identifying the platform via DMI to decide whether certain workarounds for buggy hardware and/or firmware need to be enabled. This would require the DMI subsystem to be enabled much earlier than we do on ARM, which is non-trivial. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index b7de4b6b284ca2..fd3e8485e9f6ce 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -215,6 +215,7 @@ machine-$(CONFIG_ARCH_RPC) += rpc machine-$(CONFIG_PLAT_SAMSUNG) += s3c machine-$(CONFIG_ARCH_S5PV210) += s5pv210 machine-$(CONFIG_ARCH_SA1100) += sa1100 +machine-$(CONFIG_ARCH_SC5XX) += sc5xx machine-$(CONFIG_ARCH_RENESAS) += shmobile machine-$(CONFIG_ARCH_INTEL_SOCFPGA) += socfpga machine-$(CONFIG_ARCH_STI) += sti diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig new file mode 100644 index 00000000000000..a1813a0ecf929b --- /dev/null +++ b/arch/arm/mach-sc5xx/Kconfig @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later + + +config ARCH_SC5XX + bool "ADI ADSP-SC5XX Family" + select CPU_V7 + select ARM_GIC + select VFP + select NEON + select MIGHT_HAVE_CACHE_L2X0 + select OF + select HAVE_CLK + select IRQ_DOMAIN + select SPARSE_IRQ + select GENERIC_CLOCKEVENTS + select COMMON_CLK + select GPIOLIB + select PINCTRL + select TIMER_OF + select COUNTER + select PINCTRL_ADSP_SC5XX + select GPIO_ADI_ADSP_PORT + select GENERIC_IRQ_MULTI_HANDLER + +config ARCH_SC59X + bool "ADI SC59x SoCs (Cortex-A5-based)" + depends on ARCH_SC5XX + select COMMON_CLK_ADI_SC594 + help + This enables support for 32-bit Cortex-A5-based ADI ADSP SC-59X + SoCs, like the SC594. It does not include the 64-bit Cortex-A55-based + SoCs (see ARCH_SC59X_64 for those). + +config MACH_SC594_SOM + bool "ADI SC594-SOM board" + depends on ARCH_SC59X + select MIGHT_HAVE_PCI + select HIGHMEM + select HIGHPTE + help + Say 'Y' here if you want your kernel to run on the ADI + SC594-SOM-EZKIT or SC594-SOM-EZLITE board. + diff --git a/arch/arm/mach-sc5xx/Makefile.boot b/arch/arm/mach-sc5xx/Makefile.boot new file mode 100644 index 00000000000000..4448a4ccfd675b --- /dev/null +++ b/arch/arm/mach-sc5xx/Makefile.boot @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +ifeq ($(CONFIG_MACH_SC573_EZKIT),y) +zreladdr-y += 0x82008000 +params_phys-y := 0x82000100 +endif + +ifeq ($(CONFIG_MACH_SC584_EZKIT),y) +zreladdr-y += 0x89008000 +params_phys-y := 0x89000100 +endif + +ifeq ($(CONFIG_MACH_SC589_EZKIT),y) +zreladdr-y += 0xC2008000 +params_phys-y := 0xC2000100 +endif + +ifeq ($(CONFIG_MACH_SC589_MINI),y) +zreladdr-y += 0xC2008000 +params_phys-y := 0xC2000100 +endif + +ifeq ($(CONFIG_MACH_SC594_SOM),y) +zreladdr-y += 0xA0008000 +params_phys-y := 0xA0000100 +endif From 3a0845db30cf780ae451403a5979b704f5796163 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 17:31:41 +0200 Subject: [PATCH 17/85] soc: adi: Add initial support for SC5xx SoCs Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- drivers/soc/Makefile | 1 + drivers/soc/adi/Makefile | 10 + drivers/soc/adi/mach-sc59x/Makefile | 5 + drivers/soc/adi/mach-sc59x/core.c | 54 +++++ drivers/soc/adi/mach-sc59x/core.h | 26 +++ drivers/soc/adi/mach-sc5xx/Makefile | 5 + drivers/soc/adi/mach-sc5xx/icc.c | 311 +++++++++++++++++++++++++ drivers/soc/adi/mach-sc5xx/rcu.c | 344 ++++++++++++++++++++++++++++ drivers/soc/adi/mach-sc5xx/sec.c | 281 +++++++++++++++++++++++ drivers/soc/adi/mach-sc5xx/sec.h | 62 +++++ drivers/soc/adi/pads_system.c | 127 ++++++++++ drivers/soc/adi/system.c | 240 +++++++++++++++++++ 12 files changed, 1466 insertions(+) create mode 100644 drivers/soc/adi/Makefile create mode 100644 drivers/soc/adi/mach-sc59x/Makefile create mode 100644 drivers/soc/adi/mach-sc59x/core.c create mode 100644 drivers/soc/adi/mach-sc59x/core.h create mode 100644 drivers/soc/adi/mach-sc5xx/Makefile create mode 100644 drivers/soc/adi/mach-sc5xx/icc.c create mode 100644 drivers/soc/adi/mach-sc5xx/rcu.c create mode 100644 drivers/soc/adi/mach-sc5xx/sec.c create mode 100644 drivers/soc/adi/mach-sc5xx/sec.h create mode 100644 drivers/soc/adi/pads_system.c create mode 100644 drivers/soc/adi/system.c diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index c9e689080ceb75..fd8d6f7b5b944b 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -3,6 +3,7 @@ # Makefile for the Linux Kernel SOC specific device drivers. # +obj-y += adi/ obj-y += apple/ obj-y += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ diff --git a/drivers/soc/adi/Makefile b/drivers/soc/adi/Makefile new file mode 100644 index 00000000000000..49413f4b83f003 --- /dev/null +++ b/drivers/soc/adi/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +# todo modularize; already depends on CONFIG_ARCH_SC59X_64 though + +obj-y += pads_system.o system.o + +obj-$(CONFIG_ARCH_SC59X_64) += mach-sc59x/ +obj-$(CONFIG_ARCH_SC59X_64) += mach-sc5xx/ + +obj-$(CONFIG_ARCH_SC5XX) += mach-sc5xx/ diff --git a/drivers/soc/adi/mach-sc59x/Makefile b/drivers/soc/adi/mach-sc59x/Makefile new file mode 100644 index 00000000000000..44baa95f0256de --- /dev/null +++ b/drivers/soc/adi/mach-sc59x/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later + +# todo modularize with kconfigs + +obj-y := core.o diff --git a/drivers/soc/adi/mach-sc59x/core.c b/drivers/soc/adi/mach-sc59x/core.c new file mode 100644 index 00000000000000..71b8965e5a883b --- /dev/null +++ b/drivers/soc/adi/mach-sc59x/core.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * core timer and machine init for ADI processor on-chip memory + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "core.h" + +/** @todo spu stuff move to sec.c and make it a real driver */ +static void __iomem *spu_base; + +void set_spu_securep_msec(u16 n, bool msec) +{ + /* + * This throws a data abort right now. + * I assume the SPU is inaccessible from EL1. + * If we need to adjust this from kernel-space, + * this will have to be a secure monitor call (optee?) + */ + spu_base = NULL; +} + +EXPORT_SYMBOL(set_spu_securep_msec); +/** end @todo spu stuff */ diff --git a/drivers/soc/adi/mach-sc59x/core.h b/drivers/soc/adi/mach-sc59x/core.h new file mode 100644 index 00000000000000..a8a2f8add269d7 --- /dev/null +++ b/drivers/soc/adi/mach-sc59x/core.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * core timer and machine init for ADI processor on-chip memory + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef __ASM_ARCH_SC58X_H +#define __ASM_ARCH_SC58X_H + +#include +#include + +extern void __init sc59x_init(void); +extern void __init sc59x_init_early(void); +extern void __init sc59x_init_irq(void); +extern void __init sc59x_map_io(void); +extern void sc59x_timer_init(void); +extern void sc59x_clock_init(void); +#endif diff --git a/drivers/soc/adi/mach-sc5xx/Makefile b/drivers/soc/adi/mach-sc5xx/Makefile new file mode 100644 index 00000000000000..e2a3a94a3862f8 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later + +# todo modularize with kconfigs + +obj-y := rcu.o sec.o icc.o diff --git a/drivers/soc/adi/mach-sc5xx/icc.c b/drivers/soc/adi/mach-sc5xx/icc.c new file mode 100644 index 00000000000000..090675823f2dd7 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/icc.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Analog Devices Trigger Routing Unit as used with the inter-core + * communications driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADI_TRU_REG_GCTL 0x7f4 +#define ADI_TRU_REG_MTR 0x7e0 + +#define ADI_TRU_DEFAULT_MAX_MASTER_ID 180 +#define ADI_TRU_DEFAULT_MAX_SLAVE_ID 180 + +#define ARM_SMCCC_OWNER_ADI 51 +#define ADI_SMC_FUNCID_TRU_TRIGGER 0 + +#define ADI_TRU_SMC_TRIGGER \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_ADI, ADI_SMC_FUNCID_TRU_TRIGGER) + +struct adi_tru { + void __iomem *ioaddr; + struct device *dev; + u32 max_master_id; + u32 max_slave_id; + bool use_smc; +}; + +/** + * Device tree interface for other modules that need TRU access + */ +struct adi_tru *get_adi_tru_from_node(struct device *dev) +{ + struct platform_device *tru_pdev; + struct device_node *tru_node; + struct adi_tru *ret = NULL; + + tru_node = of_parse_phandle(dev->of_node, "adi,tru", 0); + if (!tru_node) { + dev_err(dev, "Missing adi,tru phandle in device tree\n"); + return ERR_PTR(-ENODEV); + } + + tru_pdev = of_find_device_by_node(tru_node); + if (!tru_pdev) { + ret = ERR_PTR(-EPROBE_DEFER); + goto cleanup; + } + + ret = dev_get_drvdata(&tru_pdev->dev); + if (!ret) + ret = ERR_PTR(-EPROBE_DEFER); + +cleanup: + of_node_put(tru_node); + return ret; +} + +EXPORT_SYMBOL(get_adi_tru_from_node); + +void put_adi_tru(struct adi_tru *tru) +{ + put_device(tru->dev); +} + +EXPORT_SYMBOL(put_adi_tru); + +int adi_tru_trigger_device(struct adi_tru *tru, struct device *dev) +{ + struct device_node *np = dev->of_node; + u32 master = 0; + + if (of_property_read_u32(np, "adi,tru-master-id", &master)) { + dev_err(tru->dev, + "dts entry %s is missing a adi,tru-master-id", + np->full_name); + return -ENOENT; + } + + return adi_tru_trigger(tru, master); +} + +EXPORT_SYMBOL(adi_tru_trigger_device); + +static int adi_tru_smc_trigger(struct adi_tru *tru, u32 master) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ADI_TRU_SMC_TRIGGER, master, 0, 0, 0, 0, 0, 0, &res); + return (res.a0 == 0) ? 0 : -EINVAL; +} + +int adi_tru_trigger(struct adi_tru *tru, u32 master) +{ + if (master == 0 || master > tru->max_master_id) { + dev_err(tru->dev, "Invalid master ID to trigger %d\n", + master); + return -ERANGE; + } + + if (tru->use_smc) + return adi_tru_smc_trigger(tru, master); + + writel(master, tru->ioaddr + ADI_TRU_REG_MTR); + return 0; +} + +EXPORT_SYMBOL(adi_tru_trigger); + +/** + * Configure the given slave (i.e. TRU_SSR[n]) to be triggered by the given + * master ID. The IDs found in the documentation, which appear to be 1-indexed + * for masters (valid IDs are 1-182) and 0-indexed for slaves (start at SSR[0] and + * count to SSR[187]) should be used as-is. There's effectively an ID 0 master + * that is unused and undocumented. + */ +int adi_tru_set_trigger_by_id(struct adi_tru *tru, u32 master, u32 slave) +{ + if (slave > tru->max_slave_id) { + dev_err(tru->dev, "Invalid slave ID %d passed to %s", + slave, __func__); + return -ERANGE; + } + + if (master > tru->max_master_id || master == 0) { + dev_err(tru->dev, "Invalid master ID %d passed to %s", + slave, __func__); + return -ERANGE; + } + + if (!tru->use_smc) { + dev_info(tru->dev, "Connecting master %d to slave %d\n", + master, slave); + writel(master, tru->ioaddr + (slave * 4)); + } else { + dev_err(tru->dev, + "Cannot dynamically adjust TRU configuration when " + "TRU control from OPTEE is enabled\n"); + } + return 0; +} + +/** + * Configure by device tree nodes, which lets us have more flexible configurations, + * although another layer of phandle may be needed on top: + * a: a@0 { + * adi,tru-master-id = <100>; + * }; + * b: b@0 { + * adi,tru-slave-id = <101>; + * }; + * icc { + * adi,a = <&a>; + * adi,b = <&b>; + * }; + * then parse the phandles and pass the node here, then put the node back. + */ +int adi_tru_set_trigger(struct adi_tru *tru, struct device_node *master, + struct device_node *slave) +{ + u32 mid = 0; + u32 sid = 0; + + if (of_property_read_u32(master, "adi,tru-master-id", &mid)) { + dev_err(tru->dev, + "dts entry %s is missing a adi,tru-master-id", + master->full_name); + return -ENOENT; + } + + if (of_property_read_u32(slave, "adi,tru-slave-id", &sid)) { + dev_err(tru->dev, + "dts entry %s is missing a adi,tru-slave-id", + slave->full_name); + return -ENOENT; + } + + return adi_tru_set_trigger_by_id(tru, mid, sid); +} + +int adi_tru_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_tru *tru; + struct resource *res; + struct device_node *np = dev->of_node; + struct device_node *child; + void __iomem *base; + u32 master, slave; + int ret = 0; + + tru = devm_kzalloc(dev, sizeof(*tru), GFP_KERNEL); + if (!tru) + return -ENOMEM; + + tru->dev = dev; + tru->use_smc = of_property_read_bool(np, "adi,use-smc"); + + if (!tru->use_smc) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, + "Missing TRU base address (reg property in device tree)\n"); + return -ENODEV; + } + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(base)) { + dev_err(dev, "Cannot map TRU base address\n"); + return -PTR_ERR(base); + } + tru->ioaddr = base; + } + + master = ADI_TRU_DEFAULT_MAX_MASTER_ID; + if (of_property_read_u32(np, "adi,max-master-id", &master)) { + dev_warn(dev, + "Missing adi,max-master-id property, using default %d\n", + ADI_TRU_DEFAULT_MAX_MASTER_ID); + } + tru->max_master_id = master; + + slave = ADI_TRU_DEFAULT_MAX_SLAVE_ID; + if (of_property_read_u32(np, "adi,max-slave-id", &slave)) { + dev_warn(dev, + "Missing adi,max-slave-id property, using default %d\n", + ADI_TRU_DEFAULT_MAX_SLAVE_ID); + } + tru->max_slave_id = slave; + + /* Do not try to configure the hardware if we need to use smcs to trigger + * because all of the TRU is restricted from access in that case + */ + if (!tru->use_smc) { + /* + * Initialize statically defined triggers from the device tree + * as child nodes, for example something like this + * tru: tru@xyz { + * a: channel@0 { + * adi,tru-master-id = <100>; + * adi,tru-slave-id = <101>; + * }; + * b: channel@1 { + * adi,tru-master-id = <102>; + * adi,tru-slave-id = <103>; + * }; + * }; + */ + child = NULL; + while ((child = of_get_next_child(np, child))) { + ret = adi_tru_set_trigger(tru, child, child); + if (ret) { + of_node_put(child); + dev_err(dev, + "Invalid static trigger map in TRU device tree entry\n"); + return ret; + } + } + + writel(0x01, tru->ioaddr + ADI_TRU_REG_GCTL); + } + + dev_set_drvdata(dev, tru); + return 0; +} + +void adi_tru_remove(struct platform_device *pdev) +{ + return; +} + +static const struct of_device_id adi_tru_dt_ids[] = { + {.compatible = "adi,trigger-routing-unit" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, adi_tru_dt_ids); + +static struct platform_driver adi_tru_driver = { + .probe = adi_tru_probe, + .remove = adi_tru_remove, + .driver = { + .name = "adi-trigger-routing-unit", + .of_match_table = of_match_ptr(adi_tru_dt_ids), + }, +}; + +module_platform_driver(adi_tru_driver); + +MODULE_DESCRIPTION("ADI Trigger Routing Unit driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/adi/mach-sc5xx/rcu.c b/drivers/soc/adi/mach-sc5xx/rcu.c new file mode 100644 index 00000000000000..b71b60b89e2c98 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/rcu.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Analog Devices Reset Control Unit + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sec.h" + +#define ADI_RCU_REBOOT_PRIORITY 255 +#define ADI_RCU_CORE_INIT_TIMEOUT msecs_to_jiffies(2000) + +struct adi_rcu { + struct notifier_block reboot_notifier; + void __iomem *ioaddr; + struct device *dev; + struct adi_sec *sec; + int sharc_min_coreid; + int sharc_max_coreid; +}; + +static struct adi_rcu *to_adi_rcu(struct notifier_block *nb) +{ + return container_of(nb, struct adi_rcu, reboot_notifier); +} + +// RCU memory accessors for other drivers that need it +u32 adi_rcu_readl(struct adi_rcu *rcu, int offset) +{ + return readl(rcu->ioaddr + offset); +} + +void adi_rcu_writel(u32 val, struct adi_rcu *rcu, int offset) +{ + writel(val, rcu->ioaddr + offset); +} + +EXPORT_SYMBOL(adi_rcu_writel); + +void adi_rcu_msg_set(struct adi_rcu *rcu, u32 bits) +{ + writel(bits, rcu->ioaddr + ADI_RCU_REG_MSG_SET); +} + +void adi_rcu_msg_clear(struct adi_rcu *rcu, u32 bits) +{ + writel(bits, rcu->ioaddr + ADI_RCU_REG_MSG_CLR); +} + +// Device tree interface for other modules that need RCU access +struct adi_rcu *get_adi_rcu_from_node(struct device *dev) +{ + struct platform_device *rcu_pdev; + struct device_node *rcu_node; + struct adi_rcu *ret = NULL; + + rcu_node = of_parse_phandle(dev->of_node, "adi,rcu", 0); + if (!rcu_node) { + dev_err(dev, "Missing adi,rcu phandle in device tree\n"); + return ERR_PTR(-ENODEV); + } + + rcu_pdev = of_find_device_by_node(rcu_node); + if (!rcu_pdev) { + ret = ERR_PTR(-EPROBE_DEFER); + goto cleanup; + } + + ret = dev_get_drvdata(&rcu_pdev->dev); + if (!ret) { + put_device(&rcu_pdev->dev); + ret = ERR_PTR(-EPROBE_DEFER); + } + +cleanup: + of_node_put(rcu_node); + return ret; +} + +EXPORT_SYMBOL(get_adi_rcu_from_node); + +void put_adi_rcu(struct adi_rcu *rcu) +{ + put_device(rcu->dev); +} + +EXPORT_SYMBOL(put_adi_rcu); + +void adi_rcu_set_sec(struct adi_rcu *rcu, struct adi_sec *sec) +{ + rcu->sec = sec; +} + +// API for other drivers to interact with RCU +int adi_rcu_check_coreid_valid(struct adi_rcu *rcu, int coreid) +{ + if (coreid < rcu->sharc_min_coreid + || coreid > rcu->sharc_max_coreid) + return -EINVAL; + return 0; +} + +EXPORT_SYMBOL(adi_rcu_check_coreid_valid); + +int adi_rcu_reset_core(struct adi_rcu *rcu, int coreid) +{ + u32 val; + int ret; + + ret = adi_rcu_check_coreid_valid(rcu, coreid); + if (ret) + return ret; + + // First put core in reset. + // Clear CRSTAT bit for given coreid. + adi_rcu_writel(1 << coreid, rcu, ADI_RCU_REG_CRSTAT); + + // Set SIDIS to disable the system interface + val = adi_rcu_readl(rcu, ADI_RCU_REG_SIDIS); + adi_rcu_writel(val | (1 << (coreid - 1)), rcu, ADI_RCU_REG_SIDIS); + + // Wait for access to coreX have been disabled and all the pending + // transactions have completed + udelay(50); + + // Set CRCTL bit to put core in reset + val = adi_rcu_readl(rcu, ADI_RCU_REG_CRCTL); + adi_rcu_writel(val | (1 << coreid), rcu, ADI_RCU_REG_CRCTL); + + // Poll until Core is in reset + while (!(adi_rcu_readl(rcu, ADI_RCU_REG_CRSTAT) & (1 << coreid))) + ; + + // Clear SIDIS to reenable the system interface + val = adi_rcu_readl(rcu, ADI_RCU_REG_SIDIS); + adi_rcu_writel(val & ~(1 << (coreid - 1)), rcu, ADI_RCU_REG_SIDIS); + + udelay(50); + + // Take Core out of reset + val = adi_rcu_readl(rcu, ADI_RCU_REG_CRCTL); + adi_rcu_writel(val & ~(1 << coreid), rcu, ADI_RCU_REG_CRCTL); + + // Wait for done + udelay(50); + + return 0; +} + +EXPORT_SYMBOL(adi_rcu_reset_core); + +int adi_rcu_start_core(struct adi_rcu *rcu, int coreid) +{ + int ret; + + ret = adi_rcu_check_coreid_valid(rcu, coreid); + if (ret) + return ret; + + // Clear the IDLE bit when start the SHARC core + adi_rcu_msg_clear(rcu, RCU0_MSG_C0IDLE << coreid); + + // Notify CCES + adi_rcu_msg_set(rcu, RCU0_MSG_C1ACTIVATE << (coreid - 1)); + + return 0; +} + +EXPORT_SYMBOL(adi_rcu_start_core); + +int adi_rcu_is_core_idle(struct adi_rcu *rcu, int coreid) +{ + int ret = adi_rcu_check_coreid_valid(rcu, coreid); + + if (ret) + return ret; + return !!(adi_rcu_readl(rcu, ADI_RCU_REG_MSG) & + (RCU0_MSG_C0IDLE << coreid)); +} + +EXPORT_SYMBOL(adi_rcu_is_core_idle); + +int adi_rcu_stop_core(struct adi_rcu *rcu, int coreid, int coreirq) +{ + unsigned long timeout = jiffies + ADI_RCU_CORE_INIT_TIMEOUT; + bool is_timeout = true; + int ret; + + ret = adi_rcu_check_coreid_valid(rcu, coreid); + if (ret) + return ret; + + if (adi_rcu_readl(rcu, ADI_RCU_REG_CRCTL) & (1 << coreid)) + return 0; + + // Check the IDLE bit in RCU_MSG register + if (adi_rcu_is_core_idle(rcu, coreid) == 0) { + // Set core reset request bit in RCU_MSG bit(12:14) + adi_rcu_msg_set(rcu, RCU0_MSG_CRR0 << coreid); + + // Raise SOFT IRQ through SEC + // DSP enter into ISR to release interrupts used by DSP program + sec_set_ssi_coreid(rcu->sec, coreirq, coreid); + sec_enable_ssi(rcu->sec, coreirq, false, true); + sec_enable_sci(rcu->sec, coreid); + sec_raise_irq(rcu->sec, coreirq); + } + // Wait until the specific core enter into IDLE bit(8:10) + // DSP should set the IDLE bit to 1 manully in ISR + do { + if (adi_rcu_is_core_idle(rcu, coreid)) { + is_timeout = false; + break; + } + } while (time_before(jiffies, timeout)); + + if (is_timeout) + dev_warn(rcu->dev, + "Timeout waiting for remote core %d to IDLE!\n", + coreid); + + // Clear core reset request bit in RCU_MSG bit(12:14) + adi_rcu_msg_clear(rcu, RCU0_MSG_CRR0 << coreid); + + // Clear Activate bit when stop SHARC core + adi_rcu_msg_clear(rcu, RCU0_MSG_C1ACTIVATE << (coreid - 1)); + return 0; +} + +EXPORT_SYMBOL(adi_rcu_stop_core); + +static int adi_rcu_reboot(struct notifier_block *nb, unsigned long mode, + void *cmd) +{ + struct adi_rcu *adi_rcu = to_adi_rcu(nb); + u32 val; + + dev_info(adi_rcu->dev, "Reboot requested\n"); + + val = adi_rcu_readl(adi_rcu, ADI_RCU_REG_CTL); + adi_rcu_writel(val | ADI_RCU_CTL_SYSRST, adi_rcu, ADI_RCU_REG_CTL); + + dev_err(adi_rcu->dev, "Unable to reboot via RCU\n"); + return NOTIFY_DONE; +} + +static int adi_rcu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adi_rcu *adi_rcu = NULL; + struct resource *res; + void __iomem *base; + int ret; + + adi_rcu = devm_kzalloc(dev, sizeof(*adi_rcu), GFP_KERNEL); + if (!adi_rcu) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Cannot get RCU base address\n"); + return -ENODEV; + } + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(base)) { + dev_err(dev, "Cannot map RCU base address\n"); + return PTR_ERR(base); + } + + if (of_property_read_u32 + (np, "adi,sharc-min", &adi_rcu->sharc_min_coreid)) + adi_rcu->sharc_min_coreid = 1; + + if (of_property_read_u32 + (np, "adi,sharc-max", &adi_rcu->sharc_max_coreid)) + adi_rcu->sharc_max_coreid = 2; + + adi_rcu->ioaddr = base; + adi_rcu->dev = dev; + adi_rcu->reboot_notifier.priority = ADI_RCU_REBOOT_PRIORITY; + adi_rcu->reboot_notifier.notifier_call = adi_rcu_reboot; + if (of_property_read_bool(np, "adi,enable-reboot")) { + ret = register_restart_handler(&adi_rcu->reboot_notifier); + if (ret) { + dev_err(dev, + "Unable to register restart handler: %d\n", + ret); + return ret; + } + } + + dev_set_drvdata(dev, adi_rcu); + + return 0; +} + +static void adi_rcu_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_rcu *adi_rcu = dev_get_drvdata(dev); + + unregister_restart_handler(&adi_rcu->reboot_notifier); +} + +static const struct of_device_id adi_rcu_match[] = { + {.compatible = "adi,reset-controller" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adi_rcu_match); + +static struct platform_driver adi_rcu_driver = { + .probe = adi_rcu_probe, + .remove = adi_rcu_remove, + .driver = { + .name = "ADI Reset Control Unit", + .of_match_table = of_match_ptr(adi_rcu_match), + }, +}; + +module_platform_driver(adi_rcu_driver); + +MODULE_DESCRIPTION("Analog Devices RCU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Greg Malysa "); diff --git a/drivers/soc/adi/mach-sc5xx/sec.c b/drivers/soc/adi/mach-sc5xx/sec.c new file mode 100644 index 00000000000000..6e86ff51561a84 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/sec.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * sc59x SEC + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sec.h" + +struct adi_sec { + void __iomem *ioaddr; + struct adi_rcu *rcu; + struct device *dev; + int cores; + spinlock_t lock; +}; + +void adi_sec_writel(u32 val, struct adi_sec *rcu, int offset) +{ + writel(val, rcu->ioaddr + offset); +} + +u32 adi_sec_readl(struct adi_sec *rcu, int offset) +{ + return readl(rcu->ioaddr + offset); +} + +void sec_raise_irq(struct adi_sec *sec, unsigned int irq) +{ + unsigned long flags; + unsigned int sid = irq - 32; + + spin_lock_irqsave(&sec->lock, flags); + adi_sec_writel(sid, sec, ADI_SEC_REG_RAISE); + spin_unlock_irqrestore(&sec->lock, flags); +} + +EXPORT_SYMBOL(sec_raise_irq); + +void sec_enable_ssi(struct adi_sec *sec, unsigned int sid, bool fault, + bool source) +{ + unsigned long flags; + u32 val; + u32 offset; + + offset = ADI_SEC_REG_SCTL_BASE + 8 * sid; + + spin_lock_irqsave(&sec->lock, flags); + val = adi_sec_readl(sec, offset); + + if (fault) + val |= ADI_SEC_SCTL_FAULT_EN; + else + val |= ADI_SEC_SCTL_INT_EN; + + if (source) + val |= ADI_SEC_SCTL_SRC_EN; + + adi_sec_writel(val, sec, offset); + spin_unlock_irqrestore(&sec->lock, flags); +} + +EXPORT_SYMBOL(sec_enable_ssi); + +void sec_enable_sci(struct adi_sec *sec, unsigned int coreid) +{ + unsigned long flags; + u32 val; + u32 offset; + + if (coreid == 0 || coreid > sec->cores) { + dev_err(sec->dev, "Invalid core ID given to %s: %d\n", + __func__, coreid); + return; + } + + offset = ADI_SEC_REG_CCTL_BASE + coreid * ADI_SEC_CCTL_SIZE; + + spin_lock_irqsave(&sec->lock, flags); + val = adi_sec_readl(sec, offset); + val |= ADI_SEC_CCTL_EN; + adi_sec_writel(val, sec, offset); + spin_unlock_irqrestore(&sec->lock, flags); +} + +EXPORT_SYMBOL(sec_enable_sci); + +void sec_set_ssi_coreid(struct adi_sec *sec, unsigned int sid, + unsigned int coreid) +{ + unsigned long flags; + u32 val; + u32 offset; + + if (coreid == 0 || coreid > sec->cores) { + dev_err(sec->dev, "Invalid core ID given to %s: %d\n", + __func__, coreid); + return; + } + + offset = ADI_SEC_REG_SCTL_BASE + 8 * sid; + + spin_lock_irqsave(&sec->lock, flags); + val = adi_sec_readl(sec, offset); + val &= ~ADI_SEC_SCTL_CTG; + val |= ((coreid << 24) & ADI_SEC_SCTL_CTG); + adi_sec_writel(val, sec, offset); + spin_unlock_irqrestore(&sec->lock, flags); +} + +EXPORT_SYMBOL(sec_set_ssi_coreid); + +struct adi_sec *get_adi_sec_from_node(struct device *dev) +{ + struct platform_device *sec_pdev; + struct device_node *sec_node; + struct adi_sec *ret = NULL; + + sec_node = of_parse_phandle(dev->of_node, "adi,sec", 0); + if (!sec_node) { + dev_err(dev, "Missing adi,sec phandle in device tree\n"); + return ERR_PTR(-ENODEV); + } + + sec_pdev = of_find_device_by_node(sec_node); + if (!sec_pdev) { + ret = ERR_PTR(-EPROBE_DEFER); + goto cleanup; + } + + ret = dev_get_drvdata(&sec_pdev->dev); + +cleanup: + of_node_put(sec_node); + return ret; +} + +EXPORT_SYMBOL(get_adi_sec_from_node); + +void put_adi_sec(struct adi_sec *sec) +{ + put_device(sec->dev); +} + +EXPORT_SYMBOL(put_adi_sec); + +static int adi_sec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct adi_sec *adi_sec; + struct adi_rcu *adi_rcu; + struct resource *res; + void __iomem *base; + int cores; + int ret = 0; + + adi_sec = devm_kzalloc(dev, sizeof(*adi_sec), GFP_KERNEL); + if (!adi_sec) + return -ENOMEM; + + spin_lock_init(&adi_sec->lock); + dev_set_drvdata(dev, adi_sec); + + adi_rcu = get_adi_rcu_from_node(dev); + if (IS_ERR(adi_rcu)) + return PTR_ERR(adi_rcu); + + adi_sec->dev = dev; + adi_sec->rcu = adi_rcu; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto free_rcu; + } + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto free_rcu; + } + + adi_rcu_set_sec(adi_rcu, adi_sec); + + if (of_property_read_u32(np, "adi,sharc-cores", &adi_sec->cores)) { + dev_warn(dev, + "Missing property adi,sharc-cores, default to 0\n"); + adi_sec->cores = 0; + } + + adi_sec->ioaddr = base; + + /* Disable SYSCD_RESETb and clear RCU reset status */ + adi_rcu_writel(0x00, adi_rcu, ADI_RCU_REG_CTL); + adi_rcu_writel(0x0f, adi_rcu, ADI_RCU_REG_STAT); + + /* Reset SEC */ + adi_sec_writel(0x02, adi_sec, ADI_SEC_REG_GCTL); + adi_sec_writel(0x02, adi_sec, ADI_SEC_REG_FCTL); + + /* Initialize each core */ + for (cores = 0; cores < adi_sec->cores; ++cores) { + adi_sec_writel(0x02, adi_sec, + ADI_SEC_REG_CCTL_BASE + (cores + + 1) * + ADI_SEC_CCTL_SIZE); + } + udelay(100); + + /* Enable SEC fault event */ + adi_sec_writel(0x01, adi_sec, ADI_SEC_REG_GCTL); + + /* ANOMALY 36100004 spurious external fault event occurs when FCTL is + * re-programmed when active fault is not cleared + */ + adi_sec_writel(0xc0, adi_sec, ADI_SEC_REG_FCTL); + adi_sec_writel(0xc1, adi_sec, ADI_SEC_REG_FCTL); + + /* Enable SYSCD_RESETb input */ + adi_rcu_writel(0x100, adi_rcu, ADI_RCU_REG_CTL); + +#ifdef CONFIG_ADI_WATCHDOG + /* @todo verify sec watchdog event number, make device tree based */ + sec_enable_ssi(adi_sec, 3, true, true); +#endif + + return 0; + +free_rcu: + put_adi_rcu(adi_rcu); + return ret; +} + +static void adi_sec_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_sec *adi_sec; + + adi_sec = dev_get_drvdata(dev); + put_adi_rcu(adi_sec->rcu); +} + +static const struct of_device_id adi_sec_match[] = { + {.compatible = "adi,system-event-controller" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adi_sec_match); + +static struct platform_driver adi_sec_driver = { + .probe = adi_sec_probe, + .remove = adi_sec_remove, + .driver = { + .name = "adi-system-event-controller", + .of_match_table = of_match_ptr(adi_sec_match) + }, +}; + +module_platform_driver(adi_sec_driver); + +MODULE_DESCRIPTION("System Event Controller for ADI SC5xx SoCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/adi/mach-sc5xx/sec.h b/drivers/soc/adi/mach-sc5xx/sec.h new file mode 100644 index 00000000000000..4aae2e05c49565 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/sec.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef SOC_ADI_SEC_H +#define SOC_ADI_SEC_H + +/* Global Registers */ +#define ADI_SEC_REG_GCTL 0x000 +#define ADI_SEC_REG_GSTAT 0x004 +#define ADI_SEC_REG_RAISE 0x008 +#define ADI_SEC_REG_END 0x00c + +/* Fault management interface (SFI) registers */ +#define ADI_SEC_REG_FCTL 0x010 +#define ADI_SEC_REG_FSTAT 0x014 +#define ADI_SEC_REG_FSID 0x018 +#define ADI_SEC_REG_FEND 0x01c +#define ADI_SEC_REG_FDLY 0x020 +#define ADI_SEC_REG_FDLY_CUR 0x024 +#define ADI_SEC_REG_FSRDLY 0x028 +#define ADI_SEC_REG_FSRDLY_CUR 0x02c +#define ADI_SEC_REG_FCOPP 0x030 +#define ADI_SEC_REG_FCOPP_CUR 0x034 + +/* Start of CCTL registers */ +#define ADI_SEC_REG_CCTL_BASE 0x400 +#define ADI_SEC_CCTL_SIZE 0x040 +#define ADI_SEC_REG_CCTL1 0x440 +#define ADI_SEC_REG_CCTL2 0x480 + +/* Start of SCTL registesr */ +#define ADI_SEC_REG_SCTL_BASE 0x800 + +/* Register bits */ +#define ADI_SEC_CCTL_EN 0x00000001 /* SCI Enable */ +#define ADI_SEC_SCTL_SRC_EN 0x00000004 /* SEN: Enable */ +#define ADI_SEC_SCTL_FAULT_EN 0x00000002 /* FEN: Enable */ +#define ADI_SEC_SCTL_INT_EN 0x00000001 /* IEN: Enable */ +#define ADI_SEC_SCTL_CTG 0x0F000000 /* Core Target Select */ + +struct adi_sec; + +void sec_raise_irq(struct adi_sec *sec, unsigned int irq); +void sec_enable_sci(struct adi_sec *sec, unsigned int coreid); +void sec_enable_ssi(struct adi_sec *sec, unsigned int sid, bool fault, + bool source); +void sec_set_ssi_coreid(struct adi_sec *sec, unsigned int sid, + unsigned int coreid); +struct adi_sec *get_adi_sec_from_node(struct device *dev); +void put_adi_sec(struct adi_sec *sec); +void adi_sec_writel(u32 val, struct adi_sec *rcu, int offset); +u32 adi_sec_readl(struct adi_sec *rcu, int offset); + +#endif diff --git a/drivers/soc/adi/pads_system.c b/drivers/soc/adi/pads_system.c new file mode 100644 index 00000000000000..d03e8720446863 --- /dev/null +++ b/drivers/soc/adi/pads_system.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PADS-related system config register driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Author: Greg Malysa + * + */ + +#include +#include +#include +#include +#include + +#define ADI_SYSREG_BITS(_id, _offset, _width, _shift) \ + { \ + .id = ADI_SYSTEM_REG_##_id, \ + .offset = _offset, \ + .mask = GENMASK(_width-1, 0) << _shift, \ + .shift = _shift, \ + .is_bits = true, \ + } + +#define ADI_SYSREG(_id, _offset) \ + { \ + .id = ADI_SYSTEM_REG_##_id, \ + .offset = _offset, \ + .is_bits = false, \ + } + +// Fields in PADS CFG0 at offset +0x04 +static struct system_register adi_pads_regs[] = { + // PTP Clock Source 0 + ADI_SYSREG_BITS(EMAC0_PTPCLK0, 0x04, 2, 0), + // Reset Enable for RGMII + ADI_SYSREG_BITS(EMAC0_EMACRESET, 0x04, 1, 2), + // Select PHY Interface RGMII/RMII/MII + ADI_SYSREG_BITS(EMAC0_PHYISEL, 0x04, 2, 3), + // CNT0 Down Input Select + ADI_SYSREG_BITS(CNT0UDSEL, 0x04, 2, 6), + // CNT0 Up Input Select + ADI_SYSREG_BITS(CNT0DGSEL, 0x04, 2, 7), + +#if defined(CONFIG_ARCH_SC58X) || defined(CONFIG_ARCH_SC57X) + // TWI2 Voltage Select + ADI_SYSREG_BITS(TWI0VSEL, 0x04, 2, 8), + // TWI1 Voltage Select + ADI_SYSREG_BITS(TWI1VSEL, 0x04, 2, 9), + // TWI0 Voltage Select + ADI_SYSREG_BITS(TWI2VSEL, 0x04, 2, 10), +#endif + +#if defined(CONFIG_ARCH_SC58X) + // Pull-Up Enable for MSI DATA[3:0] bits and CMD Pin + ADI_SYSREG_BITS(PUMSIDLC, 0x04, 2, 14), + // Pull-Up Enable for MSI DATA[7:4] bits + ADI_SYSREG_BITS(PUMSIHL, 0x04, 2, 15), +#endif + + // Pull-Up Enable for TMS/SWDIO (debug port) + ADI_SYSREG_BITS(PUTMS, 0x04, 2, 16), + // Input enable control for PTP_AUXIN pins + ADI_SYSREG_BITS(EMAC0_AUXIE, 0x04, 1, 17), + // FAULT does not exist + ADI_SYSREG_BITS(FAULT_DIS, 0x04, 1, 18), + +#if defined(CONFIG_ARCH_SC59X_64) + // EMAC0 DMA transfer endian format + ADI_SYSREG_BITS(EMAC0_ENDIANNESS, 0x04, 1, 19), + // EMAC1 DMA transfer endian format + ADI_SYSREG_BITS(EMAC1_ENDIANNESS, 0x04, 1, 20), + // Enable MSHC Card Clock Divider + ADI_SYSREG_BITS(MSHC_CCLK_DIV_EN, 0x04, 1, 22), +#endif + +// DAIn port input enable registers +#if defined(CONFIG_ARCH_SC58X) + ADI_SYSREG(DAI0_IE, 0x60), + ADI_SYSREG(DAI1_IE, 0x64), +#endif + +#if defined(CONFIG_ARCH_SC59X_64) || defined(CONFIG_ARCH_SC59X) + ADI_SYSREG(DAI0_IE, 0x90), + ADI_SYSREG(DAI1_IE, 0x94), +#endif +}; + +static struct system_config adi_pads_config = { + .registers = adi_pads_regs, + .len = ARRAY_SIZE(adi_pads_regs), + .max_register = __ADI_SYSTEM_REG_COUNT, +}; + +int adi_pads_probe(struct platform_device *pdev) +{ + return system_config_probe(pdev, &adi_pads_config); +} + +void adi_pads_remove(struct platform_device *pdev) +{ + system_config_remove(pdev); +} + +static const struct of_device_id pads_dt_ids[] = { + { .compatible = "adi,pads-system-config", }, + { } +}; +MODULE_DEVICE_TABLE(of, pads_dt_ids); + +static struct platform_driver pads_driver = { + .driver = { + .name = "adi-pads-system-config", + .of_match_table = pads_dt_ids, + }, + .probe = adi_pads_probe, + .remove = adi_pads_remove, +}; +module_platform_driver(pads_driver); + +MODULE_AUTHOR("Greg Malysa "); +MODULE_DESCRIPTION("ADI ADSP PADS CFG-based System Configuration Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/adi/system.c b/drivers/soc/adi/system.c new file mode 100644 index 00000000000000..06b2f746e3ff5a --- /dev/null +++ b/drivers/soc/adi/system.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Author: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct system_context { + /* underlying regmap_mmio */ + struct regmap *regmap; + /* tree of register definitions by index */ + struct radix_tree_root tree; + /* configuration we were created with */ + struct system_config *config; +}; + +static int regmap_system_read(void *context, unsigned int reg, unsigned int *val) +{ + struct system_context *ctx = context; + struct system_register *sreg = radix_tree_lookup(&ctx->tree, reg); + int ret; + + if (!sreg) + return -EIO; + + if (sreg->is_bits) { + u32 tmp; + + ret = regmap_read(ctx->regmap, sreg->offset, &tmp); + if (ret) + return ret; + + tmp = (tmp & sreg->mask) >> sreg->shift; + *val = tmp; + return 0; + } + + return regmap_read(ctx->regmap, sreg->offset, val); +} + +static int regmap_system_write(void *context, unsigned int reg, unsigned int val) +{ + struct system_context *ctx = context; + struct system_register *sreg = radix_tree_lookup(&ctx->tree, reg); + + if (!sreg) + return -EIO; + + if (sreg->is_bits) { + return regmap_update_bits(ctx->regmap, sreg->offset, sreg->mask, + (val << sreg->shift) & sreg->mask); + } + + return regmap_write(ctx->regmap, sreg->offset, val); +} + +static struct system_context *create_context(struct system_config *config) +{ + struct regmap *regmap = config->mmio_regmap; + struct system_context *ctx; + size_t i; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->regmap = regmap; + INIT_RADIX_TREE(&ctx->tree, GFP_KERNEL); + + for (i = 0; i < config->len; ++i) { + struct system_register *sreg = &config->registers[i]; + + ret = radix_tree_insert(&ctx->tree, sreg->id, sreg); + if (ret) + return ERR_PTR(ret); + } + + config->config.max_register = config->max_register; + config->config.reg_bits = 8 * sizeof(u32); + config->config.val_bits = 8 * sizeof(u32); + config->config.reg_stride = 1; + + return ctx; +} + +static void regmap_system_free_context(void *context) +{ + struct system_context *ctx = context; + unsigned int i; + + for (i = 0; i < ctx->config->len; ++i) + radix_tree_delete(&ctx->tree, ctx->config->registers[i].id); + + WARN_ON(!radix_tree_empty(&ctx->tree)); + + kfree(ctx); +} + +static const struct regmap_bus regmap_system_bus = { + .fast_io = true, + .reg_write = regmap_system_write, + .reg_read = regmap_system_read, + .free_context = regmap_system_free_context, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +struct regmap *__regmap_init_system_config(struct device *dev, + struct system_config *config, + struct lock_class_key *lock_key, const char *lock_name) +{ + struct system_context *ctx = create_context(config); + + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return __regmap_init(dev, ®map_system_bus, ctx, &config->config, + lock_key, lock_name); +} + +struct regmap *__devm_regmap_init_system_config(struct device *dev, + struct system_config *config, + struct lock_class_key *lock_key, const char *lock_name) +{ + struct system_context *ctx = create_context(config); + + if (IS_ERR(ctx)) + return ERR_PTR(PTR_ERR(ctx)); + + return __devm_regmap_init(dev, ®map_system_bus, ctx, &config->config, + lock_key, lock_name); +} + +static DEFINE_SPINLOCK(system_config_lock); +static LIST_HEAD(system_config_list); + +struct regmap *system_config_regmap_lookup_by_phandle(struct device_node *np, + const char *property) +{ + struct system_config *config = NULL; + struct system_config *entry; + struct device_node *config_np; + unsigned long flags; + + config_np = of_parse_phandle(np, property, 0); + if (!config_np) + return ERR_PTR(-ENODEV); + + spin_lock_irqsave(&system_config_lock, flags); + list_for_each_entry(entry, &system_config_list, list) { + if (entry->np == config_np) { + config = entry; + break; + } + } + spin_unlock_irqrestore(&system_config_lock, flags); + + of_node_put(config_np); + + if (!config) + return ERR_PTR(-EPROBE_DEFER); + + return config->system_regmap; +} +EXPORT_SYMBOL_GPL(system_config_regmap_lookup_by_phandle); + +int system_config_probe(struct platform_device *pdev, struct system_config *config) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regmap *regmap_mmio; + struct regmap *regmap_system; + struct resource *res; + void __iomem *base; + unsigned long flags; + + struct regmap_config mmio_config = { + .reg_bits = 8 * sizeof(u32), + .val_bits = 8 * sizeof(u32), + .reg_stride = sizeof(u32), + }; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(base)) + return PTR_ERR(base); + + mmio_config.name = of_node_full_name(np); + mmio_config.max_register = resource_size(res) - sizeof(u32); + mmio_config.cache_type = REGCACHE_NONE; + + regmap_mmio = devm_regmap_init_mmio(dev, base, &mmio_config); + if (IS_ERR(regmap_mmio)) { + dev_err(dev, "mmio regmap initialization failed\n"); + return PTR_ERR(regmap_mmio); + } + + config->mmio_regmap = regmap_mmio; + regmap_system = devm_regmap_init_system_config(dev, config); + if (IS_ERR(regmap_system)) { + dev_err(dev, "system config regmap initialization failed\n"); + return PTR_ERR(regmap_system); + } + + config->np = np; + config->system_regmap = regmap_system; + platform_set_drvdata(pdev, config); + + spin_lock_irqsave(&system_config_lock, flags); + list_add_tail(&config->list, &system_config_list); + spin_unlock_irqrestore(&system_config_lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(system_config_probe); + +int system_config_remove(struct platform_device *pdev) +{ + struct system_config *config = platform_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&system_config_lock, flags); + list_del(&config->list); + spin_unlock_irqrestore(&system_config_lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(system_config_remove); From 9b918d3defb771aff76b645521be61ea5b150827 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 16:23:47 +0200 Subject: [PATCH 18/85] ARM: sc5xx: add ADSP-SC594 support Signed-off-by: Philip Molloy --- arch/arm/mach-sc5xx/Kconfig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index a1813a0ecf929b..5c244577b76b88 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -1,10 +1,18 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later -config ARCH_SC5XX - bool "ADI ADSP-SC5XX Family" - select CPU_V7 +menuconfig ARCH_SC5XX + bool "SC5xx SoCs (Cortex-A5-based)" + depends on ARCH_MULTI_V7 select ARM_GIC + select PINCTRL + select COUNTER + select GPIOLIB + select SERIAL_ADI_UART4 + select ADI_ADSP_IRQ + select GPIO_ADI_ADSP_PORT + select COMMON_CLK + select GENERIC_IRQ_MULTI_HANDLER select VFP select NEON select MIGHT_HAVE_CACHE_L2X0 @@ -13,14 +21,7 @@ config ARCH_SC5XX select IRQ_DOMAIN select SPARSE_IRQ select GENERIC_CLOCKEVENTS - select COMMON_CLK - select GPIOLIB - select PINCTRL - select TIMER_OF - select COUNTER select PINCTRL_ADSP_SC5XX - select GPIO_ADI_ADSP_PORT - select GENERIC_IRQ_MULTI_HANDLER config ARCH_SC59X bool "ADI SC59x SoCs (Cortex-A5-based)" From 7439002e746e59abe9cf3f2559f07ef55f6f23db Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 15:55:37 +0200 Subject: [PATCH 19/85] ARM: sc5xx: add ADSP-SC58x support Signed-off-by: Philip Molloy --- arch/arm/mach-sc5xx/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index 5c244577b76b88..4c697902c1d4af 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -42,3 +42,12 @@ config MACH_SC594_SOM Say 'Y' here if you want your kernel to run on the ADI SC594-SOM-EZKIT or SC594-SOM-EZLITE board. +config ARCH_SC58X + bool "ADI SC58x SoCs (Cortex-A5-based)" + depends on ARCH_SC5XX + select COMMON_CLK_ADI_SC58X + help + This enables support for 32-bit Cortex-A5-based ADI ADSP SC-58X + SoCs, like the SC589. It does not include the 64-bit Cortex-A55-based + SoCs (see ARCH_SC58X_64 for those). + From d87a8ee800751606822f427727c92a595c07f838 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 26 May 2025 11:17:28 +0100 Subject: [PATCH 20/85] ARM: sc5xx: add ADSP-SC573 support Signed-off-by: Utsav Agarwal --- arch/arm/mach-sc5xx/Kconfig | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index 4c697902c1d4af..2391981e4d457c 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -48,6 +48,26 @@ config ARCH_SC58X select COMMON_CLK_ADI_SC58X help This enables support for 32-bit Cortex-A5-based ADI ADSP SC-58X - SoCs, like the SC589. It does not include the 64-bit Cortex-A55-based - SoCs (see ARCH_SC58X_64 for those). + SoCs, like the SC589. +config MACH_SC589_MINI + bool "ADI sc589-mini board" + depends on ARCH_SC58X + select MIGHT_HAVE_PCI + help + Say 'Y' here if you want your kernel to run on the ADI + SC589-MINI board. + +config ARCH_SC57X + bool "ADI SC57x SoCs" + depends on ARCH_SC5XX + select COMMON_CLK_ADI_SC57X + help + This enables support for ADI ADSP SC-57x SoCs + +config MACH_SC573_EZKIT + bool "ADI SC573 EZKIT board" + depends on ARCH_SC57X + help + Say 'Y' here if you want your kernel to run on the ADI + SC573-EZKIT board. From 2f7d5b1cacbbb5ed6c5c39736ab1242a791587df Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 26 May 2025 12:01:12 +0100 Subject: [PATCH 21/85] ARM: mach-sc5xx: Add init for 32-bit ADSP-SC5xx boards Signed-off-by: Utsav Agarwal --- arch/arm/mach-sc5xx/Makefile | 6 +++ arch/arm/mach-sc5xx/core.c | 54 ++++++++++++++++++++++++ arch/arm/mach-sc5xx/core.h | 19 +++++++++ arch/arm/mach-sc5xx/sc57x-ezkit.c | 68 +++++++++++++++++++++++++++++++ arch/arm/mach-sc5xx/sc58x-ezkit.c | 38 +++++++++++++++++ arch/arm/mach-sc5xx/sc59x-ezkit.c | 40 ++++++++++++++++++ arch/arm/mach-sc5xx/spu.c | 23 +++++++++++ 7 files changed, 248 insertions(+) create mode 100644 arch/arm/mach-sc5xx/Makefile create mode 100644 arch/arm/mach-sc5xx/core.c create mode 100644 arch/arm/mach-sc5xx/core.h create mode 100644 arch/arm/mach-sc5xx/sc57x-ezkit.c create mode 100644 arch/arm/mach-sc5xx/sc58x-ezkit.c create mode 100644 arch/arm/mach-sc5xx/sc59x-ezkit.c create mode 100644 arch/arm/mach-sc5xx/spu.c diff --git a/arch/arm/mach-sc5xx/Makefile b/arch/arm/mach-sc5xx/Makefile new file mode 100644 index 00000000000000..74c3f17e77b307 --- /dev/null +++ b/arch/arm/mach-sc5xx/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +obj-$(CONFIG_ARCH_SC5XX) := core.o spu.o +obj-$(CONFIG_ARCH_SC57X) += sc57x-ezkit.o +obj-$(CONFIG_ARCH_SC58X) += sc58x-ezkit.o +obj-$(CONFIG_ARCH_SC59X) += sc59x-ezkit.o diff --git a/arch/arm/mach-sc5xx/core.c b/arch/arm/mach-sc5xx/core.c new file mode 100644 index 00000000000000..00060444780120 --- /dev/null +++ b/arch/arm/mach-sc5xx/core.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Common functionality needed on all ezkits. Things like phy fixup functions, etc. + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include + +#include "core.h" + +#define DP83865_PHY_ID 0x20005c7a +#define REG_DP83865_AUX_CTRL 0x12 +#define BITP_AUX_CTRL_RGMII_EN 12 +#define RGMII_3COM_MODE 3 +static int dp83865_fixup(struct phy_device *phydev) +{ + int phy_data = 0; + + phy_data = phy_read(phydev, REG_DP83865_AUX_CTRL); + + /* enable 3com mode for RGMII */ + phy_write(phydev, REG_DP83865_AUX_CTRL, + (RGMII_3COM_MODE << BITP_AUX_CTRL_RGMII_EN) | phy_data); + + return 0; +} + +#define DP83848_PHY_ID 0x20005c90 +#define REG_DP83848_PHY_MICR 0x11 +#define BITM_PHY_MICR_INTEN 0x2 +#define BITM_PHY_MICR_INT_OE 0x1 +static int dp83848_fixup(struct phy_device *phydev) +{ + phy_write(phydev, REG_DP83848_PHY_MICR, + BITM_PHY_MICR_INTEN | BITM_PHY_MICR_INT_OE); + + return 0; +} + +void __init sc5xx_init_ethernet(void) +{ + if (IS_BUILTIN(CONFIG_PHYLIB)) { + phy_register_fixup_for_uid(DP83865_PHY_ID, 0xffffffff, dp83865_fixup); + phy_register_fixup_for_uid(DP83848_PHY_ID, 0xffffffff, dp83848_fixup); + } +} diff --git a/arch/arm/mach-sc5xx/core.h b/arch/arm/mach-sc5xx/core.h new file mode 100644 index 00000000000000..8af57b7efea651 --- /dev/null +++ b/arch/arm/mach-sc5xx/core.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef ARCH_SC5XX_CORE_H +#define ARCH_SC5XX_CORE_H + +#include + +void __init sc5xx_init_ethernet(void); + +#endif diff --git a/arch/arm/mach-sc5xx/sc57x-ezkit.c b/arch/arm/mach-sc5xx/sc57x-ezkit.c new file mode 100644 index 00000000000000..c8e51b11191d35 --- /dev/null +++ b/arch/arm/mach-sc5xx/sc57x-ezkit.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Machine entries for the sc573 ezkit + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include + +#include +#include +#include + +#include "core.h" + +static const char * const sc57x_dt_board_compat[] __initconst = { + "adi,sc57x", + NULL +}; + +static bool first_fault = true; + +static int sc57x_abort_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + if (fsr == 0x1c06 && first_fault) { + first_fault = false; + + /* + * These faults with code 0x1c06 happens for no good reason, + * possibly left over from the CFE boot loader. + */ + pr_warn("External imprecise Data abort at addr=%#lx, fsr=%#x ignored.\n", + addr, fsr); + return 0; + } + + /* Others should cause a fault */ + return 1; +} + +static void __init sc57x_init_early(void) +{ + hook_fault_code(16 + 6, sc57x_abort_handler, SIGBUS, BUS_OBJERR, + "imprecise external abort"); +} + +static void __init sc57x_init(void) +{ + pr_info("%s: registering device resources\n", __func__); + of_platform_default_populate(NULL, NULL, NULL); + sc5xx_init_ethernet(); +} + +DT_MACHINE_START(SC57X_DT, "SC57x-EZKIT (Device Tree Support)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_early = sc57x_init_early, + .init_machine = sc57x_init, + .dt_compat = sc57x_dt_board_compat, +MACHINE_END diff --git a/arch/arm/mach-sc5xx/sc58x-ezkit.c b/arch/arm/mach-sc5xx/sc58x-ezkit.c new file mode 100644 index 00000000000000..b07f1a69792c6f --- /dev/null +++ b/arch/arm/mach-sc5xx/sc58x-ezkit.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Machine entries for the sc58x boards (ezkit, mini, etc.) + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include + +#include + +#include "core.h" + +static const char * const sc58x_dt_board_compat[] __initconst = { + "adi,sc58x", + NULL +}; + +static void __init sc58x_init(void) +{ + pr_info("%s: registering device resources\n", __func__); + of_platform_default_populate(NULL, NULL, NULL); + sc5xx_init_ethernet(); +} + +DT_MACHINE_START(SC58X_DT, "SC58x-EZKIT (Device Tree Support)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_machine = sc58x_init, + .dt_compat = sc58x_dt_board_compat, +MACHINE_END diff --git a/arch/arm/mach-sc5xx/sc59x-ezkit.c b/arch/arm/mach-sc5xx/sc59x-ezkit.c new file mode 100644 index 00000000000000..56929980e8e1c1 --- /dev/null +++ b/arch/arm/mach-sc5xx/sc59x-ezkit.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Machine entries for the sc594 ezkit + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include + +#include +#include +#include + +#include "core.h" + +static const char * const sc59x_dt_board_compat[] __initconst = { + "adi,sc59x", + NULL +}; + +static void __init sc59x_init(void) +{ + pr_info("%s: registering device resources\n", __func__); + of_platform_default_populate(NULL, NULL, NULL); + sc5xx_init_ethernet(); +} + +DT_MACHINE_START(SC59X_DT, "SC59x-EZKIT (Device Tree Support)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_machine = sc59x_init, + .dt_compat = sc59x_dt_board_compat, +MACHINE_END diff --git a/arch/arm/mach-sc5xx/spu.c b/arch/arm/mach-sc5xx/spu.c new file mode 100644 index 00000000000000..a54d43e32de4ff --- /dev/null +++ b/arch/arm/mach-sc5xx/spu.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Legacy SPU-compatibility layer. No SPU functionality is used currently, + * this will be removed when it is eliminated from all drivers. + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include + +void set_spu_securep_msec(u16 n, bool msec) +{ + (void)n; + (void)msec; +} +EXPORT_SYMBOL(set_spu_securep_msec); From 9b7d5b63e68cc228322941dc3e5332d3869e0693 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 12 Sep 2025 12:28:05 +0200 Subject: [PATCH 22/85] clk: adi: Add clock driver for ADSP-SC594 Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- arch/arm/mach-sc5xx/Kconfig | 2 - drivers/clk/Kconfig | 1 + drivers/clk/adi/Kconfig | 18 +++ drivers/clk/adi/Makefile | 2 + drivers/clk/adi/clk-adi-sc594.c | 231 ++++++++++++++++++++++++++++++++ 5 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/adi/Kconfig create mode 100644 drivers/clk/adi/clk-adi-sc594.c diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index 2391981e4d457c..d982ed8496f69b 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -1,6 +1,4 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later - - menuconfig ARCH_SC5XX bool "SC5xx SoCs (Cortex-A5-based)" depends on ARCH_MULTI_V7 diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3a1611008e48e9..284cb7afc8abea 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -512,6 +512,7 @@ config COMMON_CLK_RPMI the RISC-V platform management interface (RPMI) specification. source "drivers/clk/actions/Kconfig" +source "drivers/clk/adi/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/baikal-t1/Kconfig" source "drivers/clk/bcm/Kconfig" diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig new file mode 100644 index 00000000000000..588f80c752a958 --- /dev/null +++ b/drivers/clk/adi/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC594 + bool "ADI SC594 clock driver" + depends on ARCH_SC5XX || COMPILE_TEST + help + This enables the ADI SC594 clock driver. The driver provides + support for the clock controller on the ADI SC594 SoC. + If you are using the ADI SC594 SoC, say Y here. + If unsure, say N. + +config COMMON_CLK_ADI_SC598 + bool "ADI SC598 clock driver" + depends on ARCH_SC59X_64 || COMPILE_TEST + help + This enables the ADI SC594 clock driver. The driver provides + support for the clock controller on the ADI SC598 SoC. + If you are using the ADI SC598 SoC, say Y here. + If unsure, say N. diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile index 914cc0537d5cb4..1b0bd668ffe836 100644 --- a/drivers/clk/adi/Makefile +++ b/drivers/clk/adi/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +obj-$(CONFIG_ARCH_SC5XX) += clk-adi-pll.o obj-$(CONFIG_ARCH_SC59X_64) += clk-adi-pll.o obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o +obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o diff --git a/drivers/clk/adi/clk-adi-sc594.c b/drivers/clk/adi/clk-adi-sc594.c new file mode 100644 index 00000000000000..b0fe1637216f88 --- /dev/null +++ b/drivers/clk/adi/clk-adi-sc594.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Clock support for ADI processor + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static DEFINE_SPINLOCK(cdu_lock); + +static struct clk *clks[ADSP_SC594_CLK_END]; +static struct clk_onecell_data clk_data; + +static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"}; +static const char * const cgu0_s1sels[] = {"cgu0_s1seldiv", "cgu0_s1selexdiv"}; +static const char * const cgu1_s1sels[] = {"cgu1_s1seldiv", "cgu1_s1selexdiv"}; +static const char * const sharc0_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"}; +static const char * const sharc1_sels[] = {"cclk0_0", "dummy", "dummy", "dummy"}; +static const char * const arm_sels[] = {"cclk1_0", "dummy", "dummy", "dummy"}; +static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"}; +static const char * const can_sels[] = {"oclk_0", "oclk_1", "dummy", "dummy"}; +static const char * const spdif_sels[] = {"sclk1_0", "dummy", "dummy", "dummy"}; +static const char * const spi_sels[] = {"sclk0_0", "oclk_0", "dummy", "dummy"}; +static const char * const gige_sels[] = {"sclk0_0", "sclk0_1", "cclk0_1", "dummy"}; +static const char * const lp_sels[] = {"oclk_0", "sclk0_0", "cclk0_1", "dummy"}; +static const char * const lpddr_sels[] = {"oclk_0", "dclk_0", "sysclkin_1", "dummy"}; +static const char * const ospi_sels[] = {"sysclk_0", "sclk0_0", "sclk1_1", "dummy"}; +static const char * const trace_sels[] = {"sclk0_0", "dummy", "dummy", "dummy"}; + +static void sc594_clock_probe(struct device_node *np) +{ + void __iomem *cgu0; + void __iomem *cgu1; + void __iomem *cdu; + int ret; + int i; + + cgu0 = of_iomap(np, 0); + if (!cgu0) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (!cgu1) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (!cdu) { + pr_err("Unable to remap CDU address (resource 2)\n"); + return; + } + + // Input clock configuration + clks[ADSP_SC594_CLK_DUMMY] = clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); + clks[ADSP_SC594_CLK_SYS_CLKIN0] = of_clk_get_by_name(np, "sys_clkin0"); + clks[ADSP_SC594_CLK_SYS_CLKIN1] = of_clk_get_by_name(np, "sys_clkin1"); + clks[ADSP_SC594_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", + cgu1_in_sels, 2, CLK_SET_RATE_PARENT, cdu + CDU_CLKINSEL, 0, 1, 0, + &cdu_lock); + + // CGU configuration and internal clocks + clks[ADSP_SC594_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df", + "sys_clkin0", CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 0, 1, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df", + "cgu1_in_sel", CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 0, 1, 0, &cdu_lock); + + // VCO output inside PLL + clks[ADSP_SC594_CLK_CGU0_VCO_OUT] = sc5xx_cgu_pll("cgu0_vco", "cgu0_df", + cgu0 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_VCO_OUT] = sc5xx_cgu_pll("cgu1_vco", "cgu1_df", + cgu1 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + + // Final PLL output + clks[ADSP_SC594_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, + "cgu0_pllclk", "cgu0_vco", CLK_SET_RATE_PARENT, 1, 1); + clks[ADSP_SC594_CLK_CGU1_PLLCLK] = clk_register_fixed_factor(NULL, + "cgu1_pllclk", "cgu1_vco", CLK_SET_RATE_PARENT, 1, 1); + + // Dividers from pll output + clks[ADSP_SC594_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk", + cgu0 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", + "sysclk_0", cgu0 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", + "sysclk_0", cgu0 + CGU_DIV, 13, 3, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S1SELEXDIV] = cgu_divider("cgu0_s1selexdiv", + "cgu0_pllclk", cgu0 + CGU_DIVEX, 16, 8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU0_S1SEL] = clk_register_mux(NULL, "cgu0_sclk1sel", + cgu0_s1sels, 2, CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 17, 1, 0, &cdu_lock); + + clks[ADSP_SC594_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk", + cgu1 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", + "sysclk_1", cgu1 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", + "sysclk_1", cgu1 + CGU_DIV, 13, 3, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S1SELEXDIV] = cgu_divider("cgu1_s1selexdiv", + "cgu1_pllclk", cgu1 + CGU_DIVEX, 16, 8, 0, &cdu_lock); + clks[ADSP_SC598_CLK_CGU1_S1SEL] = clk_register_mux(NULL, "cgu1_sclk1sel", + cgu1_s1sels, 2, CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 17, 1, 0, &cdu_lock); + + // Gates to enable CGU outputs + clks[ADSP_SC594_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv", + cgu0 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv", + cgu0 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv", + cgu0 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_sclk1sel", + cgu0 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC594_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv", + cgu0 + CGU_SCBF_DIS, 0, &cdu_lock); + + clks[ADSP_SC594_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv", + cgu1 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv", + cgu1 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_sclk1sel", + cgu1 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC594_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv", + cgu1 + CGU_SCBF_DIS, 0, &cdu_lock); + + // CDU output muxes + clks[ADSP_SC594_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0, + sharc0_sels, &cdu_lock); + clks[ADSP_SC594_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1, + sharc1_sels, &cdu_lock); + clks[ADSP_SC594_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, + arm_sels, &cdu_lock); + clks[ADSP_SC594_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3, + cdu_ddr_sels, &cdu_lock); + clks[ADSP_SC594_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, + can_sels, &cdu_lock); + clks[ADSP_SC594_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, + spdif_sels, &cdu_lock); + clks[ADSP_SC594_CLK_RESERVED_SEL] = cdu_mux("spi_sel", cdu + CDU_CFG6, + spi_sels, &cdu_lock); + clks[ADSP_SC594_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, + gige_sels, &cdu_lock); + clks[ADSP_SC594_CLK_LP_SEL] = cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels, + &cdu_lock); + clks[ADSP_SC594_CLK_LPDDR_SEL] = cdu_mux("lpddr_sel", cdu + CDU_CFG9, lpddr_sels, + &cdu_lock); + clks[ADSP_SC594_CLK_OSPI_SEL] = cdu_mux("ospi_sel", cdu + CDU_CFG10, + ospi_sels, &cdu_lock); + clks[ADSP_SC594_CLK_TRACE_SEL] = cdu_mux("trace_sel", cdu + CDU_CFG12, trace_sels, + &cdu_lock); + + // CDU output enable gates + clks[ADSP_SC594_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", + cdu + CDU_CFG0, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC594_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", + cdu + CDU_CFG1, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC594_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2, + CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC594_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", + cdu + CDU_CFG3, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC594_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0, + &cdu_lock); + clks[ADSP_SC594_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, + 0, &cdu_lock); + clks[ADSP_SC594_CLK_SPI] = cdu_gate("spi", "spi_sel", cdu + CDU_CFG6, 0, + &cdu_lock); + clks[ADSP_SC594_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0, + &cdu_lock); + clks[ADSP_SC594_CLK_LP] = cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0, &cdu_lock); + clks[ADSP_SC594_CLK_LPDDR] = cdu_gate("lpddr", "lpddr_sel", cdu + CDU_CFG9, 0, + &cdu_lock); + clks[ADSP_SC594_CLK_OSPI] = cdu_gate("ospi", "ospi_sel", cdu + CDU_CFG10, 0, + &cdu_lock); + clks[ADSP_SC594_CLK_TRACE] = cdu_gate("trace", "trace_sel", cdu + CDU_CFG12, 0, + &cdu_lock); + + ret = cdu_check_clocks(clks, ARRAY_SIZE(clks)); + if (ret) + goto cleanup; + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + pr_err("Failed to register SoC clock information\n"); + goto cleanup; + } + + return; + +cleanup: + for (i = 0; i < ARRAY_SIZE(clks); i++) + clk_unregister(clks[i]); +} + +CLK_OF_DECLARE(sc594_clocks, "adi,sc594-clocks", sc594_clock_probe); From 0a363aa9010829d394f684b70d0699fa50c1da26 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 13:52:34 +0100 Subject: [PATCH 23/85] clk: adi: Add clock driver for ADSP-SC589 Only supports the ADZS-SC589-MINI Co-developed-by: Utsav Agarwal Signed-off-by: Utsav Agarwal Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- drivers/clk/adi/Kconfig | 9 ++ drivers/clk/adi/Makefile | 1 + drivers/clk/adi/clk-adi-sc58x.c | 217 ++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 drivers/clk/adi/clk-adi-sc58x.c diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index 588f80c752a958..dfc91aa4e1893f 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -1,4 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC58X + bool "ADI SC589 clock driver" + depends on ARCH_SC5XX || COMPILE_TEST + help + This enables the ADI SC589 clock driver. The driver provides + support for the clock controller on the ADI SC589 SoC. + If you are using the ADI SC589 SoC, say Y here. + If unsure, say N. + config COMMON_CLK_ADI_SC594 bool "ADI SC594 clock driver" depends on ARCH_SC5XX || COMPILE_TEST diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile index 1b0bd668ffe836..622c2ac2a6b8c3 100644 --- a/drivers/clk/adi/Makefile +++ b/drivers/clk/adi/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ARCH_SC59X_64) += clk-adi-pll.o obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o +obj-$(CONFIG_COMMON_CLK_ADI_SC58X) += clk-adi-sc58x.o diff --git a/drivers/clk/adi/clk-adi-sc58x.c b/drivers/clk/adi/clk-adi-sc58x.c new file mode 100644 index 00000000000000..f0a5fcd3ac16b4 --- /dev/null +++ b/drivers/clk/adi/clk-adi-sc58x.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Clock support for ADI processor + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static DEFINE_SPINLOCK(cdu_lock); + +static struct clk *clks[ADSP_SC58X_CLK_END]; +static struct clk_onecell_data clk_data; + +static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"}; +static const char * const sharc0_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"}; +static const char * const sharc1_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"}; +static const char * const arm_sels[] = {"cclk1_0", "sysclk_0", "dummy", "dummy"}; +static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"}; +static const char * const can_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dummy"}; +static const char * const spdif_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dclk_0"}; +static const char * const reserved_sels[] = {"sclk0_0", "oclk_0", "dummy", "dummy"}; +static const char * const gige_sels[] = {"sclk0_0", "sclk1_1", "cclk0_1", "oclk_0"}; +static const char * const lp_sels[] = {"sclk0_0", "sclk0_1", "cclk1_1", "dclk_1"}; +static const char * const sdio_sels[] = {"oclk_0_half", "cclk1_1_half", "cclk1_1", "dclk_1"}; + +static void sc58x_clock_probe(struct device_node *np) +{ + void __iomem *cgu0; + void __iomem *cgu1; + void __iomem *cdu; + int ret; + int i; + + cgu0 = of_iomap(np, 0); + if (!cgu0) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (!cgu1) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (!cdu) { + pr_err("Unable to remap CDU address (resource 2)\n"); + return; + } + + // Input clock configuration + clks[ADSP_SC58X_CLK_DUMMY] = clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); + clks[ADSP_SC58X_CLK_SYS_CLKIN0] = of_clk_get_by_name(np, "sys_clkin0"); + clks[ADSP_SC58X_CLK_SYS_CLKIN1] = of_clk_get_by_name(np, "sys_clkin1"); + clks[ADSP_SC58X_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", + cgu1_in_sels, 2, CLK_SET_RATE_PARENT, cdu + CDU_CLKINSEL, 0, 1, 0, + &cdu_lock); + + // CGU configuration and internal clocks + clks[ADSP_SC58X_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df", + "sys_clkin0", CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 0, 1, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df", + "cgu1_in_sel", CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 0, 1, 0, &cdu_lock); + + // VCO output inside PLL + clks[ADSP_SC58X_CLK_CGU0_VCO_OUT] = sc5xx_cgu_pll("cgu0_vco", "cgu0_df", + cgu0 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_VCO_OUT] = sc5xx_cgu_pll("cgu1_vco", "cgu1_df", + cgu1 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + + // Final PLL output + clks[ADSP_SC58X_CLK_CGU0_PLLCLK] = clk_register_fixed_factor(NULL, + "cgu0_pllclk", "cgu0_vco", CLK_SET_RATE_PARENT, 1, 1); + clks[ADSP_SC58X_CLK_CGU1_PLLCLK] = clk_register_fixed_factor(NULL, + "cgu1_pllclk", "cgu1_vco", CLK_SET_RATE_PARENT, 1, 1); + + // Dividers from pll output + clks[ADSP_SC58X_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk", + cgu0 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", + "sysclk_0", cgu0 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", + "sysclk_0", cgu0 + CGU_DIV, 13, 3, 0, &cdu_lock); + + clks[ADSP_SC58X_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk", + cgu1 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", + "sysclk_1", cgu1 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", + "sysclk_1", cgu1 + CGU_DIV, 13, 3, 0, &cdu_lock); + + // Gates to enable CGU outputs + clks[ADSP_SC58X_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv", + cgu0 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv", + cgu0 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv", + cgu0 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_s1seldiv", + cgu0 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv", + cgu0 + CGU_SCBF_DIS, 0, &cdu_lock); + + clks[ADSP_SC58X_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv", + cgu1 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv", + cgu1 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_s1seldiv", + cgu1 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC58X_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv", + cgu1 + CGU_SCBF_DIS, 0, &cdu_lock); + + // Extra half rate clocks generated in the CDU + clks[ADSP_SC58X_CLK_OCLK0_HALF] = clk_register_fixed_factor(NULL, "oclk_0_half", + "oclk_0", CLK_SET_RATE_PARENT, 1, 2); + clks[ADSP_SC58X_CLK_CCLK1_1_HALF] = clk_register_fixed_factor(NULL, "cclk1_1_half", + "cclk1_1", CLK_SET_RATE_PARENT, 1, 2); + + // CDU output muxes + clks[ADSP_SC58X_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0, + sharc0_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1, + sharc1_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, + arm_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3, + cdu_ddr_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, + can_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, + spdif_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_RESERVED_SEL] = cdu_mux("reserved_sel", cdu + CDU_CFG6, + reserved_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, + gige_sels, &cdu_lock); + clks[ADSP_SC58X_CLK_LP_SEL] = cdu_mux("lp_sel", cdu + CDU_CFG8, lp_sels, + &cdu_lock); + clks[ADSP_SC58X_CLK_SDIO_SEL] = cdu_mux("sdio_sel", cdu + CDU_CFG9, sdio_sels, + &cdu_lock); + + // CDU output enable gates + clks[ADSP_SC58X_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", + cdu + CDU_CFG0, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC58X_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", + cdu + CDU_CFG1, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC58X_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2, + CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC58X_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", + cdu + CDU_CFG3, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC58X_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0, + &cdu_lock); + clks[ADSP_SC58X_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, + 0, &cdu_lock); + clks[ADSP_SC58X_CLK_RESERVED] = cdu_gate("reserved", "reserved_sel", + cdu + CDU_CFG6, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0, + &cdu_lock); + clks[ADSP_SC58X_CLK_LP] = cdu_gate("lp", "lp_sel", cdu + CDU_CFG8, 0, &cdu_lock); + clks[ADSP_SC58X_CLK_SDIO] = cdu_gate("sdio", "sdio_sel", cdu + CDU_CFG9, 0, + &cdu_lock); + + ret = cdu_check_clocks(clks, ARRAY_SIZE(clks)); + if (ret) + goto cleanup; + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + pr_err("Failed to register SoC clock information\n"); + goto cleanup; + } + + return; + +cleanup: + for (i = 0; i < ARRAY_SIZE(clks); i++) + clk_unregister(clks[i]); +} + +CLK_OF_DECLARE(sc58x_clocks, "adi,sc58x-clocks", sc58x_clock_probe); From 14afecf8e0b1c49426c764695a7ac709bb68b36c Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:19:57 +0100 Subject: [PATCH 24/85] clk: adi: Add clock driver for ADSP-SC573 Co-developed-by: UtsavAgarwalADI Signed-off-by: UtsavAgarwalADI Co-developed-by: Qasim Ijaz Signed-off-by: Qasim Ijaz Signed-off-by: Philip Molloy --- drivers/clk/adi/Kconfig | 9 ++ drivers/clk/adi/Makefile | 1 + drivers/clk/adi/clk-adi-sc57x.c | 202 ++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 drivers/clk/adi/clk-adi-sc57x.c diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index dfc91aa4e1893f..7c18a2195cfd42 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -1,4 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC57X + bool "ADI SC573 clock driver" + depends on ARCH_SC5XX || COMPILE_TEST + help + This enables the ADI SC573-ezkit clock driver. The driver provides + support for the clock controller on the ADI SC573-ezkit SoC. + If you are using the ADI SC573-ezkit SoC, say Y here. + If unsure, say N. + config COMMON_CLK_ADI_SC58X bool "ADI SC589 clock driver" depends on ARCH_SC5XX || COMPILE_TEST diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile index 622c2ac2a6b8c3..4dbb2095a8dc08 100644 --- a/drivers/clk/adi/Makefile +++ b/drivers/clk/adi/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_ARCH_SC59X_64) += clk-adi-pll.o obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o obj-$(CONFIG_COMMON_CLK_ADI_SC58X) += clk-adi-sc58x.o +obj-$(CONFIG_COMMON_CLK_ADI_SC57X) += clk-adi-sc57x.o diff --git a/drivers/clk/adi/clk-adi-sc57x.c b/drivers/clk/adi/clk-adi-sc57x.c new file mode 100644 index 00000000000000..d077645b5116b7 --- /dev/null +++ b/drivers/clk/adi/clk-adi-sc57x.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Clock support for ADI processor + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa + * Contact: Nathan Barrett-Morrison + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static DEFINE_SPINLOCK(cdu_lock); + +static struct clk *clks[ADSP_SC57X_CLK_END]; +static struct clk_onecell_data clk_data; + +static const char * const cgu1_in_sels[] = {"sys_clkin0", "sys_clkin1"}; +static const char * const sharc0_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"}; +static const char * const sharc1_sels[] = {"cclk0_0", "sysclk_0", "dummy", "dummy"}; +static const char * const arm_sels[] = {"cclk1_0", "sysclk_0", "dummy", "dummy"}; +static const char * const cdu_ddr_sels[] = {"dclk_0", "dclk_1", "dummy", "dummy"}; +static const char * const can_sels[] = {"oclk_0", "oclk_1", "dclk_1", "oclk_0_half"}; +static const char * const spdif_sels[] = {"oclk_0", "oclk_1", "dclk_1", "dclk_0"}; +static const char * const gige_sels[] = {"sclk1_0", "sclk1_1", "cclk0_1", "oclk_0"}; +static const char * const sdio_sels[] = {"oclk_0_half", "cclk1_1_half", "cclk1_1", "dclk_1"}; + +static void __init sc57x_clock_probe(struct device_node *np) +{ + void __iomem *cgu0; + void __iomem *cgu1; + void __iomem *cdu; + int ret; + int i; + + cgu0 = of_iomap(np, 0); + if (!cgu0) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (!cgu1) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (!cdu) { + pr_err("Unable to remap CDU address (resource 2)\n"); + return; + } + + // Input clock configuration + clks[ADSP_SC57X_CLK_DUMMY] = clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); + clks[ADSP_SC57X_CLK_SYS_CLKIN0] = of_clk_get_by_name(np, "sys_clkin0"); + clks[ADSP_SC57X_CLK_SYS_CLKIN1] = of_clk_get_by_name(np, "sys_clkin1"); + clks[ADSP_SC57X_CLK_CGU1_IN] = clk_register_mux(NULL, "cgu1_in_sel", + cgu1_in_sels, 2, CLK_SET_RATE_PARENT, cdu + CDU_CLKINSEL, 0, 1, 0, + &cdu_lock); + + // CGU configuration and internal clocks + clks[ADSP_SC57X_CLK_CGU0_PLL_IN] = clk_register_divider(NULL, "cgu0_df", + "sys_clkin0", CLK_SET_RATE_PARENT, cgu0 + CGU_CTL, 0, 1, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_PLL_IN] = clk_register_divider(NULL, "cgu1_df", + "cgu1_in_sel", CLK_SET_RATE_PARENT, cgu1 + CGU_CTL, 0, 1, 0, &cdu_lock); + + // VCO output == PLL output + clks[ADSP_SC57X_CLK_CGU0_PLLCLK] = sc5xx_cgu_pll("cgu0_pllclk", "cgu0_df", + cgu0 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_PLLCLK] = sc5xx_cgu_pll("cgu1_pllclk", "cgu1_df", + cgu1 + CGU_CTL, CGU_MSEL_SHIFT, CGU_MSEL_WIDTH, 0, false, &cdu_lock); + + // Dividers from pll output + clks[ADSP_SC57X_CLK_CGU0_CDIV] = cgu_divider("cgu0_cdiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_SYSCLK] = cgu_divider("sysclk_0", "cgu0_pllclk", + cgu0 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_DDIV] = cgu_divider("cgu0_ddiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_ODIV] = cgu_divider("cgu0_odiv", "cgu0_pllclk", + cgu0 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_S0SELDIV] = cgu_divider("cgu0_s0seldiv", + "sysclk_0", cgu0 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_S1SELDIV] = cgu_divider("cgu0_s1seldiv", + "sysclk_0", cgu0 + CGU_DIV, 13, 3, 0, &cdu_lock); + + clks[ADSP_SC57X_CLK_CGU1_CDIV] = cgu_divider("cgu1_cdiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 0, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_SYSCLK] = cgu_divider("sysclk_1", "cgu1_pllclk", + cgu1 + CGU_DIV, 8, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_DDIV] = cgu_divider("cgu1_ddiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 16, 5, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_ODIV] = cgu_divider("cgu1_odiv", "cgu1_pllclk", + cgu1 + CGU_DIV, 22, 7, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_S0SELDIV] = cgu_divider("cgu1_s0seldiv", + "sysclk_1", cgu1 + CGU_DIV, 5, 3, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_S1SELDIV] = cgu_divider("cgu1_s1seldiv", + "sysclk_1", cgu1 + CGU_DIV, 13, 3, 0, &cdu_lock); + + // Gates to enable CGU outputs + clks[ADSP_SC57X_CLK_CGU0_CCLK0] = cgu_gate("cclk0_0", "cgu0_cdiv", + cgu0 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_CCLK1] = cgu_gate("cclk1_0", "cgu0_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_OCLK] = cgu_gate("oclk_0", "cgu0_odiv", + cgu0 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_DCLK] = cgu_gate("dclk_0", "cgu0_ddiv", + cgu0 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_SCLK1] = cgu_gate("sclk1_0", "cgu0_s1seldiv", + cgu0 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU0_SCLK0] = cgu_gate("sclk0_0", "cgu0_s0seldiv", + cgu0 + CGU_SCBF_DIS, 0, &cdu_lock); + + clks[ADSP_SC57X_CLK_CGU1_CCLK0] = cgu_gate("cclk0_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 0, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_CCLK1] = cgu_gate("cclk1_1", "cgu1_cdiv", + cgu1 + CGU_CCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_OCLK] = cgu_gate("oclk_1", "cgu1_odiv", + cgu1 + CGU_SCBF_DIS, 3, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_DCLK] = cgu_gate("dclk_1", "cgu1_ddiv", + cgu1 + CGU_SCBF_DIS, 2, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_SCLK1] = cgu_gate("sclk1_1", "cgu1_s1seldiv", + cgu1 + CGU_SCBF_DIS, 1, &cdu_lock); + clks[ADSP_SC57X_CLK_CGU1_SCLK0] = cgu_gate("sclk0_1", "cgu1_s0seldiv", + cgu1 + CGU_SCBF_DIS, 0, &cdu_lock); + + // Extra half rate clocks generated in the CDU + clks[ADSP_SC57X_CLK_OCLK0_HALF] = clk_register_fixed_factor(NULL, "oclk_0_half", + "oclk_0", CLK_SET_RATE_PARENT, 1, 2); + clks[ADSP_SC57X_CLK_CCLK1_1_HALF] = clk_register_fixed_factor(NULL, "cclk1_1_half", + "cclk1_1", CLK_SET_RATE_PARENT, 1, 2); + + // CDU output muxes + clks[ADSP_SC57X_CLK_SHARC0_SEL] = cdu_mux("sharc0_sel", cdu + CDU_CFG0, + sharc0_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_SHARC1_SEL] = cdu_mux("sharc1_sel", cdu + CDU_CFG1, + sharc1_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_ARM_SEL] = cdu_mux("arm_sel", cdu + CDU_CFG2, + arm_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_CDU_DDR_SEL] = cdu_mux("cdu_ddr_sel", cdu + CDU_CFG3, + cdu_ddr_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_CAN_SEL] = cdu_mux("can_sel", cdu + CDU_CFG4, + can_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_SPDIF_SEL] = cdu_mux("spdif_sel", cdu + CDU_CFG5, + spdif_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_GIGE_SEL] = cdu_mux("gige_sel", cdu + CDU_CFG7, + gige_sels, &cdu_lock); + clks[ADSP_SC57X_CLK_SDIO_SEL] = cdu_mux("sdio_sel", cdu + CDU_CFG9, sdio_sels, + &cdu_lock); + + // CDU output enable gates + clks[ADSP_SC57X_CLK_SHARC0] = cdu_gate("sharc0", "sharc0_sel", + cdu + CDU_CFG0, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC57X_CLK_SHARC1] = cdu_gate("sharc1", "sharc1_sel", + cdu + CDU_CFG1, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC57X_CLK_ARM] = cdu_gate("arm", "arm_sel", cdu + CDU_CFG2, + CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC57X_CLK_CDU_DDR] = cdu_gate("cdu_ddr", "cdu_ddr_sel", + cdu + CDU_CFG3, CLK_IS_CRITICAL, &cdu_lock); + clks[ADSP_SC57X_CLK_CAN] = cdu_gate("can", "can_sel", cdu + CDU_CFG4, 0, + &cdu_lock); + clks[ADSP_SC57X_CLK_SPDIF] = cdu_gate("spdif", "spdif_sel", cdu + CDU_CFG5, + 0, &cdu_lock); + clks[ADSP_SC57X_CLK_GIGE] = cdu_gate("gige", "gige_sel", cdu + CDU_CFG7, 0, + &cdu_lock); + clks[ADSP_SC57X_CLK_SDIO] = cdu_gate("sdio", "sdio_sel", cdu + CDU_CFG9, 0, + &cdu_lock); + + ret = cdu_check_clocks(clks, ARRAY_SIZE(clks)); + if (ret) + goto cleanup; + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + pr_err("Failed to register SoC clock information\n"); + goto cleanup; + } + + return; + +cleanup: + for (i = 0; i < ARRAY_SIZE(clks); i++) + clk_unregister(clks[i]); +} + +CLK_OF_DECLARE(sc57x_clocks, "adi,sc57x-clocks", sc57x_clock_probe); From e801aff525c904f2099b14fedac1bbb8efeadc03 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 12 Sep 2025 13:30:36 +0200 Subject: [PATCH 25/85] net: stmmac: dwmac-adi: Add support for ADSP-SC598 Signed-off-by: Philip Molloy --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 12 ++ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../net/ethernet/stmicro/stmmac/dwmac-adi.c | 123 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-adi.c diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 9507131875b2ca..e40489b1cbff72 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -58,6 +58,18 @@ config DWMAC_GENERIC platform specific code to function or is using platform data for setup. +config DWMAC_ADI + tristate "ADI DWMAC support" + default ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X + depends on OF && (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X || COMPILE_TEST) + help + Support for ethernet controller on SC5XX SOCs. + + This selects SC5XX SoC glue layer support for the stmmac + device driver. This driver is used on for the SC5XX series + SOCs EMAC ethernet controller. + GMAC ethernet controller. + config DWMAC_ANARION tristate "Adaptrum Anarion GMAC support" default ARC diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 51e068e26ce499..9aed1e9180eb3e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -13,6 +13,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o +obj-$(CONFIG_DWMAC_ADI) += dwmac-adi.o obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-adi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-adi.c new file mode 100644 index 00000000000000..06b562bea6290e --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-adi.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices EMAC driver for sc5xx + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +#define ADI_PHYISEL_MII 0 +#define ADI_PHYISEL_RGMII 1 +#define ADI_PHYISEL_RMII 2 + +static int dwmac_adi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct plat_stmmacenet_data *plat_dat; + struct regmap *regmap; + struct stmmac_resources stmmac_res; + int ret; + int emac_alias; + u32 val; + phy_interface_t phy_mode; + + if (!of_property_read_bool(np, "adi,skip-phyconfig")) { + emac_alias = of_alias_get_id(np, "ethernet"); + if (emac_alias < 0) { + dev_err(&pdev->dev, "Failed to get EMAC alias id\n"); + return -ENODEV; + } + + regmap = system_config_regmap_lookup_by_phandle(np, "adi,system-config"); + if (IS_ERR(regmap)) { + if (PTR_ERR(regmap) == -EPROBE_DEFER) + return PTR_ERR(regmap); + + dev_err(&pdev->dev, "adi,system-config regmap not connected\n"); + return PTR_ERR(regmap); + } + + if (emac_alias == 0) { + ret = of_get_phy_mode(np, &phy_mode); + if (ret) { + dev_err(dev, "phy-mode must be specified to configure interface\n"); + return ret; + } + + val = 0; + switch (phy_mode) { + case PHY_INTERFACE_MODE_MII: + val = ADI_PHYISEL_MII; + break; + case PHY_INTERFACE_MODE_RMII: + val = ADI_PHYISEL_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + val = ADI_PHYISEL_RGMII; + break; + default: + dev_err(dev, "Unsupported PHY interface mode %d selected\n", ret); + return -EINVAL; + } + + /* write config registers if available */ + regmap_write(regmap, ADI_SYSTEM_REG_EMAC0_EMACRESET, 0); + regmap_write(regmap, ADI_SYSTEM_REG_EMAC0_PHYISEL, val); + regmap_write(regmap, ADI_SYSTEM_REG_EMAC0_EMACRESET, 1); + regmap_write(regmap, ADI_SYSTEM_REG_EMAC0_ENDIANNESS, 0); + } else if (emac_alias == 1) { + regmap_write(regmap, ADI_SYSTEM_REG_EMAC1_ENDIANNESS, 0); + } + } + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) { + dev_err(dev, "dt configuration failed\n"); + return PTR_ERR(plat_dat); + } + + return stmmac_dvr_probe(dev, plat_dat, &stmmac_res); +} + +static const struct of_device_id dwmac_adi_match[] = { + { .compatible = "adi,dwmac"}, + { } +}; +MODULE_DEVICE_TABLE(of, dwmac_adi_match); + +static struct platform_driver dwmac_adi_driver = { + .probe = dwmac_adi_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "adi-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(dwmac_adi_match), + }, +}; +module_platform_driver(dwmac_adi_driver); + +MODULE_DESCRIPTION("EMAC driver for ADI SC598 based boards"); +MODULE_LICENSE("GPL v2"); From b103b3e2a8a12564eefba20e2e493b0cff507bd6 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Fri, 29 Mar 2024 16:00:10 +0000 Subject: [PATCH 26/85] remoteproc: adi: Add driver for ADSP SHARC cores Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs --- drivers/remoteproc/Kconfig | 8 +- drivers/remoteproc/Makefile | 1 + drivers/remoteproc/adi_remoteproc.c | 1034 +++++++++++++++++++++++++++ 3 files changed, 1042 insertions(+), 1 deletion(-) create mode 100644 drivers/remoteproc/adi_remoteproc.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48a0d3a69ed080..2d95b9d17207e3 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -132,7 +132,7 @@ config KEYSTONE_REMOTEPROC tristate "Keystone Remoteproc support" depends on ARCH_KEYSTONE help - Say Y here here to support Keystone remote processors (DSP) + Say Y here to support Keystone remote processors (DSP) via the remote processor framework. It's safe to say N here if you're not interested in the Keystone @@ -378,6 +378,12 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores. +config ADI_REMOTEPROC + tristate "ADI remoteproc support" + depends on ARCH_SC59X_64 || ARCH_SC5XX || COMPILE_TEST + help + Say y here to support ADI's remote processors (SHARC DSP Core onSC5XX) via the remote processor framework. + endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 1c7598b8475d60..e35fe784c1f8bf 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o ti_k3_common.o obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o ti_k3_common.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o ti_k3_common.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o +obj-$(CONFIG_ADI_REMOTEPROC) += adi_remoteproc.o \ No newline at end of file diff --git a/drivers/remoteproc/adi_remoteproc.c b/drivers/remoteproc/adi_remoteproc.c new file mode 100644 index 00000000000000..0f8b2915e6ce04 --- /dev/null +++ b/drivers/remoteproc/adi_remoteproc.c @@ -0,0 +1,1034 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Device SHARC Image Loader for SC5XX processors + * + * Copyright 2020-2022 Analog Devices + * + * @todo: + * - sharc idle core as default with dts override + * - timeout as default with dts override + * - resource table dynamically constructed from dts data or executable file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "remoteproc_internal.h" + +/* location of bootrom that loops idle */ +#define SHARC_IDLE_ADDR (0x00090004) + +#define SPU_MDMA0_SRC_ID 88 +#define SPU_MDMA0_DST_ID 89 + +#define CORE_INIT_TIMEOUT_MS (2000) +#define CORE_INIT_TIMEOUT msecs_to_jiffies(CORE_INIT_TIMEOUT_MS) + +#define MEMORY_COUNT 2 + +#define ADI_FW_LDR 0 +#define ADI_FW_ELF 1 + +#define NUM_TABLE_ENTRIES 1 +/* Resource table for the given remote */ + +struct bcode_flag_t { + uint32_t bCode:4, /* 0-3 */ + bFlag_save:1, /* 4 */ + bFlag_aux:1, /* 5 */ + bReserved:1, /* 6 */ + bFlag_forward:1, /* 7 */ + bFlag_fill:1, /* 8 */ + bFlag_quickboot:1, /* 9 */ + bFlag_callback:1, /* 10 */ + bFlag_init:1, /* 11 */ + bFlag_ignore:1, /* 12 */ + bFlag_indirect:1, /* 13 */ + bFlag_first:1, /* 14 */ + bFlag_final:1, /* 15 */ + bHdrCHK:8, /* 16-23 */ + bHdrSIGN:8; /* 0xAD, 0xAC or 0xAB */ +}; + +struct ldr_hdr { + struct bcode_flag_t bcode_flag; + u32 target_addr; + u32 byte_count; + u32 argument; +}; + +struct sharc_resource_table { + struct resource_table table_hdr; + unsigned int offset[NUM_TABLE_ENTRIES]; + struct fw_rsc_hdr rpmsg_vdev_hdr; + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring vring[2]; +} __packed; + +struct adi_sharc_resource_table { + struct adi_resource_table_hdr adi_table_hdr; + struct sharc_resource_table rsc_table; +} __packed; + +#define VRING_ALIGN 0x1000 +#define VRING_DEFAULT_SIZE 0x800 + +/* + * In regular case the table comes from a firmware file, since ldr format doesn't have + * resource_table section we initialize the table here, and let remoteproc_core.c + * copy the initialized cached_table to reserved memory (adi,rsc-table) shared with remote core. + * The table must be initialized before core start so the remote core + * can't initialize the reserved memory either. + */ +static struct adi_sharc_resource_table _rsc_table_template = { + .adi_table_hdr = { + .tag = ADI_RESOURCE_TABLE_TAG, + .version = 1, + .initialized = 0, + }, + .rsc_table = { + .table_hdr = { + /* resource table header */ + 1, /* version */ + NUM_TABLE_ENTRIES, /* number of table entries */ + {0, 0,}, /* reserved fields */ + }, + .offset = {offsetof(struct sharc_resource_table, rpmsg_vdev_hdr), + }, + /* virtio device entry */ + .rpmsg_vdev_hdr = {RSC_VDEV,}, /* virtio dev type */ + .rpmsg_vdev = { + VIRTIO_ID_RPMSG, /* it's rpmsg virtio */ + 1, /* kick sharc0 */ + /* 1<<0 is VIRTIO_RPMSG_F_NS bit defined in virtio_rpmsg_bus.c */ + 1<<0, 0, 0, 0, /* dfeatures, gfeatures, config len, status */ + 2, /* num_of_vrings */ + {0, 0,}, /* reserved */ + }, + .vring = { + /* da allocated by remoteproc driver */ + {FW_RSC_ADDR_ANY, VRING_ALIGN, VRING_DEFAULT_SIZE, 1, 0}, + /* da allocated by remoteproc driver */ + {FW_RSC_ADDR_ANY, VRING_ALIGN, VRING_DEFAULT_SIZE, 1, 0}, + }, + }, +}; + +enum adi_rproc_rpmsg_state { + ADI_RP_RPMSG_SYNCED = 0, + ADI_RP_RPMSG_WAITING = 1, + ADI_RP_RPMSG_TIMED_OUT = 2, +}; + +struct adi_rproc_data { + struct device *dev; + struct rproc *rproc; + struct adi_rcu *rcu; + struct adi_tru *tru; + const char *firmware_name; + int core_id; + int core_irq; + int icc_irq; + int icc_irq_flags; + void *mem_virt; + dma_addr_t mem_handle; + size_t fw_size; + unsigned long ldr_load_addr; + int firmware_format; + void __iomem *L1_shared_base; + void __iomem *L2_shared_base; + struct workqueue_struct *core_workqueue; + enum adi_rproc_rpmsg_state rpmsg_state; + u64 l1_da_range[2]; + u64 l2_da_range[2]; + u32 verify; + struct adi_sharc_resource_table *adi_rsc_table; + struct sharc_resource_table *loaded_rsc_table; +}; + +static int adi_core_set_svect(struct adi_rproc_data *rproc_data, + unsigned long svect) +{ + int coreid = rproc_data->core_id; + + if (svect && (coreid == 1)) + adi_rcu_writel(svect, rproc_data->rcu, ADI_RCU_REG_SVECT1); + else if (svect && (coreid == 2)) + adi_rcu_writel(svect, rproc_data->rcu, ADI_RCU_REG_SVECT2); + else { + dev_err(rproc_data->dev, "%s, invalid svect:0x%lx, cord_id:%d\n", + __func__, svect, coreid); + return -EINVAL; + } + + return 0; +} + +static irqreturn_t sharc_virtio_irq_threaded_handler(int irq, void *p); + +static int adi_core_start(struct adi_rproc_data *rproc_data) +{ + int ret = 0; + + if (rproc_data->adi_rsc_table != NULL) { + rproc_data->rpmsg_state = ADI_RP_RPMSG_WAITING; + ret = devm_request_threaded_irq(rproc_data->dev, + rproc_data->icc_irq, NULL, + sharc_virtio_irq_threaded_handler, + rproc_data->icc_irq_flags, + "ICC virtio IRQ", rproc_data); + } + if (ret) { + dev_err(rproc_data->dev, "Fail to request ICC IRQ\n"); + return -ENOENT; + } + + return adi_rcu_start_core(rproc_data->rcu, rproc_data->core_id); +} + +static int adi_core_reset(struct adi_rproc_data *rproc_data) +{ + return adi_rcu_reset_core(rproc_data->rcu, rproc_data->core_id); +} + +static int adi_core_stop(struct adi_rproc_data *rproc_data) +{ + /* After time out the irq is already released */ + if (rproc_data->adi_rsc_table != NULL) { + if (rproc_data->rpmsg_state != ADI_RP_RPMSG_TIMED_OUT) + devm_free_irq(rproc_data->dev, rproc_data->icc_irq, rproc_data); + } + return adi_rcu_stop_core(rproc_data->rcu, + rproc_data->core_id, rproc_data->core_irq); +} + +static int is_final(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bFlag_final; +} + +static int is_empty(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bFlag_ignore || (hdr->byte_count == 0); +} + +static void load_callback(void *p) +{ + struct completion *cmp = p; + + complete(cmp); +} + +/* @todo this needs to return status */ +/* @todo the error paths here leak tremendously, this needs further cleanup */ +static void ldr_load(struct adi_rproc_data *rproc_data) +{ + struct ldr_hdr *block_hdr = NULL; + struct ldr_hdr *next_hdr = NULL; + u8 *virbuf = (u8 *) rproc_data->mem_virt; + dma_addr_t phybuf = rproc_data->mem_handle; + int offset; +// part of verify buffer code +// int i; +// uint32_t verfied = 0; +// uint8_t *pCompareBuffer; +// uint8_t *pVerifyBuffer; + struct dma_chan *chan = dma_find_channel(DMA_MEMCPY); + struct dma_async_tx_descriptor *tx; + struct completion cmp; + + if (!chan) { + dev_err(rproc_data->dev, "Could not find dma memcpy channel\n"); + return; + } + + init_completion(&cmp); + + do { + /* read the header */ + block_hdr = (struct ldr_hdr *) virbuf; + offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bFlag_fill ? + 0 : block_hdr->byte_count); + next_hdr = (struct ldr_hdr *) (virbuf + offset); + tx = NULL; + + /* Overwrite the ldr_load_addr */ + if (block_hdr->bcode_flag.bFlag_first) + rproc_data->ldr_load_addr = (unsigned long)block_hdr->target_addr; + + if (!is_empty(block_hdr)) { + if (block_hdr->bcode_flag.bFlag_fill) { + tx = dmaengine_prep_dma_memset(chan, + block_hdr->target_addr, + block_hdr->argument, + block_hdr->byte_count, 0); + } else { + tx = dmaengine_prep_dma_memcpy(chan, + block_hdr->target_addr, + phybuf + sizeof(struct ldr_hdr), + block_hdr->byte_count, 0); + +// if (rproc_data->verify) { +// @todo implement verification +// pCompareBuffer = virbuf + sizeof(struct ldr_hdr); +// pVerifyBuffer = virbuf + rproc_data->fw_size; +// +// dma_memcpy(phybuf + rproc_data->fw_size, +// block_hdr->target_addr, +// block_hdr->byte_count); +// +// /* check the data */ +// for (i = 0; i < block_hdr->byte_count; i++) { +// if (pCompareBuffer[i] != pVerifyBuffer[i]) { +// dev_err(rproc_data->dev, +// "dirty data, compare[%d]:0x%x," +// "verify[%d]:0x%x\n", +// i, pCompareBuffer[i], i, +// pVerifyBuffer[i]); +// verfied++; +// break; +// } +// } +// } + } + + if (!tx) { + dev_err(rproc_data->dev, "Failed to allocate dma transaction\n"); + return; + } + + if (is_final(block_hdr) || (is_final(next_hdr) && is_empty(next_hdr))) { + tx->callback = load_callback; + tx->callback_param = &cmp; + } + dmaengine_submit(tx); + dma_async_issue_pending(chan); + } + + if (is_final(block_hdr)) { + wait_for_completion(&cmp); + break; + } + + virbuf += offset; + phybuf += offset; + } while (1); + +// if (rproc_data->verify) { +// if (verfied == 0) +// dev_err(rproc_data->dev, "success to verify all the data\n"); +// else +// dev_err(rproc_data->dev, "fail to verify all the data %d\n", verfied); +// } +} + +static int adi_valid_firmware(struct rproc *rproc, const struct firmware *fw) +{ + struct ldr_hdr *adi_ldr_hdr = (struct ldr_hdr *)fw->data; + + if (!adi_ldr_hdr->byte_count && + (adi_ldr_hdr->bcode_flag.bHdrSIGN == 0xAD || + adi_ldr_hdr->bcode_flag.bHdrSIGN == 0xAC || + adi_ldr_hdr->bcode_flag.bHdrSIGN == 0xAB)) + return ADI_FW_LDR; + + if (!rproc_elf_sanity_check(rproc, fw)) { + dev_err(&rproc->dev, "ELF format not supported\n"); + return -EOPNOTSUPP; + } + + dev_err(&rproc->dev, "## No valid image at address %p\n", fw->data); + return -EINVAL; +} + +void set_spu_securep_msec(u16 n, bool msec); +static void enable_spu(void) +{ + set_spu_securep_msec(SPU_MDMA0_SRC_ID, true); + set_spu_securep_msec(SPU_MDMA0_DST_ID, true); +} + +static void disable_spu(void) +{ + set_spu_securep_msec(SPU_MDMA0_SRC_ID, false); + set_spu_securep_msec(SPU_MDMA0_DST_ID, false); +} + +static int adi_ldr_load(struct adi_rproc_data *rproc_data, + const struct firmware *fw) +{ + rproc_data->fw_size = fw->size; + if (!rproc_data->mem_virt) { + rproc_data->mem_virt = dma_alloc_coherent(rproc_data->dev, + fw->size * MEMORY_COUNT, + &rproc_data->mem_handle, + GFP_KERNEL); + if (rproc_data->mem_virt == NULL) { + dev_err(rproc_data->dev, "Unable to allocate memory\n"); + return -ENOMEM; + } + } + + memcpy((char *)rproc_data->mem_virt, fw->data, fw->size); + + enable_spu(); + ldr_load(rproc_data); + disable_spu(); + + return 0; +} + +/* + * adi_rproc_load: parse and load ADI SHARC LDR file into memory + * + * This function would be called when user run the start command + * echo start > /sys/class/remoteproc/remoteprocX/state + */ +static int adi_rproc_load(struct rproc *rproc, const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + int ret; + + switch (rproc_data->firmware_format) { + case ADI_FW_LDR: + ret = adi_ldr_load(rproc_data, fw); + break; + case ADI_FW_ELF: + ret = rproc_elf_load_segments(rproc, fw); + break; + default: + WARN(1, "Invalid rproc_data->firmware_format\n"); + return -EINVAL; + } + + if (ret) + dev_err(rproc_data->dev, "Failed to load ldr, ret:%d\n", ret); + + return ret; +} + +/* + * adi_rproc_start: to start run the applicaiton which is loaded in memory + * + * This function would be called when user run the start command + * echo start > /sys/class/remoteproc/remoteprocX/state + */ +static int adi_rproc_start(struct rproc *rproc) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + int ret; + + ret = adi_core_set_svect(rproc_data, rproc_data->ldr_load_addr); + if (ret) + return ret; + + ret = adi_core_reset(rproc_data); + if (ret) + return ret; + + return adi_core_start(rproc_data); +} + +/* + * adi_rproc_stop: to stop the running applicaiton in DSP + * This would be called when user run the stop command + * echo stop > /sys/class/remoteproc/remoteprocX/state + */ +static int adi_rproc_stop(struct rproc *rproc) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + int ret; + + ret = adi_core_set_svect(rproc_data, SHARC_IDLE_ADDR); + if (ret) + return ret; + + ret = adi_core_stop(rproc_data); + if (ret) + return ret; + + ret = adi_core_reset(rproc_data); + if (ret) + return ret; + + if (rproc_data->mem_virt) { + memset(rproc_data->mem_virt, 0, rproc_data->fw_size * MEMORY_COUNT); + dma_free_coherent(rproc_data->dev, rproc_data->fw_size * MEMORY_COUNT, + rproc_data->mem_virt, rproc_data->mem_handle); + rproc_data->mem_virt = NULL; + rproc_data->fw_size = 0; + } + + rproc_data->ldr_load_addr = SHARC_IDLE_ADDR; + rproc_data->loaded_rsc_table = NULL; + return ret; +} + +static int adi_ldr_load_rsc_table(struct rproc *rproc, const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + size_t size = sizeof(_rsc_table_template.rsc_table); + + if (rproc_data->adi_rsc_table == NULL) + return -EINVAL; + + /* kfree in remoteproc_core.c */ + rproc->cached_table = kmemdup(&_rsc_table_template.rsc_table, size, GFP_KERNEL); + if (!rproc->cached_table) + return -ENOMEM; + + if (rproc_data->core_id == 1) { + ((struct sharc_resource_table *)rproc->cached_table)->rpmsg_vdev.notifyid = 1; + ((struct sharc_resource_table *)rproc->cached_table)->vring[0].notifyid = 1; + ((struct sharc_resource_table *)rproc->cached_table)->vring[1].notifyid = 1; + } else { + ((struct sharc_resource_table *)rproc->cached_table)->rpmsg_vdev.notifyid = 2; + ((struct sharc_resource_table *)rproc->cached_table)->vring[0].notifyid = 2; + ((struct sharc_resource_table *)rproc->cached_table)->vring[1].notifyid = 2; + } + + /* Initialize ADI resource table header*/ + rproc_data->adi_rsc_table->adi_table_hdr = _rsc_table_template.adi_table_hdr; + + rproc->table_ptr = rproc->cached_table; + rproc->table_sz = size; + + return 0; +} + +static int adi_rproc_map_carveout(struct rproc *rproc, struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + void *va; + + va = ioremap_wc(mem->dma, mem->len); + if (!va) { + dev_err(dev, "Unable to map memory carveout %pa+%zx\n", &mem->dma, mem->len); + return -ENOMEM; + } + mem->va = va; + return 0; +} + +static int adi_rproc_unmap_carveout(struct rproc *rproc, struct rproc_mem_entry *mem) +{ + iounmap(mem->va); + return 0; +} + +static int adi_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct sharc_resource_table *rsc_table; + struct rproc_mem_entry *mem; + struct device_node *node; + struct reserved_mem *rmem; + phys_addr_t size; + int ret, i, mem_regions, num; + + if (rproc_data->adi_rsc_table == NULL) + return 0; + + switch (rproc_data->firmware_format) { + case ADI_FW_LDR: + ret = adi_ldr_load_rsc_table(rproc, fw); + break; + case ADI_FW_ELF: + ret = rproc_elf_load_rsc_table(rproc, fw); + break; + default: + WARN(1, "Invalid rproc_data->firmware_format\n"); + return -EINVAL; + } + + if (ret < 0) + return ret; + + /* Set defaults */ + rsc_table = (struct sharc_resource_table *)rproc->cached_table; + rsc_table->vring[0].da = FW_RSC_ADDR_ANY; + rsc_table->vring[0].num = VRING_DEFAULT_SIZE; + rsc_table->vring[1].da = FW_RSC_ADDR_ANY; + rsc_table->vring[1].num = VRING_DEFAULT_SIZE; + + /* + * Find reserved memory for vrings, if not found uses CMA region + * The reserved memory can be in internal SRAM for better access time. + */ + mem_regions = of_count_phandle_with_args(np, "vdev-vring", NULL); + for (i = 0; i < mem_regions; i++) { + node = of_parse_phandle(np, "vdev-vring", i); + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(dev, "Failed to acquire vdev-vring at idx %d\n", i); + return -EINVAL; + } + + /* We need at least 16kB for vdev-vring */ + if (rmem->size < 0x4000) { + dev_err(dev, "Insufficient space, vdev-vring idx %d, min req 16kB\n", i); + return -EINVAL; + } + + /* Split the range for two vrings, vring0 -rx and vring1 - tx*/ + size = rmem->size / 2; + + mem = rproc_mem_entry_init(dev, NULL, + (dma_addr_t)rmem->base, + size, rmem->base, + adi_rproc_map_carveout, + adi_rproc_unmap_carveout, + "vdev%dvring0", i); + if (!mem) + return -ENOMEM; + + rproc_add_carveout(rproc, mem); + + mem = rproc_mem_entry_init(dev, NULL, + (dma_addr_t)rmem->base + size, + size, rmem->base + size, + adi_rproc_map_carveout, + adi_rproc_unmap_carveout, + "vdev%dvring1", i); + if (!mem) + return -ENOMEM; + + rproc_add_carveout(rproc, mem); + + /* Update the resource table before loading*/ + /* TODO add support for multiple vdev devices*/ + if (i > 0) { + continue; + } else { + /* Calc how many buffers we can fit in the vring region, + * number of buffers must be power of 2 + */ + for (num = 2; num < 0x00400000; num <<= 1) { + if (PAGE_ALIGN(vring_size(num, VRING_ALIGN)) > size) { + num >>= 1; // It's too much, restore + // previous value and break + break; + } + } + + rsc_table->vring[0].da = rmem->base; + rsc_table->vring[0].num = num; + rsc_table->vring[1].da = rmem->base + size; + rsc_table->vring[1].num = num; + } + } + + /* + * Find reserved memory for vring buffers. + * The reserved memory be in internal SRAM + * for better access time. If found sets + * DMA API to use the region, if not found + * uses CMA region + */ + mem_regions = of_count_phandle_with_args(np, "memory-region", NULL); + for (i = 0; i < mem_regions; i++) { + node = of_parse_phandle(np, "memory-region", i); + rmem = of_reserved_mem_lookup(node); + mem = rproc_of_resm_mem_entry_init(dev, i, rmem->size, + rmem->base, "vdev%dbuffer", i); + if (!mem) + return -ENOMEM; + rproc_add_carveout(rproc, mem); + } + + return 0; +} + +static struct resource_table *adi_ldr_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + + if (rproc_data->adi_rsc_table == NULL) + return NULL; + + return &rproc_data->adi_rsc_table->rsc_table.table_hdr; +} + +static struct resource_table *adi_rproc_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + struct resource_table *ret = NULL; + + if (rproc_data->adi_rsc_table == NULL) + return NULL; + + switch (rproc_data->firmware_format) { + case ADI_FW_LDR: + ret = adi_ldr_find_loaded_rsc_table(rproc, fw); + break; + case ADI_FW_ELF: + ret = rproc_elf_find_loaded_rsc_table(rproc, fw); + break; + default: + WARN(1, "Invalid rproc_data->firmware_format\n"); + return NULL; + } + rproc_data->loaded_rsc_table = (struct sharc_resource_table *)ret; + return ret; +} + +/* @todo store number of vrings from resource table and use it to dynamically + * notify the correct number of vrings + */ +static irqreturn_t sharc_virtio_irq_threaded_handler(int irq, void *p) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)p; + struct sharc_resource_table *table = rproc_data->loaded_rsc_table; + + /* Firmwares witout resource table shouldn't enable the virtio irq */ + if (!table) { + WARN(1, "Invalid rproc_data->firmware_format\n"); + return -EINVAL; + } + + rproc_vq_interrupt(rproc_data->rproc, table->vring[0].notifyid); + rproc_vq_interrupt(rproc_data->rproc, table->vring[1].notifyid); + + return IRQ_HANDLED; +} + +/* kick a virtqueue */ +static void adi_rproc_kick(struct rproc *rproc, int vqid) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + int wait_time; + + /* On first kick check if remote core has done its initialization */ + if (rproc_data->rpmsg_state == ADI_RP_RPMSG_WAITING) { + for (wait_time = 0; wait_time < CORE_INIT_TIMEOUT_MS; wait_time += 20) { + if (rproc_data->adi_rsc_table->adi_table_hdr.initialized == + ADI_RSC_TABLE_INIT_MAGIC) { + rproc_data->rpmsg_state = ADI_RP_RPMSG_SYNCED; + break; + } + msleep(20); + } + if (rproc_data->rpmsg_state != ADI_RP_RPMSG_SYNCED) { + rproc_data->rpmsg_state = ADI_RP_RPMSG_TIMED_OUT; + devm_free_irq(rproc_data->dev, rproc_data->icc_irq, rproc_data); + dev_info(rproc_data->dev, + "Core%d rpmsg init timeout, probably not supported.\n", + rproc_data->core_id); + } + } + + if (rproc_data->rpmsg_state == ADI_RP_RPMSG_SYNCED) + adi_tru_trigger_device(rproc_data->tru, rproc_data->dev); +} + +static int adi_rproc_sanity_check(struct rproc *rproc, const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + + /* Check if it is a LDR or ELF file */ + rproc_data->firmware_format = adi_valid_firmware(rproc, fw); + + if (rproc_data->firmware_format < 0) + return rproc_data->firmware_format; + else + return 0; +} + +static u64 adi_rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + u64 ret; + + switch (rproc_data->firmware_format) { + case ADI_FW_LDR: + ret = 0; + break; + case ADI_FW_ELF: + ret = rproc_elf_get_boot_addr(rproc, fw); + break; + default: + WARN(1, "Invalid rproc_data->firmware_format\n"); + return -EINVAL; + } + return ret; +} + +static void *adi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *unused) +{ + struct adi_rproc_data *rproc_data = (struct adi_rproc_data *)rproc->priv; + void __iomem *L1_shared_base = rproc_data->L1_shared_base; + void __iomem *L2_shared_base = rproc_data->L2_shared_base; + void *ret = NULL; + + if (len == 0) + return NULL; + + if (da >= rproc_data->l1_da_range[0] && da < rproc_data->l1_da_range[1]) + ret = L1_shared_base + da; + else if (da >= rproc_data->l2_da_range[0] && da < rproc_data->l2_da_range[1]) + ret = L2_shared_base + (da - rproc_data->l2_da_range[0]); + + return ret; +} + +static const struct rproc_ops adi_rproc_ops = { + .start = adi_rproc_start, + .stop = adi_rproc_stop, + .kick = adi_rproc_kick, + .load = adi_rproc_load, + .da_to_va = adi_rproc_da_to_va, + .parse_fw = adi_rproc_parse_fw, + .find_loaded_rsc_table = adi_rproc_find_loaded_rsc_table, + .sanity_check = adi_rproc_sanity_check, + .get_boot_addr = adi_rproc_get_boot_addr, +}; + +static int adi_remoteproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_rproc_data *rproc_data; + struct device_node *np = dev->of_node; + struct device_node *node; + struct adi_rcu *adi_rcu; + struct adi_tru *adi_tru; + struct rproc *rproc; + struct resource *res; + struct reserved_mem *rmem; + u32 addr[2]; + int ret, core_id; + const char *name; + + ret = of_property_read_string(np, "firmware-name", &name); + if (ret) { + dev_err(dev, "Unable to get firmware-name property\n"); + return ret; + } + + adi_rcu = get_adi_rcu_from_node(dev); + if (IS_ERR(adi_rcu)) + return PTR_ERR(adi_rcu); + + ret = of_property_read_u32(np, "core-id", &core_id); + if (ret) { + dev_err(dev, "Unable to get core-id property\n"); + goto free_adi_rcu; + } + + ret = adi_rcu_is_core_idle(adi_rcu, core_id); + if (ret < 0) { + dev_err(dev, "Invalid core-id\n"); + goto free_adi_rcu; + } else if (ret == 0) { + dev_err(dev, "Error: Core%d not idle\n", core_id); + ret = -EBUSY; + goto free_adi_rcu; + } + + adi_tru = get_adi_tru_from_node(dev); + if (IS_ERR(adi_tru)) { + ret = PTR_ERR(adi_tru); + goto free_adi_rcu; + } + + rproc = rproc_alloc(dev, np->name, &adi_rproc_ops, + name, sizeof(*rproc_data)); + if (!rproc) { + dev_err(dev, "Unable to allocate remoteproc\n"); + ret = -ENOMEM; + goto free_adi_tru; + } + + rproc_data = (struct adi_rproc_data *)rproc->priv; + platform_set_drvdata(pdev, rproc); + + /* for now device addresses are represented as 32 bits and expanded to 64 + * here in driver code + */ + if (of_property_read_u32_array(np, "adi,l1-da", addr, 2)) { + dev_err(dev, "Missing adi,l1-da with L1 device address range information\n"); + ret = -ENODEV; + goto free_rproc; + } + rproc_data->l1_da_range[0] = addr[0]; + rproc_data->l1_da_range[1] = addr[1]; + + if (of_property_read_u32_array(np, "adi,l2-da", addr, 2)) { + dev_err(dev, "Missing adi,l2-da with L2 device address range information\n"); + ret = -ENODEV; + goto free_rproc; + } + rproc_data->l2_da_range[0] = addr[0]; + rproc_data->l2_da_range[1] = addr[1]; + + /* Get ADI resource table address */ + node = of_parse_phandle(np, "adi,rsc-table", 0); + if (node) { + dev_info(&pdev->dev, "Resource table set, enable rpmsg\n"); + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(&pdev->dev, "Translating adi,rsc-table failed\n"); + ret = -ENOMEM; + goto free_adi_rcu; + } + + rproc_data->adi_rsc_table = devm_ioremap_wc(dev, + rmem->base, + rmem->size); + if (IS_ERR(rproc_data->adi_rsc_table)) { + dev_err(dev, "Can't map adi,rsc-table\n"); + ret = PTR_ERR(rproc_data->adi_rsc_table); + goto free_adi_rcu; + } + + rproc_data->icc_irq = platform_get_irq(pdev, 0); + if (rproc_data->icc_irq <= 0) { + dev_err(dev, "No ICC IRQ specified\n"); + ret = -ENOENT; + goto free_adi_rcu; + } + + rproc_data->icc_irq_flags = IRQF_PERCPU | IRQF_SHARED | IRQF_ONESHOT; + + } else { + rproc_data->adi_rsc_table = NULL; + } + + rproc_data->core_workqueue = alloc_workqueue("Core workqueue", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!rproc_data->core_workqueue) { + dev_err(dev, "Unable to allocate core workqueue\n"); + goto free_rproc; + } + + ret = of_property_read_u32(np, "core-irq", &rproc_data->core_irq); + if (ret) { + dev_err(dev, "Unable to get core-irq property\n"); + goto free_workqueue; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Cannot get L1 base address (reg 0)\n"); + ret = -ENODEV; + goto free_workqueue; + } + rproc_data->L1_shared_base = devm_ioremap_wc(dev, + res->start, + resource_size(res)); + if (IS_ERR(rproc_data->L1_shared_base)) { + dev_err(dev, "Cannot map L1 shared memory\n"); + ret = PTR_ERR(rproc_data->L1_shared_base); + goto free_workqueue; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "Cannot get L2 base address (reg 1)\n"); + ret = -ENODEV; + goto free_workqueue; + } + rproc_data->L2_shared_base = devm_ioremap_wc(dev, + res->start, + resource_size(res)); + if (IS_ERR(rproc_data->L2_shared_base)) { + dev_err(dev, "Cannot map L2 shared memory\n"); + ret = PTR_ERR(rproc_data->L2_shared_base); + goto free_workqueue; + } + + rproc_data->verify = 0; + of_property_read_u32(np, "adi,verify", &rproc_data->verify); + rproc_data->verify = !!rproc_data->verify; + if (rproc_data->verify) + dev_info(dev, "Load verification enabled\n"); + + rproc_data->dev = &pdev->dev; + rproc_data->core_id = core_id; + rproc_data->rcu = adi_rcu; + rproc_data->tru = adi_tru; + rproc_data->rproc = rproc; + rproc_data->firmware_name = name; + rproc_data->mem_virt = NULL; + rproc_data->fw_size = 0; + rproc_data->ldr_load_addr = SHARC_IDLE_ADDR; + rproc_data->rpmsg_state = ADI_RP_RPMSG_TIMED_OUT; + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "Failed to add rproc\n"); + goto free_workqueue; + } + + dmaengine_get(); + + return 0; + +free_workqueue: + destroy_workqueue(rproc_data->core_workqueue); + +free_rproc: + rproc_free(rproc); + +free_adi_tru: + put_adi_tru(adi_tru); + +free_adi_rcu: + put_adi_rcu(adi_rcu); + + return ret; +} + +static void adi_remoteproc_remove(struct platform_device *pdev) +{ + struct adi_rproc_data *rproc_data = platform_get_drvdata(pdev); + + dmaengine_put(); + put_adi_tru(rproc_data->tru); + put_adi_rcu(rproc_data->rcu); + rproc_del(rproc_data->rproc); + rproc_free(rproc_data->rproc); +} + +static const struct of_device_id adi_rproc_of_match[] = { + { .compatible = "adi,remoteproc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adi_rproc_of_match); + +static struct platform_driver adi_rproc_driver = { + .probe = adi_remoteproc_probe, + .remove = adi_remoteproc_remove, + .driver = { + .name = "adi_remoteproc", + .of_match_table = of_match_ptr(adi_rproc_of_match), + }, +}; +module_platform_driver(adi_rproc_driver); + +MODULE_DESCRIPTION("Analog Device sc5xx SHARC Image Loader"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Greg Chen "); +MODULE_AUTHOR("Piotr Wojtaszczyk "); From e60ce415530cba162eb96e3b9613da7be5424e98 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Thu, 4 Apr 2024 16:23:33 +0100 Subject: [PATCH 27/85] misc: sram: adi: Add drivers for ADSP-SCxxx SoCs Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs Signed-off-by: Philip Molloy --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/adi/Kconfig | 22 +++ drivers/misc/adi/Makefile | 5 + drivers/misc/adi/sram.c | 199 ++++++++++++++++++++++++ drivers/misc/adi/sram.h | 23 +++ drivers/misc/adi/sram_mmap.c | 259 ++++++++++++++++++++++++++++++++ drivers/misc/adi/sram_mmap_v7.c | 158 +++++++++++++++++++ 8 files changed, 668 insertions(+) create mode 100644 drivers/misc/adi/Kconfig create mode 100644 drivers/misc/adi/Makefile create mode 100644 drivers/misc/adi/sram.c create mode 100644 drivers/misc/adi/sram.h create mode 100644 drivers/misc/adi/sram_mmap.c create mode 100644 drivers/misc/adi/sram_mmap_v7.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b9c11f67315f0b..78615f58ebe4da 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -661,4 +661,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" source "drivers/misc/rp1/Kconfig" +source "drivers/misc/adi/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b32a2597d2467b..5a9ea0a5decf80 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-y += adi/ diff --git a/drivers/misc/adi/Kconfig b/drivers/misc/adi/Kconfig new file mode 100644 index 00000000000000..447cb30b2d4f80 --- /dev/null +++ b/drivers/misc/adi/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later + +config ADI_SRAM_MMAP + bool "Aarch64 MMAP driver for sc5xx onchip SRAM" + depends on ARCH_SC59X_64 && SRAM + default n + help + Enable the mmap driver for sc5xx on chip SRAM. + +config ADI_SRAM_MMAP_V7 + bool "Legacy (armv7) mmap driver for sc5xx onchip SRAM" + depends on ARCH_SC5XX && SRAM + default n + help + Enable the mmap driver for sc5xx on chip SRAM. + +config ADI_SRAM_CONTROLLER + bool "/proc/sraminfo support for sc5xx onchip SRAM" + depends on (ARCH_SC59X_64 || ARCH_SC5XX) && SRAM + default n + help + Exports total/used/avail information for sc5xx onchip SRAM. diff --git a/drivers/misc/adi/Makefile b/drivers/misc/adi/Makefile new file mode 100644 index 00000000000000..c7b6b012ddfd76 --- /dev/null +++ b/drivers/misc/adi/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later +obj-$(CONFIG_ADI_SRAM_CONTROLLER) += sram.o +obj-$(CONFIG_ADI_SRAM_MMAP_V7) += sram_mmap_v7.o +obj-$(CONFIG_ADI_SRAM_MMAP) += sram_mmap.o + diff --git a/drivers/misc/adi/sram.c b/drivers/misc/adi/sram.c new file mode 100644 index 00000000000000..b6400c5bafdb82 --- /dev/null +++ b/drivers/misc/adi/sram.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SRAM driver for ADI processor on-chip memory + * + * (C) Copyright 2025 - Analog Devices, Inc. + * + * Authors: + * Nathan Barrett-Morrison + * Greg Malysa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sram.h" + +static struct device *sram_dev; +static void __iomem *l2_ctl_vaddr; + +static irqreturn_t sram_ecc_err(int irq, void *dev_id) +{ + int status; + + pr_err("SRAM ECC error happened\n"); + status = readl(l2_ctl_vaddr + L2CTL0_STAT_OFFSET); + pr_err("status 0x%x ctl %x\n", status, readl(l2_ctl_vaddr)); + + if (status & 0x1) + pr_err("Core channel error type:0x%x, addr:0x%x\n", + readl(l2_ctl_vaddr + L2CTL0_ET0_OFFSET), + readl(l2_ctl_vaddr + L2CTL0_EADDR0_OFFSET)); + if (status & 0x2) + pr_err("System channel error type:0x%x, addr:0x%x\n", + readl(l2_ctl_vaddr + L2CTL0_ET1_OFFSET), + readl(l2_ctl_vaddr + L2CTL0_EADDR1_OFFSET)); + + status = status >> 8; + if (status) + pr_err("SRAM Bank%d error, addr:0x%x\n", status, + readl(l2_ctl_vaddr + L2CTL0_ERRADDR0_OFFSET + status)); + panic("Can't recover from the SRAM ECC error."); + + return IRQ_HANDLED; +} + +static int adi_sram_show(struct seq_file *s, void *data) +{ + struct device_node *sram_node; + struct gen_pool *sram_pool = NULL; + size_t pool_size = 0, avail = 0, used = 0; + int index, count = 0; + const __be32 *sram_pbase; + + count = of_count_phandle_with_args(sram_dev->of_node, "adi,sram", NULL); + if (!count) { + pr_err("no adi,sram phandle defined in sram controller\n"); + return -ENODEV; + } + + for (index = 0; index < count; index++) { + /* Get the name and addr of the sram pool */ + sram_node = of_parse_phandle(sram_dev->of_node, "adi,sram", index); + if (!sram_node) { + pr_err("Unable to parse phandle\n"); + return -ENODEV; + } + + sram_pbase = of_get_address(sram_node, 0, NULL, NULL); + if (!sram_pbase) { + pr_err("Unable to get phandle address\n"); + return -ENODEV; + } + seq_printf(s, "%s@%x:\n", + sram_node->name, be32_to_cpu(*sram_pbase)); + + /* Get the sram pool info */ + sram_pool = of_gen_pool_get(sram_dev->of_node, "adi,sram", index); + if (!sram_pool) { + pr_err("sram_pool not available\n"); + of_node_put(sram_node); + return -ENOMEM; + } + + /* Calculate the sram total/available/used size */ + pool_size = gen_pool_size(sram_pool); + avail = gen_pool_avail(sram_pool); + used = pool_size - avail; + seq_printf(s, "\tTotal size: %lu B\n\tUsed sram: %lu B\n\tAvail sram: %lu B\n", + pool_size, used, avail); + + of_node_put(sram_node); + } + + return 0; +} + +static int adi_sram_open(struct inode *inode, struct file *file) +{ + return single_open(file, adi_sram_show, NULL); +} + +static const struct proc_ops adi_sram_ops = { + .proc_open = adi_sram_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static const struct of_device_id adi_sram_of_match[] = { + { .compatible = "adi,sram-controller" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adi_sram_of_match); + +static int adi_sram_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq; + struct proc_dir_entry *d; + struct device *dev = &pdev->dev; + struct resource *res; + + sram_dev = &pdev->dev; + + /* create sram proc show data */ + d = proc_create("sraminfo", 0, NULL, &adi_sram_ops); + if (!d) { + dev_err(dev, "Cannot create proc sraminfo entry\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Cannot find L2 CTL address (reg property in device tree)\n"); + ret = -ENOENT; + goto free_proc; + } + + l2_ctl_vaddr = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(l2_ctl_vaddr)) { + dev_err(dev, "Cannot map L2 control address\n"); + ret = -ENOENT; + goto free_proc; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(dev, "invalid irq from dts node\n"); + ret = -ENOENT; + goto free_proc; + } + + ret = devm_request_threaded_irq(dev, irq, sram_ecc_err, NULL, + 0, "sram-ecc-err", dev); + if (unlikely(ret < 0)) { + dev_err(dev, "Fail to request SRAM ECC error interrupt.ret:%d\n", ret); + ret = -ENOENT; + goto free_proc; + } + + /* clear all status bits */ + writel(readl(l2_ctl_vaddr + L2CTL0_STAT_OFFSET), + (l2_ctl_vaddr + L2CTL0_STAT_OFFSET)); + + return 0; + +free_proc: + remove_proc_entry("sraminfo", NULL); + return ret; +} + +static void adi_sram_remove(struct platform_device *pdev) +{ + remove_proc_entry("sraminfo", NULL); +} + +static struct platform_driver adi_sram_driver = { + .probe = adi_sram_probe, + .remove = adi_sram_remove, + .driver = { + .name = "sram_controller", + .of_match_table = of_match_ptr(adi_sram_of_match), + }, +}; + +module_platform_driver(adi_sram_driver); + +MODULE_DESCRIPTION("ADI on-chip sram Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/adi/sram.h b/drivers/misc/adi/sram.h new file mode 100644 index 00000000000000..61b753a937e667 --- /dev/null +++ b/drivers/misc/adi/sram.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * SRAM driver for ADI processor on-chip memory + * + * (C) Copyright 2025 - Analog Devices, Inc. + * + * Authors: + * Nathan Barrett-Morrison + * Greg Malysa + */ + +#ifndef __MACH_SRAM_H +#define __MACH_SRAM_H + +#define L2CTL0_CTL_OFFSET 0x0 /* L2CTL0 Control Register Offset */ +#define L2CTL0_STAT_OFFSET 0x10 /* L2CTL0 Status Register Offset */ +#define L2CTL0_ERRADDR0_OFFSET 0x40 /* L2CTL0 ECC Error Address 0 Register Offset */ +#define L2CTL0_ET0_OFFSET 0x80 /* L2CTL0 Error Type 0 Register Offset */ +#define L2CTL0_EADDR0_OFFSET 0x84 /* L2CTL0 Error Type 0 Address Register Offset */ +#define L2CTL0_ET1_OFFSET 0x88 /* L2CTL0 Error Type 1 Register Offset */ +#define L2CTL0_EADDR1_OFFSET 0x8C /* L2CTL0 Error Type 1 Address Register Offset */ + +#endif /* __MACH_SRAM_H */ diff --git a/drivers/misc/adi/sram_mmap.c b/drivers/misc/adi/sram_mmap.c new file mode 100644 index 00000000000000..f3fd45ed49fd3e --- /dev/null +++ b/drivers/misc/adi/sram_mmap.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SRAM mmap misc driver for ADI processor on-chip memory + * + * (C) Copyright 2025 - Analog Devices, Inc. + * + * Authors: + * Nathan Barrett-Morrison + * Greg Malysa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SRAM_MMAP_DRV_NAME "sram_mmap" + +#define MAX_SRAM_REGIONS 4 +struct sram_region_info { + struct reserved_mem *rmem; + struct device *dev; +}; + +static struct sram_region_info sram_regions[MAX_SRAM_REGIONS]; +static int next_region; + +static struct reserved_mem *find_sram_mem(struct device *dev, int *id) +{ + int i; + + if (!dev) { + pr_err("NULL device to find_sram_mem\n"); + return NULL; + } + + for (i = 0; i < MAX_SRAM_REGIONS; ++i) { + if (sram_regions[i].dev == dev) { + *id = i; + return sram_regions[i].rmem; + } + } + + dev_err(dev, "Unable to find SRAM reservation!\n"); + return NULL; +} + +struct adi_sram_mmap { + struct miscdevice miscdev; + struct device *dev; + struct page *start; + struct reserved_mem *rmem; +}; + +static bool sram_dirty_folio(struct address_space *mapping, struct folio *folio) +{ + /* do nothing but avoid using __set_page_dirty_buffers which would + * actually mark the page as dirty and cause a warning later */ + return false; +} + +struct address_space_operations sram_aops = { + .dirty_folio = sram_dirty_folio, +}; + +/** + * For now ignore pgoff supplied by the user and start mapping at + * the start of SRAM + */ +static int sram_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct adi_sram_mmap *sram = container_of(fp->private_data, + struct adi_sram_mmap, miscdev); + size_t sram_size = vma->vm_end - vma->vm_start; + struct page *start_page; + int pages, i; + int ret = 0; + + if ((vma->vm_pgoff * PAGE_SIZE) + sram_size > sram->rmem->size) { + dev_err(sram->dev, "Tried to map 0x%zx@0x%zx, only 0x%zx available\n", + sram_size, vma->vm_pgoff * PAGE_SIZE, (size_t)sram->rmem->size); + return -ENOMEM; + } + + dev_err(sram->dev, "Requested mapping is not a multiple of page size, requested 0x%lx bytes\n", sram_size); + if (sram_size % PAGE_SIZE) { + dev_err(sram->dev, "Requested mapping is not a multiple of page size, requested 0x%lx bytes\n", sram_size); + return -EINVAL; + } + + fp->f_mapping->a_ops = &sram_aops; + vma->vm_page_prot = __pgprot_modify(vma->vm_page_prot, PTE_ATTRINDX_MASK, + PTE_ATTRINDX(MT_NORMAL) | PTE_PXN | PTE_UXN); + vma->vm_private_data = sram; + vma->vm_ops = NULL; + + start_page = sram->start; + pages = sram_size / PAGE_SIZE; + + for (i = 0; i < pages; ++i) { + struct page *page = start_page + vma->vm_pgoff + i; + + if (!page->mapping) + page->mapping = fp->f_mapping; + + ret = vm_insert_page(vma, vma->vm_start + (i*PAGE_SIZE), page); + if (ret) { + dev_err(sram->dev, "Failed to map page to userspace\n"); + return ret; + } + } + + dev_info(sram->dev, "Mapped 0x%zx : 0x%zx successfully\n", + (size_t) (sram->rmem->base + vma->vm_pgoff * PAGE_SIZE), + (size_t) (sram->rmem->base + vma->vm_pgoff * PAGE_SIZE + sram_size)); + return 0; +} + +static const struct file_operations sram_fops = { + .mmap = sram_mmap, +}; + +static const struct of_device_id adi_sram_mmap_of_match[] = { + { .compatible = "adi,sram-mmap" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adi_sram_mmap_of_match); + +static int adi_sram_mmap_probe(struct platform_device *pdev) +{ + int ret = 0; + struct adi_sram_mmap *sram; + struct device *dev; + struct page *page; + struct reserved_mem *mem; + int pages, i, id; + + dev = &pdev->dev; + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "Unable to configure SRAM reserved memory\n"); + return ret; + } + + mem = find_sram_mem(dev, &id); + if (!mem) { + dev_err(dev, "SRAM MMAP requires adi,sram-access reserved memory, please check your device tree\n"); + return -ENOENT; + } + + page = pfn_to_page(PFN_DOWN(mem->base)); + pages = mem->size / PAGE_SIZE; + + // Grab reference to page so they're never freed back into the allocator + for (i = 0; i < pages; ++i) { + set_page_count(page+i, 1); + } + + sram = devm_kzalloc(dev, sizeof(*sram), GFP_KERNEL); + if (!sram) { + dev_err(dev, "Unable to allocate sram device data\n"); + return -ENOMEM; + } + + sram->dev = dev; + sram->start = page; + sram->rmem = mem; + dev_set_drvdata(&pdev->dev, sram); + + sram->miscdev.minor = MISC_DYNAMIC_MINOR; + sram->miscdev.name = kasprintf(GFP_KERNEL, "%s%d", SRAM_MMAP_DRV_NAME, id); + sram->miscdev.fops = &sram_fops; + sram->miscdev.parent = dev; + + ret = misc_register(&sram->miscdev); + if (ret < 0) + dev_err(dev, "Faied to register sram mmap misc device\n"); + + return ret; +} + +static void adi_sram_mmap_remove(struct platform_device *pdev) +{ + struct adi_sram_mmap *sram = dev_get_drvdata(&pdev->dev); + + misc_deregister(&sram->miscdev); +} + +static struct platform_driver adi_sram_mmap_driver = { + .probe = adi_sram_mmap_probe, + .remove = adi_sram_mmap_remove, + .driver = { + .name = SRAM_MMAP_DRV_NAME, + .of_match_table = of_match_ptr(adi_sram_mmap_of_match), + }, +}; + +static int rmem_sram_init(struct reserved_mem *rmem, struct device *dev) +{ + struct sram_region_info *info = rmem->priv; + + printk(KERN_ERR"sram_init\n"); + info->dev = dev; + return 0; +} + +static void rmem_sram_release(struct reserved_mem *rmem, struct device *dev) +{ + struct sram_region_info *info = rmem->priv; + + info->dev = NULL; +} + +static const struct reserved_mem_ops rmem_sram_ops = { + .device_init = rmem_sram_init, + .device_release = rmem_sram_release, +}; + +static int __init rmem_sram_setup(struct reserved_mem *rmem) +{ + if (next_region >= MAX_SRAM_REGIONS) { + pr_err("Cannot allocate more SRAM regions--increase MAX_SRAM_REGIONS\n"); + return -EINVAL; + } + + if (rmem->base & (PAGE_SIZE-1)) { + pr_err("sram region starting at 0x%px is not page aligned!\n", (void *)rmem->base); + return -EINVAL; + } + + if (rmem->size & (PAGE_SIZE-1)) { + pr_err("sram region starting at 0x%px is not a multiple of the page size (requested 0x%llx bytes)\n", + (void *)rmem->base, rmem->size); + return -EINVAL; + } + + rmem->ops = &rmem_sram_ops; + rmem->priv = &sram_regions[next_region]; + sram_regions[next_region].rmem = rmem; + next_region += 1; + + pr_info("Reserved memory: SRAM at %pa, size %ld KiB\n", + &rmem->base, (unsigned long) (rmem->size / SZ_1K)); + return 0; +} +RESERVEDMEM_OF_DECLARE(adi_sram, "adi,sram-access", rmem_sram_setup); + +module_platform_driver(adi_sram_mmap_driver); + +MODULE_DESCRIPTION("SRAM mmap misc driver for ADI processor on-chip memory"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/adi/sram_mmap_v7.c b/drivers/misc/adi/sram_mmap_v7.c new file mode 100644 index 00000000000000..59d8e6d7052d0f --- /dev/null +++ b/drivers/misc/adi/sram_mmap_v7.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SRAM mmap misc driver for ADI processor on-chip memory + * + * (C) Copyright 2025 - Analog Devices, Inc. + * + * Authors: + * Nathan Barrett-Morrison + * Greg Malysa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SRAM_MMAP_DRV_NAME "sram_mmap" + +struct adi_sram_mmap { + struct miscdevice miscdev; + struct gen_pool *sram_pool; +}; + +struct mmap_private_data { + struct gen_pool *pool; + unsigned long vaddr; +}; + +static void mmap_open(struct vm_area_struct *vma) +{ + struct mmap_private_data *pdata = vma->vm_private_data; + size_t sram_size = vma->vm_end - vma->vm_start; + + /* Alloc the virtual address from specific sram_pool */ + pdata->vaddr = gen_pool_alloc(pdata->pool, sram_size); +} + +static void mmap_close(struct vm_area_struct *vma) +{ + struct mmap_private_data *pdata = vma->vm_private_data; + size_t sram_size = vma->vm_end - vma->vm_start; + + gen_pool_free(pdata->pool, pdata->vaddr, sram_size); + kfree(pdata); +} + +const struct vm_operations_struct sram_mmap_vm_ops = { + .open = mmap_open, + .close = mmap_close, +}; + +static int sram_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct adi_sram_mmap *sram = container_of(fp->private_data, + struct adi_sram_mmap, miscdev); + struct mmap_private_data *pdata; + size_t sram_size = vma->vm_end - vma->vm_start; + unsigned long paddr; + int ret = 0; + + /* Allocate private pdata */ + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->pool = sram->sram_pool; + vma->vm_private_data = pdata; + vma->vm_ops = &sram_mmap_vm_ops; + vma->vm_ops->open(vma); + + if (!pdata->vaddr) { + ret = -EAGAIN; + goto out_free; + } + + paddr = gen_pool_virt_to_phys(pdata->pool, pdata->vaddr); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(paddr), sram_size, + vma->vm_page_prot)) { + ret = -EAGAIN; + goto out_free; + } + + return 0; + +out_free: + kfree(pdata); + return ret; +} + +static const struct file_operations sram_fops = { + .mmap = sram_mmap, +}; + +static int adi_sram_mmap_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_sram_mmap *sram; + int ret = 0; + + sram = devm_kzalloc(dev, sizeof(*sram), GFP_KERNEL); + if (!sram) + return dev_err_probe(dev, -ENOMEM, + "Failed to allocate sram device data"); + + sram->sram_pool = of_gen_pool_get(dev->of_node, "adi,sram", 0); + if (!sram->sram_pool) + return dev_err_probe(dev, -ENODEV, + "Unable to get sram pool"); + + dev_set_drvdata(&pdev->dev, sram); + + sram->miscdev.minor = MISC_DYNAMIC_MINOR; + sram->miscdev.name = SRAM_MMAP_DRV_NAME; + sram->miscdev.fops = &sram_fops; + sram->miscdev.parent = dev; + + ret = misc_register(&sram->miscdev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to register sram mmap misc device"); + + return 0; +} + +static void adi_sram_mmap_remove(struct platform_device *pdev) +{ + struct adi_sram_mmap *sram = dev_get_drvdata(&pdev->dev); + + misc_deregister(&sram->miscdev); +} + +static const struct of_device_id adi_sram_mmap_of_match[] = { + { .compatible = "adi,sram-mmap" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adi_sram_mmap_of_match); + +static struct platform_driver adi_sram_mmap_driver = { + .probe = adi_sram_mmap_probe, + .remove = adi_sram_mmap_remove, + .driver = { + .name = SRAM_MMAP_DRV_NAME, + .of_match_table = of_match_ptr(adi_sram_mmap_of_match), + }, +}; + +module_platform_driver(adi_sram_mmap_driver); +MODULE_DESCRIPTION("SRAM mmap misc driver for ADI processor on-chip memory"); +MODULE_LICENSE("GPL"); From 576cce57d385702adeb4d25c7fc0e4728357420c Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Tue, 9 Apr 2024 11:37:44 +0100 Subject: [PATCH 28/85] rpmsg: Add support for ADSP-SC598 Signed-off-by: Arturs Artamonovs --- drivers/rpmsg/Kconfig | 4 + drivers/rpmsg/Makefile | 1 + drivers/rpmsg/adi_rpmsg.c | 598 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 603 insertions(+) create mode 100644 drivers/rpmsg/adi_rpmsg.c diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index d3795860f5c080..f1e73336c7a260 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -81,4 +81,8 @@ config RPMSG_VIRTIO select RPMSG_NS select VIRTIO +config RPMSG_ADI + tristate "SC598 SHARC rpmsg driver" + depends on ARCH_SC59X_64 + endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 1e02b58ff61fe1..8de70565869a37 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o +obj-$(CONFIG_RPMSG_ADI) += adi_rpmsg.o diff --git a/drivers/rpmsg/adi_rpmsg.c b/drivers/rpmsg/adi_rpmsg.c new file mode 100644 index 00000000000000..1c618e809d3b11 --- /dev/null +++ b/drivers/rpmsg/adi_rpmsg.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Device RPMSG driver for SC5XX processors + * + * Copyright 2022 Analog Devices + * + * Author: + * Piotr Wojtaszczyk + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ADI_INIT_TIMEOUT_MS (0) +#define __ADI_DELAY_MS (20) + +#define ADI_RPMSG_FLAG_VRING_IN_DMA (1<<0) +#define ADI_RPMSG_FLAG_HAS_MEMORY_REGION (1<<1) + +#define VRING_ALIGN 0x1000 +#define VRING_DEFAULT_SIZE 0x800 + +enum adi_rpmsg_soc { + SC598, +}; + +enum adi_rpmsg_state { + ADI_RP_RPMSG_SYNCED = 0, + ADI_RP_RPMSG_WAITING = 1, + ADI_RP_RPMSG_TIMED_OUT = 2, +}; + +struct adi_sharc_resource_table { + struct adi_resource_table_hdr adi_table_hdr; + struct resource_table rsc_table; +} __packed; + +struct adi_rpmsg_vring { + struct virtqueue *vq; + u32 da; + u32 align; + void *va; + unsigned int size; + unsigned int num; + unsigned int id; + unsigned int notify_id; + struct adi_rpmsg_channel *rpchan; +}; + +struct adi_rpmsg_channel { + struct platform_device *pdev; + struct device *dev; + enum adi_rpmsg_soc soc; + struct adi_sharc_resource_table *adi_rsc_table; + struct adi_rcu *rcu; + struct adi_tru *tru; + int icc_irq; + int icc_irq_flags; + int core_id; + int flags; + enum adi_rpmsg_state rpmsg_state; + + struct virtio_device vdev; + struct adi_rpmsg_vring vring[2]; + + /* Resource table for remote core*/ + struct fw_rsc_vdev *rsc_vdev; + struct fw_rsc_vdev_vring *rsc_vring[2]; +}; + +static u64 adi_rpmsg_get_features(struct virtio_device *vdev) +{ + /* 1<<0 is VIRTIO_RPMSG_F_NS bit defined in virtio_rpmsg_bus.c */ + return 1 << 0; +} + +static int adi_rpmsg_finalize_features(struct virtio_device *vdev) +{ + vring_transport_features(vdev); + return 0; +} + +static bool adi_rpmsg_notify(struct virtqueue *vq) +{ + struct adi_rpmsg_vring *vring = vq->priv; + struct adi_rpmsg_channel *rpchan = vring->rpchan; + int wait_time, step; + + /* Delay a little the first notify and check if remote core has done its initialization */ + if (rpchan->rpmsg_state == ADI_RP_RPMSG_WAITING) { + msleep(__ADI_DELAY_MS); + if (rpchan->adi_rsc_table->adi_table_hdr.initialized == ADI_RSC_TABLE_INIT_MAGIC) { + rpchan->rpmsg_state = ADI_RP_RPMSG_SYNCED; + } else { + dev_info(rpchan->dev, + "Core%d resource table not initialized, delay first notify\n", + rpchan->core_id); + if (ADI_INIT_TIMEOUT_MS == 0) { + /* Wait forever */ + step = 0; + dev_info(rpchan->dev, + "Wait forever for Core%d resource table init\n", + rpchan->core_id); + } else { + step = __ADI_DELAY_MS; + } + + for (wait_time = 0; wait_time <= ADI_INIT_TIMEOUT_MS; wait_time += step) { + if (rpchan->adi_rsc_table->adi_table_hdr.initialized == + ADI_RSC_TABLE_INIT_MAGIC) { + dev_info(rpchan->dev, + "Core%d resource table initialized\n", + rpchan->core_id); + rpchan->rpmsg_state = ADI_RP_RPMSG_SYNCED; + break; + } + msleep(__ADI_DELAY_MS); + } + if (rpchan->rpmsg_state != ADI_RP_RPMSG_SYNCED) { + rpchan->rpmsg_state = ADI_RP_RPMSG_TIMED_OUT; + dev_info(rpchan->dev, + "Core%d rpmsg init timeout, probably not supported.\n", + rpchan->core_id); + } + } + } + + if (rpchan->rpmsg_state == ADI_RP_RPMSG_SYNCED) + adi_tru_trigger_device(rpchan->tru, rpchan->dev); + + return true; +} + +static struct virtqueue *adi_find_vq(struct virtio_device *vdev, + unsigned int id, + void (*callback)(struct virtqueue *vq), + const char *name, bool ctx) +{ + struct adi_rpmsg_channel *rpchan = container_of(vdev, struct adi_rpmsg_channel, vdev); + struct device *dev = rpchan->dev; + void *addr; + unsigned int num, size, align; + struct virtqueue *vq; + + if (!name) + return NULL; + + addr = rpchan->vring[id].va; + size = rpchan->vring[id].size; + num = rpchan->vring[id].num; + align = rpchan->vring[id].align; + + memset(addr, 0, size); + + vq = vring_new_virtqueue(id, num, align, vdev, false, ctx, + addr, adi_rpmsg_notify, callback, name); + if (!vq) { + dev_err(dev, "vring_new_virtqueue %s failed\n", name); + return ERR_PTR(-ENOMEM); + } + + rpchan->vring[id].id = id; + rpchan->vring[id].rpchan = rpchan; + + rpchan->vring[id].vq = vq; + rpchan->vring[id].vq->priv = &rpchan->vring[id]; + + return vq; +} + +static void adi_rproc_virtio_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + struct adi_rpmsg_vring *vring; + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + vring = vq->priv; + vring->vq = NULL; + vring_del_virtqueue(vq); + } +} + +static int adi_rpmsg_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, + struct virtqueue *vqs[], + struct virtqueue_info vqs_info[], + struct irq_affinity *desc) +{ + int i, ret; + + if (nvqs != 2) + return -EINVAL; + + for (i = 0; i < nvqs; ++i) { + struct virtqueue_info *vqi = &vqs_info[i]; + + if (!vqi->name) { + vqs[i] = NULL; + continue; + } + + vqs[i] = adi_find_vq(vdev, i, vqi->callback, vqi->name, + vqi->ctx); + + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + goto error; + } + } + + return 0; + +error: + adi_rproc_virtio_del_vqs(vdev); + return ret; +} + +static u8 adi_virtio_get_status(struct virtio_device *vdev) +{ + struct adi_rpmsg_channel *rpchan = container_of(vdev, struct adi_rpmsg_channel, vdev); + struct fw_rsc_vdev *rsc_vdev = rpchan->rsc_vdev; + + return rsc_vdev->status; +} + +static void adi_virtio_set_status(struct virtio_device *vdev, u8 status) +{ + struct adi_rpmsg_channel *rpchan = container_of(vdev, struct adi_rpmsg_channel, vdev); + struct fw_rsc_vdev *rsc_vdev = rpchan->rsc_vdev; + + rsc_vdev->status = status; +} + +static void adi_virtio_reset(struct virtio_device *vdev) +{ + struct adi_rpmsg_channel *rpchan = container_of(vdev, struct adi_rpmsg_channel, vdev); + struct fw_rsc_vdev *rsc_vdev = rpchan->rsc_vdev; + + rsc_vdev->status = 0; +} + +static struct virtio_config_ops adi_rpmsg_config_ops = { + .get_features = adi_rpmsg_get_features, + .finalize_features = adi_rpmsg_finalize_features, + .find_vqs = adi_rpmsg_virtio_find_vqs, + .del_vqs = adi_rproc_virtio_del_vqs, + .reset = adi_virtio_reset, + .set_status = adi_virtio_set_status, + .get_status = adi_virtio_get_status, +}; + +static int adi_rpmsg_parse_resource_table(struct adi_rpmsg_channel *rpchan) +{ + struct adi_sharc_resource_table *adi_rsc_table = rpchan->adi_rsc_table; + struct device *dev = rpchan->dev; + struct fw_rsc_hdr *hdr; + int i, offset; + + if (strcmp(adi_rsc_table->adi_table_hdr.tag, ADI_RESOURCE_TABLE_TAG)) { + dev_err(dev, "Corrupted resource table\n"); + return -ENODEV; + } + if (adi_rsc_table->adi_table_hdr.version != ADI_RESOURCE_TABLE_VERSION) { + dev_err(dev, "Invalid resource table version\n"); + return -ENODEV; + } + + /* Look for a VDEV entry */ + for (i = 0; i < adi_rsc_table->rsc_table.num; i++) { + offset = adi_rsc_table->rsc_table.offset[i]; + hdr = (void *)&adi_rsc_table->rsc_table + offset; + if (hdr->type == RSC_VDEV) { + rpchan->rsc_vdev = (struct fw_rsc_vdev *)hdr->data; + if (rpchan->rsc_vdev->id != VIRTIO_ID_RPMSG) + continue; + rpchan->rsc_vring[0] = &rpchan->rsc_vdev->vring[0]; + rpchan->rsc_vring[1] = &rpchan->rsc_vdev->vring[1]; + return 0; + } + } + + dev_err(dev, "Resource table doesn't have RSC_VDEV entry with VIRTIO_ID_RPMSG id.\n"); + return -ENODEV; +} + +static void adi_rpmsg_vproc_release(struct device *dev) +{ +} + +static irqreturn_t adi_rpmsg_virtio_irq_threaded_handler(int irq, void *p) +{ + struct adi_rpmsg_channel *rpchan = (struct adi_rpmsg_channel *)p; + + vring_interrupt(0, rpchan->vring[0].vq); + vring_interrupt(0, rpchan->vring[1].vq); + + return IRQ_HANDLED; +} + +static int adi_rpmsg_probe(struct platform_device *pdev) +{ + struct adi_rpmsg_channel *rpchan; + struct device *dev = &pdev->dev; + struct adi_rcu *adi_rcu; + struct adi_tru *adi_tru; + struct device_node *dev_node = dev_of_node(&pdev->dev); + struct device_node *node; + struct resource *res; + struct reserved_mem *rmem; + dma_addr_t dma; + void *va; + int ret, size, num; + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + rpchan = devm_kzalloc(dev, sizeof(struct adi_rpmsg_channel), GFP_KERNEL); + if (!rpchan) + return -ENOMEM; + + adi_tru = get_adi_tru_from_node(dev); + if (IS_ERR(adi_tru)) + return PTR_ERR(adi_tru); + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + adi_rcu = get_adi_rcu_from_node(dev); + if (IS_ERR(adi_rcu)) { + ret = PTR_ERR(adi_rcu); + goto free_adi_tru; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + platform_set_drvdata(pdev, rpchan); + rpchan->pdev = pdev; + rpchan->dev = dev; + rpchan->soc = (enum adi_rpmsg_soc)of_device_get_match_data(dev); + rpchan->tru = adi_tru; + rpchan->rcu = adi_rcu; + rpchan->rpmsg_state = ADI_RP_RPMSG_WAITING; + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + ret = of_property_read_u32(dev_node, "core-id", &rpchan->core_id); + if (ret) { + dev_err(dev, "Unable to get core-id property\n"); + goto free_adi_rcu; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + rpchan->icc_irq = platform_get_irq(pdev, 0); + if (rpchan->icc_irq <= 0) { + dev_err(dev, "No ICC IRQ specified\n"); + ret = -ENOENT; + goto free_adi_rcu; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + //res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + rpchan->icc_irq_flags = IRQF_PERCPU | IRQF_SHARED | IRQF_ONESHOT; + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + ret = devm_request_threaded_irq(rpchan->dev, rpchan->icc_irq, NULL, + adi_rpmsg_virtio_irq_threaded_handler, rpchan->icc_irq_flags, + "ICC virtio IRQ", rpchan); + if (ret) { + dev_err(rpchan->dev, "Fail to request ICC IRQ\n"); + ret = -ENOENT; + goto free_adi_rcu; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + if (of_property_read_bool(dev_node, "adi,check-idle")) { + ret = adi_rcu_is_core_idle(rpchan->rcu, rpchan->core_id); + if (ret < 0) { + dev_err(dev, "Invalid core-id\n"); + goto free_adi_rcu; + } else if (ret > 0) { + dev_err(dev, "Error: Core%d idle\n", rpchan->core_id); + ret = -ENODEV; + goto free_adi_rcu; + } + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + /* Get ADI resource table address */ + node = of_parse_phandle(dev_node, "adi,rsc-table", 0); + if (!node) { + dev_err(&pdev->dev, "Can't find adi,rsc-table\n"); + ret = -EINVAL; + goto free_adi_rcu; + } + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(&pdev->dev, "Translating adi,rsc-table failed\n"); + ret = -ENOMEM; + goto free_adi_rcu; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + rpchan->adi_rsc_table = devm_ioremap_wc(dev, rmem->base, rmem->size); + if (IS_ERR(rpchan->adi_rsc_table)) { + dev_err(dev, "Can't map adi,rsc-table\n"); + ret = PTR_ERR(rpchan->adi_rsc_table); + goto free_adi_rcu; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + ret = adi_rpmsg_parse_resource_table(rpchan); + if (ret) + goto free_adi_rcu; + + /* Check reserved memory for vrings */ + node = of_parse_phandle(dev_node, "vdev-vring", 0); + if (node) { + /* Vrings at specific reserved address */ + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(dev, "Failed to acquire vdev-vring\n"); + ret = -EINVAL; + goto free_adi_rcu; + } + + if (rmem->size < 0x4000) { + dev_err(dev, "Insufficient space in vdev-vring, min space req is 16kB\n"); + ret = -EINVAL; + goto free_adi_rcu; + } + + /* Split the range for two vrings, vring0 -rx and vring1 - tx*/ + size = rmem->size / 2; + + /* + * Calc how many buffers we can fit in the vring region, + * number of buffers must be power of 2 + */ + for (num = 2; num < 0x00400000; num <<= 1) { + if (PAGE_ALIGN(vring_size(num, VRING_ALIGN)) > size) { + num >>= 1; // It's too much, restore previous value and break + break; + } + } + + va = devm_ioremap_wc(dev, rmem->base, rmem->size); + if (!(va)) { + dev_err(dev, "Unable to map vdev-vring\n"); + ret = -ENOMEM; + goto free_adi_rcu; + } + + dev_info(dev, "vrings in vdev-vring reserved-memory.\n"); + + rpchan->vring[0].num = num; + rpchan->vring[1].num = num; + + rpchan->vring[0].align = VRING_ALIGN; + rpchan->vring[1].align = VRING_ALIGN; + + rpchan->vring[0].size = size; + rpchan->vring[1].size = size; + + rpchan->vring[0].da = rmem->base; + rpchan->vring[1].da = rpchan->vring[0].da + rpchan->vring[0].size; + + rpchan->vring[0].va = va; + rpchan->vring[1].va = rpchan->vring[0].va + rpchan->vring[0].size; + + } else { + /* Vrings from DMA pool */ + dev_info(dev, "vrings in generic DMA pool.\n"); + rpchan->flags |= ADI_RPMSG_FLAG_VRING_IN_DMA; + + rpchan->vring[0].num = rpchan->rsc_vring[0]->num; + rpchan->vring[1].num = rpchan->rsc_vring[1]->num; + + rpchan->vring[0].align = rpchan->rsc_vring[0]->align; + rpchan->vring[1].align = rpchan->rsc_vring[1]->align; + + rpchan->vring[0].size = PAGE_ALIGN(vring_size(rpchan->rsc_vring[0]->num, + rpchan->rsc_vring[0]->align)); + rpchan->vring[1].size = PAGE_ALIGN(vring_size(rpchan->rsc_vring[1]->num, + rpchan->rsc_vring[1]->align)); + va = dma_alloc_coherent(dev, rpchan->vring[0].size + rpchan->vring[1].size, + &dma, GFP_KERNEL); + if (!(va)) { + dev_err(dev, "Unable to map vdev-vring\n"); + ret = -ENOMEM; + goto free_adi_rcu; + } + + rpchan->vring[0].da = dma; + rpchan->vring[1].da = rpchan->vring[0].da + rpchan->vring[0].size; + rpchan->vring[0].va = va; + rpchan->vring[1].va = rpchan->vring[0].va + rpchan->vring[0].size; + } + + printk(KERN_ERR"adi rpmsg probe %d \n", __LINE__); + + /* Update resource table */ + rpchan->rsc_vring[0]->da = rpchan->vring[0].da; + rpchan->rsc_vring[0]->align = rpchan->vring[0].align; + rpchan->rsc_vring[0]->num = rpchan->vring[0].num; + rpchan->rsc_vring[0]->notifyid = rpchan->core_id; + + rpchan->rsc_vring[1]->da = rpchan->vring[1].da; + rpchan->rsc_vring[1]->align = rpchan->vring[1].align; + rpchan->rsc_vring[1]->num = rpchan->vring[1].num; + rpchan->rsc_vring[1]->notifyid = rpchan->core_id; + + if (of_reserved_mem_device_init(dev)) { + dev_info(dev, "msg buffers in generic DMA pool.\n"); + } else { + dev_info(dev, "msg buffers in memory-region.\n"); + rpchan->flags |= ADI_RPMSG_FLAG_HAS_MEMORY_REGION; + } + + rpchan->vring[0].notify_id = rpchan->core_id; + rpchan->vring[1].notify_id = rpchan->core_id; + + rpchan->vdev.id.device = VIRTIO_ID_RPMSG; + rpchan->vdev.config = &adi_rpmsg_config_ops; + rpchan->vdev.dev.parent = &rpchan->pdev->dev; + rpchan->vdev.dev.release = adi_rpmsg_vproc_release; + + ret = register_virtio_device(&rpchan->vdev); + if (ret) { + dev_err(dev, "failed to register vdev\n"); + goto free_adi_rcu; + } + + return 0; + +free_adi_rcu: + put_adi_rcu(adi_rcu); +free_adi_tru: + put_adi_tru(adi_tru); + + return ret; +} + +static void adi_rpmsg_remove(struct platform_device *pdev) +{ + struct adi_rpmsg_channel *rpchan = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int size; + + put_adi_rcu(rpchan->rcu); + + if (rpchan->flags & ADI_RPMSG_FLAG_HAS_MEMORY_REGION) + of_reserved_mem_device_release(dev); + + if (rpchan->flags & ADI_RPMSG_FLAG_VRING_IN_DMA) { + size = rpchan->vring[0].size + rpchan->vring[1].size; + dma_free_coherent(dev, size, rpchan->vring[0].va, rpchan->vring[0].da); + } +} + +static const struct of_device_id adi_rpmsg_dt_ids[] = { + { .compatible = "adi,rpmsg-SC598", .data = (void *)SC598, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, adi_rpmsg_dt_ids); + +static struct platform_driver adi_rpmsg_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adi-rpmsg", + .of_match_table = adi_rpmsg_dt_ids, + }, + .probe = adi_rpmsg_probe, + .remove = adi_rpmsg_remove, +}; +module_platform_driver(adi_rpmsg_driver); + +MODULE_DESCRIPTION("Analog Devices rpmsg driver"); +MODULE_AUTHOR("Piotr Wojtaszczyk "); +MODULE_LICENSE("GPL v2"); From 8d6b8809eeecb1f4ef5c8142c58bf4eb85e26fb0 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 12 Sep 2025 13:31:21 +0200 Subject: [PATCH 29/85] sound: soc: adi: Add ALSA support for ADSP-SC598 Adding SoC audio support for the following modes: - ASoC (linux only audio) - SHARC ALSA (Hybrid Audio) - SHARC Co-developed-by: Utsav Agarwal Signed-off-by: Utsav Agarwal Signed-off-by: Philip Molloy --- include/sound/sc5xx-dai.h | 212 ++++ sound/soc/adi/Kconfig | 97 ++ sound/soc/adi/Makefile | 14 +- sound/soc/adi/axi-i2s.c | 4 +- sound/soc/adi/icap/LICENSE | 521 ++++++++++ sound/soc/adi/icap/README.md | 78 ++ sound/soc/adi/icap/include/icap.h | 392 +++++++ sound/soc/adi/icap/include/icap_application.h | 241 +++++ sound/soc/adi/icap/include/icap_compiler.h | 61 ++ sound/soc/adi/icap/include/icap_config.h | 63 ++ sound/soc/adi/icap/include/icap_device.h | 183 ++++ .../icap/include/icap_linux_kernel_rpmsg.h | 60 ++ sound/soc/adi/icap/src/icap.c | 615 +++++++++++ .../src/platform/icap_linux_kernel_rpmsg.c | 272 +++++ .../adi/icap/src/platform/icap_transport.h | 221 ++++ sound/soc/adi/sc5xx-asoc-card.c | 540 ++++++++++ sound/soc/adi/sc5xx-i2s.c | 280 +++++ sound/soc/adi/sc5xx-pcm.c | 265 +++++ sound/soc/adi/sc5xx-sport-sharc.c | 968 ++++++++++++++++++ sound/soc/adi/sc5xx-sport.c | 328 ++++++ sound/soc/adi/sc5xx-sport.h | 233 +++++ sound/soc/adi/sharc-alsa-asoc-card.c | 766 ++++++++++++++ sound/soc/codecs/Kconfig | 8 + sound/soc/codecs/Makefile | 4 + sound/soc/codecs/adau1962-i2c.c | 67 ++ sound/soc/codecs/adau1962.c | 836 +++++++++++++++ sound/soc/codecs/adau1962.h | 37 + 27 files changed, 7363 insertions(+), 3 deletions(-) create mode 100644 include/sound/sc5xx-dai.h create mode 100644 sound/soc/adi/icap/LICENSE create mode 100644 sound/soc/adi/icap/README.md create mode 100644 sound/soc/adi/icap/include/icap.h create mode 100644 sound/soc/adi/icap/include/icap_application.h create mode 100644 sound/soc/adi/icap/include/icap_compiler.h create mode 100644 sound/soc/adi/icap/include/icap_config.h create mode 100644 sound/soc/adi/icap/include/icap_device.h create mode 100644 sound/soc/adi/icap/include/icap_linux_kernel_rpmsg.h create mode 100644 sound/soc/adi/icap/src/icap.c create mode 100644 sound/soc/adi/icap/src/platform/icap_linux_kernel_rpmsg.c create mode 100644 sound/soc/adi/icap/src/platform/icap_transport.h create mode 100644 sound/soc/adi/sc5xx-asoc-card.c create mode 100644 sound/soc/adi/sc5xx-i2s.c create mode 100644 sound/soc/adi/sc5xx-pcm.c create mode 100644 sound/soc/adi/sc5xx-sport-sharc.c create mode 100644 sound/soc/adi/sc5xx-sport.c create mode 100644 sound/soc/adi/sc5xx-sport.h create mode 100644 sound/soc/adi/sharc-alsa-asoc-card.c create mode 100644 sound/soc/codecs/adau1962-i2c.c create mode 100644 sound/soc/codecs/adau1962.c create mode 100644 sound/soc/codecs/adau1962.h diff --git a/include/sound/sc5xx-dai.h b/include/sound/sc5xx-dai.h new file mode 100644 index 00000000000000..da33e5a0e8cd4f --- /dev/null +++ b/include/sound/sc5xx-dai.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Analog Devices SC5XX DAI code + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef __SC5XX_DAI_H_ +#define __SC5XX_DAI_H_ + +#ifdef CONFIG_ARCH_SC59X_64 +#define DAI0_BASE_ADDRESS (0x310C9000) +#define DAI1_BASE_ADDRESS (0x310CA000) +#endif + +#define REG_DAI_EXTD_CLK0 0x000 /* DAI0 Extended Clock Routing Control Register 0 */ +#define REG_DAI_EXTD_CLK1 0x004 /* DAI0 Extended Clock Routing Control Register 1 */ +#define REG_DAI_EXTD_CLK2 0x008 /* DAI0 Extended Clock Routing Control Register 2 */ +#define REG_DAI_EXTD_CLK3 0x00C /* DAI0 Extended Clock Routing Control Register 3 */ +#define REG_DAI_EXTD_CLK4 0x010 /* DAI0 Extended Clock Routing Control Register 4 */ +#define REG_DAI_EXTD_CLK5 0x014 /* DAI0 Extended Clock Routing Control Register 5 */ +#define REG_DAI_EXTD_DAT0 0x018 /* DAI0 Extended Serial Data Routing Control Register 0 */ +#define REG_DAI_EXTD_DAT1 0x01C /* DAI0 Extended Serial Data Routing Control Register 1 */ +#define REG_DAI_EXTD_DAT2 0x020 /* DAI0 Extended Serial Data Routing Control Register 2 */ +#define REG_DAI_EXTD_DAT3 0x024 /* DAI0 Extended Serial Data Routing Control Register 3 */ +#define REG_DAI_EXTD_DAT4 0x028 /* DAI0 Extended Serial Data Routing Control Register 4 */ +#define REG_DAI_EXTD_DAT5 0x02C /* DAI0 Extended Serial Data Routing Control Register 5 */ +#define REG_DAI_EXTD_DAT6 0x030 /* DAI0 Extended Serial Data Routing Control Register 6 */ +#define REG_DAI_EXTD_FS0 0x034 /* DAI0 Extended Frame Sync Routing Control Register 0 */ +#define REG_DAI_EXTD_FS1 0x038 /* DAI0 Extended Frame Sync Routing Control Register 1 */ +#define REG_DAI_EXTD_FS2 0x03C /* DAI0 Extended Frame Sync Routing Control Register 2 */ +#define REG_DAI_EXTD_FS4 0x044 /* DAI0 Extended Frame Sync Routing Control Register 4 */ +#define REG_DAI_EXTD_PIN0 0x048 /* DAI0 Extended Pin Buffer Assignment Register 0 */ +#define REG_DAI_EXTD_PIN1 0x04C /* DAI0 Extended Pin Buffer Assignment Register 1 */ +#define REG_DAI_EXTD_PIN2 0x050 /* DAI0 Extended Pin Buffer Assignment Register 2 */ +#define REG_DAI_EXTD_PIN3 0x054 /* DAI0 Extended Pin Buffer Assignment Register 3 */ +#define REG_DAI_EXTD_PIN4 0x058 /* DAI0 Extended Pin Buffer Assignment Register 4 */ +#define REG_DAI_EXTD_MISC0 0x05C /* DAI0 Extended Miscellaneous Control Register 0 */ +#define REG_DAI_EXTD_MISC1 0x060 /* DAI0 Extended Miscellaneous Control Register 1 */ +#define REG_DAI_EXTD_MISC2 0x064 /* DAI0 Extended Miscellaneous Control Register 2 */ +#define REG_DAI_EXTD_PBEN0 0x068 /* DAI0 Extended Pin Buffer Enable Register 0 */ +#define REG_DAI_EXTD_PBEN1 0x06C /* DAI0 Extended Pin Buffer Enable Register 1 */ +#define REG_DAI_EXTD_PBEN2 0x070 /* DAI0 Extended Pin Buffer Enable Register 2 */ +#define REG_DAI_EXTD_PBEN3 0x074 /* DAI0 Extended Pin Buffer Enable Register 3 */ +#define REG_DAI_CLK0 0x0C0 /* DAI0 Clock Routing Control Register 0 */ +#define REG_DAI_CLK1 0x0C4 /* DAI0 Clock Routing Control Register 1 */ +#define REG_DAI_CLK2 0x0C8 /* DAI0 Clock Routing Control Register 2 */ +#define REG_DAI_CLK3 0x0CC /* DAI0 Clock Routing Control Register 3 */ +#define REG_DAI_CLK4 0x0D0 /* DAI0 Clock Routing Control Register 4 */ +#define REG_DAI_CLK5 0x0D4 /* DAI0 Clock Routing Control Register 5 */ +#define REG_DAI_DAT0 0x100 /* DAI0 Serial Data Routing Control Register 0 */ +#define REG_DAI_DAT1 0x104 /* DAI0 Serial Data Routing Control Register 1 */ +#define REG_DAI_DAT2 0x108 /* DAI0 Serial Data Routing Control Register 2 */ +#define REG_DAI_DAT3 0x10C /* DAI0 Serial Data Routing Control Register 3 */ +#define REG_DAI_DAT4 0x110 /* DAI0 Serial Data Routing Control Register 4 */ +#define REG_DAI_DAT5 0x114 /* DAI0 Serial Data Routing Control Register 5 */ +#define REG_DAI_DAT6 0x118 /* DAI0 Serial Data Routing Control Register 6 */ +#define REG_DAI_FS0 0x140 /* DAI0 Frame Sync Routing Control Register 0 */ +#define REG_DAI_FS1 0x144 /* DAI0 Frame Sync Routing Control Register 1 */ +#define REG_DAI_FS2 0x148 /* DAI0 Frame Sync Routing Control Register 2 */ +#define REG_DAI_FS4 0x150 /* DAI0 Frame Sync Routing Control Register 4 */ +#define REG_DAI_PIN0 0x180 /* DAI0 Pin Buffer Assignment Register 0 */ +#define REG_DAI_PIN1 0x184 /* DAI0 Pin Buffer Assignment Register 1 */ +#define REG_DAI_PIN2 0x188 /* DAI0 Pin Buffer Assignment Register 2 */ +#define REG_DAI_PIN3 0x18C /* DAI0 Pin Buffer Assignment Register 3 */ +#define REG_DAI_PIN4 0x190 /* DAI0 Pin Buffer Assignment Register 4 */ +#define REG_DAI_MISC0 0x1C0 /* DAI0 Miscellaneous Control Register 0 */ +#define REG_DAI_MISC1 0x1C4 /* DAI0 Miscellaneous Control Register 1 */ +#define REG_DAI_MISC2 0x1C8 /* DAI0 Miscellaneous Control Register 1 */ +#define REG_DAI_PBEN0 0x1E0 /* DAI0 Pin Buffer Enable Register 0 */ +#define REG_DAI_PBEN1 0x1E4 /* DAI0 Pin Buffer Enable Register 1 */ +#define REG_DAI_PBEN2 0x1E8 /* DAI0 Pin Buffer Enable Register 2 */ +#define REG_DAI_PBEN3 0x1EC /* DAI0 Pin Buffer Enable Register 3 */ +#define REG_DAI_IMSK_FE 0x200 /* DAI0 Falling-Edge Interrupt Mask Register */ +#define REG_DAI_IMSK_RE 0x204 /* DAI0 Rising-Edge Interrupt Mask Register */ +#define REG_DAI_IMSK_PRI 0x210 /* DAI0 Core Interrupt Priority Assignment Register */ +#define REG_DAI_IRPTL_H 0x220 /* DAI0 High Priority Interrupt Latch Register */ +#define REG_DAI_IRPTL_L 0x224 /* DAI0 Low Priority Interrupt Latch Register */ +#define REG_DAI_IRPTL_HS 0x230 /* DAI0 Shadow High Priority Interrupt Latch Register */ +#define REG_DAI_IRPTL_LS 0x234 /* DAI0 Shadow Low Priority Interrupt Latch Register */ +#define REG_DAI_PIN_STAT 0x2E4 /* DAI0 Pin Status Register */ +#define REG_DAI_GBL_SP_EN 0x2E8 /* DAI0 Global SPORT Enable Register */ +#define REG_DAI_GBL_INT_EN 0x2EC /* DAI0 Global SPORT Interrupt Grouping Register */ +#define REG_DAI_GBL_PCG_EN 0x2F0 /* DAI0 Global PCG Enable Control Register */ + +/* DAI0 */ +#define REG_DAI0_CLK0 0x310C90C0 +#define REG_DAI0_CLK1 0x310C90C4 +#define REG_DAI0_CLK2 0x310C90C8 +#define REG_DAI0_CLK3 0x310C90CC +#define REG_DAI0_CLK4 0x310C90D0 +#define REG_DAI0_CLK5 0x310C90D4 +#define REG_DAI0_DAT0 0x310C9100 +#define REG_DAI0_DAT1 0x310C9104 +#define REG_DAI0_DAT2 0x310C9108 +#define REG_DAI0_DAT3 0x310C910C +#define REG_DAI0_DAT4 0x310C9110 +#define REG_DAI0_DAT5 0x310C9114 +#define REG_DAI0_DAT6 0x310C9118 +#define REG_DAI0_FS0 0x310C9140 +#define REG_DAI0_FS1 0x310C9144 +#define REG_DAI0_FS2 0x310C9148 +#define REG_DAI0_FS4 0x310C9150 +#define REG_DAI0_PIN0 0x310C9180 +#define REG_DAI0_PIN1 0x310C9184 +#define REG_DAI0_PIN2 0x310C9188 +#define REG_DAI0_PIN3 0x310C918C +#define REG_DAI0_PIN4 0x310C9190 +#define REG_DAI0_MISC0 0x310C91C0 +#define REG_DAI0_MISC1 0x310C91C4 +#define REG_DAI0_PBEN0 0x310C91E0 +#define REG_DAI0_PBEN1 0x310C91E4 +#define REG_DAI0_PBEN2 0x310C91E8 +#define REG_DAI0_PBEN3 0x310C91EC +#define REG_DAI0_IMSK_FE 0x310C9200 +#define REG_DAI0_IMSK_RE 0x310C9204 +#define REG_DAI0_IMSK_PRI 0x310C9210 +#define REG_DAI0_IRPTL_H 0x310C9220 +#define REG_DAI0_IRPTL_L 0x310C9224 +#define REG_DAI0_IRPTL_HS 0x310C9230 +#define REG_DAI0_IRPTL_LS 0x310C9234 +#define REG_DAI0_PIN_STAT 0x310C92E4 + +/* DAI1 */ +#ifndef CONFIG_ARCH_SC59X + #define REG_DAI1_CLK0 0x310CB0C0 + #define REG_DAI1_CLK1 0x310CB0C4 + #define REG_DAI1_CLK2 0x310CB0C8 + #define REG_DAI1_CLK3 0x310CB0CC + #define REG_DAI1_CLK4 0x310CB0D0 + #define REG_DAI1_CLK5 0x310CB0D4 + #define REG_DAI1_DAT0 0x310CB100 + #define REG_DAI1_DAT1 0x310CB104 + #define REG_DAI1_DAT2 0x310CB108 + #define REG_DAI1_DAT3 0x310CB10C + #define REG_DAI1_DAT4 0x310CB110 + #define REG_DAI1_DAT5 0x310CB114 + #define REG_DAI1_DAT6 0x310CB118 + #define REG_DAI1_FS0 0x310CB140 + #define REG_DAI1_FS1 0x310CB144 + #define REG_DAI1_FS2 0x310CB148 + #define REG_DAI1_FS4 0x310CB150 + #define REG_DAI1_PIN0 0x310CB180 + #define REG_DAI1_PIN1 0x310CB184 + #define REG_DAI1_PIN2 0x310CB188 + #define REG_DAI1_PIN3 0x310CB18C + #define REG_DAI1_PIN4 0x310CB190 + #define REG_DAI1_MISC0 0x310CB1C0 + #define REG_DAI1_MISC1 0x310CB1C4 + #define REG_DAI1_PBEN0 0x310CB1E0 + #define REG_DAI1_PBEN1 0x310CB1E4 + #define REG_DAI1_PBEN2 0x310CB1E8 + #define REG_DAI1_PBEN3 0x310CB1EC + #define REG_DAI1_IMSK_FE 0x310CB200 + #define REG_DAI1_IMSK_RE 0x310CB204 + #define REG_DAI1_IMSK_PRI 0x310CB210 + #define REG_DAI1_IRPTL_H 0x310CB220 + #define REG_DAI1_IRPTL_L 0x310CB224 + #define REG_DAI1_IRPTL_HS 0x310CB230 + #define REG_DAI1_IRPTL_LS 0x310CB234 + #define REG_DAI1_PIN_STAT 0x310CB2E4 +#endif + +#ifdef CONFIG_ARCH_SC59X + #define REG_DAI1_CLK0 0x310CA0C0 + #define REG_DAI1_CLK1 0x310CA0C4 + #define REG_DAI1_CLK2 0x310CA0C8 + #define REG_DAI1_CLK3 0x310CA0CC + #define REG_DAI1_CLK4 0x310CA0D0 + #define REG_DAI1_CLK5 0x310CA0D4 + #define REG_DAI1_DAT0 0x310CA100 + #define REG_DAI1_DAT1 0x310CA104 + #define REG_DAI1_DAT2 0x310CA108 + #define REG_DAI1_DAT3 0x310CA10C + #define REG_DAI1_DAT4 0x310CA110 + #define REG_DAI1_DAT5 0x310CA114 + #define REG_DAI1_DAT6 0x310CA118 + #define REG_DAI1_FS0 0x310CA140 + #define REG_DAI1_FS1 0x310CA144 + #define REG_DAI1_FS2 0x310CA148 + #define REG_DAI1_FS4 0x310CA150 + #define REG_DAI1_PIN0 0x310CA180 + #define REG_DAI1_PIN1 0x310CA184 + #define REG_DAI1_PIN2 0x310CA188 + #define REG_DAI1_PIN3 0x310CA18C + #define REG_DAI1_PIN4 0x310CA190 + #define REG_DAI1_MISC0 0x310CA1C0 + #define REG_DAI1_MISC1 0x310CA1C4 + #define REG_DAI1_PBEN0 0x310CA1E0 + #define REG_DAI1_PBEN1 0x310CA1E4 + #define REG_DAI1_PBEN2 0x310CA1E8 + #define REG_DAI1_PBEN3 0x310CA1EC + #define REG_DAI1_IMSK_FE 0x310CA200 + #define REG_DAI1_IMSK_RE 0x310CA204 + #define REG_DAI1_IMSK_PRI 0x310CA210 + #define REG_DAI1_IRPTL_H 0x310CA220 + #define REG_DAI1_IRPTL_L 0x310CA224 + #define REG_DAI1_IRPTL_HS 0x310CA230 + #define REG_DAI1_IRPTL_LS 0x310CA234 + #define REG_DAI1_PIN_STAT 0x310CA2E4 +#endif + +/* Pads init function for dai */ +void pads_init(void); + +#endif /*__SC5XX_DAI_H_ */ diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig index d47dffbf40d06a..c7767a56fb3279 100644 --- a/sound/soc/adi/Kconfig +++ b/sound/soc/adi/Kconfig @@ -15,4 +15,101 @@ config SND_SOC_ADI_AXI_SPDIF help ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral. +config SND_SC5XX_PCM + tristate "SoC Audio for the ADI SC5XX chip" + depends on ARCH_SC57X || ARCH_SC58X || ARCH_SC59X || ARCH_SC59X_64 + help + Say Y or M if you want to add support for codecs attached to + the SC5XX SPORT (synchronous serial ports) interface in I2S + or TDM mode. + You will also need to select the audio interfaces to support below. + +config SND_SOC_ADI_SC5XX_I2S + tristate + depends on ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X + help + Describes the Digital Audio Interface format, which is physcally + built on the SPORT + Most audio devices on SC5xx requires this + Say Y or M if you want to enable this + +choice + prompt "SPORT data feed" + depends on SND_SC5XX_PCM + default SND_SC5XX_SPORT + + config SND_SC5XX_SPORT + bool "Regular SPORT operation" + help + SPORT interface driver for SC5XX audio, audio data are sent and + received through the SPORT DMA buffers + + config SND_SC5XX_SPORT_SHARC + bool "SHARC process SPORT data" + help + Say Y if you want to process audio data in SHARC core, + requires firmware with ICAP (Inter Core Audio Protocol) + running on a SHARC core. + SPORT DMA is configured and controlled by linux, + DMA interrupt handed over to SHARC firmware. +endchoice + +config SND_SC5XX_MACHINE + tristate + depends on ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X + help + Describes the ASoC Machine driver for ADI SC5XX based boards + Audio devices on SC5xx requires this + Say Y or M if you want to enable this + +config SND_SC5XX_ADAU1761 + tristate "Support for the ADAU1761 Machine driver on SHARC Audio board" + depends on SND_SC5XX_PCM && I2C + select SND_SC5XX_MACHINE + select SND_SOC_ADI_SC5XX_I2S + select SND_SOC_ADAU1761_I2C + help + Say Y if you want to add support for the Analog Devices ADAU1761 + machine driver on the SHARC Audio board. + +config SND_SC5XX_ADAU1979 + tristate "Support for the ADAU1979 Machine driver on SC5XX ezkit board" + depends on SND_SC5XX_PCM && I2C && (!SND_SC5XX_ADAU1761) + select SND_SC5XX_MACHINE + select SND_SOC_ADI_SC5XX_I2S + select SND_SOC_ADAU1977_I2C + help + Say Y if you want to add support for the Analog Devices ADAU1979 + machine driver on the SC5XX ezkit board. + +config SND_SC5XX_ADAU1962 + tristate "Support for the ADAU1962 Machine driver on SC5XX ezkit board" + depends on SND_SC5XX_PCM && I2C && (!SND_SC5XX_ADAU1761) + select SND_SC5XX_MACHINE + select SND_SOC_ADI_SC5XX_I2S + select SND_SOC_ADAU1962_I2C + help + Say Y if you want to add support for the Analog Devices ADAU1962 + machine driver on the SC5XX ezkit board. + +config SND_SC5XX_ADAU1372 + tristate "Support for the ADAU1372 Machine driver on SC5XX ezkit board" + depends on SND_SC5XX_PCM && I2C && (!SND_SC5XX_ADAU1761) + select SND_SC5XX_MACHINE + select SND_SOC_ADI_SC5XX_I2S + select SND_SOC_ADAU1372_I2C + help + Say Y if you want to add support for the Analog Devices ADAU1962 + machine driver on the SC5XX ezkit board. + +config SND_SC5XX_SHARC_ALSA_CARD + tristate "SHARC-ALSA SoC Audio card for the ADI SC5XX chip" + depends on ADI_REMOTEPROC && RPMSG && (ARCH_SC57X || ARCH_SC58X || ARCH_SC59X || ARCH_SC59X_64) + help + Virtual SHARC ASoC card driver. Creates a sound card for each "sharc-audio" + rpmsg endpoint announced on a rpmsg channel. The driver is just + a communication bridge between ASoC and SHARC firmware which controls + I2S/SPORT, DMA, codecs and amplifiers. Supported number of channels, + sample rates, and formats depends on features reported by SHARC firmware. + endmenu diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile index 0d2db8d05806e8..c11cd654c282b1 100644 --- a/sound/soc/adi/Makefile +++ b/sound/soc/adi/Makefile @@ -1,6 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-adi-axi-i2s-y := axi-i2s.o -snd-soc-adi-axi-spdif-y := axi-spdif.o +snd-soc-adi-axi-i2s-objs := axi-i2s.o +snd-soc-adi-axi-spdif-objs := axi-spdif.o +snd-soc-sc5xx-pcm-objs := sc5xx-pcm.o +snd-soc-sc5xx-sport-$(CONFIG_SND_SC5XX_SPORT) := sc5xx-sport.o +snd-soc-adi-sc5xx-i2s-objs := sc5xx-i2s.o +snd-soc-sc5xx-asoc-card-objs := sc5xx-asoc-card.o +snd-soc-sc5xx-sport-$(CONFIG_SND_SC5XX_SPORT_SHARC) := sc5xx-sport-sharc.o icap/src/icap.o icap/src/platform/icap_linux_kernel_rpmsg.o +snd-soc-sharc-alsa-asoc-card-objs := sharc-alsa-asoc-card.o icap/src/icap.o icap/src/platform/icap_linux_kernel_rpmsg.o obj-$(CONFIG_SND_SOC_ADI_AXI_I2S) += snd-soc-adi-axi-i2s.o obj-$(CONFIG_SND_SOC_ADI_AXI_SPDIF) += snd-soc-adi-axi-spdif.o +obj-$(CONFIG_SND_SC5XX_PCM) += snd-soc-sc5xx-pcm.o snd-soc-sc5xx-sport.o +obj-$(CONFIG_SND_SOC_ADI_SC5XX_I2S) += snd-soc-adi-sc5xx-i2s.o +obj-$(CONFIG_SND_SC5XX_MACHINE) += snd-soc-sc5xx-asoc-card.o +obj-$(CONFIG_SND_SC5XX_SHARC_ALSA_CARD) += snd-soc-sharc-alsa-asoc-card.o \ No newline at end of file diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index 41f89384f8fd74..6fdec9cc4d555b 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -274,11 +274,13 @@ static int axi_i2s_probe(struct platform_device *pdev) return ret; } -static void axi_i2s_dev_remove(struct platform_device *pdev) +static int axi_i2s_dev_remove(struct platform_device *pdev) { struct axi_i2s *i2s = platform_get_drvdata(pdev); clk_disable_unprepare(i2s->clk); + + return 0; } static const struct of_device_id axi_i2s_of_match[] = { diff --git a/sound/soc/adi/icap/LICENSE b/sound/soc/adi/icap/LICENSE new file mode 100644 index 00000000000000..44da3ebb70214c --- /dev/null +++ b/sound/soc/adi/icap/LICENSE @@ -0,0 +1,521 @@ +Copyright 2021-2022 Analog Devices Inc. + +This software is available to you under General Public License (GPL) Version 2 +when distributed as part of the Linux kernel otherwise the software is available +to you under Apache 2.0 license. + + +################################################################################ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +################################################################################ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/sound/soc/adi/icap/README.md b/sound/soc/adi/icap/README.md new file mode 100644 index 00000000000000..fa06e2f2d9515e --- /dev/null +++ b/sound/soc/adi/icap/README.md @@ -0,0 +1,78 @@ +# Inter Core Audio Protocol + +Inter Core Audio Protocol (ICAP) to exchange playback and record audio data +between audio device and audio application on different cores. Currently it is +using rpmsg as a transport layer but can be expanded to other transport layers. + +Current ICAP implementations include bare metal application using rpmsg-lite +library and Linux kernel implementation. Future implementations will cover +Linux user space library for user space applications. + +ICAP has GPLv2 license when distributed with Linux kernel, otherwise it has +Apache 2.0 license. For details see the LICENSE file. + +## Overview +Software running on different cores communicate with each using ICAP in +application-device relation. One end of the ICAP communication must be +application, other end must be device - application creates audio data stream +for device to playback and reads audio data recorded by device. + +Application side includes icap_application.h with application specific functions +and callbacks. Device side includes icap_device.h with device specific functions +and callbacks. Each ICAP function call sends appropriate message to the other +side which triggers corresponding callback (#icap_device_callbacks or +#icap_application_callbacks) for the message. The other side sends back a +positive response message (#ICAP_ACK) with or without payload. In case of +failure the other side can send back a negative response (#ICAP_NAK) with error +code. ICAP application functions are synchronous, they wait for response until +#ICAP_MSG_TIMEOUT_US. Application functions work like Remote Function Calls +(RFC). ICAP device functions are asynchronous, they don't wait for corresponding +response message therefore it is possible to call them in interrupt context +which may be required to implement proper playback and record audio streams. +When an ICAP device receives a response the proper callback is executed. + +It is possible to cascade ICAP communication by calling application functions +inside device callbacks creating a proxy between first ICAP application side and +final ICAP device side. E.G on SC584 SOC it's possible to establish ICAP +communication between:
+`ICAP application on ARM <-> ICAP proxy on SHARC0 <-> ICAP device on SHARC1` + +## Simplified usage +### ICAP application +1. Include icap_application.h and allocate statically or dynamically +`struct icap_instance` and `struct icap_application_callbacks`. +2. Initialize `icap_application_callbacks` with proper callback functions. +3. Set appropriate field of the `icap_instance.transport`: + * for bare metal + rpmsg-lite set the `icap_transport.rpmsg_instance` and + `icap_transport.rpmsg_ept` fields. + * for linux kernel set the `icap_transport.rpdev` field. + * for linux user space set the `icap_transport.fd` field. +4. Initialize the ICAP instance with `icap_application_init()`. +5. Get number of subdevices from ICAP device using `icap_get_subdevices()`. +6. Get features of each device using `icap_get_subdevice_features()`. +7. For a playback subdevice (`#ICAP_DEV_PLAYBACK`) allocate source buffer and +attach the buffer to the `subdevice using icap_add_src()`. +8. For a record subdevice `(#ICAP_DEV_RECORD)` allocate destination buffer and +attach the buffer to the subdevice `using icap_add_dst()`. +9. Fill the playback buffer with audio data. +10. Start subdevices with `icap_start()`. +11. Monitor buffer levels with `icap_application_callbacks.frag_ready()`, +fill more playback audio data if necessary and read recorded audio data. + +### ICAP device +1. Include icap_device.h and allocate statically or dynamically +`struct icap_instance` and `struct icap_device_callbacks`. +2. Initialize icap_device_callbacks with proper callback functions. +2. Set appropriate field of the `icap_instance.transport`: + * for bare metal + rpmsg-lite set the `icap_transport.rpmsg_instance` and + `icap_transport.rpmsg_ept` fields + * for linux kernel set the `icap_transport.rpdev` field + * for linux user space set the `icap_transport.fd` field +3. Initialize the ICAP instance with `icap_device_init()` +4. Wait until playback and record buffers are attached by `add_src()` and +`add_dst()` callbacks. +5. Wait until a subdevice is started by `start()` callback. +6. Read audio data from playback buffer and write the data to audio hardware. +7. Read audio data from audio hardware and write to record buffer. +8. Notify application side about audio fragments consumed from the buffers +using `icap_frag_ready()`. diff --git a/sound/soc/adi/icap/include/icap.h b/sound/soc/adi/icap/include/icap.h new file mode 100644 index 00000000000000..6227531d86c4f7 --- /dev/null +++ b/sound/soc/adi/icap/include/icap.h @@ -0,0 +1,392 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_H_ +#define _ICAP_H_ + +/** + * @file icap.h + * @author Piotr Wojtaszczyk + * @brief Common ICAP header for both application and device side. + * Don't include this file directly. + * Include icap_application.h or icap_device.h file instead. + * + * @copyright 2021-2022 Analog Devices Inc. + * + */ + +#include "icap_config.h" +#include "icap_compiler.h" + +#if defined(ICAP_LINUX_KERNEL_RPMSG) +#include "icap_linux_kernel_rpmsg.h" +#elif defined(ICAP_BM_RPMSG_LITE) +#include "icap_bm_rpmsg-lite.h" +#elif defined(ICAP_LINUX_RPMSG_CHARDEV) +#include "icap_linux_rpmsg_chardev.h" +#else +#error "Invalid platform" +#endif + +/** + * @defgroup error_codes Return error codes + * @{ + */ +#define ICAP_ERROR_INIT 6 +#define ICAP_ERROR_NOMEM 12 +#define ICAP_ERROR_BUSY 16 +#define ICAP_ERROR_INVALID 22 +#define ICAP_ERROR_BROKEN_CON 32 +#define ICAP_ERROR_MSG_TYPE 42 +#define ICAP_ERROR_PROTOCOL 71 +#define ICAP_ERROR_MSG_ID 74 +#define ICAP_ERROR_REMOTE_ADDR 78 +#define ICAP_ERROR_MSG_LEN 90 +#define ICAP_ERROR_PROTOCOL_NOT_SUP 93 +#define ICAP_ERROR_TIMEOUT 110 +#define ICAP_ERROR_NO_BUFS 233 +#define ICAP_ERROR_NOT_SUP 252 +/**@}*/ + +/** + * @defgroup sample_format Sample format + * @{ + */ +#define ICAP_FORMAT_S8 0 +#define ICAP_FORMAT_U8 1 +#define ICAP_FORMAT_S16_LE 2 +#define ICAP_FORMAT_S16_BE 3 +#define ICAP_FORMAT_U16_LE 4 +#define ICAP_FORMAT_U16_BE 5 +#define ICAP_FORMAT_S24_LE 6 +#define ICAP_FORMAT_S24_BE 7 +#define ICAP_FORMAT_U24_LE 8 +#define ICAP_FORMAT_U24_BE 9 +#define ICAP_FORMAT_S32_LE 10 +#define ICAP_FORMAT_S32_BE 11 +#define ICAP_FORMAT_U32_LE 12 +#define ICAP_FORMAT_U32_BE 13 +#define ICAP_FORMAT_FLOAT_LE 14 +#define ICAP_FORMAT_FLOAT_BE 15 +#define ICAP_FORMAT_FLOAT64_LE 16 +#define ICAP_FORMAT_FLOAT64_BE 17 +/**@}*/ + +/** + * @defgroup sample_format_bit Sample format bit field + * @{ + */ +#define ICAP_FMTBIT_S8 (1 << ICAP_FORMAT_S8) +#define ICAP_FMTBIT_U8 (1 << ICAP_FORMAT_U8) +#define ICAP_FMTBIT_S16_LE (1 << ICAP_FORMAT_S16_LE) +#define ICAP_FMTBIT_S16_BE (1 << ICAP_FORMAT_S16_BE) +#define ICAP_FMTBIT_U16_LE (1 << ICAP_FORMAT_U16_LE) +#define ICAP_FMTBIT_U16_BE (1 << ICAP_FORMAT_U16_BE) +#define ICAP_FMTBIT_S24_LE (1 << ICAP_FORMAT_S24_LE) +#define ICAP_FMTBIT_S24_BE (1 << ICAP_FORMAT_S24_BE) +#define ICAP_FMTBIT_U24_LE (1 << ICAP_FORMAT_U24_LE) +#define ICAP_FMTBIT_U24_BE (1 << ICAP_FORMAT_U24_BE) +#define ICAP_FMTBIT_S32_LE (1 << ICAP_FORMAT_S32_LE) +#define ICAP_FMTBIT_S32_BE (1 << ICAP_FORMAT_S32_BE) +#define ICAP_FMTBIT_U32_LE (1 << ICAP_FORMAT_U32_LE) +#define ICAP_FMTBIT_U32_BE (1 << ICAP_FORMAT_U32_BE) +#define ICAP_FMTBIT_FLOAT_LE (1 << ICAP_FORMAT_FLOAT_LE) +#define ICAP_FMTBIT_FLOAT_BE (1 << ICAP_FORMAT_FLOAT_BE) +#define ICAP_FMTBIT_FLOAT64_LE (1 << ICAP_FORMAT_FLOAT64_LE) +#define ICAP_FMTBIT_FLOAT64_BE (1 << ICAP_FORMAT_FLOAT64_BE) +/**@}*/ + +/** + * @defgroup sample_rate Sample rate bit field + * @{ + */ +/** @brief 5.512kHz sample rate */ +#define ICAP_RATE_5512 (1 << 0) +/** @brief 8kHz sample rate */ +#define ICAP_RATE_8000 (1 << 1) +/** @brief 11.025kHz sample rate */ +#define ICAP_RATE_11025 (1 << 2) +/** @brief 16kHz sample rate */ +#define ICAP_RATE_16000 (1 << 3) +/** @brief 22.05kHz sample rate */ +#define ICAP_RATE_22050 (1 << 4) +/** @brief 32kHz sample rate */ +#define ICAP_RATE_32000 (1 << 5) +/** @brief 44.1kHz sample rate */ +#define ICAP_RATE_44100 (1 << 6) +/** @brief 48kHz sample rate */ +#define ICAP_RATE_48000 (1 << 7) +/** @brief 64kHz sample rate */ +#define ICAP_RATE_64000 (1 << 8) +/** @brief 88.2kHz sample rate */ +#define ICAP_RATE_88200 (1 << 9) +/** @brief 96kHz sample rate */ +#define ICAP_RATE_96000 (1 << 10) +/** @brief 176.4kHz sample rate */ +#define ICAP_RATE_176400 (1 << 11) +/** @brief 192kHz sample rate */ +#define ICAP_RATE_192000 (1 << 12) +/** @brief 352.8kHz sample rate */ +#define ICAP_RATE_352800 (1 << 13) +/** @brief 384kHz sample rate */ +#define ICAP_RATE_384000 (1 << 14) +/** @brief Linear range of frequencies */ +#define ICAP_RATE_ALL_FREQ (1 << 30) + +/** @brief Range 8kHz to 44.1kHz */ +#define ICAP_RATES_8000_44100 ( \ + ICAP_RATE_8000 | ICAP_RATE_11025 | ICAP_RATE_16000 | \ + ICAP_RATE_22050 | ICAP_RATE_32000 | ICAP_RATE_44100) +/** @brief Range 8kHz to 48kHz */ +#define ICAP_RATES_8000_48000 (ICAP_RATES_8000_44100 | ICAP_RATE_48000) +/** @brief Range 8kHz to 96kHz */ +#define ICAP_RATES_8000_96000 ( \ + ICAP_RATES_8000_48000 | ICAP_RATE_64000 | \ + ICAP_RATE_88200 | ICAP_RATE_96000) +/** @brief Range 8kHz to 192kHz */ +#define ICAP_RATES_8000_192000 (ICAP_RATES_8000_96000 | ICAP_RATE_176400 | ICAP_RATE_192000) +/** @brief Range 8kHz to 384kHz */ +#define ICAP_RATES_8000_384000 (ICAP_RATES_8000_192000 | ICAP_RATE_352800 | ICAP_RATE_384000) +/**@}*/ + +/** @brief Max length of ICAP buffer name /ref icap_buf_descriptor.name */ +#define ICAP_BUF_NAME_LEN (64) +#define ICAP_BUF_MAX_FRAGS_OFFSETS_NUM (64) + +/** @brief ICAP subdevice type */ +enum icap_dev_type { + ICAP_DEV_PLAYBACK = 0, /**< Playback subdevice */ + ICAP_DEV_RECORD = 1, /**< Record subdevice */ +}; + +/** @brief Audio buffer type, defines buffer type in the icap_buf_descriptor.type */ +enum icap_buf_type { + /** Audio fragments are in sequence, separated by gaps, if + * icap_buf_descriptor.gap_size = 0 the buffer is continuous + */ + ICAP_BUF_CIRCURAL = 0, + + /* Audio fragments are scattered, needs new #icap_buf_offsets + * after fragments are consumed + */ + ICAP_BUF_SCATTERED = 1, +}; + +/** @brief ICAP instance, initialized by icap_device_init() or + * icap_application_init() except of some members in #icap_transport + */ +struct icap_instance { + /** @brief Platform specific transport internals, some fields of this struct + * must be initialized before icap_device_init() or icap_application_init() + */ + struct icap_transport transport; + + /** @brief Optional ICAP instance name */ + char *name; + + /** @brief ICAP instance type, one of the #icap_instance_type */ + u32 type; + + /** @brief Private pointer for caller use */ + void *priv; + + /** @brief Pointer to callbacks #icap_device_callbacks or icap_application_callbacks */ + void *callbacks; + + /** @brief Internal counter for messages */ + u32 seq_num; +}; + +/** + * @defgroup msg_structs Message structs send and received by application and device sides. + * @{ + */ +/** @brief ICAP buffer descriptor */ +ICAP_PACKED_BEGIN +struct icap_buf_descriptor { + /** @brief Optional buffer name */ + char name[ICAP_BUF_NAME_LEN]; + + /** @brief Subdevice id to which the buffer should be attached */ + s32 subdev_id; + + /** @brief Pointer to shared memory with the audio data */ + u64 buf; + + /** @brief Size of the shared memory */ + u32 buf_size; + + /** @brief Buffer type, one of the #icap_buf_type */ + u32 type; + + /** @brief Gaps between audio fragments in the shared memory */ + u32 gap_size; + + /** @brief Audio fragments size in the shared memory */ + u32 frag_size; + + /** @brief Number of channels */ + u32 channels; + + /** @brief Sample format, one of the @ref sample_format */ + u32 format; + + /** @brief Sample rate, integer frequency value which corresponds to @ref sample_rate */ + u32 rate; + + /* @brief Set this flag if ICAP device must report that it + * consumed audio fragment from this buffer + */ + u32 report_frags; +} ICAP_PACKED_END; + +/** @brief Struct send by icap_frag_ready() device function */ +ICAP_PACKED_BEGIN +struct icap_buf_frags { + /** @brief Indicates buffer which the fragments were consumed */ + u32 buf_id; + + /** @brief Indicates how many audio fragments were consumed */ + u32 frags; +} ICAP_PACKED_END; + +/* @brief Struct send by icap_frags() application function, + * used with #ICAP_BUF_SCATTERED buffer type + */ +ICAP_PACKED_BEGIN +struct icap_buf_offsets { + /** @brief Indicates buffer which the offset table refers to */ + u32 buf_id; + + /** @brief Number of valid offsets in the table */ + u32 num; + + /** @brief Offset table with the new audio fragments */ + u32 frags_offsets[ICAP_BUF_MAX_FRAGS_OFFSETS_NUM]; +} ICAP_PACKED_END; + +/** @brief Subdevice requested by icap_get_subdevice_features() */ +ICAP_PACKED_BEGIN +struct icap_subdevice_features { + /** @brief One of the #icap_dev_type */ + u32 type; + + /** @brief Max number of supported source buffers */ + u32 src_buf_max; + + /** @brief Max number of supported destination buffers */ + u32 dst_buf_max; + + /** @brief Min number of supported channels */ + u32 channels_min; + + /** @brief Max number of supported channels */ + u32 channels_max; + + /** @brief Supported sample formats, bitfield @ref sample_format_bit */ + u32 formats; + + /** @brief Supported sample rates, bitfield @ref sample_rate */ + u32 rates; +} ICAP_PACKED_END; + +/** @brief Subdevice params to be initialized with, send by icap_subdevice_init() */ +struct icap_subdevice_params { + /** @brief Subdevice id to be initialized */ + u32 subdev_id; + + /** @brief Number of channels requested */ + u32 channels; + + /** @brief Sample format requested, one of the @ref sample_format */ + u32 format; + + /** @brief Integer value of the sample rate frequency */ + u32 rate; +} ICAP_PACKED_END; + +/**@}*/ + +/** @brief Used to verify remote address, only rpmsg supported currently */ +union icap_remote_addr { + u32 rpmsg_addr; + void *tcpip_addr; +}; + +/** + * @defgroup msg_handlers Handling of the ICAP messages. + * @{ + */ + +/** + * @brief Parse received ICAP message. + * + * @param icap Pointer to icap instance + * @param src_addr Source address of the message + * @param data Pointer to a message buffer + * @param size Length of the message + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_parse_msg(struct icap_instance *icap, union icap_remote_addr *src_addr, + void *data, u32 size); + +/** + * @brief Save a message in a queue to be parsed later by icap_loop() + * + * @param icap Pointer to icap instance + * @param src_addr Source address of the message + * @param data Pointer to a message buffer + * @param size Length of the message + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_put_msg(struct icap_instance *icap, union icap_remote_addr *src_addr, + void *data, u32 size); + +/** + * @brief Parses messages saved by icap_put_msg() + * + * @param icap Pointer to icap instance + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_loop(struct icap_instance *icap); + +/**@}*/ + +#endif /* _ICAP_H_ */ diff --git a/sound/soc/adi/icap/include/icap_application.h b/sound/soc/adi/icap/include/icap_application.h new file mode 100644 index 00000000000000..7759b68a1f23bb --- /dev/null +++ b/sound/soc/adi/icap/include/icap_application.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_APPLICATION_H_ +#define _ICAP_APPLICATION_H_ + +/** + * @file icap_application.h + * @author Piotr Wojtaszczyk + * @brief ICAP definitions for application side. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + * + */ + +#include "icap.h" + +/** + * @addtogroup app_functions + * @{ + */ + +/** + * @brief Callbacks used on application side, executed when appropriate device message + * received by application side, please see @ref dev_functions. + * + * These callbacks are optional. If a callback isn't implemented ICAP by default + * responses with #ICAP_ACK to the message received. + * Implementation of a callback should return 0 on success, + * a negative error code on failure or if a received parameter is invalid. + * + */ +struct icap_application_callbacks { + int32_t (*frag_ready)(struct icap_instance *icap, struct icap_buf_frags *frags); + int32_t (*xrun)(struct icap_instance *icap, struct icap_buf_frags *frags); + int32_t (*error)(struct icap_instance *icap, int32_t error_code); +}; + +/**@}*/ + +/** + * @defgroup init_functions ICAP initialization functions + * @{ + */ + +/** + * @brief Initializes application side ICAP instance, + * requires some fields in icap_instance.transport initialized depending on platform. + * + * @param icap Pointer to new instance struct, the + * struct can be empty except some fields in icap_instance.transport. + * @param name Optional ICAP instance name. + * @param cb Pointer to #icap_application_callbacks. + * @param priv Private pointer for caller use. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_application_init(struct icap_instance *icap, + char *name, struct icap_application_callbacks *cb, void *priv); + +/** + * @brief Deinitialize ICAP instance and frees allocated resources. + * + * @param icap Pointer to ICAP instance. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_application_deinit(struct icap_instance *icap); + +/**@}*/ + +/** + * @defgroup app_functions Application side functions + * + * Each function sends appropriate ICAP message to ICAP device which triggers + * appropriate device callback #icap_application_callbacks (if implemented) + * and waits for a default response or a response generated by appropriate + * callback - Remote Function Call (RFC). + * @{ + */ + +/** + * @brief Get number of supported subdevices. + * + * @param icap Pointer to ICAP instance. + * @return int32_t Returns positive number of subdevices or negative @ref error_codes on failure. + */ +int32_t icap_get_subdevices(struct icap_instance *icap); + +/** + * @brief Get supported features of a subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice id asked for supported features. + * @param [out] features Pointer for features received from subdevice. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_get_subdevice_features(struct icap_instance *icap, uint32_t subdev_id, + struct icap_subdevice_features *features); + +/** + * @brief Initialize subdevice with requested parameters. + * + * @param icap Pointer to ICAP instance. + * @param params for the subdevice, must match supported features. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_subdevice_init(struct icap_instance *icap, struct icap_subdevice_params *params); + +/** + * @brief Stops and deinitialize subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice id. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_subdevice_deinit(struct icap_instance *icap, uint32_t subdev_id); + +/** + * @brief Send information about source buffer to the device side. + * + * @param icap Pointer to ICAP instance. + * @param buf Pointer to a buffer descriptor #icap_buf_descriptor. + * @return int32_t Returns buffer_id assigned by device side, negative error code on failure. + */ +int32_t icap_add_src(struct icap_instance *icap, struct icap_buf_descriptor *buf); + +/** + * @brief Send information about destination buffer to the device side. + * + * @param icap Pointer to ICAP instance. + * @param buf Pointer to a buffer descriptor #icap_buf_descriptor. + * @return int32_t Returns buffer_id assigned by device side, negative error code on failure. + */ +int32_t icap_add_dst(struct icap_instance *icap, struct icap_buf_descriptor *buf); + +/** + * @brief Notifies device side that a source buffer is about to be released + * and will be no longer available. + * + * @param icap Pointer to ICAP instance. + * @param buf_id Buffer to be released. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_remove_src(struct icap_instance *icap, uint32_t buf_id); + +/** + * @brief Notifies device side that a destination buffer is about to be released + * and will be no longer available. + * + * @param icap Pointer to ICAP instance. + * @param buf_id Buffer to be released. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_remove_dst(struct icap_instance *icap, uint32_t buf_id); + +/** + * @brief Start audio on a subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice to be started. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_start(struct icap_instance *icap, uint32_t subdev_id); + +/** + * @brief Stop audio on a subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice to be stopped. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_stop(struct icap_instance *icap, uint32_t subdev_id); + +/** + * @brief Pause audio on a subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice to be paused. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_pause(struct icap_instance *icap, uint32_t subdev_id); + +/** + * @brief Resume paused audio on a subdevice. + * + * @param icap Pointer to ICAP instance. + * @param subdev_id Subdevice to be resumed. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_resume(struct icap_instance *icap, uint32_t subdev_id); + +/** + * @brief Send array of new audio fragments offsets. This is needed if buffer is + * #ICAP_BUF_SCATTERED, application needs continuously send information where + * the next audio fragments are. + * + * @param icap Pointer to ICAP instance. + * @param offsets Struct with the array of offsets. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_frags(struct icap_instance *icap, struct icap_buf_offsets *offsets); + +/**@}*/ + +#endif /* _ICAP_APPLICATION_H_ */ diff --git a/sound/soc/adi/icap/include/icap_compiler.h b/sound/soc/adi/icap/include/icap_compiler.h new file mode 100644 index 00000000000000..d7c675e4444dd4 --- /dev/null +++ b/sound/soc/adi/icap/include/icap_compiler.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_COMPILER_H_ +#define _ICAP_COMPILER_H_ + +/** + * @file icap_compiler.h + * @brief Compiler specific definitions + * + */ + +#if defined(__CCESVERSION__) +/* Cross Code Embedded Studio project */ +#define ICAP_PACKED_BEGIN _Pragma("pack(1)") +#define ICAP_PACKED_END _Pragma("pack()") + +#else +/* GCC */ +#define ICAP_PACKED_BEGIN +#define ICAP_PACKED_END __packed +#endif + +#endif /* _ICAP_COMPILER_H_ */ diff --git a/sound/soc/adi/icap/include/icap_config.h b/sound/soc/adi/icap/include/icap_config.h new file mode 100644 index 00000000000000..3f9217ed198a37 --- /dev/null +++ b/sound/soc/adi/icap/include/icap_config.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_CONFIG_H_ +#define _ICAP_CONFIG_H_ + +/** + * @file icap_config.h + * @brief Example ICAP configuration file + * + */ + +/** @brief ICAP message timeout */ +#define ICAP_MSG_TIMEOUT_US (2000*1000) + +/* Choose one of the transport layers */ +#define ICAP_LINUX_KERNEL_RPMSG /* For use in linux kernel */ +//#define ICAP_BM_RPMSG_LITE /* For use in bare metal applications */ +//#define ICAP_LINUX_RPMSG_CHARDEV /* For use in linux user space application */ + +#if defined(ICAP_BM_RPMSG_LITE) +/* For static allocation of message queues */ +#define ICAP_MSG_QUEUE_SIZE 10 +#endif + +#endif /* _ICAP_CONFIG_H_ */ diff --git a/sound/soc/adi/icap/include/icap_device.h b/sound/soc/adi/icap/include/icap_device.h new file mode 100644 index 00000000000000..4eea26768a68eb --- /dev/null +++ b/sound/soc/adi/icap/include/icap_device.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_DEVICE_H_ +#define _ICAP_DEVICE_H_ + +/** + * @file icap_device.h + * @author Piotr Wojtaszczyk + * @brief ICAP definitions for device side. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + * + */ + +#include "icap.h" + +/** + * @addtogroup dev_functions + * @{ + */ + +/** + * @brief Callbacks used on device side, executed when appropriate application message + * received by device side, please see @ref app_functions. + * + * The #get_subdevices and #get_subdevice_features are mandatory, + * other callbacks are optional. If a callback isn't implemented ICAP by default + * responses with #ICAP_ACK to the message received. + * Implementation of a callback should return 0 on success (except #get_subdevices), + * a negative error code on failure or if a received parameter is invalid. + * + */ +struct icap_device_callbacks { + /** @brief Mandatory - Device callback for icap_get_subdevices(), + * the callback should return number of supported subdevices. + */ + int32_t (*get_subdevices)(struct icap_instance *icap); + + /* @brief Mandatory - Device callback for get_subdevice_features(), + * returns features of a subdevice. + */ + int32_t (*get_subdevice_features)(struct icap_instance *icap, uint32_t subdev_id, + struct icap_subdevice_features *features); + int32_t (*subdevice_init)(struct icap_instance *icap, + struct icap_subdevice_params *params); + int32_t (*subdevice_deinit)(struct icap_instance *icap, uint32_t subdev_id); + int32_t (*add_src)(struct icap_instance *icap, struct icap_buf_descriptor *buf); + int32_t (*add_dst)(struct icap_instance *icap, struct icap_buf_descriptor *buf); + int32_t (*remove_src)(struct icap_instance *icap, uint32_t buf_id); + int32_t (*remove_dst)(struct icap_instance *icap, uint32_t buf_id); + int32_t (*start)(struct icap_instance *icap, uint32_t subdev_id); + int32_t (*stop)(struct icap_instance *icap, uint32_t subdev_id); + int32_t (*pause)(struct icap_instance *icap, uint32_t subdev_id); + int32_t (*resume)(struct icap_instance *icap, uint32_t subdev_id); + int32_t (*frags)(struct icap_instance *icap, struct icap_buf_offsets *offsets); + + /** @brief Callback executed when a response to icap_frag_ready() is received. */ + int32_t (*frag_ready_response)(struct icap_instance *icap, int32_t buf_id); + + /** @brief Callback executed when a response to icap_xrun() is received. */ + int32_t (*xrun_response)(struct icap_instance *icap, int32_t buf_id); + + /** @brief Callback executed when a response to icap_error() is received. */ + int32_t (*error_response)(struct icap_instance *icap, int32_t error); +}; + +/**@}*/ + +/** + * @addtogroup init_functions + * @{ + */ + +/** + * @brief Initializes device side ICAP instance, + * requires some fields in icap_instance.transport initialized depending on platform. + * + * @param icap Pointer to new instance struct, the struct can be empty + * except some fields in icap_instance.transport. + * @param name Optional ICAP instance name. + * @param cb Pointer to #icap_device_callbacks. + * @param priv Private pointer for caller use. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_device_init(struct icap_instance *icap, char *name, + struct icap_device_callbacks *cb, void *priv); + +/** + * @brief Deinitialize ICAP instance and frees allocated resources. + * + * @param icap Pointer to ICAP instance. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_device_deinit(struct icap_instance *icap); + +/**@}*/ + +/** + * @defgroup dev_functions Device side functions + * + * Each function sends appropriate ICAP message to ICAP application which + * triggers appropriate application callback #icap_application_callbacks + * (if implemented), doesn't wait for response therefore it's safe to use the + * functions in interrupt context. + * + * When a response is received to the ICAP message appropriate callback is + * executed: + * - icap_device_callbacks.frag_ready_response() + * - icap_device_callbacks.xrun_response() + * - icap_device_callbacks.error_response() + * + * @{ + */ + +/** + * @brief Device should call this function when icap_buf_descriptor.report_frags + * is set and an audio fragment/s was consumed from the buffer by the device. + * + * @param icap Pointer to ICAP instance. + * @param frags Pointer to struct containing buffer id and number of fragments consumed. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_frag_ready(struct icap_instance *icap, struct icap_buf_frags *frags); + +/** + * @brief Device can call this function if xrun event is detected. + * + * @param icap Pointer to ICAP instance. + * @param frags Pointer to struct containing buffer id and number of fragments affected. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_xrun(struct icap_instance *icap, struct icap_buf_frags *frags); + +/** + * @brief Device can call this function to report an error condition in the device. + * + * @param icap Pointer to ICAP instance. + * @param error Positive error code. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +int32_t icap_error(struct icap_instance *icap, uint32_t error); + +/**@}*/ + +#endif /* _ICAP_DEVICE_H_ */ diff --git a/sound/soc/adi/icap/include/icap_linux_kernel_rpmsg.h b/sound/soc/adi/icap/include/icap_linux_kernel_rpmsg.h new file mode 100644 index 00000000000000..f49a43936dd20f --- /dev/null +++ b/sound/soc/adi/icap/include/icap_linux_kernel_rpmsg.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_LINUX_KERNEL_RPMSG_H_ +#define _ICAP_LINUX_KERNEL_RPMSG_H_ + +/** + * @file icap_linux_kernel_rpmsg.h + * @author Piotr Wojtaszczyk + * @brief ICAP `icap_transport` definition for Linux kernel platform. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + * + */ + +#include +#include +#include +#include +#include + +/** + * @brief ICAP `icap_transport` for Linux kernel ICAP implementation. + * + */ +struct icap_transport { + /** @brief This field needs to be set to appropriate `struct rpmsg_device` + * before ICAP initialization icap_application_init() or icap_device_init(). + */ + struct rpmsg_device *rpdev; + struct mutex rpdev_lock; + spinlock_t skb_spinlock; + struct sk_buff_head response_queue; + struct wait_queue_head response_event; + struct mutex response_lock; + struct mutex platform_lock; +}; + +#endif /* _ICAP_LINUX_KERNEL_RPMSG_H_ */ diff --git a/sound/soc/adi/icap/src/icap.c b/sound/soc/adi/icap/src/icap.c new file mode 100644 index 00000000000000..07e42985c75af9 --- /dev/null +++ b/sound/soc/adi/icap/src/icap.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +/** + * @file icap.c + * @author Piotr Wojtaszczyk + * @brief ICAP (Inter Core Audio Protocol) platform independent source code. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + */ + +#include "../include/icap_application.h" +#include "../include/icap_device.h" +#include "platform/icap_transport.h" + +/** + * @brief ICAP instance type. + * + * ICAP specifies communication between application and device. + * Application side sends audio for playback and receives recorded audio. + * Device side receives audio for playback and sends recorded audio. + */ +enum icap_instance_type { + ICAP_APPLICATION_INSTANCE = 0, /**< ICAP application instance. */ + ICAP_DEVICE_INSTANCE = 1, /**< ICAP device instance. */ +}; + +int32_t icap_application_init(struct icap_instance *icap, char *name, + struct icap_application_callbacks *cb, void *priv) +{ + if ((icap == NULL) || (cb == NULL)) + return -ICAP_ERROR_INVALID; + + if (icap->callbacks != NULL) + return -ICAP_ERROR_BUSY; + + icap->name = name; + icap->type = ICAP_APPLICATION_INSTANCE; + icap->priv = priv; + icap->callbacks = cb; + icap->seq_num = 0; + return icap_init_transport(icap); +} + +int32_t icap_application_deinit(struct icap_instance *icap) +{ + if (icap->callbacks == NULL) + return 0; + + icap->callbacks = NULL; + return icap_deinit_transport(icap); +} + +int32_t icap_device_init(struct icap_instance *icap, char *name, + struct icap_device_callbacks *cb, void *priv) +{ + if ((icap == NULL) || (cb == NULL)) + return -ICAP_ERROR_INVALID; + + icap->name = name; + icap->type = ICAP_DEVICE_INSTANCE; + icap->priv = priv; + icap->callbacks = cb; + icap->seq_num = 0; + return icap_init_transport(icap); +} + +int32_t icap_device_deinit(struct icap_instance *icap) +{ + if (icap->callbacks == NULL) + return 0; + + icap->callbacks = NULL; + return icap_deinit_transport(icap); +} + +static +s32 icap_send_msg(struct icap_instance *icap, enum icap_msg_cmd cmd, + void *data, u32 size, u32 sync, struct icap_msg *response) +{ + struct icap_msg msg; + uint32_t seq_num; + int32_t ret; + + /* Copy data to msg payload */ + if (data) { + if (size > sizeof(msg.payload)) + return -ICAP_ERROR_MSG_LEN; + memcpy(&msg.payload, data, size); + } else { + size = 0; + } + + /* Increment the seq_num */ + icap_platform_lock(icap); + icap->seq_num++; + seq_num = icap->seq_num; + icap_platform_unlock(icap); + + /* Initialize msg header */ + msg.header.protocol_version = ICAP_PROTOCOL_VERSION; + msg.header.seq_num = seq_num; + msg.header.cmd = cmd; + msg.header.type = ICAP_MSG; + memset(&msg.header.reserved, 0, sizeof(msg.header.reserved)); + msg.header.payload_len = size; + + size += sizeof(msg.header); + + if (sync) { + ret = icap_prepare_wait(icap, &msg); + if (ret) + return ret; + } + + ret = icap_send_platform(icap, &msg, size); + + if (!sync) + return ret; + + return icap_wait_for_response(icap, seq_num, response); +} + +static +s32 icap_send_response(struct icap_instance *icap, enum icap_msg_cmd cmd, + enum icap_msg_type type, u32 seq_num, void *data, u32 size) +{ + struct icap_msg msg; + + /* Copy data to response payload */ + if (data) { + if (size > sizeof(msg.payload)) + return -ICAP_ERROR_MSG_LEN; + memcpy(&msg.payload, data, size); + } else { + size = 0; + } + + /* Initialize msg header */ + msg.header.protocol_version = ICAP_PROTOCOL_VERSION; + msg.header.seq_num = seq_num; + msg.header.cmd = cmd; + msg.header.type = type; + memset(&msg.header.reserved, 0, sizeof(msg.header.reserved)); + msg.header.payload_len = size; + + size += sizeof(msg.header); + + return icap_send_platform(icap, &msg, size); +} + +static +s32 icap_send_ack(struct icap_instance *icap, enum icap_msg_cmd cmd, + u32 seq_num, void *data, u32 size) +{ + return icap_send_response(icap, cmd, ICAP_ACK, seq_num, data, size); +} + +static +s32 icap_send_nak(struct icap_instance *icap, enum icap_msg_cmd cmd, + u32 seq_num, s32 error) +{ + return icap_send_response(icap, cmd, ICAP_NAK, seq_num, &error, sizeof(error)); +} + +s32 icap_get_subdevices(struct icap_instance *icap) +{ + struct icap_msg response; + int32_t ret; + + ret = icap_send_msg(icap, ICAP_MSG_GET_DEV_NUM, NULL, 0, 1, &response); + if (ret) + return ret; + + if (response.header.payload_len != sizeof(uint32_t)) + return -ICAP_ERROR_MSG_LEN; + + return response.payload.s32; +} + +s32 icap_get_subdevice_features(struct icap_instance *icap, u32 subdev_id, + struct icap_subdevice_features *features) +{ + struct icap_msg response; + int32_t ret; + + if (features == NULL) + return -ICAP_ERROR_INVALID; + + ret = icap_send_msg(icap, ICAP_MSG_GET_DEV_FEATURES, &subdev_id, + sizeof(subdev_id), 1, &response); + if (ret) + return ret; + + if (response.header.payload_len != sizeof(struct icap_subdevice_features)) + return -ICAP_ERROR_MSG_LEN; + + memcpy(features, &response.payload, sizeof(struct icap_subdevice_features)); + return 0; +} + +s32 icap_subdevice_init(struct icap_instance *icap, + struct icap_subdevice_params *params) +{ + if (params == NULL) + return -ICAP_ERROR_INVALID; + + return icap_send_msg(icap, ICAP_MSG_DEV_INIT, params, + sizeof(struct icap_subdevice_params), 1, NULL); +} + +s32 icap_subdevice_deinit(struct icap_instance *icap, u32 subdev_id) +{ + return icap_send_msg(icap, ICAP_MSG_DEV_DEINIT, &subdev_id, sizeof(subdev_id), 1, NULL); +} + +s32 icap_add_src(struct icap_instance *icap, struct icap_buf_descriptor *buf) +{ + struct icap_msg response; + int32_t ret; + + if (buf == NULL) + return -ICAP_ERROR_INVALID; + + ret = icap_send_msg(icap, ICAP_MSG_ADD_SRC, buf, + sizeof(struct icap_buf_descriptor), 1, &response); + if (ret) + return ret; + + if (response.header.payload_len != sizeof(uint32_t)) + return -ICAP_ERROR_MSG_LEN; + + return response.payload.s32; +} + +s32 icap_add_dst(struct icap_instance *icap, struct icap_buf_descriptor *buf) +{ + struct icap_msg response; + int32_t ret; + + if (buf == NULL) + return -ICAP_ERROR_INVALID; + + ret = icap_send_msg(icap, ICAP_MSG_ADD_DST, buf, + sizeof(struct icap_buf_descriptor), 1, &response); + if (ret) + return ret; + + if (response.header.payload_len != sizeof(uint32_t)) + return -ICAP_ERROR_MSG_LEN; + + return response.payload.s32; +} + +s32 icap_remove_src(struct icap_instance *icap, u32 buf_id) +{ + return icap_send_msg(icap, ICAP_MSG_REMOVE_SRC, &buf_id, sizeof(buf_id), 1, NULL); +} + +s32 icap_remove_dst(struct icap_instance *icap, u32 buf_id) +{ + return icap_send_msg(icap, ICAP_MSG_REMOVE_DST, &buf_id, sizeof(buf_id), 1, NULL); +} + +s32 icap_start(struct icap_instance *icap, u32 subdev_id) +{ + return icap_send_msg(icap, ICAP_MSG_START, &subdev_id, sizeof(subdev_id), 1, NULL); +} + +s32 icap_stop(struct icap_instance *icap, u32 subdev_id) +{ + return icap_send_msg(icap, ICAP_MSG_STOP, &subdev_id, sizeof(subdev_id), 1, NULL); +} + +s32 icap_pause(struct icap_instance *icap, u32 subdev_id) +{ + return icap_send_msg(icap, ICAP_MSG_PAUSE, &subdev_id, sizeof(subdev_id), 1, NULL); +} + +s32 icap_resume(struct icap_instance *icap, u32 subdev_id) +{ + return icap_send_msg(icap, ICAP_MSG_RESUME, &subdev_id, sizeof(subdev_id), 1, NULL); +} + +s32 icap_frags(struct icap_instance *icap, struct icap_buf_offsets *offsets) +{ + if (offsets == NULL) + return -ICAP_ERROR_INVALID; + + return icap_send_msg(icap, ICAP_MSG_BUF_OFFSETS, offsets, + sizeof(struct icap_buf_offsets), 1, NULL); +} + +s32 icap_frag_ready(struct icap_instance *icap, struct icap_buf_frags *frags) +{ + if (frags == NULL) + return -ICAP_ERROR_INVALID; + + return icap_send_msg(icap, ICAP_MSG_FRAG_READY, frags, + sizeof(struct icap_buf_frags), 0, NULL); +} + +s32 icap_xrun(struct icap_instance *icap, struct icap_buf_frags *frags) +{ + if (frags == NULL) + return -ICAP_ERROR_INVALID; + + return icap_send_msg(icap, ICAP_MSG_XRUN, frags, sizeof(struct icap_buf_frags), 0, NULL); +} + +s32 icap_error(struct icap_instance *icap, u32 error) +{ + return icap_send_msg(icap, ICAP_MSG_ERROR, &error, sizeof(error), 0, NULL); +} + +static +s32 icap_application_parse_response(struct icap_instance *icap, + struct icap_msg *msg) +{ + /* + * Currently all responses to application are for synchronous messages + * notify the waiter. + */ + return icap_response_notify(icap, msg); +} + +static +s32 icap_application_parse_msg(struct icap_instance *icap, + struct icap_msg *msg) +{ + struct icap_application_callbacks *cb = icap->callbacks; + struct icap_msg_header *msg_header = &msg->header; + int32_t send_generic_ack = 1; + uint32_t buf_id; + int32_t ret = 0; + + switch (msg_header->cmd) { + case ICAP_MSG_FRAG_READY: + if (cb->frag_ready) { + buf_id = msg->payload.frags.buf_id; + ret = cb->frag_ready(icap, &msg->payload.frags); + if (ret == 0) { + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + } else { + buf_id = msg->payload.frags.buf_id; + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + break; + case ICAP_MSG_XRUN: + if (cb->xrun) { + buf_id = msg->payload.frags.buf_id; + ret = cb->xrun(icap, &msg->payload.frags); + if (ret == 0) { + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + } else { + buf_id = msg->payload.frags.buf_id; + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + break; + case ICAP_MSG_ERROR: + if (cb->error) + ret = cb->error(icap, msg->payload.s32); + break; + default: + ret = -ICAP_ERROR_MSG_ID; + break; + } + + if (send_generic_ack) { + if (ret) + icap_send_nak(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, ret); + else + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, NULL, 0); + } + + return 0; +} + +static +s32 icap_device_parse_response(struct icap_instance *icap, + struct icap_msg *msg) +{ + struct icap_device_callbacks *cb = (struct icap_device_callbacks *)icap->callbacks; + struct icap_msg_header *msg_header = &msg->header; + int32_t ret = 0; + int32_t error; + + /* + * Currently all responses to device are for asynchronous messages + * execute a response callback. + */ + + if (msg_header->type == ICAP_NAK) + error = msg->payload.s32; + else + error = 0; + + switch (msg_header->cmd) { + case ICAP_MSG_FRAG_READY: + if (cb->frag_ready_response) { + if (msg_header->type == ICAP_ACK) + error = msg->payload.s32; + ret = cb->frag_ready_response(icap, error); + } + break; + case ICAP_MSG_XRUN: + if (cb->xrun_response) { + if (msg_header->type == ICAP_ACK) + error = msg->payload.s32; + ret = cb->xrun_response(icap, error); + } + break; + case ICAP_MSG_ERROR: + if (cb->error_response) + ret = cb->error_response(icap, error); + break; + default: + ret = -ICAP_ERROR_MSG_ID; + break; + } + return ret; +} + +static +s32 icap_device_parse_msg(struct icap_instance *icap, struct icap_msg *msg) +{ + struct icap_device_callbacks *cb = (struct icap_device_callbacks *)icap->callbacks; + struct icap_msg_header *msg_header = &msg->header; + int32_t send_generic_ack = 1; + int32_t ret = 0; + uint32_t buf_id; + uint32_t dev_num; + struct icap_subdevice_features features; + + switch (msg_header->cmd) { + case ICAP_MSG_GET_DEV_NUM: + if (cb->get_subdevices) { + ret = cb->get_subdevices(icap); + if (ret >= 0) { + dev_num = ret; + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &dev_num, sizeof(dev_num)); + send_generic_ack = 0; + } + } + break; + case ICAP_MSG_GET_DEV_FEATURES: + if (cb->get_subdevice_features) { + ret = cb->get_subdevice_features(icap, msg->payload.u32, &features); + if (ret >= 0) { + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &features, + sizeof(struct icap_subdevice_features)); + send_generic_ack = 0; + } + } + break; + case ICAP_MSG_DEV_INIT: + if (cb->subdevice_init) + ret = cb->subdevice_init(icap, &msg->payload.dev_params); + break; + case ICAP_MSG_DEV_DEINIT: + if (cb->subdevice_deinit) + ret = cb->subdevice_deinit(icap, msg->payload.u32); + break; + case ICAP_MSG_ADD_SRC: + if (cb->add_src) { + ret = cb->add_src(icap, &msg->payload.buf); + if (ret >= 0) { + buf_id = ret; + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + } + break; + case ICAP_MSG_ADD_DST: + if (cb->add_dst) { + ret = cb->add_dst(icap, &msg->payload.buf); + if (ret >= 0) { + buf_id = ret; + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, &buf_id, sizeof(buf_id)); + send_generic_ack = 0; + } + } + break; + case ICAP_MSG_REMOVE_SRC: + if (cb->remove_src) + ret = cb->remove_src(icap, msg->payload.u32); + break; + case ICAP_MSG_REMOVE_DST: + if (cb->remove_dst) + ret = cb->remove_dst(icap, msg->payload.u32); + break; + case ICAP_MSG_START: + if (cb->start) + ret = cb->start(icap, msg->payload.u32); + break; + case ICAP_MSG_STOP: + if (cb->stop) + ret = cb->stop(icap, msg->payload.u32); + break; + case ICAP_MSG_PAUSE: + if (cb->pause) + ret = cb->pause(icap, msg->payload.u32); + break; + case ICAP_MSG_RESUME: + if (cb->resume) + ret = cb->resume(icap, msg->payload.u32); + break; + case ICAP_MSG_BUF_OFFSETS: + if (cb->frags) + ret = cb->frags(icap, &msg->payload.offsets); + break; + default: + ret = -ICAP_ERROR_MSG_ID; + break; + } + + if (send_generic_ack) { + if (ret) + icap_send_nak(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, ret); + else + icap_send_ack(icap, (enum icap_msg_cmd)msg_header->cmd, + msg_header->seq_num, NULL, 0); + } + return 0; +} + +s32 icap_parse_msg(struct icap_instance *icap, + union icap_remote_addr *src_addr, void *data, u32 size) +{ + struct icap_msg *msg = (struct icap_msg *)data; + struct icap_msg_header *msg_header = &msg->header; + int32_t ret; + + if (icap->callbacks == NULL) + return -ICAP_ERROR_INIT; + + if (msg_header->protocol_version != ICAP_PROTOCOL_VERSION) + return -ICAP_ERROR_PROTOCOL_NOT_SUP; + + if (size != sizeof(struct icap_msg_header) + msg_header->payload_len) + return -ICAP_ERROR_MSG_LEN; + + ret = icap_verify_remote(icap, src_addr); + if (ret) + return ret; + + if ((msg_header->type == ICAP_ACK) || (msg_header->type == ICAP_NAK)) { + if (icap->type == ICAP_APPLICATION_INSTANCE) + return icap_application_parse_response(icap, msg); + else + return icap_device_parse_response(icap, msg); + } + + if (msg_header->type == ICAP_MSG) { + if (icap->type == ICAP_APPLICATION_INSTANCE) + return icap_application_parse_msg(icap, msg); + else + return icap_device_parse_msg(icap, msg); + } + + return -ICAP_ERROR_MSG_TYPE; +} diff --git a/sound/soc/adi/icap/src/platform/icap_linux_kernel_rpmsg.c b/sound/soc/adi/icap/src/platform/icap_linux_kernel_rpmsg.c new file mode 100644 index 00000000000000..832bc454b328c3 --- /dev/null +++ b/sound/soc/adi/icap/src/platform/icap_linux_kernel_rpmsg.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +/** + * @file icap_linux_kernel_rpmsg.c + * @author Piotr Wojtaszczyk + * @brief ICAP implementation for Linux kernel rpmsg platform. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + * + */ + +#include "icap_transport.h" + +#ifdef ICAP_LINUX_KERNEL_RPMSG + +#include +#include + +#define __ICAP_MSG_TIMEOUT usecs_to_jiffies(ICAP_MSG_TIMEOUT_US) + +s32 icap_init_transport(struct icap_instance *icap) +{ + struct icap_transport *transport = &icap->transport; + + mutex_init(&transport->rpdev_lock); + mutex_init(&transport->platform_lock); + mutex_init(&transport->response_lock); + spin_lock_init(&transport->skb_spinlock); + init_waitqueue_head(&transport->response_event); + skb_queue_head_init(&transport->response_queue); + return 0; +} + +s32 icap_deinit_transport(struct icap_instance *icap) +{ + struct icap_transport *transport = &icap->transport; + struct sk_buff *skb; + unsigned long flags; + + mutex_lock(&transport->rpdev_lock); + + spin_lock_irqsave(&transport->skb_spinlock, flags); + while (!skb_queue_empty(&transport->response_queue)) { + skb = skb_dequeue(&transport->response_queue); + kfree_skb(skb); + } + spin_unlock_irqrestore(&transport->skb_spinlock, flags); + + transport->rpdev = NULL; + + mutex_unlock(&transport->rpdev_lock); + return 0; +} + +s32 icap_verify_remote(struct icap_instance *icap, + union icap_remote_addr *src_addr) +{ + /* rpmsg endpoints on linux are one to one - no need to verify src address*/ + return 0; +} + +s32 icap_send_platform(struct icap_instance *icap, void *data, u32 size) +{ + struct icap_transport *transport = &icap->transport; + int32_t ret; + + mutex_lock(&transport->rpdev_lock); + if (transport->rpdev != NULL) + ret = rpmsg_send(transport->rpdev->ept, data, size); + else + ret = -ICAP_ERROR_BROKEN_CON; + mutex_unlock(&transport->rpdev_lock); + return ret; +} + +struct _icap_wait_hint { + u32 received; + u32 seq_num; + u32 msg_cmd; +}; + +static +struct sk_buff *_find_seq_num(struct sk_buff_head *queue, u32 seq_num) +{ + struct _icap_wait_hint *hint; + struct sk_buff *skb; + + skb_queue_walk(queue, skb) { + hint = (struct _icap_wait_hint *)skb->head; + if (hint->seq_num == seq_num) + return skb; + } + return NULL; +} + +s32 icap_prepare_wait(struct icap_instance *icap, struct icap_msg *msg) +{ + struct icap_transport *transport = &icap->transport; + struct sk_buff *skb; + struct _icap_wait_hint *hint; + unsigned long flags; + int32_t ret = 0; + + mutex_lock(&transport->rpdev_lock); + + if (transport->rpdev == NULL) { + ret = -ICAP_ERROR_BROKEN_CON; + goto prepare_wait_unlock; + } + + skb = alloc_skb(sizeof(struct _icap_wait_hint) + sizeof(struct icap_msg), GFP_KERNEL); + if (!skb) { + ret = -ENOMEM; + goto prepare_wait_unlock; + } + + skb_reserve(skb, sizeof(struct _icap_wait_hint)); + + hint = (struct _icap_wait_hint *)skb->head; + hint->received = 0; + hint->msg_cmd = msg->header.cmd; + hint->seq_num = msg->header.seq_num; + + spin_lock_irqsave(&transport->skb_spinlock, flags); + skb_queue_tail(&transport->response_queue, skb); + spin_unlock_irqrestore(&transport->skb_spinlock, flags); + +prepare_wait_unlock: + mutex_unlock(&transport->rpdev_lock); + return ret; +} + +s32 icap_response_notify(struct icap_instance *icap, struct icap_msg *response) +{ + struct icap_transport *transport = &icap->transport; + struct sk_buff *skb; + struct _icap_wait_hint *hint; + unsigned long flags; + uint32_t size; + int32_t ret; + + spin_lock_irqsave(&transport->skb_spinlock, flags); + skb = _find_seq_num(&transport->response_queue, response->header.seq_num); + if (skb != NULL) { + /* Waiter found, copy msg to its buffer */ + size = sizeof(response->header) + response->header.payload_len; + skb_put_data(skb, response, size); + hint = (struct _icap_wait_hint *)skb->head; + hint->received = 1; + wake_up_interruptible_all(&transport->response_event); + ret = 0; + } else { + /* + * Got a unexpected or very late message, + * waiter could timeout and remove from the hint from the queue. + * Drop the message. + */ + ret = -ICAP_ERROR_TIMEOUT; + } + spin_unlock_irqrestore(&transport->skb_spinlock, flags); + return ret; +} + +s32 icap_wait_for_response(struct icap_instance *icap, u32 seq_num, + struct icap_msg *response) +{ + struct icap_transport *transport = &icap->transport; + struct device *dev; + u8 icap_id; + char _env[64]; + char *envp[] = { _env, NULL }; + long timeout; + unsigned long flags; + struct sk_buff *skb; + struct _icap_wait_hint *hint; + struct icap_msg *tmp_msg; + int32_t ret; + + mutex_lock(&transport->response_lock); + spin_lock_irqsave(&transport->skb_spinlock, flags); + skb = _find_seq_num(&transport->response_queue, seq_num); + spin_unlock_irqrestore(&transport->skb_spinlock, flags); + mutex_unlock(&transport->response_lock); + + if (skb == NULL) { + /* This should never happen */ + return -ICAP_ERROR_PROTOCOL; + } + hint = (struct _icap_wait_hint *)skb->head; + + timeout = wait_event_interruptible_timeout(transport->response_event, + hint->received, + __ICAP_MSG_TIMEOUT); + + /* Remove the skb from response queue */ + mutex_lock(&transport->response_lock); + spin_lock_irqsave(&transport->skb_spinlock, flags); + skb_unlink(skb, &transport->response_queue); + spin_unlock_irqrestore(&transport->skb_spinlock, flags); + mutex_unlock(&transport->response_lock); + + if (timeout > 0) { + /* Got response in time */ + tmp_msg = (struct icap_msg *)skb->data; + if (tmp_msg->header.type == ICAP_NAK) { + ret = tmp_msg->payload.s32; + } else { + if (response) + memcpy(response, skb->data, skb->len); + ret = 0; + } + } else if (timeout < 0) { + /* Got error */ + ret = timeout; + } else { + mutex_lock(&transport->rpdev_lock); + + if (transport->rpdev == NULL) { + ret = -ICAP_ERROR_BROKEN_CON; + } else { + dev = &transport->rpdev->dev; + icap_id = transport->rpdev->dst; + /* Timeout */ + snprintf(_env, sizeof(_env), "EVENT=ICAP%d_MSG%d_TIMEOUT", + icap_id, hint->msg_cmd); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + ret = -ETIMEDOUT; + } + mutex_unlock(&transport->rpdev_lock); + } + + kfree_skb(skb); + return ret; +} + +void icap_platform_lock(struct icap_instance *icap) +{ + struct icap_transport *transport = &icap->transport; + + mutex_lock(&transport->platform_lock); +} + +void icap_platform_unlock(struct icap_instance *icap) +{ + struct icap_transport *transport = &icap->transport; + + mutex_unlock(&transport->platform_lock); +} + +#endif /* ICAP_LINUX_KERNEL_RPMSG */ diff --git a/sound/soc/adi/icap/src/platform/icap_transport.h b/sound/soc/adi/icap/src/platform/icap_transport.h new file mode 100644 index 00000000000000..4fed992444992a --- /dev/null +++ b/sound/soc/adi/icap/src/platform/icap_transport.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR Apache-2.0) */ + +/* + * Copyright 2021-2022 Analog Devices Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2021-2022 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program + */ + +/* + * Authors: + * Piotr Wojtaszczyk + */ + +#ifndef _ICAP_TRANSPORT_H_ +#define _ICAP_TRANSPORT_H_ + +/** + * @file icap_transport.h + * @author Piotr Wojtaszczyk + * @brief Private header file with messaging definitions. + * + * @copyright Copyright 2021-2022 Analog Devices Inc. + * + */ + +#include "../../include/icap.h" + +#define ICAP_PROTOCOL_VERSION (1) + +/** + * @brief ICAP message type + * + * Each ICAP message should have corresponding response from remote core. + */ +enum icap_msg_type { + ICAP_MSG = 0, /**< Message. */ + ICAP_ACK = 1, /**< Positive response. */ + ICAP_NAK = 2, /**< Negative response. */ +}; + +/** + * @brief ICAP command definitions + * + */ +enum icap_msg_cmd { + /* Control commands */ + ICAP_MSG_GET_DEV_NUM = 9, /**< Get number of subdevices. */ + ICAP_MSG_GET_DEV_FEATURES = 10, /**< Get subdevice features. */ + ICAP_MSG_DEV_INIT = 11, /**< Init subdevice. */ + ICAP_MSG_DEV_DEINIT = 12, /**< Deinit subdevice. */ + + /* Stream commands */ + ICAP_MSG_ADD_SRC = 50, /**< Add source buffer. */ + ICAP_MSG_ADD_DST = 51, /**< Add destination buffer. */ + ICAP_MSG_REMOVE_SRC = 52, /**< Remove source buffer. */ + ICAP_MSG_REMOVE_DST = 53, /**< Remove destination buffer. */ + ICAP_MSG_START = 54, /**< Start subdevice. */ + ICAP_MSG_STOP = 55, /**< Stop subdevice. */ + ICAP_MSG_PAUSE = 56, /**< Pause subdevice. */ + ICAP_MSG_RESUME = 57, /**< Resume subdevice. */ + ICAP_MSG_BUF_OFFSETS = 58, /* < Send offsets for new fragments, + * used in #ICAP_BUF_SCATTERED. + */ + ICAP_MSG_FRAG_READY = 59, /**< Audio fragment consumed. */ + ICAP_MSG_XRUN = 60, /**< Report buffer xrun. */ + + /* Other messages */ + ICAP_MSG_ERROR = 200, /**< Report error. */ +}; + +/** + * @brief Message payload, valid union field depends on command. + * + */ +ICAP_PACKED_BEGIN +union icap_msg_payload { + u8 bytes[ICAP_BUF_NAME_LEN]; + char name[ICAP_BUF_NAME_LEN]; + u32 u32; + s32 s32; + struct icap_buf_descriptor buf; + struct icap_buf_frags frags; + struct icap_buf_offsets offsets; + struct icap_subdevice_features features; + struct icap_subdevice_params dev_params; +} ICAP_PACKED_END; + +/** + * @brief Message header with control fields. + * + */ +ICAP_PACKED_BEGIN +struct icap_msg_header { + u32 protocol_version; /**< ICAP protocol version. */ + u32 seq_num; /**< Sequence number of a message, increments every msg.*/ + u32 cmd; /**< Command ID of the message.*/ + u32 type; /* < Specifies if message or response to a message: + * ICAP_MSG, ICAP_ACK, ICAP_NAK. + */ + u32 reserved[5]; /**< Reserved for future use.*/ + u32 payload_len; /**< Payload length in bytes.*/ +} ICAP_PACKED_END; + +/** + * @brief ICAP message definition. + * + */ +ICAP_PACKED_BEGIN +struct icap_msg { + struct icap_msg_header header; + union icap_msg_payload payload; +} ICAP_PACKED_END; + +/** + * @brief Initializes platform specific transport layer. + * + * @param icap Pointer to ICAP instance. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_init_transport(struct icap_instance *icap); + +/** + * @brief Releases platform specific transport layer. + * + * @param icap Pointer to ICAP instance. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_deinit_transport(struct icap_instance *icap); + +/** + * @brief Verifies if source address is correct. + * + * @param icap Pointer to ICAP instance. + * @param src_addr Source address to verify. + * @return int32_t Returns 0 when address is correct, -ICAP_ERROR_REMOTE_ADDR if wrong. + */ +s32 icap_verify_remote(struct icap_instance *icap, union icap_remote_addr *src_addr); + +/** + * @brief Send ICAP message using platform specific transport. + * + * @param icap Pointer to ICAP instance. + * @param data Pointer to ICAP message. + * @param size Totall size of the ICAP message. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_send_platform(struct icap_instance *icap, void *data, u32 size); + +/** + * @brief Notifies about received response, may unblock a thread waiting for the response. + * May be called in interrupt context. + * + * @param icap Pointer to ICAP instance. + * @param response Pointer to response message received. + * @return int32_t Returns -ICAP_ERROR_TIMEOUT if nobody waits + * for the message, 0 otherwise. + */ +s32 icap_response_notify(struct icap_instance *icap, struct icap_msg *response); + +/** + * @brief Allows platform to prepare for expected response before sending the message. + * + * @param icap Pointer to ICAP instance. + * @param msg Pointer to ICAP message which response to is expected. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_prepare_wait(struct icap_instance *icap, struct icap_msg *msg); + +/** + * @brief Puts the thread into sleep while waiting for response. + * + * @param icap Pointer to ICAP instance. + * @param seq_num Sequence number of the expected response. + * @param response If not NULL the expected response is copied to the struct. + * @return int32_t Returns 0 on success, negative error code on failure. + */ +s32 icap_wait_for_response(struct icap_instance *icap, + u32 seq_num, struct icap_msg *response); + +/** + * @brief Lock critical section. + * + * @param icap Pointer to ICAP instance. + */ +void icap_platform_lock(struct icap_instance *icap); + +/** + * @brief Unlock critical section. + * + * @param icap Pointer to ICAP instance. + */ +void icap_platform_unlock(struct icap_instance *icap); + +#endif /* _ICAP_TRANSPORT_H_ */ diff --git a/sound/soc/adi/sc5xx-asoc-card.c b/sound/soc/adi/sc5xx-asoc-card.c new file mode 100644 index 00000000000000..422b1bc4c36a79 --- /dev/null +++ b/sound/soc/adi/sc5xx-asoc-card.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices ASoC Machine driver for sc5xx + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Scott Jiang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/adau1372.h" +#include "../codecs/adau1962.h" +#include "../codecs/adau1977.h" +#include "../codecs/adau17x1.h" + +static const struct snd_soc_dapm_widget sc5xx_adau1761_dapm_widgets[] = { + SND_SOC_DAPM_LINE("In 1", NULL), + SND_SOC_DAPM_LINE("In 2", NULL), + SND_SOC_DAPM_LINE("In 3-4", NULL), + + SND_SOC_DAPM_LINE("Diff Out L", NULL), + SND_SOC_DAPM_LINE("Diff Out R", NULL), + SND_SOC_DAPM_LINE("Stereo Out", NULL), + SND_SOC_DAPM_HP("Capless HP Out", NULL), +}; + +static const struct snd_soc_dapm_route sc5xx_adau1761_dapm_routes[] = { + { "LAUX", NULL, "In 3-4" }, + { "RAUX", NULL, "In 3-4" }, + { "LINP", NULL, "In 1" }, + { "LINN", NULL, "In 1"}, + { "RINP", NULL, "In 2" }, + { "RINN", NULL, "In 2" }, + + { "In 1", NULL, "MICBIAS" }, + { "In 2", NULL, "MICBIAS" }, + + { "Capless HP Out", NULL, "LHP" }, + { "Capless HP Out", NULL, "RHP" }, + { "Diff Out L", NULL, "LOUT" }, + { "Diff Out R", NULL, "ROUT" }, + { "Stereo Out", NULL, "LOUT" }, + { "Stereo Out", NULL, "ROUT" }, +}; + +static int sc5xx_adau1372_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + unsigned int fmt, rx_mask = 0; + unsigned int slot_width = 0; + int ret, slots = 0; + + switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 4; + rx_mask = 0xf; + break; + case 8: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 8; + rx_mask = 0xff; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + slot_width = 16; + break; + case 24: + case 32: + slot_width = 32; + break; + default: + return -EINVAL; + } + + ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); + if (ret) + return ret; + + return snd_soc_dai_set_tdm_slot(codec_dai, 0, rx_mask, + slots, slot_width); + +} + +static const struct snd_soc_ops adau1372_ops = { + .hw_params = sc5xx_adau1372_hw_params, +}; + +static int __maybe_unused sc5xx_adau1372_init(struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static int sc5xx_adau1962_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + unsigned int fmt, rx_mask = 0; + unsigned int slot_width = 0; + int ret, slots = 0; + + switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 4; + rx_mask = 0xf; + break; + case 8: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 8; + rx_mask = 0xff; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + slot_width = 16; + break; + case 24: + case 32: + slot_width = 32; + break; + default: + return -EINVAL; + } + + ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); + if (ret) + return ret; + + return snd_soc_dai_set_tdm_slot(codec_dai, 0, rx_mask, + slots, slot_width); + +} + +static const struct snd_soc_ops adau1962_ops = { + .hw_params = sc5xx_adau1962_hw_params, +}; + +static int __maybe_unused sc5xx_adau1962_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = dai->component; + int ret = snd_soc_component_set_sysclk(component, ADAU1962_SYSCLK, + ADAU1962_SYSCLK_SRC_MCLK, 24576000, SND_SOC_CLOCK_IN); + return ret; +} + +static int sc5xx_adau1979_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + int ret, slots = 0; + unsigned int slot_width = 0; + unsigned int fmt, rx_mask = 0; + + switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + slots = 4; + rx_mask = 0xf; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + slot_width = 16; + break; + case 24: + slot_width = 24; + break; + case 32: + slot_width = 32; + break; + default: + return -EINVAL; + } + + ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); + if (ret) + return ret; + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, rx_mask, + slots, slot_width); + return ret; +} + +static int sam_adau1761_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + unsigned int fmt, rx_mask = 0; + unsigned int slot_width = 0; + int ret, slots = 0; + + switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + slot_width = 16; + break; + case 24: + case 32: + slot_width = 32; + break; + default: + return -EINVAL; + } + + ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); + if (ret) + return ret; + + return snd_soc_dai_set_tdm_slot(codec_dai, 0, rx_mask, + slots, slot_width); +} + +static int sc5xx_adau1761_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + int pll_rate; + int ret; + + switch (params_rate(params)) { + case 48000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 96000: + pll_rate = 48000 * 1024; + break; + case 44100: + case 7350: + case 11025: + case 14700: + case 22050: + case 29400: + case 88200: + pll_rate = 44100 * 1024; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL, + ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, 12288000, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("%s error, ret:%d\n", __func__, ret); + return ret; + } + + return sam_adau1761_hw_params(substream, params); +} + +static const struct snd_soc_ops sc5xx_adau1761_ops = { + .hw_params = sc5xx_adau1761_hw_params, +}; + +static const struct snd_soc_ops adau1979_ops = { + .hw_params = sc5xx_adau1979_hw_params, +}; + +static int __maybe_unused sc5xx_adau1979_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = dai->component; + + return snd_soc_component_set_sysclk(component, ADAU1977_SYSCLK, + ADAU1977_SYSCLK_SRC_MCLK, 24576000, SND_SOC_CLOCK_IN); +} + +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1372) +static struct snd_soc_dai_link_component adau1372_codec_component[] = { + { + .name = NULL, + .of_node = NULL, + .dai_name = "adau1372", + }, +}; +#endif + +static struct snd_soc_dai_link_component adau1962_codec_component[] = { + { + .name = NULL, + .of_node = NULL, + .dai_name = "adau1962-hifi", + }, +}; + +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1979) +static struct snd_soc_dai_link_component adau1979_codec_component[] = { + { + .name = NULL, + .of_node = NULL, + .dai_name = "adau1977-hifi", + }, +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1761) +static struct snd_soc_dai_link_component adau1961_codec_component[] = { + { + .name = NULL, + .of_node = NULL, + .dai_name = "adau1961-hifi", + }, +}; +#endif + +static struct snd_soc_dai_link_component sc5xx_platform_component[] = { + { + .name = "sc5xx-pcm-audio", + .of_node = NULL, + .dai_name = NULL, + }, +}; + +static struct snd_soc_dai_link_component sc5xx_cpu_component[] = { + { + .name = NULL, + .of_node = NULL, + .dai_name = NULL, + }, +}; + +/* Digital audio interface glue - connect codec <--> CPU */ +static struct snd_soc_dai_link sc5xx_snd_soc_dai_links[] = { +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1372) + { + .name = "adau1372", + .stream_name = "ADAU1372", + .cpus = sc5xx_cpu_component, + .num_cpus = ARRAY_SIZE(sc5xx_cpu_component), + .codecs = adau1372_codec_component, + .num_codecs = ARRAY_SIZE(adau1372_codec_component), + .platforms = sc5xx_platform_component, + .num_platforms = ARRAY_SIZE(sc5xx_platform_component), + .init = sc5xx_adau1372_init, + .ops = &adau1372_ops, + }, +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1962) + { + .name = "adau1962", + .stream_name = "ADAU1962", + .cpus = sc5xx_cpu_component, + .num_cpus = ARRAY_SIZE(sc5xx_cpu_component), + .codecs = adau1962_codec_component, + .num_codecs = ARRAY_SIZE(adau1962_codec_component), + .platforms = sc5xx_platform_component, + .num_platforms = ARRAY_SIZE(sc5xx_platform_component), + .init = sc5xx_adau1962_init, + .ops = &adau1962_ops, + }, +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1979) + { + .name = "adau1979", + .stream_name = "ADAU1979", + .cpus = sc5xx_cpu_component, + .num_cpus = ARRAY_SIZE(sc5xx_cpu_component), + .codecs = adau1979_codec_component, + .num_codecs = ARRAY_SIZE(adau1979_codec_component), + .platforms = sc5xx_platform_component, + .num_platforms = ARRAY_SIZE(sc5xx_platform_component), + .init = sc5xx_adau1979_init, + .ops = &adau1979_ops, + }, +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1761) + { + .name = "adau1761", + .stream_name = "adau1761", + .cpus = sc5xx_cpu_component, + .num_cpus = ARRAY_SIZE(sc5xx_cpu_component), + .codecs = adau1961_codec_component, + .num_codecs = ARRAY_SIZE(adau1961_codec_component), + .platforms = sc5xx_platform_component, + .num_platforms = ARRAY_SIZE(sc5xx_platform_component), + .ops = &sc5xx_adau1761_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP, + }, +#endif +}; + +/* ADI sc5xx audio machine driver */ +static struct snd_soc_card sc5xx_snd_soc_card = { + .name = "sc5xx-asoc-card", + .owner = THIS_MODULE, + .dai_link = sc5xx_snd_soc_dai_links, + .num_links = ARRAY_SIZE(sc5xx_snd_soc_dai_links), +#ifdef CONFIG_SND_SC5XX_ADAU1761 + .dapm_widgets = sc5xx_adau1761_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc5xx_adau1761_dapm_widgets), + .dapm_routes = sc5xx_adau1761_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sc5xx_adau1761_dapm_routes), + .fully_routed = true, +#endif +}; + +static int sc5xx_snd_soc_probe(struct platform_device *pdev) +{ + int id = 0; + int ret = 0; + + sc5xx_snd_soc_card.dev = &pdev->dev; + + sc5xx_cpu_component->of_node = of_parse_phandle(pdev->dev.of_node, "adi,cpu-dai", 0); + +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1372) + sc5xx_snd_soc_dai_links[id++].codecs[0].of_node = of_parse_phandle(pdev->dev.of_node, + "adi,codec", 0); +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1962) + sc5xx_snd_soc_dai_links[id++].codecs[0].of_node = of_parse_phandle(pdev->dev.of_node, + "adi,codec", 0); +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1979) + sc5xx_snd_soc_dai_links[id++].codecs[0].of_node = of_parse_phandle(pdev->dev.of_node, + "adi,codec", 1); +#endif +#if IS_ENABLED(CONFIG_SND_SC5XX_ADAU1761) + sc5xx_snd_soc_dai_links[id++].codecs[0].of_node = of_parse_phandle(pdev->dev.of_node, + "adi,codec", 0); +#endif + + ret = devm_snd_soc_register_card(&pdev->dev, &sc5xx_snd_soc_card); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id sc5xx_snd_soc_of_match[] = { + { .compatible = "adi,sc5xx-asoc-card" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sc5xx_snd_soc_of_match); +#endif + +static struct platform_driver sc5xx_snd_soc_driver = { + .driver = { + .name = "snd-sc5xx", + .pm = &snd_soc_pm_ops, + .of_match_table = of_match_ptr(sc5xx_snd_soc_of_match), + }, + .probe = sc5xx_snd_soc_probe, +}; +module_platform_driver(sc5xx_snd_soc_driver); + +MODULE_DESCRIPTION("ASoC Machine driver for ADI SC5xx based boards"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/adi/sc5xx-i2s.c b/sound/soc/adi/sc5xx-i2s.c new file mode 100644 index 00000000000000..1d93678ed252a9 --- /dev/null +++ b/sound/soc/adi/sc5xx-i2s.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices digital audio interface driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Scott Jiang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sc5xx-sport.h" + +struct sport_params param; + +static int sc5xx_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = &sport->pdev->dev; + int ret = 0; + + param.spctl &= ~(SPORT_CTL_OPMODE | SPORT_CTL_CKRE | SPORT_CTL_FSR + | SPORT_CTL_LFS | SPORT_CTL_LAFS); + param.spmctl &= ~(SPORT_MCTL_MCE); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_CKRE + | SPORT_CTL_LFS; + break; + case SND_SOC_DAIFMT_DSP_A: + param.spctl |= SPORT_CTL_FSR; + param.spmctl |= SPORT_MCTL_MCE | SPORT_MCTL_MCPDE + | (0x10 & SPORT_MCTL_MFD); + param.spcs0 = 0xff; + break; + case SND_SOC_DAIFMT_LEFT_J: + param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_LFS + | SPORT_CTL_LAFS; + break; + default: + dev_err(dev, "%s: Unknown DAI format type\n", __func__); + ret = -EINVAL; + break; + } + + param.spctl &= ~(SPORT_CTL_ICLK | SPORT_CTL_IFS); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + break; + case SND_SOC_DAIFMT_CBC_CFC: + case SND_SOC_DAIFMT_CBP_CFC: + case SND_SOC_DAIFMT_CBC_CFP: + ret = -ENOTSUPP; + break; + default: + dev_err(dev, "%s: Unknown DAI master type\n", __func__); + ret = -EINVAL; + break; + } + + return ret; +} + +static int sc5xx_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); + struct device *dev = &sport->pdev->dev; + int ret = 0; + + param.spctl &= ~SPORT_CTL_SLEN; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + param.spctl |= 0x70; + sport->wdsize = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + param.spctl |= 0xf0; + sport->wdsize = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + param.spctl |= 0x170; + sport->wdsize = 3; + break; + case SNDRV_PCM_FORMAT_S32_LE: + param.spctl |= 0x1f0; + sport->wdsize = 4; + break; + } + + /* set window size in SPORT_MCTL register */ + param.spmctl &= ~SPORT_MCTL_WSIZE; + if (param.spmctl && SPORT_MCTL_MCE) + param.spmctl |= (((params_channels(params) - 1) << 8) + & SPORT_MCTL_WSIZE); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport->tx_hw_params = *params; + ret = sport_set_tx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT tx is busy!\n"); + return ret; + } + } else { + sport->rx_hw_params = *params; + ret = sport_set_rx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT rx is busy!\n"); + return ret; + } + } + return 0; +} + +#ifdef CONFIG_PM +static int sc5xx_dai_suspend(struct snd_soc_component *component) +{ + struct sport_device *sport = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *dai; + int stream; + + for_each_component_dais(component, dai) { + for_each_pcm_streams(stream) { + if (snd_soc_dai_stream_active(dai, stream)) { + if (stream == SNDRV_PCM_STREAM_CAPTURE) + sport_rx_stop(sport); + else if (stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_tx_stop(sport); + } + } + } + + return 0; +} + +static int sc5xx_dai_resume(struct snd_soc_component *component) +{ + struct sport_device *sport = snd_soc_component_get_drvdata(component); + struct device *dev = &sport->pdev->dev; + int ret; + + ret = sport_set_tx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT tx is busy!\n"); + return ret; + } + ret = sport_set_rx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT rx is busy!\n"); + return ret; + } + + return 0; +} +#else +#define sc5xx_dai_suspend NULL +#define sc5xx_dai_resume NULL +#endif + +#define SC5XX_DAI_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define SC5XX_DAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops sc5xx_i2s_dai_ops = { + .hw_params = sc5xx_dai_hw_params, + .set_fmt = sc5xx_dai_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sc5xx_i2s_dai = { + .name = "sc5xx", + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = SC5XX_DAI_RATES, + .formats = SC5XX_DAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 4, + .rates = SC5XX_DAI_RATES, + .formats = SC5XX_DAI_FORMATS, + }, + .ops = &sc5xx_i2s_dai_ops, +}; + +static const struct snd_soc_component_driver sc5xx_dai_component = { + .name = "sc5xx-i2s", + .suspend = sc5xx_dai_suspend, + .resume = sc5xx_dai_resume, +}; + +#ifdef CONFIG_OF +static const struct of_device_id sc5xx_audio_of_match[] = { + { + .compatible = "adi,sc5xx-i2s-dai", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sc5xx_audio_of_match); +#endif + +static int sc5xx_dai_probe(struct platform_device *pdev) +{ + struct sport_device *sport; + struct device *dev = &pdev->dev; + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, "sclk"); + if (IS_ERR(clk)) { + dev_err(dev, "Missing clock node `sclk` for i2s\n"); + return PTR_ERR(clk); + } + + sport = sport_create(pdev); + if (IS_ERR(sport)) + return PTR_ERR(sport); + + sport->clk = clk; + clk_prepare_enable(clk); + + /* register with the ASoC layers */ + ret = devm_snd_soc_register_component(dev, &sc5xx_dai_component, + &sc5xx_i2s_dai, 1); + if (ret) + goto cleanup; + + platform_set_drvdata(pdev, sport); + return 0; + +cleanup: + sport_delete(sport); + clk_disable_unprepare(clk); + return ret; +} + +static void sc5xx_dai_remove(struct platform_device *pdev) +{ + struct sport_device *sport = platform_get_drvdata(pdev); + + sport_delete(sport); + clk_disable_unprepare(sport->clk); +} + +static struct platform_driver sc5xx_i2s_dai_driver = { + .probe = sc5xx_dai_probe, + .remove = sc5xx_dai_remove, + .driver = { + .name = "sc5xx-i2s-dai", + .of_match_table = of_match_ptr(sc5xx_audio_of_match), + }, +}; + +module_platform_driver(sc5xx_i2s_dai_driver); + +MODULE_DESCRIPTION("Analog Devices SC5XX I2S DAI driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/adi/sc5xx-pcm.c b/sound/soc/adi/sc5xx-pcm.c new file mode 100644 index 00000000000000..a41280462d9159 --- /dev/null +++ b/sound/soc/adi/sc5xx-pcm.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SC5XX audio dma driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Scott Jiang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sc5xx-sport.h" + +static void sc5xx_dma_irq(void *data) +{ + struct snd_pcm_substream *pcm = data; + + snd_pcm_period_elapsed(pcm); +} + +static const struct snd_pcm_hardware sc5xx_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 32, + .period_bytes_max = 0x10000, + .periods_min = 1, + .periods_max = PAGE_SIZE/32, + .buffer_bytes_max = 0x20000, /* 128 kbytes */ + .fifo_size = 16, +}; + +static int sc5xx_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int period_bytes = frames_to_bytes(runtime, runtime->period_size); + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport_set_tx_callback(sport, sc5xx_dma_irq, substream); + ret = sport_config_tx_dma(sport, (void *)runtime->dma_addr, + runtime->periods, period_bytes, substream); + } else { + sport_set_rx_callback(sport, sc5xx_dma_irq, substream); + ret = sport_config_rx_dma(sport, (void *)runtime->dma_addr, + runtime->periods, period_bytes, substream); + } + + return ret; +} + +static int sc5xx_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = sport_tx_start(sport); + else + ret = sport_rx_start(sport); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = sport_tx_stop(sport); + else + ret = sport_rx_stop(sport); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t sc5xx_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + unsigned int diff; + snd_pcm_uframes_t frames; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + diff = sport_curr_offset_tx(sport); + else + diff = sport_curr_offset_rx(sport); + + /* + * TX at least can report one frame beyond the end of the + * buffer if we hit the wraparound case - clamp to within the + * buffer as the ALSA APIs require. + */ + if (diff == snd_pcm_lib_buffer_bytes(substream)) + diff = 0; + + frames = bytes_to_frames(substream->runtime, diff); + + return frames; +} + +static int sc5xx_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *buf = &substream->dma_buffer; + int ret; + + snd_soc_set_runtime_hwparams(substream, &sc5xx_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport->tx_buf = (dma_addr_t)buf->area; + else + sport->rx_buf = (dma_addr_t)buf->area; + + runtime->private_data = sport; + return 0; +} + +static int sc5xx_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + size_t size = sc5xx_pcm_hardware.buffer_bytes_max; + int ret = 0; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + /* Prefers iram pool, if not available it fallbacks to CMA */ + snd_pcm_set_managed_buffer_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV_IRAM, card->dev, size, size); + return 0; +} + +static const struct snd_soc_component_driver sc5xx_pcm_component = { + .open = sc5xx_pcm_open, + .prepare = sc5xx_pcm_prepare, + .trigger = sc5xx_pcm_trigger, + .pointer = sc5xx_pcm_pointer, + .pcm_construct = sc5xx_pcm_new, +}; + +static int sc5xx_soc_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &sc5xx_pcm_component, NULL, 0); +} + +static struct platform_driver sc5xx_pcm_driver = { + .driver = { + .name = "sc5xx-pcm-audio", + }, + .probe = sc5xx_soc_platform_probe, +}; + +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) +static struct rpmsg_device_id rpmsg_icap_sport_id_table[] = { + { .name = "icap-sport0-core1" }, + { .name = "icap-sport1-core1" }, + { .name = "icap-sport2-core1" }, + { .name = "icap-sport3-core1" }, + { .name = "icap-sport4-core1" }, + { .name = "icap-sport5-core1" }, + { .name = "icap-sport6-core1" }, + { .name = "icap-sport7-core1" }, + { .name = "icap-sport0-core2" }, + { .name = "icap-sport1-core2" }, + { .name = "icap-sport2-core2" }, + { .name = "icap-sport3-core2" }, + { .name = "icap-sport4-core2" }, + { .name = "icap-sport5-core2" }, + { .name = "icap-sport6-core2" }, + { .name = "icap-sport7-core2" }, + { }, +}; +static struct rpmsg_driver rpmsg_icap_sport = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_icap_sport_id_table, + .probe = rpmsg_icap_sport_probe, + .callback = rpmsg_icap_sport_cb, + .remove = rpmsg_icap_sport_remove, +}; +#endif + +static struct platform_device *sc5xx_pcm_dev; + +static int sc5xx_pcm_driver_init(void) +{ + int ret; + + ret = platform_driver_register(&sc5xx_pcm_driver); + if (ret < 0) { + pr_err("sc5xx_pcm_driver: failed to register pcm driver\n"); + return ret; + } + + sc5xx_pcm_dev = platform_device_register_simple("sc5xx-pcm-audio", -1, NULL, 0); + if (IS_ERR(sc5xx_pcm_dev)) { + pr_err("sc5xx_pcm_driver: failed to register pcm device\n"); + platform_driver_unregister(&sc5xx_pcm_driver); + return PTR_ERR(sc5xx_pcm_dev); + } + +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) + ret = register_rpmsg_driver(&rpmsg_icap_sport); + if (ret < 0) { + pr_err("sc5xx_pcm_driver: failed to register rpmsg driver\n"); + platform_device_unregister(sc5xx_pcm_dev); + platform_driver_unregister(&sc5xx_pcm_driver); + return ret; + } +#endif + + return 0; +} +module_init(sc5xx_pcm_driver_init); + +static void sc5xx_pcm_driver_exit(void) +{ + platform_device_unregister(sc5xx_pcm_dev); + platform_driver_unregister(&sc5xx_pcm_driver); +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) + unregister_rpmsg_driver(&rpmsg_icap_sport); +#endif +} +module_exit(sc5xx_pcm_driver_exit); + +MODULE_DESCRIPTION("Analog Devices SC5XX audio dma driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/adi/sc5xx-sport-sharc.c b/sound/soc/adi/sc5xx-sport-sharc.c new file mode 100644 index 00000000000000..e54ba6acf7f419 --- /dev/null +++ b/sound/soc/adi/sc5xx-sport-sharc.c @@ -0,0 +1,968 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SC5XX SHARC SPORT driver. + * Data proccessed and feed into SPORT DMA buff by a SHARC core. + * Code based on sc5xx-sport.c + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Scott Jiang + * Author: Piotr Wojtaszczyk + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sc5xx-sport.h" + +#define _DEBUG 0 + +#define SHARC_MSG_TIMEOUT usecs_to_jiffies(1000) + +static struct sport_device *sport_devices[8]; + +void sharc_playback_underrun_uevent(struct sport_device *sport, int core) +{ + char _env[64]; + char *envp[] = {_env, NULL}; + + snprintf(_env, sizeof(_env), "EVENT=SHARC%d_UNDERRUN", core); + kobject_uevent_env(&sport->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +void sharc_record_overrun_uevent(struct sport_device *sport, int core) +{ + char _env[64]; + char *envp[] = {_env, NULL}; + + snprintf(_env, sizeof(_env), "EVENT=SHARC%d_OVERRUN", core); + kobject_uevent_env(&sport->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +void sharc_msg_dropped_uevent(struct sport_device *sport, int core) +{ + char _env[64]; + char *envp[] = {_env, NULL}; + + snprintf(_env, sizeof(_env), "EVENT=SHARC%d_MSG_DROPPED", core); + kobject_uevent_env(&sport->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +static int sport_frag_ready_cb(struct icap_instance *icap, struct icap_buf_frags *frags) +{ + struct sport_device *sport = (struct sport_device *)icap->priv; + unsigned long flags; + + if (frags->buf_id == sport->tx_alsa_icap_buf_id) { + spin_lock_irqsave(&sport->sharc_tx_buf_pos_lock, flags); + sport->sharc_tx_buf_pos += frags->frags * sport->tx_fragsize; + if (sport->sharc_tx_buf_pos >= sport->sharc_tx_dma_buf.bytes) + sport->sharc_tx_buf_pos = sport->sharc_tx_buf_pos - + sport->sharc_tx_dma_buf.bytes; + spin_unlock_irqrestore(&sport->sharc_tx_buf_pos_lock, flags); + sport->tx_callback(sport->tx_data); + } + + if (frags->buf_id == sport->rx_alsa_icap_buf_id) { + spin_lock_irqsave(&sport->sharc_rx_buf_pos_lock, flags); + sport->sharc_rx_buf_pos += frags->frags * sport->rx_fragsize; + if (sport->sharc_rx_buf_pos >= sport->sharc_rx_dma_buf.bytes) + sport->sharc_rx_buf_pos = sport->sharc_rx_buf_pos - + sport->sharc_rx_dma_buf.bytes; + spin_unlock_irqrestore(&sport->sharc_rx_buf_pos_lock, flags); + sport->rx_callback(sport->rx_data); + } + + return 0; +} + +struct icap_application_callbacks sport_icap_callbacks = { + .frag_ready = sport_frag_ready_cb, +}; + +int sport_set_tx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (ioread32(&sport->tx_regs->spctl) & SPORT_CTL_SPENPRI) { + //try to stop tx + dev_warn(&sport->pdev->dev, "tx pcm is running during playback init, stopping ...\n"); + sport_tx_stop(sport); + if (ioread32(&sport->tx_regs->spctl) & SPORT_CTL_SPENPRI) + return -EBUSY; + } + + iowrite32(params->spctl | SPORT_CTL_SPTRAN, &sport->tx_regs->spctl); + iowrite32(params->div, &sport->tx_regs->div); + iowrite32(params->spmctl, &sport->tx_regs->spmctl); + iowrite32(params->spcs0, &sport->tx_regs->spcs0); + return 0; +} +EXPORT_SYMBOL(sport_set_tx_params); + +int sport_set_rx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (ioread32(&sport->rx_regs->spctl) & SPORT_CTL_SPENPRI) + return -EBUSY; + iowrite32(params->spctl & ~SPORT_CTL_SPTRAN, &sport->rx_regs->spctl); + iowrite32(params->div, &sport->rx_regs->div); + iowrite32(params->spmctl, &sport->rx_regs->spmctl); + iowrite32(params->spcs0, &sport->rx_regs->spcs0); + return 0; +} +EXPORT_SYMBOL(sport_set_rx_params); + +void get_sharc_features(struct sport_device *sport, int sharc_core) +{ + struct icap_instance *icap = &sport->icap[sharc_core]; + struct icap_subdevice_features features; + u32 dev_num, i; + s32 ret; + + ret = icap_get_subdevices(icap); + if (ret < 0) + dev_err(&sport->pdev->dev, "Get sharc%d devices error: %d", sharc_core, ret); + return; + + dev_num = (u32)ret; + + for (i = 0; i < dev_num; i++) { + ret = icap_get_subdevice_features(icap, i, &features); + if (ret) { + dev_err(&sport->pdev->dev, "Get sharc%d dev%d features error: %d", + sharc_core, i, ret); + return; + } + if (features.type == ICAP_DEV_PLAYBACK) { + sport->sharc_tx_core = sharc_core; + sport->icap_tx_dev_id = i; + } else if (features.type == ICAP_DEV_RECORD) { + sport->sharc_rx_core = sharc_core; + sport->icap_rx_dev_id = i; + } else { + dev_err(&sport->pdev->dev, "Get sharc%d unknown dev%d type %d", + sharc_core, i, features.type); + return; + } + } +} + +void get_sharc1_feature_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, + struct sport_device, + get_sharc1_feature_work); + const int sharc_core = 0; + + get_sharc_features(sport, sharc_core); +} + +void get_sharc2_feature_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, + struct sport_device, + get_sharc2_feature_work); + const int sharc_core = 1; + + get_sharc_features(sport, sharc_core); +} + +void sport_tx_start_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, + struct sport_device, + send_tx_start_work); + unsigned long flags; + u32 sharc_core, dev_id; + s32 ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_tx_core; + dev_id = sport->icap_tx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core != -1) && (dev_id != -1)) { + ret = icap_start(&sport->icap[sharc_core], dev_id); + if (ret) + dev_err(&sport->pdev->dev, "tx_start error: %d", ret); + } +} + +int sport_tx_start(struct sport_device *sport) +{ + s32 ret; + + ret = queue_work(system_highpri_wq, &sport->send_tx_start_work); + if (ret == 0) + return -EIO; + + //enable DMA, after SHARC ACKs START + sport->tx_cookie = dmaengine_submit(sport->tx_desc); + dma_async_issue_pending(sport->tx_dma_chan); + iowrite32(ioread32(&sport->tx_regs->spctl) | SPORT_CTL_SPENPRI, &sport->tx_regs->spctl); + + return 0; +} +EXPORT_SYMBOL(sport_tx_start); + +void sport_rx_start_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, struct sport_device, send_rx_start_work); + unsigned long flags; + u32 sharc_core, dev_id; + s32 ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_rx_core; + dev_id = sport->icap_rx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core != -1) && (dev_id != -1)) { + ret = icap_start(&sport->icap[sharc_core], dev_id); + if (ret) + dev_err(&sport->pdev->dev, "rx_start error: %d", ret); + } +} + +int sport_rx_start(struct sport_device *sport) +{ + int ret; + + sport->rx_cookie = dmaengine_submit(sport->rx_desc); + dma_async_issue_pending(sport->rx_dma_chan); + iowrite32(ioread32(&sport->rx_regs->spctl) | SPORT_CTL_SPENPRI, + &sport->rx_regs->spctl); + + ret = queue_work(system_highpri_wq, &sport->send_rx_start_work); + return !ret; +} +EXPORT_SYMBOL(sport_rx_start); + +void sport_tx_stop_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, struct sport_device, send_tx_stop_work); + unsigned long flags; + u32 sharc_core, dev_id; + s32 ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_tx_core; + dev_id = sport->icap_tx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core != -1) && (dev_id != -1)) { + ret = icap_stop(&sport->icap[sharc_core], dev_id); + if (ret) + dev_err(&sport->pdev->dev, "tx_stop error: %d", ret); + } + sport->pending_tx_stop = 0; + wake_up_interruptible_all(&sport->pending_tx_stop_event); +} + +int sport_tx_stop(struct sport_device *sport) +{ + int ret; + + iowrite32(ioread32(&sport->tx_regs->spctl) & ~SPORT_CTL_SPENPRI, + &sport->tx_regs->spctl); + dmaengine_terminate_sync(sport->tx_dma_chan); + + /* + * Can't send icap message and wait for response here as we can be in interrupt context. + * FRAG_READY callbacks are processed in the rpmsg interrupt context. + * The FRAG_READY cb calls snd_pcm_period_elapsed() which can call stop trigger on xrun + * event. Waiting here blocks entire rpmsg communication on the rpdev. + */ + sport->pending_tx_stop = 1; + ret = queue_work(system_highpri_wq, &sport->send_tx_stop_work); + return !ret; +} +EXPORT_SYMBOL(sport_tx_stop); + +void sport_rx_stop_work_func(struct work_struct *work) +{ + struct sport_device *sport = container_of(work, struct sport_device, send_rx_stop_work); + unsigned long flags; + u32 sharc_core, dev_id; + s32 ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_rx_core; + dev_id = sport->icap_rx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core != -1) && (dev_id != -1)) { + ret = icap_stop(&sport->icap[sharc_core], dev_id); + if (ret) + dev_err(&sport->pdev->dev, "rx_stop error: %d", ret); + } + sport->pending_rx_stop = 0; + wake_up_interruptible_all(&sport->pending_rx_stop_event); +} + +int sport_rx_stop(struct sport_device *sport) +{ + int ret; + + iowrite32(ioread32(&sport->rx_regs->spctl) & ~SPORT_CTL_SPENPRI, + &sport->rx_regs->spctl); + dmaengine_terminate_sync(sport->rx_dma_chan); + + /* + * Can't send icap message and wait for response here as we can be in interrupt context. + * FRAG_READY callbacks are processed in the rpmsg interrupt context. + * The FRAG_READY cb calls snd_pcm_period_elapsed() which can call stop trigger on xrun + * event. Waiting here blocks entire rpmsg communication on the rpdev. + */ + sport->pending_rx_stop = 1; + ret = queue_work(system_highpri_wq, &sport->send_rx_stop_work); + return !ret; +} +EXPORT_SYMBOL(sport_rx_stop); + +void sport_set_tx_callback(struct sport_device *sport, + void (*tx_callback)(void *), void *tx_data) +{ + sport->tx_callback = tx_callback; + sport->tx_data = tx_data; +} +EXPORT_SYMBOL(sport_set_tx_callback); + +void sport_set_rx_callback(struct sport_device *sport, + void (*rx_callback)(void *), void *rx_data) +{ + sport->rx_callback = rx_callback; + sport->rx_data = rx_data; +} +EXPORT_SYMBOL(sport_set_rx_callback); + +int sport_config_tx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream) +{ + struct icap_buf_descriptor audio_buf; + struct icap_subdevice_params params; + unsigned long flags; + u32 sharc_core, dev_id; + struct dma_slave_config dma_config = {0}; + int ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_tx_core; + dev_id = sport->icap_tx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core == -1) || (dev_id == -1)) + return -ENODEV; + + ret = wait_event_interruptible(sport->pending_tx_stop_event, !sport->pending_tx_stop); + if (ret) + return ret; + + /* Allocate buffer for SHARC output - DMA, prefers + * iram pool, if not available it fallbacks to CMA + */ + if (sport->sharc_tx_dma_buf.dev.type) + snd_dma_free_pages(&sport->sharc_tx_dma_buf); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM, + &sport->pdev->dev, fragsize * fragcount, + &sport->sharc_tx_dma_buf); + if (ret) + return ret; + + sport->tx_buf = sport->sharc_tx_dma_buf.addr; + sport->tx_fragsize = fragsize; + sport->tx_frags = fragcount; + sport->sharc_tx_buf_pos = 0; + + if (sport->tx_desc) + dmaengine_terminate_sync(sport->tx_dma_chan); + + dma_config.direction = DMA_MEM_TO_DEV; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = sport->wdsize; + dma_config.dst_maxburst = sport->wdsize; + ret = dmaengine_slave_config(sport->tx_dma_chan, &dma_config); + if (ret) { + dev_err(&sport->pdev->dev, "tx dma slave config failed: %d\n", ret); + return ret; + } + + sport->tx_desc = dmaengine_prep_dma_cyclic(sport->tx_dma_chan, + sport->sharc_tx_dma_buf.addr, fragsize * fragcount, fragsize, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + + sport->tx_substream = substream; + + memset(¶ms, 0, sizeof(params)); + + /* Rest of the params don't care as + * sharc doesn't set the hardware params + */ + params.subdev_id = dev_id; + + ret = icap_subdevice_init(&sport->icap[sharc_core], ¶ms); + if (ret == -ERESTARTSYS || ret == -ETIMEDOUT) + return ret; + + if (sport->tx_dma_icap_buf_id != -1) { + ret = icap_remove_dst(&sport->icap[sharc_core], sport->tx_dma_icap_buf_id); + if (ret) + dev_err(&sport->pdev->dev, "tx_stop dst remove error: %d", ret); + sport->tx_dma_icap_buf_id = -1; + } + + if (sport->tx_alsa_icap_buf_id != -1) { + ret = icap_remove_src(&sport->icap[sharc_core], sport->tx_alsa_icap_buf_id); + if (ret) + dev_err(&sport->pdev->dev, "tx_stop src remove error: %d", ret); + sport->tx_alsa_icap_buf_id = -1; + } + + // Set ALSA buffer size and pointer + snprintf(audio_buf.name, ICAP_BUF_NAME_LEN, "%s-alsa-playback", sport->pdev->name); + audio_buf.subdev_id = dev_id; + audio_buf.buf = (u64)buf; + audio_buf.buf_size = fragsize * fragcount; + audio_buf.type = ICAP_BUF_CIRCURAL; + audio_buf.gap_size = 0; // continuous circural + audio_buf.frag_size = fragsize; + // Set audio data format + audio_buf.channels = params_channels(&sport->tx_hw_params); + audio_buf.format = params_format(&sport->tx_hw_params); + audio_buf.rate = params_rate(&sport->tx_hw_params); + audio_buf.report_frags = 1; + +#if _DEBUG + dev_info(&sport->pdev->dev, + "ALSA playback buf PA:0x%p size:%d fragsize:%d\n", + (void *)audio_buf.buf, audio_buf.buf_size, audio_buf.frag_size); +#endif + ret = icap_add_src(&sport->icap[sharc_core], &audio_buf); + if (ret < 0) + return ret; + + sport->tx_alsa_icap_buf_id = (u32)ret; + + // Set DMA buffer size and pointer + snprintf(audio_buf.name, ICAP_BUF_NAME_LEN, "%s-dma-playback", sport->pdev->name); + audio_buf.subdev_id = dev_id; + audio_buf.buf = (u64)sport->sharc_tx_dma_buf.addr; + audio_buf.buf_size = fragsize * fragcount; + audio_buf.type = ICAP_BUF_CIRCURAL; + audio_buf.gap_size = 0; // continuous circural + audio_buf.frag_size = fragsize; + // Set audio data format - same as ALSA buffer + audio_buf.channels = params_channels(&sport->tx_hw_params); + audio_buf.format = params_format(&sport->tx_hw_params); + audio_buf.rate = params_rate(&sport->tx_hw_params); + audio_buf.report_frags = 0; + +#if _DEBUG + dev_info(&sport->pdev->dev, + "SHARC playback buf PA:0x%p size:%d fragsize:%d\n", + (void *)audio_buf.buf, audio_buf.buf_size, audio_buf.frag_size); +#endif + ret = icap_add_dst(&sport->icap[sharc_core], &audio_buf); + if (ret < 0) { + icap_remove_src(&sport->icap[sharc_core], sport->tx_alsa_icap_buf_id); + return ret; + } + sport->tx_dma_icap_buf_id = (u32)ret; + + return 0; +} +EXPORT_SYMBOL(sport_config_tx_dma); + +int sport_config_rx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream) +{ + struct icap_buf_descriptor audio_buf; + struct icap_subdevice_params params; + unsigned long flags; + u32 sharc_core, dev_id; + struct dma_slave_config dma_config = {0}; + int ret; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + sharc_core = sport->sharc_rx_core; + dev_id = sport->icap_rx_dev_id; + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if ((sharc_core == -1) || (dev_id == -1)) + return -ENODEV; + + ret = wait_event_interruptible(sport->pending_rx_stop_event, !sport->pending_rx_stop); + if (ret) + return ret; + + /* Allocate buffer for SHARC input - DMA, prefers + * iram pool, if not available it fallbacks to CMA + */ + if (sport->sharc_rx_dma_buf.dev.type) + snd_dma_free_pages(&sport->sharc_rx_dma_buf); + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM, + &sport->pdev->dev, fragsize * fragcount, + &sport->sharc_rx_dma_buf); + if (ret) + return ret; + + sport->rx_buf = sport->sharc_rx_dma_buf.addr; + sport->rx_fragsize = fragsize; + sport->rx_frags = fragcount; + sport->sharc_rx_buf_pos = 0; + + if (sport->rx_desc) + dmaengine_terminate_sync(sport->rx_dma_chan); + + dma_config.direction = DMA_DEV_TO_MEM; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = sport->wdsize; + dma_config.dst_maxburst = sport->wdsize; + ret = dmaengine_slave_config(sport->rx_dma_chan, &dma_config); + if (ret) { + dev_err(&sport->pdev->dev, "rx dma slave config failed: %d\n", ret); + return ret; + } + + sport->rx_desc = dmaengine_prep_dma_cyclic(sport->rx_dma_chan, + sport->sharc_rx_dma_buf.addr, fragsize * fragcount, fragsize, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + + sport->rx_substream = substream; + + memset(¶ms, 0, sizeof(params)); + + /* Rest of the params don't care as sharc + * doesn't set the hardware params + */ + params.subdev_id = dev_id; + + ret = icap_subdevice_init(&sport->icap[sharc_core], ¶ms); + if (ret == -ERESTARTSYS || ret == -ETIMEDOUT) + return ret; + + if (sport->rx_alsa_icap_buf_id != -1) { + ret = icap_remove_dst(&sport->icap[sharc_core], sport->rx_alsa_icap_buf_id); + if (ret) + dev_err(&sport->pdev->dev, "rx_stop dst remove error: %d", ret); + sport->rx_alsa_icap_buf_id = -1; + } + + if (sport->rx_dma_icap_buf_id != -1) { + ret = icap_remove_src(&sport->icap[sharc_core], sport->rx_dma_icap_buf_id); + if (ret) + dev_err(&sport->pdev->dev, "rx_stop src remove error: %d", ret); + sport->rx_dma_icap_buf_id = -1; + } + + // Set ALSA buffer size and pointer + snprintf(audio_buf.name, ICAP_BUF_NAME_LEN, "%s-alsa-record", sport->pdev->name); + audio_buf.subdev_id = dev_id; + audio_buf.buf = (u64)buf; + audio_buf.buf_size = fragsize * fragcount; + audio_buf.type = ICAP_BUF_CIRCURAL; + audio_buf.gap_size = 0; // continuous circural + audio_buf.frag_size = fragsize; + // Set audio data format + audio_buf.channels = params_channels(&sport->rx_hw_params); + audio_buf.format = params_format(&sport->rx_hw_params); + audio_buf.rate = params_rate(&sport->rx_hw_params); + audio_buf.report_frags = 1; + +#if _DEBUG + dev_info(&sport->pdev->dev, + "ALSA record buf PA:0x%p size:%d fragsize:%d\n", + (void *)audio_buf.buf, audio_buf.buf_size, audio_buf.frag_size); +#endif + ret = icap_add_dst(&sport->icap[sharc_core], &audio_buf); + if (ret < 0) + return ret; + + sport->rx_alsa_icap_buf_id = (u32)ret; + + // Set DMA buffer size and pointer + snprintf(audio_buf.name, ICAP_BUF_NAME_LEN, "%s-dma-record", sport->pdev->name); + audio_buf.subdev_id = dev_id; + audio_buf.buf = (u64)sport->sharc_rx_dma_buf.addr; + audio_buf.buf_size = fragsize * fragcount; + audio_buf.type = ICAP_BUF_CIRCURAL; + audio_buf.gap_size = 0; // continuous circural + audio_buf.frag_size = fragsize; + // Set audio data format - same as ALSA buffer + audio_buf.channels = params_channels(&sport->rx_hw_params); + audio_buf.format = params_format(&sport->rx_hw_params); + audio_buf.rate = params_rate(&sport->rx_hw_params); + audio_buf.report_frags = 0; + +#if _DEBUG + dev_info(&sport->pdev->dev, + "SHARC record buf PA:0x%p size:%d fragsize:%d\n", + (void *)audio_buf.buf, audio_buf.buf_size, audio_buf.frag_size); +#endif + ret = icap_add_src(&sport->icap[sharc_core], &audio_buf); + if (ret < 0) { + icap_remove_dst(&sport->icap[sharc_core], sport->rx_alsa_icap_buf_id); + return ret; + } + sport->rx_dma_icap_buf_id = (u32)ret; + + return 0; +} +EXPORT_SYMBOL(sport_config_rx_dma); + +unsigned long sport_curr_offset_tx(struct sport_device *sport) +{ + unsigned long off; + unsigned long flags; + + spin_lock_irqsave(&sport->sharc_tx_buf_pos_lock, flags); + off = sport->sharc_tx_buf_pos; + spin_unlock_irqrestore(&sport->sharc_tx_buf_pos_lock, flags); + + return off; +} +EXPORT_SYMBOL(sport_curr_offset_tx); + +unsigned long sport_curr_offset_rx(struct sport_device *sport) +{ + unsigned long off; + unsigned long flags; + + spin_lock_irqsave(&sport->sharc_rx_buf_pos_lock, flags); + off = sport->sharc_rx_buf_pos; + spin_unlock_irqrestore(&sport->sharc_rx_buf_pos_lock, flags); + + return off; +} +EXPORT_SYMBOL(sport_curr_offset_rx); + +static int sport_get_resource(struct sport_device *sport) +{ + struct platform_device *pdev = sport->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + if (!dev->of_node) { + dev_err(dev, "No device tree node\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "No tx MEM resource\n"); + return -ENODEV; + } + sport->tx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sport->tx_regs)) { + dev_err(dev, "Failed to map tx registers\n"); + return PTR_ERR(sport->tx_regs); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "No rx MEM resource\n"); + return -ENODEV; + } + sport->rx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sport->rx_regs)) { + dev_err(dev, "Failed to map rx registers\n"); + return PTR_ERR(sport->rx_regs); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "No tx error irq resource\n"); + return -ENODEV; + } + sport->tx_err_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res) { + dev_err(dev, "No rx error irq resource\n"); + return -ENODEV; + } + sport->rx_err_irq = res->start; + + return 0; +} + +static int sport_request_resource(struct sport_device *sport) +{ + struct platform_device *pdev = sport->pdev; + struct device *dev = &pdev->dev; + int ret; + + sport->tx_dma_chan = dma_request_chan(dev, "tx"); + if (IS_ERR(sport->tx_dma_chan)) { + ret = PTR_ERR(sport->tx_dma_chan); + dev_err(dev, "Missing `tx` dma channel: %d\n", ret); + return ret; + } + + sport->rx_dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(sport->rx_dma_chan)) { + ret = PTR_ERR(sport->rx_dma_chan); + dev_err(dev, "Missing `rx` dma channel: %d\n", ret); + goto err_rx_dma; + } + + /* NOTE: tx_irq, rx_irq and err_irqs handled by SHARC core*/ + + return 0; + +err_rx_dma: + dma_release_channel(sport->tx_dma_chan); + return ret; +} + +static void sport_free_resource(struct sport_device *sport) +{ + dma_release_channel(sport->tx_dma_chan); + dma_release_channel(sport->rx_dma_chan); +} + +int rpmsg_icap_sport_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct sport_device **sport_p = (struct sport_device **)dev_get_drvdata(&rpdev->dev); + struct sport_device *sport = *sport_p; + union icap_remote_addr src_addr; + int sharc_core; + int ret; + + if (sport == NULL) + return -ENODEV; + + /* Find corresponding core */ + for (sharc_core = 0; sharc_core < SHARC_CORES_NUM; sharc_core++) { + if (sport->icap[sharc_core].transport.rpdev == rpdev) + break; + } + + if (sharc_core >= SHARC_CORES_NUM) + return -ENODEV; + + src_addr.rpmsg_addr = src; + ret = icap_parse_msg(&sport->icap[sharc_core], &src_addr, data, len); + if (ret && (ret != -ICAP_ERROR_INIT)) { + if (ret == -ICAP_ERROR_TIMEOUT) + dev_notice_ratelimited(&rpdev->dev, "ICAP late response\n"); + else + dev_err_ratelimited(&rpdev->dev, "ICAP parse msg error: %d\n", ret); + } + return 0; +} +EXPORT_SYMBOL(rpmsg_icap_sport_cb); + +int rpmsg_icap_sport_probe(struct rpmsg_device *rpdev) +{ + struct sport_device **sport_p; + struct sport_device *sport; + int sharc_core; + int ret; + + if (!strncmp(rpdev->id.name, "icap-sport4-core1", RPMSG_NAME_SIZE)) { + sport_p = &sport_devices[4]; + sharc_core = 0; + } else if (!strncmp(rpdev->id.name, "icap-sport4-core2", RPMSG_NAME_SIZE)) { + sport_p = &sport_devices[4]; + sharc_core = 1; + } else { + /* Currently supports only sport4 on sharc core 1 or 2 */ + return -ENODEV; + } + + sport = *sport_p; + + if (sport == NULL) + return -ENODEV; + + dev_set_drvdata(&rpdev->dev, sport_p); + + sport->icap[sharc_core].transport.rpdev = rpdev; + ret = icap_application_init(&sport->icap[sharc_core], + "sc5xx-sport", + &sport_icap_callbacks, + (void *)sport); + + if (ret) + goto error_out; + + if (sharc_core == 0) + ret = queue_work(system_highpri_wq, &sport->get_sharc1_feature_work); + else + ret = queue_work(system_highpri_wq, &sport->get_sharc2_feature_work); + + if (ret == 0) { + icap_application_deinit(&sport->icap[sharc_core]); + ret = -EIO; + goto error_out; + } + + dev_info(&sport->pdev->dev, "sharc-alsa client dev attached, addr: 0x%03x\n", rpdev->dst); + return 0; + +error_out: + dev_err(&sport->pdev->dev, "sharc-alsa client dev err, addr: 0x%03x, %d\n", + rpdev->dst, ret); + return ret; +} +EXPORT_SYMBOL(rpmsg_icap_sport_probe); + +void rpmsg_icap_sport_remove(struct rpmsg_device *rpdev) +{ + struct sport_device **sport_p = (struct sport_device **)dev_get_drvdata(&rpdev->dev); + struct sport_device *sport = *sport_p; + int sharc_core; + unsigned long flags; + + if (sport == NULL) + return; + + /* Find corresponding core */ + for (sharc_core = 0; sharc_core < SHARC_CORES_NUM; sharc_core++) { + if (sport->icap[sharc_core].transport.rpdev == rpdev) + break; + } + + if (sharc_core >= SHARC_CORES_NUM) + return; + + spin_lock_irqsave(&sport->icap_spinlock, flags); + + icap_application_deinit(&sport->icap[sharc_core]); + + if (sharc_core == sport->sharc_tx_core) { + sport->sharc_tx_core = -1; + sport->icap_tx_dev_id = -1; + } + + if (sharc_core == sport->sharc_rx_core) { + sport->sharc_rx_core = -1; + sport->icap_rx_dev_id = -1; + } + + spin_unlock_irqrestore(&sport->icap_spinlock, flags); + + if (sport->tx_substream && + sport->tx_substream->runtime && + snd_pcm_running(sport->tx_substream)) { + snd_pcm_stream_lock_irq(sport->tx_substream); + snd_pcm_stop(sport->tx_substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock_irq(sport->tx_substream); + } + + if (sport->rx_substream && + sport->rx_substream->runtime && + snd_pcm_running(sport->rx_substream)) { + snd_pcm_stream_lock_irq(sport->rx_substream); + snd_pcm_stop(sport->rx_substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock_irq(sport->rx_substream); + } + + dev_info(&rpdev->dev, "sharc-alsa client device is removed, addr: 0x%03x\n", rpdev->dst); +} +EXPORT_SYMBOL(rpmsg_icap_sport_remove); + +struct sport_device *sport_create(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sport_device *sport; + int ret; + + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) + return ERR_PTR(-ENOMEM); + + sport->pdev = pdev; + + ret = sport_get_resource(sport); + if (ret) + goto err_free_data; + + ret = sport_request_resource(sport); + if (ret) + goto err_free_data; + + spin_lock_init(&sport->icap_spinlock); + spin_lock_init(&sport->sharc_tx_buf_pos_lock); + spin_lock_init(&sport->sharc_rx_buf_pos_lock); + INIT_WORK(&sport->send_tx_start_work, sport_tx_start_work_func); + INIT_WORK(&sport->send_rx_start_work, sport_rx_start_work_func); + INIT_WORK(&sport->send_tx_stop_work, sport_tx_stop_work_func); + INIT_WORK(&sport->send_rx_stop_work, sport_rx_stop_work_func); + INIT_WORK(&sport->get_sharc1_feature_work, get_sharc1_feature_work_func); + INIT_WORK(&sport->get_sharc2_feature_work, get_sharc2_feature_work_func); + + init_waitqueue_head(&sport->pending_tx_stop_event); + init_waitqueue_head(&sport->pending_rx_stop_event); + + sport->tx_alsa_icap_buf_id = -1; + sport->tx_dma_icap_buf_id = -1; + sport->rx_alsa_icap_buf_id = -1; + sport->rx_dma_icap_buf_id = -1; + + sport->sharc_tx_core = -1; + sport->sharc_rx_core = -1; + + sport->icap_tx_dev_id = -1; + sport->icap_rx_dev_id = -1; + + sport_devices[4] = sport; + + dev_info(dev, "SPORT create success, SHARC-ALSA (PCM steram send to sharc for processing)\n"); + return sport; + +err_free_data: + kfree(sport); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(sport_create); + +void sport_delete(struct sport_device *sport) +{ + + cancel_work_sync(&sport->get_sharc1_feature_work); + cancel_work_sync(&sport->get_sharc2_feature_work); + cancel_work_sync(&sport->send_tx_start_work); + cancel_work_sync(&sport->send_rx_start_work); + cancel_work_sync(&sport->send_tx_stop_work); + cancel_work_sync(&sport->send_rx_stop_work); + + sport_devices[4] = NULL; + + snd_dma_free_pages(&sport->sharc_tx_dma_buf); + snd_dma_free_pages(&sport->sharc_rx_dma_buf); + + dmaengine_terminate_sync(sport->tx_dma_chan); + dmaengine_terminate_sync(sport->rx_dma_chan); + sport_free_resource(sport); + kfree(sport); +} +EXPORT_SYMBOL(sport_delete); + +MODULE_DESCRIPTION("Analog Devices SC5XX SPORT driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_AUTHOR("Piotr Wojtaszczyk "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/adi/sc5xx-sport.c b/sound/soc/adi/sc5xx-sport.c new file mode 100644 index 00000000000000..e843b9387cc449 --- /dev/null +++ b/sound/soc/adi/sc5xx-sport.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SC5XX SPORT driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Scott Jiang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sc5xx-sport.h" + +int sport_set_tx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (ioread32(&sport->tx_regs->spctl) & SPORT_CTL_SPENPRI) + return -EBUSY; + iowrite32(params->spctl | SPORT_CTL_SPTRAN, &sport->tx_regs->spctl); + iowrite32(params->div, &sport->tx_regs->div); + iowrite32(params->spmctl, &sport->tx_regs->spmctl); + iowrite32(params->spcs0, &sport->tx_regs->spcs0); + return 0; +} +EXPORT_SYMBOL(sport_set_tx_params); + +int sport_set_rx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (ioread32(&sport->rx_regs->spctl) & SPORT_CTL_SPENPRI) + return -EBUSY; + iowrite32(params->spctl & ~SPORT_CTL_SPTRAN, &sport->rx_regs->spctl); + iowrite32(params->div, &sport->rx_regs->div); + iowrite32(params->spmctl, &sport->rx_regs->spmctl); + iowrite32(params->spcs0, &sport->rx_regs->spcs0); + return 0; +} +EXPORT_SYMBOL(sport_set_rx_params); + +int sport_tx_start(struct sport_device *sport) +{ + sport->tx_cookie = dmaengine_submit(sport->tx_desc); + dma_async_issue_pending(sport->tx_dma_chan); + iowrite32(ioread32(&sport->tx_regs->spctl) | SPORT_CTL_SPENPRI, + &sport->tx_regs->spctl); + return 0; +} +EXPORT_SYMBOL(sport_tx_start); + +int sport_rx_start(struct sport_device *sport) +{ + sport->rx_cookie = dmaengine_submit(sport->rx_desc); + dma_async_issue_pending(sport->rx_dma_chan); + iowrite32(ioread32(&sport->rx_regs->spctl) | SPORT_CTL_SPENPRI, + &sport->rx_regs->spctl); + return 0; +} +EXPORT_SYMBOL(sport_rx_start); + +int sport_tx_stop(struct sport_device *sport) +{ + iowrite32(ioread32(&sport->tx_regs->spctl) & ~SPORT_CTL_SPENPRI, + &sport->tx_regs->spctl); + dmaengine_terminate_sync(sport->tx_dma_chan); + sport->tx_cookie = 0; + sport->tx_desc = NULL; + return 0; +} +EXPORT_SYMBOL(sport_tx_stop); + +int sport_rx_stop(struct sport_device *sport) +{ + iowrite32(ioread32(&sport->rx_regs->spctl) & ~SPORT_CTL_SPENPRI, + &sport->rx_regs->spctl); + dmaengine_terminate_sync(sport->rx_dma_chan); + sport->rx_cookie = 0; + sport->rx_desc = NULL; + return 0; +} +EXPORT_SYMBOL(sport_rx_stop); + +void sport_set_tx_callback(struct sport_device *sport, + void (*tx_callback)(void *), void *tx_data) +{ + sport->tx_callback = tx_callback; + sport->tx_data = tx_data; +} +EXPORT_SYMBOL(sport_set_tx_callback); + +void sport_set_rx_callback(struct sport_device *sport, + void (*rx_callback)(void *), void *rx_data) +{ + sport->rx_callback = rx_callback; + sport->rx_data = rx_data; +} +EXPORT_SYMBOL(sport_set_rx_callback); + +void sport_tx_dma_callback(void *ptr) +{ + struct sport_device *sport = ptr; + + sport->tx_count += 1; + if (sport->tx_count >= sport->tx_frags) + sport->tx_count = 0; + sport->tx_callback(sport->tx_data); +} + +void sport_rx_dma_callback(void *ptr) +{ + struct sport_device *sport = ptr; + + sport->rx_count += 1; + if (sport->rx_count >= sport->rx_frags) + sport->rx_count = 0; + sport->rx_callback(sport->rx_data); +} + +int sport_config_tx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream) +{ + struct dma_slave_config dma_config = {0}; + size_t total = fragsize * fragcount; + int ret; + + if (sport->tx_desc) + dmaengine_terminate_sync(sport->tx_dma_chan); + + dma_config.direction = DMA_MEM_TO_DEV; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = sport->wdsize; + dma_config.dst_maxburst = sport->wdsize; + ret = dmaengine_slave_config(sport->tx_dma_chan, &dma_config); + if (ret) { + dev_err(&sport->pdev->dev, "tx dma slave config failed: %d\n", ret); + return ret; + } + + sport->tx_desc = dmaengine_prep_dma_cyclic(sport->tx_dma_chan, + (dma_addr_t) buf, total, fragsize, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + + sport->tx_desc->callback = sport_tx_dma_callback; + sport->tx_desc->callback_param = sport; + + sport->tx_buf = (dma_addr_t)buf; + sport->tx_fragsize = fragsize; + sport->tx_frags = fragcount; + sport->tx_totalsize = total; + sport->tx_count = 0; + + sport->tx_substream = substream; + + return 0; +} +EXPORT_SYMBOL(sport_config_tx_dma); + +int sport_config_rx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream) +{ + struct dma_slave_config dma_config = {0}; + size_t total = fragcount * fragsize; + int ret; + + if (sport->rx_desc) + dmaengine_terminate_sync(sport->rx_dma_chan); + + dma_config.direction = DMA_DEV_TO_MEM; + dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_config.src_maxburst = sport->wdsize; + dma_config.dst_maxburst = sport->wdsize; + ret = dmaengine_slave_config(sport->rx_dma_chan, &dma_config); + if (ret) { + dev_err(&sport->pdev->dev, "rx dma slave config failed: %d\n", ret); + return ret; + } + + sport->rx_desc = dmaengine_prep_dma_cyclic(sport->rx_dma_chan, + (dma_addr_t) buf, total, fragsize, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + + sport->rx_desc->callback = sport_rx_dma_callback; + sport->rx_desc->callback_param = sport; + + sport->rx_buf = (dma_addr_t)buf; + sport->rx_fragsize = fragsize; + sport->rx_frags = fragcount; + sport->rx_totalsize = total; + sport->rx_count = 0; + + sport->rx_substream = substream; + + return 0; +} +EXPORT_SYMBOL(sport_config_rx_dma); + +unsigned long sport_curr_offset_tx(struct sport_device *sport) +{ + return sport->tx_count * sport->tx_fragsize; +} +EXPORT_SYMBOL(sport_curr_offset_tx); + +unsigned long sport_curr_offset_rx(struct sport_device *sport) +{ + return sport->rx_count * sport->rx_fragsize; +} +EXPORT_SYMBOL(sport_curr_offset_rx); + +static int sport_get_resource(struct sport_device *sport) +{ + struct platform_device *pdev = sport->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + + if (!dev->of_node) { + dev_err(dev, "No device tree node\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "No tx MEM resource\n"); + return -ENODEV; + } + sport->tx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sport->tx_regs)) { + dev_err(dev, "Failed to map tx registers\n"); + return PTR_ERR(sport->tx_regs); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "No rx MEM resource\n"); + return -ENODEV; + } + sport->rx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sport->rx_regs)) { + dev_err(dev, "Failed to map rx registers\n"); + return PTR_ERR(sport->rx_regs); + } + + return 0; +} + +static int sport_request_resource(struct sport_device *sport) +{ + struct platform_device *pdev = sport->pdev; + struct device *dev = &pdev->dev; + int ret; + + sport->tx_dma_chan = dma_request_chan(dev, "tx"); + if (IS_ERR(sport->tx_dma_chan)) { + dev_err(dev, "Missing `tx` dma channel: %ld\n", PTR_ERR(sport->tx_dma_chan)); + return PTR_ERR(sport->tx_dma_chan); + } + + sport->rx_dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(sport->rx_dma_chan)) { + dev_err(dev, "Missing `rx` dma channel: %ld\n", PTR_ERR(sport->rx_dma_chan)); + ret = PTR_ERR(sport->rx_dma_chan); + goto err_rx_dma; + } + + return 0; + +err_rx_dma: + dma_release_channel(sport->tx_dma_chan); + return ret; +} + +static void sport_free_resource(struct sport_device *sport) +{ + dma_release_channel(sport->tx_dma_chan); + dma_release_channel(sport->rx_dma_chan); +} +struct sport_device *sport_create(struct platform_device *pdev) +{ + struct sport_device *sport; + int ret; + + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) + return ERR_PTR(-ENOMEM); + + sport->pdev = pdev; + + ret = sport_get_resource(sport); + if (ret) + goto err_free_data; + + ret = sport_request_resource(sport); + if (ret) + goto err_free_data; + + return sport; + +err_free_data: + kfree(sport); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(sport_create); + +void sport_delete(struct sport_device *sport) +{ + dmaengine_terminate_sync(sport->tx_dma_chan); + dmaengine_terminate_sync(sport->rx_dma_chan); + sport_free_resource(sport); + kfree(sport); +} +EXPORT_SYMBOL(sport_delete); + +MODULE_DESCRIPTION("Analog Devices SC5XX SPORT driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/adi/sc5xx-sport.h b/sound/soc/adi/sc5xx-sport.h new file mode 100644 index 00000000000000..56ae78482eb6bf --- /dev/null +++ b/sound/soc/adi/sc5xx-sport.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Analog Devices SC5XX SPORT driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef _SC5XX_SPORT_H_ +#define _SC5XX_SPORT_H_ + +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) +#include +#include +#include "icap/include/icap_application.h" +#endif + +#define TDM_MAX_SLOTS 8 +#define SHARC_CORES_NUM 2 + +#define SPORT_CTL_SPENPRI 0x00000001 /* Enable Primary Channel */ + +#define SPORT_CTL_DTYPE 0x00000006 /* Data type select */ +#define SPORT_CTL_RJUSTIFY_ZFILL 0x00000000 /* MCM mode: Right-just, zero-fill unused MSBs */ +#define SPORT_CTL_RJUSTIFY_SFILL 0x00000002 /* MCM mode: Right-just, sign-extend unused MSBs */ +#define SPORT_CTL_USE_U_LAW 0x00000004 /* MCM mode: Compand using u-law */ +#define SPORT_CTL_USE_A_LAW 0x00000006 /* MCM mode: Compand using A-law */ + +#define SPORT_CTL_LSBF 0x00000008 /* Serial bit endian select */ +#define SPORT_CTL_SLEN 0x000001F0 /* Serial Word length select */ +#define SPORT_CTL_PACK 0x00000200 /* 16-bit to 32-bit packing enable */ +#define SPORT_CTL_ICLK 0x00000400 /* Internal Clock Select */ +#define SPORT_CTL_OPMODE 0x00000800 /* Operation mode */ +#define SPORT_CTL_CKRE 0x00001000 /* Clock rising edge select */ +#define SPORT_CTL_FSR 0x00002000 /* Frame Sync required */ +#define SPORT_CTL_IFS 0x00004000 /* Internal Frame Sync select */ +#define SPORT_CTL_DIFS 0x00008000 /* Data-independent frame sync select */ +#define SPORT_CTL_LFS 0x00010000 /* Active low frame sync select */ +#define SPORT_CTL_LAFS 0x00020000 /* Late Transmit frame select */ +#define SPORT_CTL_RJUST 0x00040000 /* Right Justified mode select */ +#define SPORT_CTL_FSED 0x00080000 /* External frame sync edge select */ +#define SPORT_CTL_TFIEN 0x00100000 /* Transmit finish interrupt enable select */ +#define SPORT_CTL_GCLKEN 0x00200000 /* Gated clock mode select */ +#define SPORT_CTL_SPENSEC 0x01000000 /* Enable secondary channel */ +#define SPORT_CTL_SPTRAN 0x02000000 /* Data direction control */ +#define SPORT_CTL_DERRSEC 0x04000000 /* Secondary channel error status */ +#define SPORT_CTL_DXSSEC 0x18000000 /* Secondary channel data buffer status */ +#define SPORT_CTL_SEC_EMPTY 0x00000000 /* DXSSEC: Empty */ +#define SPORT_CTL_SEC_PART_FULL 0x10000000 /* DXSSEC: Partially full */ +#define SPORT_CTL_SEC_FULL 0x18000000 /* DXSSEC: Full */ +#define SPORT_CTL_DERRPRI 0x20000000 /* Primary channel error status */ +#define SPORT_CTL_DXSPRI 0xC0000000 /* Primary channel data buffer status */ +#define SPORT_CTL_PRM_EMPTY 0x00000000 /* DXSPRI: Empty */ +#define SPORT_CTL_PRM_PART_FULL 0x80000000 /* DXSPRI: Partially full */ +#define SPORT_CTL_PRM_FULL 0xC0000000 /* DXSPRI: Full */ + +#define SPORT_DIV_CLKDIV 0x0000FFFF /* Clock divisor */ +#define SPORT_DIV_FSDIV 0xFFFF0000 /* Frame sync divisor */ + +#define SPORT_MCTL_MCE 0x00000001 /* Multichannel enable */ +#define SPORT_MCTL_MCPDE 0x00000004 /* Multichannel data packing select */ +#define SPORT_MCTL_MFD 0x000000F0 /* Multichannel frame delay */ +#define SPORT_MCTL_WSIZE 0x00007F00 /* Number of multichannel slots */ +#define SPORT_MCTL_WOFFSET 0x03FF0000 /* Window offset size */ + +#define SPORT_CNT_CLKCNT 0x0000FFFF /* Current state of clk div counter */ +#define SPORT_CNT_FSDIVCNT 0xFFFF0000 /* Current state of frame div counter */ + +#define SPORT_ERR_DERRPMSK 0x00000001 /* Primary channel data error interrupt enable */ +#define SPORT_ERR_DERRSMSK 0x00000002 /* Secondary channel data error interrupt enable */ +#define SPORT_ERR_FSERRMSK 0x00000004 /* Frame sync error interrupt enable */ +#define SPORT_ERR_DERRPSTAT 0x00000010 /* Primary channel data error status */ +#define SPORT_ERR_DERRSSTAT 0x00000020 /* Secondary channel data error status */ +#define SPORT_ERR_FSERRSTAT 0x00000040 /* Frame sync error status */ + +#define SPORT_MSTAT_CURCHAN 0x000003FF /* Current channel */ + +#define SPORT_CTL2_FSMUXSEL 0x00000001 /* Frame Sync MUX Select */ +#define SPORT_CTL2_CKMUXSEL 0x00000002 /* Clock MUX Select */ +#define SPORT_CTL2_LBSEL 0x00000004 /* Loopback Select */ + +struct sport_register { + u32 spctl; + u32 div; + u32 spmctl; + u32 spcs0; + u32 spcs1; + u32 spcs2; + u32 spcs3; + u32 spcnt; + u32 sperrctl; + u32 spmstat; + u32 spctl2; + u32 txa; + u32 rxa; + u32 txb; + u32 rxb; + u32 revid; +}; + +struct sport_device { + struct platform_device *pdev; + struct clk *clk; + const unsigned short *pin_req; + struct sport_register *tx_regs; + struct sport_register *rx_regs; + struct dma_chan *tx_dma_chan; + struct dma_chan *rx_dma_chan; + int tx_err_irq; + int rx_err_irq; + + void (*tx_callback)(void *data); + void *tx_data; + void (*rx_callback)(void *data); + void *rx_data; + + /* cpu address of dma descriptor */ + struct dma_async_tx_descriptor *tx_desc; + struct dma_async_tx_descriptor *rx_desc; + dma_addr_t tx_buf; + dma_addr_t rx_buf; + size_t tx_fragsize; + size_t rx_fragsize; + unsigned int tx_frags; + unsigned int rx_frags; + u32 tx_count; + u32 rx_count; + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + size_t tx_totalsize; + size_t rx_totalsize; + unsigned int wdsize; + + unsigned int tx_map[TDM_MAX_SLOTS]; + unsigned int rx_map[TDM_MAX_SLOTS]; + + struct snd_pcm_substream *tx_substream; + struct snd_pcm_substream *rx_substream; + + struct snd_pcm_hw_params tx_hw_params; + struct snd_pcm_hw_params rx_hw_params; + + unsigned int dai_format; + +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) + + struct icap_instance icap[SHARC_CORES_NUM]; + spinlock_t icap_spinlock; + + u32 sharc_tx_core; + u32 sharc_rx_core; + + u32 icap_tx_dev_id; + u32 icap_rx_dev_id; + + struct work_struct get_sharc1_feature_work; + struct work_struct get_sharc2_feature_work; + + struct snd_dma_buffer sharc_tx_dma_buf; + struct snd_dma_buffer sharc_rx_dma_buf; + + size_t sharc_tx_buf_pos; + size_t sharc_rx_buf_pos; + spinlock_t sharc_tx_buf_pos_lock; + spinlock_t sharc_rx_buf_pos_lock; + + u32 tx_alsa_icap_buf_id; + u32 tx_dma_icap_buf_id; + u32 rx_alsa_icap_buf_id; + u32 rx_dma_icap_buf_id; + + struct work_struct send_tx_start_work; + struct work_struct send_rx_start_work; + + struct work_struct send_tx_stop_work; + struct work_struct send_rx_stop_work; + + struct wait_queue_head pending_tx_stop_event; + struct wait_queue_head pending_rx_stop_event; + + u32 pending_tx_stop; + u32 pending_rx_stop; +#endif +}; + +struct sport_params { + u32 spctl; + u32 div; + u32 spmctl; + u32 spcs0; +}; + +struct sport_device *sport_create(struct platform_device *pdev); +void sport_delete(struct sport_device *sport); +int sport_set_tx_params(struct sport_device *sport, + struct sport_params *params); +int sport_set_rx_params(struct sport_device *sport, + struct sport_params *params); +int sport_tx_start(struct sport_device *sport); +int sport_rx_start(struct sport_device *sport); +int sport_tx_stop(struct sport_device *sport); +int sport_rx_stop(struct sport_device *sport); +void sport_tx_dma_callback(void *ptr); +void sport_rx_dma_callback(void *ptr); +void sport_set_tx_callback(struct sport_device *sport, + void (*tx_callback)(void *), void *tx_data); +void sport_set_rx_callback(struct sport_device *sport, + void (*rx_callback)(void *), void *rx_data); +int sport_config_tx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream); +int sport_config_rx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize, struct snd_pcm_substream *substream); +unsigned long sport_curr_offset_tx(struct sport_device *sport); +unsigned long sport_curr_offset_rx(struct sport_device *sport); + +#if IS_ENABLED(CONFIG_SND_SC5XX_SPORT_SHARC) +int rpmsg_icap_sport_probe(struct rpmsg_device *rpdev); +int rpmsg_icap_sport_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src); +void rpmsg_icap_sport_remove(struct rpmsg_device *rpdev); +#endif + +#endif diff --git a/sound/soc/adi/sharc-alsa-asoc-card.c b/sound/soc/adi/sharc-alsa-asoc-card.c new file mode 100644 index 00000000000000..220fcb3e97e8f6 --- /dev/null +++ b/sound/soc/adi/sharc-alsa-asoc-card.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices SHARC-ALSA ASoC card driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + * Author: Piotr Wojtaszczyk + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icap/include/icap_application.h" + +#define SHARC_ALSA_SUBDEV_MAX 16 + +enum sa_state { + SHARC_ALSA_STOPPED = 0, + SHARC_ALSA_RUNNING = 1, + SHARC_ALSA_STOPPING = 2, + SHARC_ALSA_STARTING = 3, +}; + +struct sa_subdev { + spinlock_t buf_pos_lock; + u32 state; + size_t buf_frags; + size_t buf_frag_pos; + u32 buf_id; + struct snd_pcm_substream *substream; + struct wait_queue_head pending_stop_event; +}; + +struct sa_card_data { + struct device *dev; + struct rpmsg_device *rpdev; + struct icap_instance icap; + + u32 subdev_num; + struct sa_subdev subdevs[SHARC_ALSA_SUBDEV_MAX]; + + u32 card_id; + char card_name[64]; + struct snd_soc_card card; + struct snd_soc_dai_link dai_links[SHARC_ALSA_SUBDEV_MAX]; + + struct snd_soc_dai_link_component cpu_links[SHARC_ALSA_SUBDEV_MAX]; + struct snd_soc_dai_link_component codec_links[SHARC_ALSA_SUBDEV_MAX]; + struct snd_soc_dai_link_component platform_links[SHARC_ALSA_SUBDEV_MAX]; + + struct platform_device *asoc_cpu_devs[SHARC_ALSA_SUBDEV_MAX]; + struct platform_device *asoc_codec_devs[SHARC_ALSA_SUBDEV_MAX]; + struct platform_device *asoc_platform_devs[SHARC_ALSA_SUBDEV_MAX]; + + u32 cpu_num; + u32 codec_num; + u32 platform_num; + + struct work_struct delayed_probe_work; + struct work_struct send_start_work; + struct work_struct send_stop_work; + + spinlock_t start_stop_spinlock; + u32 stopping; + u32 starting; + + int card_registered; +}; + +struct sa_comp_data { + char dai_driver_name[64]; + struct snd_soc_dai_driver dai_driver; + char component_driver_name[64]; + struct snd_soc_component_driver component_driver; +}; + +#define SHARC_ALSA_RATES SNDRV_PCM_RATE_8000_192000 +#define SHARC_ALSA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static const struct snd_pcm_hardware sa_pcm_params = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 32, + .period_bytes_max = 0x10000, + .periods_min = 1, + .periods_max = PAGE_SIZE/32, + .buffer_bytes_max = 0x20000, /* 128 kbytes */ + .fifo_size = 16, +}; + +static int sa_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct snd_soc_card *card = dev_get_drvdata(&rpdev->dev); + struct sa_card_data *sa = snd_soc_card_get_drvdata(card); + union icap_remote_addr src_addr; + int ret; + + src_addr.rpmsg_addr = src; + ret = icap_parse_msg(&sa->icap, &src_addr, data, len); + if (ret && (ret != -ICAP_ERROR_INIT)) { + if (ret == -ICAP_ERROR_TIMEOUT) + dev_notice_ratelimited(&rpdev->dev, "ICAP late response\n"); + else + dev_err_ratelimited(&rpdev->dev, "ICAP parse msg error: %d\n", ret); + } + return 0; +} + +static int sa_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) +{ + size_t size = params_buffer_bytes(params); + + snd_pcm_lib_malloc_pages(substream, size); + return 0; +} + +static int sa_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static snd_pcm_uframes_t sa_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sa_card_data *sa = runtime->private_data; + int subdev_id = substream->pcm->device; + struct sa_subdev *subdev = &sa->subdevs[subdev_id]; + unsigned long flags; + unsigned int period; + snd_pcm_uframes_t frames; + + spin_lock_irqsave(&subdev->buf_pos_lock, flags); + period = subdev->buf_frag_pos; + spin_unlock_irqrestore(&subdev->buf_pos_lock, flags); + + frames = period * runtime->period_size; + return frames; +} + +static int sa_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct sa_card_data *sa = snd_soc_card_get_drvdata(rtd->card); + + snd_soc_set_runtime_hwparams(substream, &sa_pcm_params); + + runtime->private_data = sa; + return 0; +} + +static int sa_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sa_card_data *sa = runtime->private_data; + struct icap_instance *icap = &sa->icap; + int period_bytes = frames_to_bytes(runtime, runtime->period_size); + struct icap_buf_descriptor icap_buf; + int subdev_id = substream->pcm->device; + struct sa_subdev *subdev = &sa->subdevs[subdev_id]; + int ret = 0; + + ret = wait_event_interruptible(subdev->pending_stop_event, + subdev->state == SHARC_ALSA_STOPPED); + if (ret) + return ret; + + subdev->substream = substream; + subdev->buf_frag_pos = 0; + subdev->buf_frags = runtime->periods; + + icap_buf.subdev_id = (u32)subdev_id; + icap_buf.buf = (u64)runtime->dma_addr; + icap_buf.buf_size = runtime->periods * period_bytes; + icap_buf.type = ICAP_BUF_CIRCURAL; + icap_buf.gap_size = 0; // continuous circural + icap_buf.frag_size = period_bytes; + icap_buf.channels = runtime->channels; + icap_buf.format = runtime->format; + icap_buf.rate = runtime->rate; + icap_buf.report_frags = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + + snprintf(icap_buf.name, ICAP_BUF_NAME_LEN, "%s-alsa-playback-%d", + sa->card_name, subdev_id); + + if (subdev->buf_id != -1) { + ret = icap_remove_src(icap, subdev->buf_id); + if (ret) + return ret; + subdev->buf_id = -1; + } + ret = icap_add_src(icap, &icap_buf); + if (ret < 0) + return ret; + subdev->buf_id = (u32)ret; + + } else { + + snprintf(icap_buf.name, ICAP_BUF_NAME_LEN, "%s-alsa-capture-%d", + sa->card_name, subdev_id); + + if (subdev->buf_id != -1) { + ret = icap_remove_dst(icap, subdev->buf_id); + if (ret) + return ret; + subdev->buf_id = -1; + } + ret = icap_add_dst(icap, &icap_buf); + if (ret < 0) + return ret; + subdev->buf_id = (u32)ret; + } + + return ret; +} + +static int sa_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sa_card_data *sa = runtime->private_data; + int subdev_id = substream->pcm->device; + struct sa_subdev *sa_subdev = &sa->subdevs[subdev_id]; + unsigned long flags; + + spin_lock_irqsave(&sa->start_stop_spinlock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + sa_subdev->state = SHARC_ALSA_STARTING; + sa->starting = 1; + queue_work(system_highpri_wq, &sa->send_start_work); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sa_subdev->state = SHARC_ALSA_STOPPING; + sa->stopping = 1; + queue_work(system_highpri_wq, &sa->send_stop_work); + break; + } + + spin_unlock_irqrestore(&sa->start_stop_spinlock, flags); + + return 0; +} + +static int sa_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + size_t size = sa_pcm_params.buffer_bytes_max;/* 128KiB */ + int ret = 0; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, card->dev, size, size); + return 0; +} + +static s32 sa_frag_ready_cb(struct icap_instance *icap, struct icap_buf_frags *buf_frags) +{ + struct sa_card_data *sa = (struct sa_card_data *)icap->priv; + struct sa_subdev *subdev; + int i; + unsigned long flags; + + for (i = 0; i < sa->subdev_num; i++) { + subdev = &sa->subdevs[i]; + if (subdev->buf_id == buf_frags->buf_id) { + spin_lock_irqsave(&subdev->buf_pos_lock, flags); + subdev->buf_frag_pos += buf_frags->frags; + if (subdev->buf_frag_pos >= subdev->buf_frags) + subdev->buf_frag_pos = subdev->buf_frag_pos - subdev->buf_frags; + spin_unlock_irqrestore(&subdev->buf_pos_lock, flags); + snd_pcm_period_elapsed(subdev->substream); + } + } + return 0; +} + +struct icap_application_callbacks icap_application_callbacks = { + .frag_ready = sa_frag_ready_cb, +}; + +static void sa_start_func(struct work_struct *work) +{ + struct sa_card_data *sa = container_of(work, struct sa_card_data, send_start_work); + struct sa_subdev *subdev; + unsigned long flags; + int ret, i, reschedule; + + spin_lock_irqsave(&sa->start_stop_spinlock, flags); + + for (i = 0; i < sa->subdev_num; i++) { + subdev = &sa->subdevs[i]; + if (subdev->state == SHARC_ALSA_STARTING) { + subdev->state = SHARC_ALSA_RUNNING; + spin_unlock_irqrestore(&sa->start_stop_spinlock, flags); + ret = icap_start(&sa->icap, i); + if (ret) + dev_err(sa->dev, "ICAP subdev%d start error %d", i, ret); + spin_lock_irqsave(&sa->start_stop_spinlock, flags); + } + } + reschedule = sa->starting; + sa->starting = 0; + + spin_unlock_irqrestore(&sa->start_stop_spinlock, flags); + + if (reschedule) + queue_work(system_highpri_wq, &sa->send_stop_work); +} + +static void sa_stop_func(struct work_struct *work) +{ + struct sa_card_data *sa = container_of(work, struct sa_card_data, send_stop_work); + struct sa_subdev *subdev; + unsigned long flags; + int ret, i, reschedule; + + spin_lock_irqsave(&sa->start_stop_spinlock, flags); + + for (i = 0; i < sa->subdev_num; i++) { + subdev = &sa->subdevs[i]; + if (subdev->state == SHARC_ALSA_STOPPING) { + spin_unlock_irqrestore(&sa->start_stop_spinlock, flags); + ret = icap_stop(&sa->icap, i); + if (ret) + dev_err(sa->dev, "ICAP subdev%d stop error %d", i, ret); + spin_lock_irqsave(&sa->start_stop_spinlock, flags); + subdev->state = SHARC_ALSA_STOPPED; + wake_up_interruptible_all(&subdev->pending_stop_event); + } + } + reschedule = sa->stopping; + sa->stopping = 0; + + spin_unlock_irqrestore(&sa->start_stop_spinlock, flags); + + if (reschedule) + queue_work(system_highpri_wq, &sa->send_stop_work); +} + +static void sa_delayed_probe(struct work_struct *work) +{ + struct sa_card_data *sa = container_of(work, struct sa_card_data, delayed_probe_work); + struct device *dev = sa->dev; + struct rpmsg_device *rpdev = sa->rpdev; + struct icap_instance *icap = &sa->icap; + const u8 card_id = sa->card_id; + struct sa_comp_data sa_comp; + struct sa_comp_data *sa_comp_priv; + struct icap_subdevice_features features; + uint32_t subdev_num, i, link_id; + char link_name[64]; + int32_t ret = 0; + + /* init Inter Core Audio protocol */ + icap->transport.rpdev = rpdev; + ret = icap_application_init(icap, sa->card_name, &icap_application_callbacks, (void *)sa); + if (ret) { + dev_err(dev, "Failed to init ICAP: %d\n", ret); + return; + } + + ret = icap_get_subdevices(icap); + if (ret < 0) { + dev_err(dev, "Get subdevices error: %d", ret); + goto sa_delayed_probe_fail; + } + subdev_num = (uint32_t)ret; + subdev_num = subdev_num > SHARC_ALSA_SUBDEV_MAX ? SHARC_ALSA_SUBDEV_MAX : subdev_num; + sa->subdev_num = subdev_num; + + /* Initialize ASoC dai_link, one for each subdevice */ + for (i = 0; i < subdev_num; i++) { + + spin_lock_init(&sa->subdevs[i].buf_pos_lock); + init_waitqueue_head(&sa->subdevs[i].pending_stop_event); + sa->subdevs[i].state = SHARC_ALSA_STOPPED; + + link_id = card_id * SHARC_ALSA_SUBDEV_MAX + i; + + ret = icap_get_subdevice_features(icap, i, &features); + if (ret) { + dev_err(dev, "Get subdev%d features error: %d", i, ret); + goto sa_delayed_probe_fail; + } + + if ((features.type != ICAP_DEV_PLAYBACK) && (features.type != ICAP_DEV_RECORD)) { + dev_err(dev, "Unsupported subdev%d type %d", i, features.type); + goto sa_delayed_probe_fail; + } + + /* Initialize ASoC common component and dai driver for codec and cpu */ + memset(&sa_comp, 0, sizeof(sa_comp)); + if (features.type == ICAP_DEV_PLAYBACK) { + if (features.src_buf_max < 1) { + dev_err(dev, "Invalid max sourcebuf, playback subdev %d", i); + ret = -EINVAL; + goto sa_delayed_probe_fail; + } + sa_comp.dai_driver.playback.stream_name = "Playback"; + sa_comp.dai_driver.playback.channels_min = features.channels_min; + sa_comp.dai_driver.playback.channels_max = features.channels_max; + sa_comp.dai_driver.playback.rates = features.rates; + sa_comp.dai_driver.playback.formats = features.formats; + } else { + /* Capture */ + if (features.dst_buf_max < 1) { + dev_err(dev, "Invalid max destbuf, capt subdev %d", i); + ret = -EINVAL; + goto sa_delayed_probe_fail; + } + sa_comp.dai_driver.capture.stream_name = "Capture"; + sa_comp.dai_driver.capture.channels_min = features.channels_min; + sa_comp.dai_driver.capture.channels_max = features.channels_max; + sa_comp.dai_driver.capture.rates = features.rates; + sa_comp.dai_driver.capture.formats = features.formats; + } + + sa_comp.component_driver.idle_bias_on = 1; + sa_comp.component_driver.use_pmdown_time = 1; + sa_comp.component_driver.endianness = 1; + + /* Initialize ASoC codec component and dai driver one for each ICAP subdevice */ + sprintf(sa_comp.dai_driver_name, "sharc-alsa-codec-dai_%d", link_id); + sa_comp.dai_driver.name = sa_comp.dai_driver_name; + sprintf(sa_comp.component_driver_name, "sharc-alsa-codec_%d", link_id); + sa_comp.component_driver.name = sa_comp.component_driver_name; + + sa->asoc_codec_devs[sa->codec_num] = platform_device_register_data( + dev, "sharc-alsa-codec", link_id, &sa_comp, sizeof(sa_comp)); + if (IS_ERR(sa->asoc_codec_devs[sa->codec_num])) { + dev_err(dev, "Failed to register codec component\n"); + ret = PTR_ERR(sa->asoc_codec_devs[sa->codec_num]); + goto sa_delayed_probe_fail; + } + sa->codec_num++; + + /* Initialize ASoC cpu component and dai drivers */ + sprintf(sa_comp.dai_driver_name, "sharc-alsa-cpu-dai_%d", link_id); + sa_comp.dai_driver.name = sa_comp.dai_driver_name; + sprintf(sa_comp.component_driver_name, "sharc-alsa-cpu_%d", link_id); + sa_comp.component_driver.name = sa_comp.component_driver_name; + + sa->asoc_cpu_devs[sa->cpu_num] = platform_device_register_data( + dev, "sharc-alsa-cpu", link_id, &sa_comp, sizeof(sa_comp)); + if (IS_ERR(sa->asoc_cpu_devs[sa->cpu_num])) { + dev_err(dev, "Failed to register cpu component\n"); + ret = PTR_ERR(sa->asoc_cpu_devs[sa->cpu_num]); + goto sa_delayed_probe_fail; + } + sa->cpu_num++; + + /* Initialize ASoC platform driver */ + memset(&sa_comp, 0, sizeof(sa_comp)); + sprintf(sa_comp.component_driver_name, "sharc-alsa-platform_%d", link_id); + sa_comp.component_driver.name = sa_comp.component_driver_name; + sa_comp.component_driver.open = sa_pcm_open; + sa_comp.component_driver.hw_params = sa_pcm_hw_params; + sa_comp.component_driver.hw_free = sa_pcm_hw_free; + sa_comp.component_driver.prepare = sa_pcm_prepare; + sa_comp.component_driver.trigger = sa_pcm_trigger; + sa_comp.component_driver.pointer = sa_pcm_pointer; + sa_comp.component_driver.pcm_construct = sa_pcm_new; + + sa->asoc_platform_devs[sa->platform_num] = platform_device_register_data( + dev, "sharc-alsa-platform", link_id, &sa_comp, sizeof(sa_comp)); + if (IS_ERR(sa->asoc_platform_devs[sa->platform_num])) { + dev_err(dev, "Failed to register platform component\n"); + ret = PTR_ERR(sa->asoc_platform_devs[sa->platform_num]); + goto sa_delayed_probe_fail; + } + sa->platform_num++; + + /* Initialize ASoC links */ + sa_comp_priv = dev_get_platdata(&sa->asoc_codec_devs[i]->dev); + sa->codec_links[i].of_node = NULL; + sa->codec_links[i].name = dev_name(&sa->asoc_codec_devs[i]->dev); + sa->codec_links[i].dai_name = sa_comp_priv->dai_driver_name; + + sa_comp_priv = dev_get_platdata(&sa->asoc_cpu_devs[i]->dev); + sa->cpu_links[i].of_node = NULL; + sa->cpu_links[i].name = dev_name(&sa->asoc_cpu_devs[i]->dev); + sa->cpu_links[i].dai_name = sa_comp_priv->dai_driver_name; + + sa->platform_links[i].of_node = NULL; + sa->platform_links[i].name = dev_name(&sa->asoc_platform_devs[i]->dev); + sa->platform_links[i].dai_name = NULL; + + if (features.type == ICAP_DEV_PLAYBACK) { + sa->dai_links[i].playback_only = 1; + snprintf(link_name, 64, "sharc-alsa-playback-%d", link_id); + } else { + /* Capture */ + sa->dai_links[i].capture_only = 1; + snprintf(link_name, 64, "sharc-alsa-capture-%d", link_id); + } + sa->dai_links[i].name = devm_kstrdup(dev, link_name, GFP_KERNEL); + sa->dai_links[i].stream_name = sa->dai_links[i].name; + sa->dai_links[i].cpus = &sa->cpu_links[i]; + sa->dai_links[i].num_cpus = 1; + sa->dai_links[i].codecs = &sa->codec_links[i]; + sa->dai_links[i].num_codecs = 1; + sa->dai_links[i].platforms = &sa->platform_links[i]; + sa->dai_links[i].num_platforms = 1; + sa->dai_links[i].init = NULL; + sa->dai_links[i].ops = NULL; + } + + /* Initialize ASoC card */ + sa->card.name = sa->card_name; + sa->card.owner = THIS_MODULE; + sa->card.dev = dev; + sa->card.probe = NULL; + sa->card.dai_link = sa->dai_links; + sa->card.num_links = subdev_num; + + ret = snd_soc_register_card(&sa->card); + + if (ret < 0) + goto sa_delayed_probe_fail; + + sa->card_registered = 1; + + dev_info(dev, "sharc-alsa card probed for rpmsg endpoint addr: 0x%03x\n", rpdev->dst); + + return; + +sa_delayed_probe_fail: + for (i = 0; i < sa->codec_num; i++) + platform_device_unregister(sa->asoc_codec_devs[i]); + + for (i = 0; i < sa->cpu_num; i++) + platform_device_unregister(sa->asoc_cpu_devs[i]); + + for (i = 0; i < sa->platform_num; i++) + platform_device_unregister(sa->asoc_platform_devs[i]); + + sa->codec_num = 0; + sa->cpu_num = 0; + sa->platform_num = 0; + + icap_application_deinit(&sa->icap); +} + +static int sa_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + struct sa_card_data *sa; + int i; + int ret = 0; + + sa = devm_kzalloc(dev, sizeof(struct sa_card_data), GFP_KERNEL); + if (!sa) + return -ENOMEM; + + sa->card_id = rpdev->dst; + sprintf(sa->card_name, "sharc-alsa-card_%d", sa->card_id); + + sa->rpdev = rpdev; + sa->dev = dev; + INIT_WORK(&sa->delayed_probe_work, sa_delayed_probe); + INIT_WORK(&sa->send_start_work, sa_start_func); + INIT_WORK(&sa->send_stop_work, sa_stop_func); + + for (i = 0; i < SHARC_ALSA_SUBDEV_MAX; i++) + sa->subdevs[i].buf_id = -1; + + /* + * The snd_soc_register_card sets the driver_data in `struct device` for its own use + * Use drvdata of the `struct snd_soc_card` to keep the sa pointer, which is + * needed in remove. + */ + sa->card.dev = dev; + dev_set_drvdata(dev, &sa->card); + snd_soc_card_set_drvdata(&sa->card, sa); + + /* + * This probe function is in interrupt context (rpmsg handling) + * Can't wait for ICAP response here, do the rest of probe in the system_wq workqueue. + */ + ret = queue_work(system_highpri_wq, &sa->delayed_probe_work); + + return !ret; +} + +static void sa_remove(struct rpmsg_device *rpdev) +{ + struct snd_soc_card *card = dev_get_drvdata(&rpdev->dev); + struct sa_card_data *sa = snd_soc_card_get_drvdata(card); + int32_t ret, i; + + /* Cancel probe work */ + cancel_work_sync(&sa->delayed_probe_work); + + if (sa->card_registered) + snd_soc_unregister_card(&sa->card); + + for (i = 0; i < sa->codec_num; i++) + platform_device_unregister(sa->asoc_codec_devs[i]); + + for (i = 0; i < sa->cpu_num; i++) + platform_device_unregister(sa->asoc_cpu_devs[i]); + + for (i = 0; i < sa->platform_num; i++) + platform_device_unregister(sa->asoc_platform_devs[i]); + + /* Cancel all other works */ + cancel_work_sync(&sa->send_start_work); + cancel_work_sync(&sa->send_stop_work); + + ret = icap_application_deinit(&sa->icap); + if (ret) + dev_err(&rpdev->dev, "ICAP deinit failed %d\n", ret); + + dev_err(&rpdev->dev, "sharc-alsa card removed for rpmsg endpoint addr: 0x%03x\n", + rpdev->dst); +} + +static int sa_comp_probe(struct platform_device *pdev) +{ + struct sa_comp_data *sa_comp = (struct sa_comp_data *) dev_get_platdata(&pdev->dev); + struct snd_soc_dai_driver *dai_drv; + int dai_num; + + if (sa_comp->dai_driver.name) { + dai_drv = &sa_comp->dai_driver; + dai_num = 1; + } else { + dai_drv = NULL; + dai_num = 0; + } + return devm_snd_soc_register_component(&pdev->dev, + &sa_comp->component_driver, + dai_drv, dai_num); +} + +static struct rpmsg_device_id sa_id_table[] = { + { .name = "sharc-alsa" }, + { }, +}; +static struct rpmsg_driver sa_rpmsg_driver = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = sa_id_table, + .probe = sa_probe, + .callback = sa_rpmsg_cb, + .remove = sa_remove, +}; + +static struct platform_driver sa_cpu_driver = { + .driver = { + .name = "sharc-alsa-cpu", + }, + .probe = sa_comp_probe, +}; + +static struct platform_driver sa_codec_driver = { + .driver = { + .name = "sharc-alsa-codec", + }, + .probe = sa_comp_probe, +}; + +static struct platform_driver sa_platform_driver = { + .driver = { + .name = "sharc-alsa-platform", + }, + .probe = sa_comp_probe, +}; + +static int sa_driver_init(void) +{ + int ret; + + ret = platform_driver_register(&sa_cpu_driver); + if (ret != 0) { + pr_err("sa: failed to register cpu driver\n"); + goto fail_cpu_driver; + } + + ret = platform_driver_register(&sa_codec_driver); + if (ret != 0) { + pr_err("sa: failed to register codec driver\n"); + goto fail_codec_driver; + } + + ret = platform_driver_register(&sa_platform_driver); + if (ret != 0) { + pr_err("sa: failed to register platform driver\n"); + goto fail_platform_driver; + } + + ret = register_rpmsg_driver(&sa_rpmsg_driver); + if (ret < 0) { + pr_err("sa: failed to register rpmsg driver\n"); + goto fail_rpmsg_driver; + } + + return 0; + +fail_rpmsg_driver: + platform_driver_unregister(&sa_platform_driver); +fail_platform_driver: + platform_driver_unregister(&sa_codec_driver); +fail_codec_driver: + platform_driver_unregister(&sa_cpu_driver); +fail_cpu_driver: + return ret; +} +module_init(sa_driver_init); + +static void sa_driver_exit(void) +{ + unregister_rpmsg_driver(&sa_rpmsg_driver); + platform_driver_unregister(&sa_platform_driver); + platform_driver_unregister(&sa_codec_driver); + platform_driver_unregister(&sa_cpu_driver); +} +module_exit(sa_driver_exit); + +MODULE_DESCRIPTION("Analog Devices SHARC-ALSA ASoC card driver"); +MODULE_AUTHOR("Piotr Wojtaszczyk "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sharc-alsa"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 91ac99bc3edb2e..a2df9ec49bd07e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -538,6 +538,14 @@ config SND_SOC_ADAU1977_I2C select SND_SOC_ADAU1977 select REGMAP_I2C +config SND_SOC_ADAU1962 + tristate + +config SND_SOC_ADAU1962_I2C + tristate + select SND_SOC_ADAU1962 + select REGMAP_I2C + config SND_SOC_ADAU7002 tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index da4077463278e3..d3e50599b4898d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -21,6 +21,8 @@ snd-soc-adau1761-spi-y := adau1761-spi.o snd-soc-adau1781-y := adau1781.o snd-soc-adau1781-i2c-y := adau1781-i2c.o snd-soc-adau1781-spi-y := adau1781-spi.o +snd-soc-adau1962-y := adau1962.o +snd-soc-adau1962-i2c-y := adau1962-i2c.o snd-soc-adau1977-y := adau1977.o snd-soc-adau1977-spi-y := adau1977-spi.o snd-soc-adau1977-i2c-y := adau1977-i2c.o @@ -455,6 +457,8 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o +obj-$(CONFIG_SND_SOC_ADAU1962) += snd-soc-adau1962.o +obj-$(CONFIG_SND_SOC_ADAU1962_I2C) += snd-soc-adau1962-i2c.o obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o diff --git a/sound/soc/codecs/adau1962-i2c.c b/sound/soc/codecs/adau1962-i2c.c new file mode 100644 index 00000000000000..d6f3ed14e88c27 --- /dev/null +++ b/sound/soc/codecs/adau1962-i2c.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices adau1962 codec driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include + +#include "adau1962.h" + +static int adau1962_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + + config = adau1962_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return adau1962_probe(&client->dev, + devm_regmap_init_i2c(client, &config), NULL); +} + +static void adau1962_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_component(&client->dev); + adau1962_remove(&client->dev); +} + +#ifdef CONFIG_OF +static const struct of_device_id adau1962_dt_ids[] = { + { .compatible = "adi,adau1962", }, + { } +}; +MODULE_DEVICE_TABLE(of, adau1962_dt_ids); +#endif + +static const struct i2c_device_id adau1962_i2c_ids[] = { + {"adau1962", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1962_i2c_ids); + +static struct i2c_driver adau1962_i2c_driver = { + .driver = { + .name = "adau1962", + .of_match_table = of_match_ptr(adau1962_dt_ids), + }, + .probe = adau1962_i2c_probe, + .remove = adau1962_i2c_remove, + .id_table = adau1962_i2c_ids, +}; +module_i2c_driver(adau1962_i2c_driver); + +MODULE_DESCRIPTION("Analog Devices ADAU1962 driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1962.c b/sound/soc/codecs/adau1962.c new file mode 100644 index 00000000000000..1a3b84c3b70357 --- /dev/null +++ b/sound/soc/codecs/adau1962.c @@ -0,0 +1,836 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices adau1962 codec driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "adau1962.h" + +#define __DEBUG 0 + +#define ADAU1962_REG_PLL_CLK_CTRL0 0x00 +#define ADAU1962_REG_PLL_CLK_CTRL1 0x01 +#define ADAU1962_REG_PDN_THRMSENS_CTRL1 0x02 +#define ADAU1962_REG_PDN_CTRL2 0x03 +#define ADAU1962_REG_PDN_CTRL3 0x04 +#define ADAU1962_REG_THRM_TEMP_STAT 0x05 +#define ADAU1962_REG_DAC_CTRL0 0x06 +#define ADAU1962_REG_DAC_CTRL1 0x07 +#define ADAU1962_REG_DAC_CTRL2 0x08 +#define ADAU1962_REG_DAC_MUTE1 0x09 +#define ADAU1962_REG_DAC_MUTE2 0x0a +#define ADAU1962_REG_DACMSTR_VOL 0x0b +#define ADAU1962_REG_DAC_VOL(x) (0x0b + (x)) +#define ADAU1962_REG_PAD_STRGTH 0x1c +#define ADAU1962_REG_DAC_POWER1 0x1d +#define ADAU1962_REG_DAC_POWER2 0x1e +#define ADAU1962_REG_DAC_POWER3 0x1f + +#define ADAU1962_PLL_CLK_PUP BIT(0) +#define ADAU1962_PLL_CLK_DLRCLK BIT(6) +#define ADAU1962_PLL_CLK_PLLIN_MASK (0x3 << 6) +#define ADAU1962_PLL_MCS_MASK (0x3 << 1) + +#define ADAU1962_DAC_CTRL0_MMUTE BIT(0) + +#define ADAU1962_DAC_CTRL0_FMT_MASK (0x3 << 6) +#define ADAU1962_DAC_CTRL0_FMT_I2S (0x0 << 6) +#define ADAU1962_DAC_CTRL0_FMT_LJ (0x1 << 6) +#define ADAU1962_DAC_CTRL0_FMT_RJ_24BIT (0x2 << 6) +#define ADAU1962_DAC_CTRL0_FMT_RJ_16BIT (0x3 << 6) + +#define ADAU1962_SAI_CTRL0_SAI_MASK (0x7 << 3) +#define ADAU1962_SAI_CTRL0_SAI_I2S (0x0 << 3) +#define ADAU1962_SAI_CTRL0_SAI_TDM_2 (0x1 << 3) +#define ADAU1962_SAI_CTRL0_SAI_TDM_4 (0x2 << 3) +#define ADAU1962_SAI_CTRL0_SAI_TDM_8 (0x3 << 3) +#define ADAU1962_SAI_CTRL0_SAI_TDM_16 (0x4 << 3) + +#define ADAU1962_DAC_CTRL0_FS_MASK (0x3) +#define ADAU1962_DAC_CTRL0_FS_32000_48000 (0x0) +#define ADAU1962_DAC_CTRL0_FS_64000_96000 (0x1) +#define ADAU1962_DAC_CTRL0_FS_128000_192000 (0x2) + +#define ADAU1962_DAC_CTRL1_LRCLK_PULSE BIT(6) +#define ADAU1962_DAC_CTRL1_LRCLK_POL BIT(5) +#define ADAU1962_DAC_CTRL1_MSB BIT(4) +#define ADAU1962_DAC_CTRL1_BCLKRATE_16 (0x1 << 2) +#define ADAU1962_DAC_CTRL1_BCLKRATE_32 (0x0 << 2) +#define ADAU1962_DAC_CTRL1_BCLKRATE_MASK (0x1 << 2) +#define ADAU1962_DAC_CTRL1_BCLK_EDGE BIT(1) +#define ADAU1962_DAC_CTRL1_MASTER BIT(0) + +#define ADAU1962_DAC_CTRL2_SLOT_WIDTH_MASK (0x1 << 4) +#define ADAU1962_DAC_CTRL2_SLOT_WIDTH_32 (0x0 << 4) +#define ADAU1962_DAC_CTRL2_SLOT_WIDTH_16 (0x1 << 4) + +#define ADAU1962_CHAN_MAP_SECOND_SLOT_OFFSET 4 +#define ADAU1962_CHAN_MAP_FIRST_SLOT_OFFSET 0 + +struct adau1962 { + struct regmap *regmap; + bool right_j; + unsigned int sysclk; + enum adau1962_sysclk_src sysclk_src; + + struct gpio_desc *enable_gpio; + struct gpio_desc *reset_gpio; + + struct snd_pcm_hw_constraint_list constraints; + + struct device *dev; + void (*switch_mode)(struct device *dev); + + unsigned int max_master_fs; + unsigned int slot_width; + bool enabled; + bool master; +}; + +static const struct reg_default adau1962_reg_defaults[] = { + { 0x00, 0x00 }, + { 0x01, 0x2a }, + { 0x02, 0xa0 }, + { 0x03, 0x00 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x01 }, + { 0x07, 0x00 }, + { 0x08, 0x06 }, + { 0x09, 0x00 }, + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, + { 0x0c, 0x00 }, + { 0x0d, 0x00 }, + { 0x0e, 0x00 }, + { 0x0f, 0x00 }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0xaa }, + { 0x1e, 0xaa }, + { 0x1f, 0xaa }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(adau1962_adc_gain, -9562, 0); + +#define ADAU1962_OUTPUT(x) \ + SND_SOC_DAPM_OUTPUT("AOUT" #x) +#define ADAU1962_DAC1(x) \ + SND_SOC_DAPM_DAC("DAC" #x, "Playback", ADAU1962_REG_PDN_CTRL2, \ + (x - 1), 1) +#define ADAU1962_DAC2(x) \ + SND_SOC_DAPM_DAC("DAC" #x, "Playback", ADAU1962_REG_PDN_CTRL3, \ + (x - 9), 1) + +static const struct snd_soc_dapm_widget adau1962_dapm_widgets[] = { + ADAU1962_DAC1(1), + ADAU1962_DAC1(2), + ADAU1962_DAC1(3), + ADAU1962_DAC1(4), + ADAU1962_DAC1(5), + ADAU1962_DAC1(6), + ADAU1962_DAC1(7), + ADAU1962_DAC1(8), + ADAU1962_DAC2(9), + ADAU1962_DAC2(10), + ADAU1962_DAC2(11), + ADAU1962_DAC2(12), + + ADAU1962_OUTPUT(1), + ADAU1962_OUTPUT(2), + ADAU1962_OUTPUT(3), + ADAU1962_OUTPUT(4), + ADAU1962_OUTPUT(5), + ADAU1962_OUTPUT(6), + ADAU1962_OUTPUT(7), + ADAU1962_OUTPUT(8), + ADAU1962_OUTPUT(9), + ADAU1962_OUTPUT(10), + ADAU1962_OUTPUT(11), + ADAU1962_OUTPUT(12), +}; + +#define ADAU1962_ROUTE(x) \ + { "AOUT" #x, NULL, "DAC" #x } + +static const struct snd_soc_dapm_route adau1962_dapm_routes[] = { + ADAU1962_ROUTE(1), + ADAU1962_ROUTE(2), + ADAU1962_ROUTE(3), + ADAU1962_ROUTE(4), + ADAU1962_ROUTE(5), + ADAU1962_ROUTE(6), + ADAU1962_ROUTE(7), + ADAU1962_ROUTE(8), + ADAU1962_ROUTE(9), + ADAU1962_ROUTE(10), + ADAU1962_ROUTE(11), + ADAU1962_ROUTE(12), +}; + +#define ADAU1962_VOLUME(x) \ + SOC_SINGLE_TLV("DAC" #x " Playback Volume", \ + ADAU1962_REG_DAC_VOL(x), \ + 0, 255, 1, adau1962_adc_gain) +#define ADAU1962_PLAYBACK_SWITCH1(x) \ + SOC_SINGLE("DAC" #x " Playback Switch", \ + ADAU1962_REG_DAC_MUTE1, ((x) - 1), \ + 1, 1) +#define ADAU1962_PLAYBACK_SWITCH2(x) \ + SOC_SINGLE("DAC" #x " Playback Switch", \ + ADAU1962_REG_DAC_MUTE2, ((x) - 9), \ + 1, 1) +static const char *adau1962_dac_power[] = {"Low Power", "Lowest Power", + "Best Performance", "Good Performance"}; +static const char *adau1962_dac_osr[] = {"256x", "128x"}; +static const struct soc_enum adau1962_enum[] = { + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_CTRL2, 1, 2, adau1962_dac_osr), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER1, 0, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER1, 2, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER1, 4, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER1, 6, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER2, 0, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER2, 2, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER2, 4, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER2, 6, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER3, 0, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER3, 2, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER3, 4, 4, adau1962_dac_power), + SOC_ENUM_SINGLE(ADAU1962_REG_DAC_POWER3, 6, 4, adau1962_dac_power), +}; +#define ADAU1962_DAC_POWER(x) \ + SOC_ENUM("DAC" #x " Power Adjust", adau1962_enum[(x)]) + +static const struct snd_kcontrol_new adau1962_snd_controls[] = { + /* global DAC playback controls */ + SOC_SINGLE_TLV("DAC Playback Volume", ADAU1962_REG_DACMSTR_VOL, + 0, 255, 1, adau1962_adc_gain), + SOC_SINGLE("DAC Playback Switch", ADAU1962_REG_DAC_CTRL0, 0, 1, 1), + SOC_SINGLE("DAC Deemphasis Switch", ADAU1962_REG_DAC_CTRL2, 0, 1, 0), + + /* DAC1-12 specific controls */ + ADAU1962_VOLUME(1), + ADAU1962_PLAYBACK_SWITCH1(1), + ADAU1962_DAC_POWER(1), + ADAU1962_VOLUME(2), + ADAU1962_PLAYBACK_SWITCH1(2), + ADAU1962_DAC_POWER(2), + ADAU1962_VOLUME(3), + ADAU1962_PLAYBACK_SWITCH1(3), + ADAU1962_DAC_POWER(3), + ADAU1962_VOLUME(4), + ADAU1962_PLAYBACK_SWITCH1(4), + ADAU1962_DAC_POWER(4), + ADAU1962_VOLUME(5), + ADAU1962_PLAYBACK_SWITCH1(5), + ADAU1962_DAC_POWER(5), + ADAU1962_VOLUME(6), + ADAU1962_PLAYBACK_SWITCH1(6), + ADAU1962_DAC_POWER(6), + ADAU1962_VOLUME(7), + ADAU1962_PLAYBACK_SWITCH1(7), + ADAU1962_DAC_POWER(7), + ADAU1962_VOLUME(8), + ADAU1962_PLAYBACK_SWITCH1(8), + ADAU1962_DAC_POWER(8), + ADAU1962_VOLUME(9), + ADAU1962_PLAYBACK_SWITCH2(9), + ADAU1962_DAC_POWER(9), + ADAU1962_VOLUME(10), + ADAU1962_PLAYBACK_SWITCH2(10), + ADAU1962_DAC_POWER(10), + ADAU1962_VOLUME(11), + ADAU1962_PLAYBACK_SWITCH2(11), + ADAU1962_DAC_POWER(11), + ADAU1962_VOLUME(12), + ADAU1962_PLAYBACK_SWITCH2(12), + ADAU1962_DAC_POWER(12), + + /* other controls */ + SOC_ENUM("DAC Oversampling Rate", adau1962_enum[0]), +}; + +#if __DEBUG +void adau1962_print(struct adau1962 *adau1962) +{ + int i; + unsigned int val[4]; + + for (i = 0; i < 32;) { + regmap_read(adau1962->regmap, i, &val[0]); + regmap_read(adau1962->regmap, i+1, &val[1]); + regmap_read(adau1962->regmap, i+2, &val[2]); + regmap_read(adau1962->regmap, i+3, &val[3]); + pr_info("%02x,%02x,%02x,%02x\n", val[0], val[1], val[2], val[3]); + i += 4; + /* skip address 0x18-0x1B */ + if (i == 24) + i += 4; + } +} +#endif + +/* + * Returns the appropriate setting for ths FS field in the CTRL0 register + * depending on the rate. + */ +static int adau1962_lookup_fs(unsigned int rate) +{ + if (rate >= 32000 && rate <= 48000) + return ADAU1962_DAC_CTRL0_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + return ADAU1962_DAC_CTRL0_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + return ADAU1962_DAC_CTRL0_FS_128000_192000; + else + return -EINVAL; +} + +static int adau1962_lookup_mcs(struct adau1962 *adau1962, unsigned int rate, + unsigned int fs) +{ + unsigned int mcs; + + rate *= 128 >> fs; + + if (adau1962->sysclk % rate != 0) + return -EINVAL; + + mcs = adau1962->sysclk / rate; + + /* The factors configured by MCS are 2, 3, 4, 6 */ + if (mcs < 2 || mcs > 6 || mcs == 5) + return -EINVAL; + + mcs = mcs - 2; + if (mcs == 4) + mcs = 3; + + return mcs; +} + +static int adau1962_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adau1962 *adau1962 = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int ctrl0, ctrl0_mask; + unsigned int ctrl1; + int mcs, fs; + int ret; + + fs = adau1962_lookup_fs(rate); + if (fs < 0) + return fs; + + if (adau1962->sysclk_src == ADAU1962_SYSCLK_SRC_MCLK) { + mcs = adau1962_lookup_mcs(adau1962, rate, fs); + if (mcs < 0) + return mcs; + } else { + mcs = 0; + } + + ctrl0_mask = ADAU1962_DAC_CTRL0_FS_MASK; + ctrl0 = fs << 1; + + if (adau1962->right_j) { + switch (params_width(params)) { + case 16: + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_RJ_16BIT; + break; + case 24: + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl0_mask |= ADAU1962_DAC_CTRL0_FMT_MASK; + } + + if (adau1962->master) { + switch (params_width(params)) { + case 16: + slot_width = 16; + break; + case 24: + case 32: + slot_width = 32; + break; + default: + return -EINVAL; + } + + /* In TDM mode there is a fixed slot width */ + if (adau1962->slot_width) + slot_width = adau1962->slot_width; + + if (slot_width == 16) + ctrl1 = ADAU1962_DAC_CTRL1_BCLKRATE_16; + else + ctrl1 = ADAU1962_DAC_CTRL1_BCLKRATE_32; + + ret = regmap_update_bits(adau1962->regmap, + ADAU1962_REG_DAC_CTRL1, + ADAU1962_DAC_CTRL1_BCLKRATE_MASK, + ctrl1); + if (ret < 0) + return ret; + } + + ret = regmap_update_bits(adau1962->regmap, ADAU1962_REG_DAC_CTRL0, + ctrl0_mask, ctrl0); + if (ret < 0) + return ret; + + return regmap_update_bits(adau1962->regmap, ADAU1962_REG_PLL_CLK_CTRL0, + ADAU1962_PLL_MCS_MASK, mcs << 1); +} + +static int adau1962_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ +// struct adau1962 *adau1962 = snd_soc_component_get_drvdata(component); +// int ret = 0; + +// switch (level) { +// case SND_SOC_BIAS_ON: +// break; +// case SND_SOC_BIAS_PREPARE: +// break; +// case SND_SOC_BIAS_STANDBY: +// if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) +// ret = adau1962_power_enable(adau1962); +// break; +// case SND_SOC_BIAS_OFF: +// ret = adau1962_power_disable(adau1962); +// break; +// } + +// if (ret) +// return ret; + + return 0; +} + +static int adau1962_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1962 *adau1962 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl0, ctrl1, ctrl2; + int ret; + + if (slots == 0) { + /* 0 = No fixed slot width */ + adau1962->slot_width = 0; + adau1962->max_master_fs = 192000; + return regmap_update_bits(adau1962->regmap, + ADAU1962_REG_DAC_CTRL0, ADAU1962_SAI_CTRL0_SAI_MASK, + ADAU1962_SAI_CTRL0_SAI_I2S); + } + + if (rx_mask == 0 || tx_mask != 0) + return -EINVAL; + + switch (slots) { + case 2: + ctrl0 = ADAU1962_SAI_CTRL0_SAI_TDM_2; + break; + case 4: + ctrl0 = ADAU1962_SAI_CTRL0_SAI_TDM_4; + break; + case 8: + ctrl0 = ADAU1962_SAI_CTRL0_SAI_TDM_8; + break; + case 16: + ctrl0 = ADAU1962_SAI_CTRL0_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(adau1962->regmap, ADAU1962_REG_DAC_CTRL0, + ADAU1962_SAI_CTRL0_SAI_MASK, ctrl0); + if (ret) + return ret; + + switch (width) { + case 16: + ctrl1 = ADAU1962_DAC_CTRL1_BCLKRATE_16; + ctrl2 = ADAU1962_DAC_CTRL2_SLOT_WIDTH_16; + break; + case 24: + case 32: + ctrl1 = ADAU1962_DAC_CTRL1_BCLKRATE_32; + ctrl2 = ADAU1962_DAC_CTRL2_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(adau1962->regmap, + ADAU1962_REG_DAC_CTRL1, + ADAU1962_DAC_CTRL1_BCLKRATE_MASK, + ctrl1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adau1962->regmap, + ADAU1962_REG_DAC_CTRL2, + ADAU1962_DAC_CTRL2_SLOT_WIDTH_MASK, + ctrl2); + if (ret < 0) + return ret; + + adau1962->slot_width = width; + + /* In master mode the maximum bitclock is 24.576 MHz */ + adau1962->max_master_fs = min(192000, 24576000 / width / slots); + return 0; +} + +static int adau1962_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct adau1962 *adau1962 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (mute) + val = ADAU1962_DAC_CTRL0_MMUTE; + else + val = 0; + + return regmap_update_bits(adau1962->regmap, ADAU1962_REG_DAC_CTRL0, + ADAU1962_DAC_CTRL0_MMUTE, val); +} + +static int adau1962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1962 *adau1962 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl0 = 0, ctrl1 = 0; + bool invert_lrclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + adau1962->master = false; + break; + case SND_SOC_DAIFMT_CBP_CFP: + ctrl1 |= ADAU1962_DAC_CTRL1_MASTER; + adau1962->master = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1 |= ADAU1962_DAC_CTRL1_BCLK_EDGE; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1 |= ADAU1962_DAC_CTRL1_BCLK_EDGE; + invert_lrclk = true; + break; + default: + return -EINVAL; + } + + adau1962->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_LJ; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_RJ_24BIT; + adau1962->right_j = true; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= ADAU1962_DAC_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_I2S; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= ADAU1962_DAC_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1962_DAC_CTRL0_FMT_LJ; + invert_lrclk = false; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) + ctrl1 |= ADAU1962_DAC_CTRL1_LRCLK_POL; + + ret = regmap_update_bits(adau1962->regmap, ADAU1962_REG_DAC_CTRL0, + ADAU1962_DAC_CTRL0_FMT_MASK, + ctrl0); + if (ret) + return ret; + + return regmap_update_bits(adau1962->regmap, ADAU1962_REG_DAC_CTRL1, + ADAU1962_DAC_CTRL1_MASTER | ADAU1962_DAC_CTRL1_BCLK_EDGE + | ADAU1962_DAC_CTRL1_LRCLK_POL + | ADAU1962_DAC_CTRL1_LRCLK_PULSE, + ctrl1); +} + +static int adau1962_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +// struct adau1962 *adau1962 = snd_soc_component_get_drvdata(dai->component); +// u64 formats = 0; + +// if (adau1962->slot_width == 16) +// formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE; +// else if (adau1962->right_j || adau1962->slot_width == 24) +// formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | +// SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE; + +// snd_pcm_hw_constraint_list(substream->runtime, 0, +// SNDRV_PCM_HW_PARAM_RATE, &adau1962->constraints); + +// if (adau1962->master) +// snd_pcm_hw_constraint_minmax(substream->runtime, +// SNDRV_PCM_HW_PARAM_RATE, 8000, adau1962->max_master_fs); + +// if (formats != 0) +// snd_pcm_hw_constraint_mask64(substream->runtime, +// SNDRV_PCM_HW_PARAM_FORMAT, formats); + return 0; +} + +static const struct snd_soc_dai_ops adau1962_dai_ops = { + .startup = adau1962_startup, + .hw_params = adau1962_hw_params, + .mute_stream = adau1962_mute, + .set_fmt = adau1962_set_dai_fmt, + .set_tdm_slot = adau1962_set_tdm_slot, +}; + +static struct snd_soc_dai_driver adau1962_dai = { + .name = "adau1962-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 12, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, + .ops = &adau1962_dai_ops, +}; + +static const unsigned int adau1962_rates[] = { + 32000, 64000, 128000, + 44100, 88200, 176400, + 48000, 96000, 192000, +}; + +#define ADAU1962_RATE_CONSTRAINT_MASK_32000 0x0007 +#define ADAU1962_RATE_CONSTRAINT_MASK_44100 0x0038 +#define ADAU1962_RATE_CONSTRAINT_MASK_48000 0x01c0 +#define ADAU1962_RATE_CONSTRAINT_MASK_LRCLK 0x01ff + +static bool adau1962_check_sysclk(unsigned int mclk, unsigned int base_freq) +{ + unsigned int mcs; + + if (mclk % (base_freq * 128) != 0) + return false; + + mcs = mclk / (128 * base_freq); + if (mcs < 2 || mcs > 6 || mcs == 5) + return false; + + return true; +} + +static int adau1962_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct adau1962 *adau1962 = snd_soc_component_get_drvdata(component); + unsigned int mask = 0; + unsigned int clk_src; + unsigned int ret; + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + if (clk_id != ADAU1962_SYSCLK) + return -EINVAL; + + switch (source) { + case ADAU1962_SYSCLK_SRC_MCLK: + clk_src = 0; + break; + case ADAU1962_SYSCLK_SRC_LRCLK: + clk_src = ADAU1962_PLL_CLK_DLRCLK; + break; + default: + return -EINVAL; + } + + if (freq != 0 && source == ADAU1962_SYSCLK_SRC_MCLK) { + if (freq < 8192000 || freq > 36864000) + return -EINVAL; + + if (adau1962_check_sysclk(freq, 32000)) + mask |= ADAU1962_RATE_CONSTRAINT_MASK_32000; + if (adau1962_check_sysclk(freq, 44100)) + mask |= ADAU1962_RATE_CONSTRAINT_MASK_44100; + if (adau1962_check_sysclk(freq, 48000)) + mask |= ADAU1962_RATE_CONSTRAINT_MASK_48000; + + if (mask == 0) + return -EINVAL; + } else if (source == ADAU1962_SYSCLK_SRC_LRCLK) { + mask = ADAU1962_RATE_CONSTRAINT_MASK_LRCLK; + } + + ret = regmap_update_bits(adau1962->regmap, ADAU1962_REG_PLL_CLK_CTRL0, + ADAU1962_PLL_CLK_PLLIN_MASK, clk_src); + if (ret) + return ret; + + adau1962->constraints.mask = mask; + adau1962->sysclk_src = source; + adau1962->sysclk = freq; + + return 0; +} + +static const struct snd_soc_component_driver adau1962_component_driver = { + .set_bias_level = adau1962_set_bias_level, + .set_sysclk = adau1962_set_sysclk, + .idle_bias_on = 1, //hfeng to check here 0?1? + .controls = adau1962_snd_controls, + .num_controls = ARRAY_SIZE(adau1962_snd_controls), + .dapm_widgets = adau1962_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1962_dapm_widgets), + .dapm_routes = adau1962_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1962_dapm_routes), +}; + +int adau1962_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)) +{ + struct adau1962 *adau1962; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1962 = devm_kzalloc(dev, sizeof(*adau1962), GFP_KERNEL); + if (adau1962 == NULL) + return -ENOMEM; + + adau1962->dev = dev; + adau1962->regmap = regmap; + adau1962->switch_mode = switch_mode; + adau1962->max_master_fs = 192000; + + adau1962->constraints.list = adau1962_rates; + adau1962->constraints.count = ARRAY_SIZE(adau1962_rates); + +#ifndef CONFIG_ARCH_SC59X //Reset currently controlled by gpio hog + adau1962->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + //Do not check for errors here, as the ADAU1977 may have already claimed this +#endif + + dev_set_drvdata(dev, adau1962); + +#ifndef CONFIG_ARCH_SC59X //Reset currently controlled by gpio hog + if (!IS_ERR(adau1962->reset_gpio)) { + /* Hardware power-on reset */ + gpiod_set_value_cansleep(adau1962->reset_gpio, 1); + msleep(38); + gpiod_set_value_cansleep(adau1962->reset_gpio, 0); + /* After asserting the PU/RST pin high, ADAU1962 + * requires 300ms to stabilize + */ + msleep(300); + } +#endif + + ret = regmap_update_bits(adau1962->regmap, ADAU1962_REG_PLL_CLK_CTRL0, + ADAU1962_PLL_CLK_PUP, ADAU1962_PLL_CLK_PUP); + if (ret) + return ret; + +#if __DEBUG + adau1962_print(adau1962); +#endif + + return snd_soc_register_component(dev, &adau1962_component_driver, + &adau1962_dai, 1); +} +EXPORT_SYMBOL_GPL(adau1962_probe); + +void adau1962_remove(struct device *dev) +{ +} +EXPORT_SYMBOL_GPL(adau1962_remove); + +static bool adau1962_register_volatile(struct device *dev, unsigned int reg) +{ + return false; +} + +const struct regmap_config adau1962_regmap_config = { + .max_register = ADAU1962_REG_DAC_POWER3, + .volatile_reg = adau1962_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adau1962_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1962_reg_defaults), +}; +EXPORT_SYMBOL_GPL(adau1962_regmap_config); + +MODULE_DESCRIPTION("Analog Devices ADAU1962 driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1962.h b/sound/soc/codecs/adau1962.h new file mode 100644 index 00000000000000..fdc202796dfeab --- /dev/null +++ b/sound/soc/codecs/adau1962.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Analog Devices adau1962 codec driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#ifndef __ADAU1962_H__ +#define __ADAU1962_H__ + +#include + +struct device; + +int adau1962_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)); + +void adau1962_remove(struct device *dev); + +extern const struct regmap_config adau1962_regmap_config; + +enum adau1962_clk_id { + ADAU1962_SYSCLK, +}; + +enum adau1962_sysclk_src { + ADAU1962_SYSCLK_SRC_MCLK, + ADAU1962_SYSCLK_SRC_LRCLK, +}; + +#endif From 33575ad3e6d77a69be7f3a08274b6eb0df0101f3 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Thu, 11 Apr 2024 11:17:15 +0100 Subject: [PATCH 30/85] watchdog: adi_wdt: Add watchdog support for ADSP-SC5xx Signed-off-by: Utsav Agarwal --- drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/adi_wdt.c | 277 +++++++++++++++++++++++++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 drivers/watchdog/adi_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 05008d937e405b..9e18041e0e6573 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -858,6 +858,17 @@ config MOXART_WDT To compile this driver as a module, choose M here: the module will be called moxart_wdt. +config ADI_WATCHDOG + tristate "adi watchdog" + select WATCHDOG_CORE + help + This is the driver for the hardware watchdog + on the adi sc5xx family processors, if you wish to have + watchdog support enabled, say Y, otherwise say N. + + To compile this driver as a module, choose M here: the + module will be called adi_wdt. + config ST_LPC_WATCHDOG tristate "STMicroelectronics LPC Watchdog" depends on ARCH_STI || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b680e4d3c1bc20..73bb52c56e60c5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o +obj-$(CONFIG_ADI_WATCHDOG) += adi_wdt.o obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o # X86 (i386 + ia64 + x86_64) Architecture diff --git a/drivers/watchdog/adi_wdt.c b/drivers/watchdog/adi_wdt.c new file mode 100644 index 00000000000000..89191f2e0d03b5 --- /dev/null +++ b/drivers/watchdog/adi_wdt.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADI On-Chip Watchdog Timer Driver + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison + * Contact: Greg Malysa + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WATCHDOG_NAME "adi-wdt" + +#define WDOG_CTL 0x0 /* Watchdog Control Register */ +#define WDOG_CNT 0x4 /* Watchdog Count Register */ +#define WDOG_STAT 0x8 /* Watchdog Status Register */ + +/* Bit in SWRST that indicates boot caused by watchdog */ +#define SWRST_RESET_WDOG 0x4000 + +/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */ +#define WDOG_EXPIRED 0x8000 + +/* Masks for WDEV field in WDOG_CTL register */ +#define ICTL_RESET 0x0 +#define ICTL_NMI 0x2 +#define ICTL_GPI 0x4 +#define ICTL_NONE 0x6 +#define ICTL_MASK 0x6 + +/* Masks for WDEN field in WDOG_CTL register */ +#define WDEN_MASK 0x0FF0 +#define WDEN_ENABLE 0x0000 +#define WDEN_DISABLE 0x0AD0 + +/* some defaults */ +#define WATCHDOG_TIMEOUT 20 + +static unsigned int timeout = WATCHDOG_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct adi_wdt { + struct watchdog_device wdd; + void __iomem *base; + spinlock_t lock; + unsigned long rate; + unsigned long timeout; + bool nowayout; + int state; + unsigned int irq; +}; + +/** + * adi_wdt_keepalive - Keep the Userspace Watchdog Alive + * + * The Userspace watchdog got a KeepAlive: schedule the next timeout. + */ +static int adi_wdt_keepalive(struct watchdog_device *wdd) +{ + struct adi_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + writel(wdt->timeout, wdt->base + WDOG_STAT); + spin_unlock(&wdt->lock); + + return 0; +} + +static int adi_wdt_stop(struct watchdog_device *wdd) +{ + struct adi_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + writel(WDEN_DISABLE, wdt->base + WDOG_CTL); + spin_unlock(&wdt->lock); + + return 0; +} + +static int adi_wdt_start(struct watchdog_device *wdd) +{ + struct adi_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + writel(WDEN_ENABLE | ICTL_RESET, wdt->base + WDOG_CTL); + spin_unlock(&wdt->lock); + + return 0; +} + +static int adi_wdt_running(struct watchdog_device *wdd) +{ + struct adi_wdt *wdt = watchdog_get_drvdata(wdd); + + return ((readl(wdt->base + WDOG_CTL) & WDEN_MASK) != WDEN_DISABLE); +} + +static int adi_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) +{ + u32 cnt; + int run; + struct adi_wdt *wdt = watchdog_get_drvdata(wdd); + + cnt = t * wdt->rate; + + run = adi_wdt_running(wdd); + + adi_wdt_stop(wdd); + writel(cnt, wdt->base + WDOG_CNT); + if (run) + adi_wdt_start(wdd); + + wdd->timeout = t; + + return 0; +} + +#ifdef CONFIG_PM + +static int adi_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct adi_wdt *wdt = platform_get_drvdata(pdev); + + wdt->state = adi_wdt_running(&wdt->wdd); + adi_wdt_stop(&wdt->wdd); + + return 0; +} + +static int adi_wdt_resume(struct platform_device *pdev) +{ + struct adi_wdt *wdt = platform_get_drvdata(pdev); + + if (wdt->state) { + adi_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + adi_wdt_start(&wdt->wdd); + } + + return 0; +} +#else +# define adi_wdt_suspend NULL +# define adi_wdt_resume NULL +#endif + +static const struct watchdog_info adi_wdt_info = { + .identity = "adi Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops adi_wdt_ops = { + .owner = THIS_MODULE, + .start = adi_wdt_start, + .stop = adi_wdt_stop, + .ping = adi_wdt_keepalive, + .set_timeout = adi_wdt_set_timeout, +}; + +static int adi_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct adi_wdt *wdt; + struct resource *res; + unsigned long sclk; + struct clk *clk; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + wdt->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + clk = devm_clk_get(&pdev->dev, "adi-watchdog"); + if (IS_ERR(clk)) + return -ENODEV; + + sclk = clk_get_rate(clk); + spin_lock_init(&wdt->lock); + wdt->wdd.parent = &pdev->dev; + wdt->wdd.info = &adi_wdt_info; + wdt->wdd.ops = &adi_wdt_ops; + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = ~0UL / sclk; + wdt->rate = sclk; + + watchdog_set_nowayout(&wdt->wdd, nowayout); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); + if (ret) { + pr_info("cannot register watchdog (%d)\n", ret); + return ret; + } + + watchdog_set_drvdata(&wdt->wdd, wdt); + platform_set_drvdata(pdev, wdt); + if (watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev)) { + if (watchdog_init_timeout(&wdt->wdd, timeout, &pdev->dev)) + return -EINVAL; + + adi_wdt_set_timeout(&wdt->wdd, timeout); + } else + adi_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + + pr_info("initialized: timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + + return 0; +} + +static void adi_wdt_shutdown(struct platform_device *pdev) +{ + struct adi_wdt *wdt = platform_get_drvdata(pdev); + + adi_wdt_stop(&wdt->wdd); +} + +#if defined(CONFIG_OF) +static const struct of_device_id adi_wdt_dt_ids[] = { + { .compatible = "adi,watchdog" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, adi_wdt_dt_ids); +#endif + +static struct platform_driver adi_wdt_driver = { + .probe = adi_wdt_probe, + .shutdown = adi_wdt_shutdown, + .suspend = adi_wdt_suspend, + .resume = adi_wdt_resume, + .driver = { + .name = WATCHDOG_NAME, + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(adi_wdt_dt_ids), +#endif + }, +}; + +module_platform_driver(adi_wdt_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("adi Watchdog Device Driver"); From ba3d7d4d18c99fb248f47e0ca7c017589dd4997e Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 23 May 2025 14:23:16 +0100 Subject: [PATCH 31/85] gpio: adi: Selecting PINT (IRQ) as a requirement GPIO works via PINT, which in turn makes it into a dependency on both, hardware as well as software. Signed-off-by: Utsav Agarwal --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 849fe157cbf896..37e47aface8812 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -161,6 +161,7 @@ config GPIO_ADI_ADSP_PORT bool "ADI ADSP PORT GPIO driver" depends on OF_GPIO select GPIO_GENERIC + select ADI_ADSP_IRQ help Say Y to enable the ADSP PORT-based GPIO driver for Analog Devices ADSP chips. From 477ab6d390cbaf68fe8f38afee89b294a4c93cfb Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Tue, 15 Jul 2025 15:52:39 +0100 Subject: [PATCH 32/85] usb: musb: adi: Adding adsp musb glue layer Adding glue layer for adsp platforms sc57x/sc58x. This is enables support for the following: * usbboot: Mount and boot into file systems contained on USB devices by switching OTG to host mode. * usb gadget audio: Utilize linux's USB gadget for streaming audio from another device by switching OTG to device mode. Co-developed-by: Utsav Agarwal Signed-off-by: Utsav Agarwal Signed-off-by: Philip Molloy --- .../devicetree/bindings/usb/adi,musb.yaml | 92 ++++ drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/adi.c | 427 ++++++++++++++++++ drivers/usb/musb/adi.h | 21 + 5 files changed, 548 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/usb/adi,musb.yaml create mode 100644 drivers/usb/musb/adi.c create mode 100644 drivers/usb/musb/adi.h diff --git a/Documentation/devicetree/bindings/usb/adi,musb.yaml b/Documentation/devicetree/bindings/usb/adi,musb.yaml new file mode 100644 index 00000000000000..8a70df202e03fd --- /dev/null +++ b/Documentation/devicetree/bindings/usb/adi,musb.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/adi,musb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Inventra USB Glue Layer for Analog Devices SC5XX Processors + +maintainers: + - Analog Devices, Inc + +description: | + For the Inventra-based USB IP on the ADI SC58X and SC57X processors + (glues to musb_core.c) + +properties: + compatible: + enum: + - adi,musb + + reg: + maxItems: 1 + + reg-names: + maxItems: 1 + + interrupts: + maxItems: 2 + + interrupt-names: + items: + - const: mc + - const: dma + + spu_securep_id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: System Protection Unit peripheral ID + + mentor,multipoint: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Indicate the musb controller supports multipoint with "1" + + mentor,num-eps: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of endpoints supported including EP0 + + mentor,ram-bits: + description: Address size of the RAM in bits + + mentor,power: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximum power consumption in mA + + phys: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - spu_securep_id + - mentor,multipoint + - mentor,num-eps + - mentor,ram-bits + - mentor,power + - phys + +additionalProperties: false + +examples: + - | + #include + #include + + usb0: usb@310c1000 { + compatible = "adi,musb"; + reg = <0x310c1000 0x390>; + reg-names = "mc"; + interrupts = , + ; + interrupt-names = "mc", "dma"; + spu_securep_id = <153>; + + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb0_phy>; + status = "disabled"; + }; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index f56929267eaa91..234f20a17021eb 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -105,6 +105,12 @@ config USB_MUSB_JZ4740 depends on USB_MUSB_GADGET select USB_ROLE_SWITCH +config USB_MUSB_ADI + tristate "ADI" + depends on ARCH_SC59X || ARCH_SC58X || ARCH_SC57X + help + Enable usb on ADI platforms. + config USB_MUSB_MEDIATEK tristate "MediaTek platforms" depends on ARCH_MEDIATEK || COMPILE_TEST @@ -146,7 +152,7 @@ config USB_UX500_DMA config USB_INVENTRA_DMA bool 'Inventra' - depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740 || USB_MUSB_POLARFIRE_SOC + depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740 || USB_MUSB_POLARFIRE_SOC || USB_MUSB_ADI help Enable DMA transfers using Mentor's engine. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 5dccf0e453e1b0..8f4f4f622db1af 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_USB_MUSB_UX500) += ux500.o obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o +obj-$(CONFIG_USB_MUSB_ADI) += adi.o obj-$(CONFIG_USB_MUSB_POLARFIRE_SOC) += mpfs.o # the kconfig must guarantee that only one of the diff --git a/drivers/usb/musb/adi.c b/drivers/usb/musb/adi.c new file mode 100644 index 00000000000000..2b04c4a4b4f5d6 --- /dev/null +++ b/drivers/usb/musb/adi.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MUSB "glue layer" driver for ADI Sc58x/Sc57x platforms + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written by Timesys Corporation + * Maintained by Analog Devices, Inc. + * + * Contact: Analog Devices, Inc + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adi.h" +#include "musb_core.h" + +#define TIMER_DELAY (1 * HZ) + +struct adi_musb_glue { + struct device *dev; + struct platform_device *musb; + struct platform_device *phy; +}; + +#define glue_to_musb(glue) platform_get_drvdata(glue->musb) + +static void musb_conn_timer_handler(struct timer_list *t) +{ + struct musb *musb = from_timer(musb, t, dev_timer); + unsigned long flags; + u16 val; + static u8 toggle; + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_IDLE: + case OTG_STATE_A_WAIT_BCON: + /* Start a new session */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + val &= ~MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); + val |= MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); + /* Check if musb is host or peripheral. */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + + if (!(val & MUSB_DEVCTL_BDEVICE)) { + musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; + } else { + /* Ignore VBUSERROR and SUSPEND IRQ */ + val = musb_readb(musb->mregs, MUSB_INTRUSBE); + val &= ~MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSBE, val); + + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSB, val); + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + } + mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY); + break; + case OTG_STATE_B_IDLE: + /* + * Start a new session. It seems that MUSB needs taking + * some time to recognize the type of the plug inserted? + */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + val |= MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); + val = musb_readw(musb->mregs, MUSB_DEVCTL); + + if (!(val & MUSB_DEVCTL_BDEVICE)) { + musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; + } else { + /* Ignore VBUSERROR and SUSPEND IRQ */ + val = musb_readb(musb->mregs, MUSB_INTRUSBE); + val &= ~MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSBE, val); + + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSB, val); + + /* Toggle the Soft Conn bit, so that we can response to + * the inserting of either A-plug or B-plug. + */ + if (toggle) { + val = musb_readb(musb->mregs, MUSB_POWER); + val &= ~MUSB_POWER_SOFTCONN; + musb_writeb(musb->mregs, MUSB_POWER, val); + toggle = 0; + } else { + val = musb_readb(musb->mregs, MUSB_POWER); + val |= MUSB_POWER_SOFTCONN; + musb_writeb(musb->mregs, MUSB_POWER, val); + toggle = 1; + } + } + + /* The delay time is set to 1/4 second by default, + * shortening it, if accelerating A-plug detection + * is needed in OTG mode. + */ + mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY / 4); + break; + default: + dev_dbg(musb->controller, "%s state not handled\n", + usb_otg_state_string(musb->xceiv->otg->state)); + break; + } + spin_unlock_irqrestore(&musb->lock, flags); + + dev_dbg(musb->controller, "state is %s\n", + usb_otg_state_string(musb->xceiv->otg->state)); +} + +static irqreturn_t adi_musb_interrupt(int irq, void *__hci) +{ + u8 devctl; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb & MUSB_INTR_VBUSERROR) { + //dev_warn(&parent->dev, "VBUS error recovery\n"); + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + devctl = musb_readw(musb->mregs, MUSB_DEVCTL); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + } + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + retval = musb_interrupt(musb); + } + + if (plat->mode == MUSB_OTG) { + /* Start sampling ID pin, when plug is removed from MUSB */ + if ((musb->xceiv->otg->state == OTG_STATE_B_IDLE + || musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON) || + (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) { + mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY); + MUSB_DEV_MODE(musb); + musb->a_wait_bcon = TIMER_DELAY; + } + } + + if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb)) + musb_writeb(musb->ctrl_base, REG_USB_VBUS_CTL, 0x0); + + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +} + +static void adi_musb_reg_init(struct musb *musb) +{ + musb_writel(musb->ctrl_base, REG_USB_PLL_OSC, 20 << 1); + musb_writeb(musb->ctrl_base, REG_USB_VBUS_CTL, 0x0); + musb_writeb(musb->ctrl_base, REG_USB_PHY_CTL, 0x80); + musb_writel(musb->ctrl_base, REG_USB_UTMI_CTL, + 0x40 | musb_readl(musb->ctrl_base, REG_USB_UTMI_CTL)); +} + +static int adi_musb_init(struct musb *musb) +{ + int spu_securep_id, ret = 0; + struct device *dev = musb->controller; + struct platform_device *parent = to_platform_device(dev->parent); + struct device_node *node = parent->dev.of_node; + + /*initialize spu */ + + ret = of_property_read_u32(node, "spu_securep_id", (u32 *)&spu_securep_id); + if (ret) { + dev_err(&parent->dev, "failed to get spu id\n"); + return -ENXIO; + } + + set_spu_securep_msec(spu_securep_id, true); + + musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0); + if (IS_ERR_OR_NULL(musb->xceiv)) { + dev_err(&parent->dev, "failed to allocate musb->xceiv\n"); + return -EPROBE_DEFER; + } + + musb->isr = adi_musb_interrupt; + timer_setup(&musb->dev_timer, + musb_conn_timer_handler, 0); + adi_musb_reg_init(musb); + + return 0; +} + +static int adi_musb_exit(struct musb *musb) +{ + usb_put_phy(musb->xceiv); + + return 0; +} + +static int adi_musb_set_mode(struct musb *musb, u8 musb_mode) +{ + struct device *dev = musb->controller; + struct platform_device *parent = to_platform_device(dev->parent); + + switch (musb_mode) { + case MUSB_HOST: + musb_writeb(musb->ctrl_base, REG_USB_ID_CTL, 0x1); + break; + case MUSB_PERIPHERAL: + musb_writeb(musb->ctrl_base, REG_USB_ID_CTL, 0x3); + break; + case MUSB_OTG: + musb_writeb(musb->ctrl_base, REG_USB_ID_CTL, 0x0); + break; + default: + dev_err(&parent->dev, "Trying to set unsupported mode %u\n", + musb_mode); + return -EINVAL; + } + + return 0; +} + +static const struct musb_platform_ops adi_musb_ops = { + .quirks = MUSB_DMA_INVENTRA, + .init = adi_musb_init, + .exit = adi_musb_exit, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif + .set_mode = adi_musb_set_mode, +}; + +static int adi_musb_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + struct resource musb_resources[3]; + struct musb_hdrc_platform_data pdata; + struct platform_device *musb; + struct adi_musb_glue *glue; + struct musb_hdrc_config *config; + int class = 0; + + char probe_fail_msg[128] = + "Unknown error occurred during probe"; + + int ret = -ENOMEM; + int val = 0; + + /*allocate glue struct */ + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + if (IS_ERR(glue)) { + ret = -ENOMEM; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to allocate memory for driver data"); + goto err0; + } + + /*alloc musb platform_device and register */ + musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); + if (IS_ERR(musb)) { + ret = -ENOMEM; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to allocate device"); + goto err1; + } + + musb->dev.parent = &pdev->dev; + glue->dev = &pdev->dev; + glue->musb = musb; + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (IS_ERR(config)) { + ret = -ENOMEM; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to allocate memory for config"); + goto err2; + } + + ret = of_property_read_u32(node, "mentor,num-eps", (u32 *)&config->num_eps); + ret = of_property_read_u32(node, "mentor,multipoint", (u32 *)&val); + if (val) + config->multipoint = true; + + ret = of_property_read_u32(node, "mentor,ram-bits", (u32 *)&config->ram_bits); + ret = of_property_read_u32(node, "mode", (u32 *)&class); + + if (class == MUSB_OTG) { +#if IS_ENABLED(CONFIG_USB_MUSB_HOST) + class = MUSB_HOST; +#elif IS_ENABLED(CONFIG_USB_MUSB_GADGET) + class = MUSB_PERIPHERAL; +#endif + } + pdata.mode = class; + + ret = of_property_read_u32(node, "mentor,power", (u32 *)&val); + pdata.power = val / 2; + pdata.config = config; + pdata.platform_ops = &adi_musb_ops; + + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) + goto err2; + + platform_set_drvdata(pdev, glue); + + memset(musb_resources, 0x00, sizeof(*musb_resources) * + ARRAY_SIZE(musb_resources)); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mc"); + if (IS_ERR(res)) { + ret = -ENOMEM; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to get resource 'mc'"); + goto err3; + } + musb_resources[0] = *res; + + ret = platform_get_irq(pdev, 0); + if (!ret) { + ret = -EINVAL; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to get IRQ 0"); + goto err3; + } + musb_resources[1].start = ret; + musb_resources[1].flags = IORESOURCE_IRQ; + musb_resources[1].name = "mc"; + + ret = platform_get_irq(pdev, 1); + if (!ret) { + ret = -EINVAL; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to get IRQ 1"); + goto err3; + } + musb_resources[2].start = ret; + musb_resources[2].flags = IORESOURCE_IRQ; + musb_resources[2].name = "dma"; + + ret = platform_device_add_resources(musb, musb_resources, 3); + if (ret) { + ret = -EINVAL; + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to add platform reosurces"); + goto err3; + } + + ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); + if (ret) { + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to add platform data"); + goto err3; + } + + ret = platform_device_add(musb); + if (ret) { + snprintf(probe_fail_msg, sizeof(probe_fail_msg), + "failed to add platform device"); + goto err3; + } + + return 0; +err3: + usb_phy_generic_unregister(glue->phy); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return dev_err_probe(&pdev->dev, ret, probe_fail_msg); +} + +static void adi_musb_remove(struct platform_device *pdev) +{ + struct adi_musb_glue *glue = platform_get_drvdata(pdev); + + platform_device_unregister(glue->musb); + usb_phy_generic_unregister(glue->phy); + kfree(glue); +} + +static const struct of_device_id adi_musb_match[] = { + {.compatible = "adi,musb",}, + {}, +}; +MODULE_DEVICE_TABLE(of, adi_musb_match); + +static struct platform_driver adi_musb_driver = { + .probe = adi_musb_probe, + .remove = adi_musb_remove, + .driver = { + .name = "musb-adi", + .of_match_table = adi_musb_match, + }, +}; +module_platform_driver(adi_musb_driver); + +MODULE_DESCRIPTION("ADI MUSB Glue Layer"); +MODULE_AUTHOR("Hao Liang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/musb/adi.h b/drivers/usb/musb/adi.h new file mode 100644 index 00000000000000..b9f1555e17b978 --- /dev/null +++ b/drivers/usb/musb/adi.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written by Timesys Corporation + * Maintained by Analog Devices, Inc. + * + * Contact: Analog Devices, Inc + * + */ + +#ifndef __ADI_H__ +#define __ADI_H__ + +#define REG_USB_VBUS_CTL 0x380 +#define REG_USB_ID_CTL 0x382 +#define REG_USB_PHY_CTL 0x394 +#define REG_USB_PLL_OSC 0x398 +#define REG_USB_UTMI_CTL 0x39c + +#endif From 7cda8fe161e900c1a95292e014162012703ad3a4 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 16:35:03 +0200 Subject: [PATCH 33/85] arm64: dts: adi: sc598: add device tree Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Co-developed-by: Caleb Ethridge Signed-off-by: Caleb Ethridge Co-developed-by: Ozan Durgut Signed-off-by: Ozan Durgut Signed-off-by: Philip Molloy --- arch/arm64/boot/dts/Makefile | 1 + arch/arm64/boot/dts/adi/Makefile | 2 + arch/arm64/boot/dts/adi/sc598-som-ezkit.dts | 317 +++++ arch/arm64/boot/dts/adi/sc598-som.dtsi | 491 +++++++ arch/arm64/boot/dts/adi/sc59x-64.dtsi | 1307 +++++++++++++++++++ 5 files changed, 2118 insertions(+) create mode 100644 arch/arm64/boot/dts/adi/Makefile create mode 100644 arch/arm64/boot/dts/adi/sc598-som-ezkit.dts create mode 100644 arch/arm64/boot/dts/adi/sc598-som.dtsi create mode 100644 arch/arm64/boot/dts/adi/sc59x-64.dtsi diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index b0844404eda183..8ded7122c2ea98 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 subdir-y += actions subdir-y += airoha +subdir-y += adi subdir-y += allwinner subdir-y += altera subdir-y += amazon diff --git a/arch/arm64/boot/dts/adi/Makefile b/arch/arm64/boot/dts/adi/Makefile new file mode 100644 index 00000000000000..10f843a4a73406 --- /dev/null +++ b/arch/arm64/boot/dts/adi/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +dtb-$(CONFIG_ARCH_SC59X_64) += sc598-som-ezkit.dtb sc598-som-ezlite.dtb diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts new file mode 100644 index 00000000000000..0664d45eccce4a --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2021 Analog Devices Incorporated + * Author: Nathan Barrett-Morrison + */ + +/dts-v1/; + +#include "sc598-som.dtsi" + +/ { + model = "ADI 64-bit SC598 SOM EZ Kit"; + compatible = "adi,sc598-som-ezkit", "adi,sc59x-64"; + + scb { + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s4>; + adi,codec = <&adau1962>, <&adau1979>; + }; + }; +}; + +&ospi { + pinctrl-names = "default"; + pinctrl-0 = <&ospi_default>; + + status = "disabled"; + + flash0: mx66lm1g45@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mx66lm1g45", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <83333334>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + + cdns,read-delay = <4>; + cdns,tshsl-ns = <50>; + cdns,tsd2d-ns = <50>; + cdns,tchsh-ns = <4>; + cdns,tslch-ns = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + ospi_0: partition@0 { + label = "U-Boot SPL"; + reg = <0x0 0x40000>; + }; + + ospi_1: partition@1 { + label = "U-Boot Proper"; + reg = <0x40000 0xC0000>; + }; + + ospi_2: partition@2 { + label = "U-Boot Environment"; + reg = <0x100000 0x20000>; + }; + + ospi_3: partition@3 { + label = "FIT Image"; + reg = <0x120000 0xF00000>; + }; + + ospi_4: partition@4 { + label = "JFFS2 Formatted RFS"; + reg = <0x1020000 0xFE0000>; + }; + }; + }; +}; + +&i2c2 { + ssw1: gpio@22 { + compatible = "microchip,mcp23017"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x22>; + status = "okay"; + + eeprom { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-low; + line-name = "eeprom-en"; + }; + + pushbutton { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-high; + line-name = "pushbutton-en"; + }; + + microsd { + gpio-hog; + gpios = <2 GPIO_ACTIVE_LOW>; + output-low; + line-name = "microsd-spi"; + }; + + ftdi { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-high; + line-name = "ftdi-usb-en"; + }; + + can { + gpio-hog; + gpios = <4 GPIO_ACTIVE_LOW>; + output-low; + line-name = "can-en"; + }; + + adau1962 { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1962-en"; + }; + + adau1979 { + gpio-hog; + gpios = <7 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1979-en"; + }; + + octal { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-low; + line-name = "octal-spi-cs-en"; + }; + + spdif-dig { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-digital-en"; + }; + + spdif-opt { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-optical-en"; + }; + + audio-jack { + gpio-hog; + gpios = <11 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "audio-jack-sel"; + }; + + mlb { + gpio-hog; + gpios = <12 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "mlb-en"; + }; + + eth1 { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-high; + line-name = "eth1-en"; + }; + + eth1-reset { + gpio-hog; + gpios = <14 GPIO_ACTIVE_LOW>; +/* USB0 lines are shared with Eth1 so Eth PHY must be held in reset + when using the USB */ + output-high; + line-name = "eth1-reset"; + }; + + gige-reset { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-low; + line-name = "gige-reset"; + }; + }; + + adau1979: adau1979@11 { + compatible = "adi,adau1979"; + reg = <0x11>; + }; + + adau1962: adau1962@4 { + compatible = "adi,adau1962"; + reg = <0x4>; + reset-gpios = <&ssw1 5 GPIO_ACTIVE_LOW>; + }; +}; + +&emac0 { + snps,reset-active-low; + snps,reset-delays-us = <0 200 500>; + phy-handle = <&dp83867>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + snps,mtl-rx-config = <&emac0rxconfig>; + snps,mtl-tx-config = <&emac0txconfig>; + + emac0txconfig: tx-config { + snps,tx-queues-to-use = <3>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,dcb-algorithm; + }; + }; + + emac0rxconfig: rx-config { + snps,rx-queues-to-use = <1>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,dcb-algorithm; + }; + }; + + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + dp83867: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + }; + }; +}; + +//&emac1 { +// phy-handle = <&dp83848>; +// phy-mode = "rmii"; +// pinctrl-names = "default"; +// pinctrl-0 = <ð1_default>; +// status = "disabled";// + +// mdio1 { +// compatible = "snps,dwmac-mdio"; +// #address-cells = <1>; +// #size-cells = <0>; +// dp83848: ethernet-phy@1 { +// reg = <1>; +// }; +// };// + +//}; + +&i2s4 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai1>; + status = "okay"; +}; + +&sru_ctrl_dai1 { + status = "okay"; + + sru_dai1: sru_dai1_mux { + route { + sru-routing = + , /* set DAI1_PIN0B to input */ + , /* route DAI1_PIN0B to SPT4_ACLK */ + , /* set DAI1_PIN04 to input */ + , /* route DAI1_PIN04 to SPT4_AFS */ + , /* set DAI1_PIN01 to output */ + , /* route SPT4_AD0 to DAI1_PIN01 */ + , /* set DAI1_PIN12 to input */ + , /* route DAI1_PIN12 to SPT4_BCLK */ + , /* set DAI1_PIN20 to input */ + , /* route DAI1_PIN20 to SPT4_BFS */ + , /* set DAI1_PIN06 to input */ + ; /* route DAI1_PIN06 to SPT4_BD0 */ + }; + }; +}; + +&pkte1 { + status = "okay"; + mode = "arm"; /* autonomous ring mode */ + /* mode = "tcm"; */ /* target command mode */ + /* mode = "dhm"; */ /* direct host mode */ +}; + +&crc0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/adi/sc598-som.dtsi b/arch/arm64/boot/dts/adi/sc598-som.dtsi new file mode 100644 index 00000000000000..5eddbf5287d1de --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-som.dtsi @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2021 Analog Devices Incorporated + * Author: Nathan Barrett-Morrison + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "sc59x-64.dtsi" + +/ { + chosen { + stdout-path = &uart1; + bootargs = "root=/dev/mtdblock4 rw rootfstype=jffs2 earlycon=adi_uart,0x31003000 console=ttySC0,115200 mem=224M"; + }; + + memory@90000000 { + device_type = "memory"; + reg = <0x90000000 0x0e000000>; + }; + + memory@20040000 { + device_type = "memory"; + reg = <0x20040000 0x40000>; + }; + + reserved-memory { + sram1_res: sram1-reserved@20040000 { + compatible = "adi,sram-access"; + reg = <0x20040000 0x40000>; + }; + + vdev0vrings: vdev0vring0@20080000 { + reg = <0x20080000 0x4000>; + no-map; + }; + + vdev0buffer: vdev0buffer@20084000 { + compatible = "shared-dma-pool"; + reg = <0x20084000 0x20000>; + no-map; + }; + + vdev1vrings: vdev0vring0@200A4000 { + reg = <0x200A4000 0x4000>; + no-map; + }; + + vdev1buffer: vdev0buffer@200A8000 { + compatible = "shared-dma-pool"; + reg = <0x200A8000 0x20000>; + no-map; + }; + }; + + sram1_mmap: sram-mmap@0 { + compatible = "adi,sram-mmap"; + memory-region = <&sram1_res>; + status = "okay"; + }; + + scb { + sharc0: core1-rproc@28240000 { + compatible = "adi,remoteproc"; + reg = <0x28240000 0x160000>, + <0x20000000 0x200000>; + core-id = <1>; + core-irq = <106>; /* SOFT1 */ + firmware-name = "adi_adsp_core1_fw.ldr"; + interrupts = ; /* TRU0_SLV3 */ + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20000000 0x20200000>; + adi,rsc-table = <&rsc_tbl0>; + adi,verify = <1>; + adi,tru = <&tru>; + adi,tru-master-id = <135>; /* trigger master SOFT4 */ + status = "okay"; + }; + + sharc1: core2-rproc@28a40000 { + compatible = "adi,remoteproc"; + reg = <0x28a40000 0x160000>, + <0x20000000 0x200000>; + core-id = <2>; + core-irq = <107>; /* SOFT2 */ + firmware-name = "adi_adsp_core2_fw.ldr"; + interrupts = ; /* TRU0_SLV3 */ + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20000000 0x20200000>; + adi,rsc-table = <&rsc_tbl1>; + adi,verify = <1>; + adi,tru = <&tru>; + adi,tru-master-id = <136>; /* trigger master SOFT5 */ + status = "okay"; + }; + + sharc0_rpmsg: core0-rpmsg@28240000 { + status = "disabled"; + compatible = "adi,rpmsg-SC598"; + core-id = <1>; + adi,rcu = <&rcu>; + adi,check-idle; + adi,rsc-table = <&rsc_tbl0>; + interrupts = ; /* TRU0_SLV3 */ + adi,tru = <&tru>; + adi,tru-master-id = <135>; /* trigger master SOFT4 */ + vdev-vring = <&vdev0vrings>; + memory-region = <&vdev0buffer>; + }; + + sharc1_rpmsg: core1-rpmsg@28a40000 { + status = "disabled"; + compatible = "adi,rpmsg-SC598"; + core-id = <2>; + adi,rcu = <&rcu>; + adi,check-idle; + adi,rsc-table = <&rsc_tbl1>; + interrupts = ; /* TRU0_SLV3 */ + adi,tru = <&tru>; + adi,tru-master-id = <136>; /* trigger master SOFT5 */ + vdev-vring = <&vdev1vrings>; + memory-region = <&vdev1buffer>; + }; + }; + +}; + +&tru { + rpmsg_to_a55: channel@0 { + adi,tru-master-id = <134>; /* trigger master SOFT3 */ + adi,tru-slave-id = <160>; /* TRU0_IRQ3 */ + }; + rpmsg_to_sharc0: channel@1 { + adi,tru-master-id = <135>; /* trigger master SOFT4 */ + adi,tru-slave-id = <164>; /* TRU0_IRQ7 */ + }; + rpmsg_to_sharc1: channel@2 { + adi,tru-master-id = <136>; /* trigger master SOFT5 */ + adi,tru-slave-id = <168>; /* TRU0_IRQ11 */ + }; +}; + +&thermal { + status = "okay"; + adi,trip-alert = <60>; + adi,trip-fault = <85>; + /* adi,average; */ +}; + +&uart0 { + /* enable-pin = <&ssw0 5 GPIO_ACTIVE_LOW>; UART0_EN */ + /* hwflow-en-pin = <&ssw0 6 GPIO_ACTIVE_LOW>; UART0_FLOW_EN */ + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_default>; + status = "disabled"; + + spidev@9{ + compatible = "adi,generic-spidev"; + reg = <9>; // SPI0_SSEL1 / GPIO_PA9 + spi-max-frequency = <4000000>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_default>; + status = "disabled"; + cs-gpios = <&gpa 13 GPIO_ACTIVE_LOW>; + + spidev@0 { + compatible = "adi,generic-spidev"; + reg = <0>; + spi-max-frequency = <4000000>; + }; +}; + +&spi3 { + pinctrl-names = "default"; + pinctrl-0 = <&spi3_default>; + status = "disabled"; + cs-gpios = <&gpg 15 GPIO_ACTIVE_LOW>; + + spidev@0 { + compatible = "adi,generic-spidev"; + reg = <0>; + spi-max-frequency = <4000000>; + }; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_quad>; + status = "okay"; + cs-gpios = <&gpa 5 GPIO_ACTIVE_LOW>; + + flash: is25lp512@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "is25lp512", "jedec,spi-nor"; + reg = <0>; + spi-cpha; + spi-cpol; + spi-max-frequency = <25000000>; + spi-rx-bus-width = <4>; + + qspi_0: partition@0 { + label = "U-Boot SPL"; + reg = <0x0 0x40000>; + }; + + qspi_1: partition@1 { + label = "U-Boot Proper"; + reg = <0x40000 0xC0000>; + }; + + qspi_2: partition@2 { + label = "U-Boot Environment"; + reg = <0x100000 0x20000>; + }; + + qspi_3: partition@3 { + label = "FIT Image"; + reg = <0x120000 0xF00000>; + }; + + qspi_4: partition@4 { + label = "JFFS2 Formatted RFS"; + reg = <0x1020000 0x2FE0000>; + }; + + }; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "disabled"; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + ssw0: gpio@20 { + compatible = "microchip,mcp23018"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x20>; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&ssw0pullups>; + + ssw0pullups: pinmux { + bias-pull-up; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio8", "gpio9"; + }; + + led1 { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led1-en"; + }; + + led2 { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led2-en"; + }; + + led3 { + gpio-hog; + gpios = <2 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led3-en"; + }; + + spi2d2-d3 { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2d2-d3-en"; + }; + + spi2flash-cs { + gpio-hog; + gpios = <4 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2flash-cs"; + }; + + uart0 { + gpio-hog; + gpios = <5 GPIO_ACTIVE_LOW>; + output-high; + line-name = "uart0-en"; + }; + + uart0-flow-en { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; + + emmc { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-high; + line-name = "emmc-en"; + }; + + emmc-som-en { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "emmc-som-en"; + }; + }; +}; + +&mmc0{ + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_8bgrp>; + bus-width = <8>; + max-frequency = <50000000>; + non-removable; + status = "okay"; +}; + +&usb0_phy { + reset-gpios = <&gpg 11 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&usb0 { + dr_mode = "host"; + pinctrl-names = "default"; + pinctrl-0 = <&usbc0_default>; + status = "okay"; +}; + +&pinctrl0 { + uart0_default: uart0_default_pins { + pins { + pinmux = , + ; + }; + }; + uart0_hwflow: uart0_hwflow_pins { + pins { + pinmux = , + , + , + ; + }; + }; + spi0_default: spi0_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi1_default: spi1_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi2_quad: spi2_quad_pins { + pins { + pinmux = , + , + , + , + ; + }; + }; + spi3_default: spi3_default_pins { + pins { + pinmux = , + , + ; + }; + }; + ospi_default: ospi_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + i2c2_pins: i2c2_default_pins { + pins { + pinmux = , + ; + }; + }; + eth0_default: eth0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + eth1_default: eth1_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + ; + }; + }; + mmc0_8bgrp: mmc0_8bgrp_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + usbc0_default: usbc0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/sc59x-64.dtsi b/arch/arm64/boot/dts/adi/sc59x-64.dtsi new file mode 100644 index 00000000000000..66249bcd9a3508 --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc59x-64.dtsi @@ -0,0 +1,1307 @@ +/* + * Copyright (c) 2021 Analog Devices Incorporated + * Author: Nathan Barrett-Morrison + */ + +#include +#include +#include + +/ { + model = "ADI 64-bit SC59X"; + compatible = "adi,sc59x-64"; + + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &uart0; + serial2 = &uart2; + serial3 = &uart3; + timer0 = &gptimer0; + timer1 = &gptimer1; + timer2 = &gptimer2; + timer3 = &gptimer3; + timer4 = &gptimer4; + timer5 = &gptimer5; + timer6 = &gptimer6; + timer7 = &gptimer7; + ethernet0 = &emac0; + ethernet1 = &emac1; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + spi3 = &spi3; +/* + can0 = &can0; + can1 = &can1; + rtc0 = &rtc0; +*/ + i2s4 = &i2s4; + mmc0 = &mmc0; + sru0 = &sru_ctrl_dai0; + sru1 = &sru_ctrl_dai1; + }; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0xdeadbeef>; + clocks = <&clk ADSP_SC598_CLK_ARM>, <&clk ADSP_SC598_CLK_DDR>; + }; + }; + + pmu { + /* compatible = "arm,cortex-a53-pmu"; */ + compatible = "arm,armv8-pmuv3"; + /*interrupts = ;*/ + interrupts = ; + interrupt-parent = <&gic>; + }; + + gic: interrupt-controller@31200000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x31200000 0x40000>, /* GIC Dist */ + <0x31240000 0x40000>; /* GICR */ + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , /* Physical Secure */ + , /* Physical Non-Secure */ + , /* Virtual */ + ; /* Hypervisor */ + }; + + sys_clkin0: oscillator@1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin0"; + }; + + sys_clkin1: oscillator@2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin1"; + }; + + clk: clocks@3108d000 { + compatible = "adi,sc598-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>, + <0x310a9000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + gptimers: gptimers@31018000 { + compatible = "adi,sc5xx-gptimers"; + reg = <0x31018000 0x200>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + gptimer0: gptimer@0 { + reg = <0>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x60>; + adi,is-clocksource; + adi,reset-timer; + }; + + gptimer1: gptimer@1 { + reg = <1>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x80>; + adi,is-clockevent; + adi,reset-timer; + }; + + gptimer2: gptimer@2 { + reg = <2>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xa0>; + }; + + gptimer3: gptimer@3 { + reg = <3>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xc0>; + }; + + gptimer4: gptimer@4 { + reg = <4>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xe0>; + }; + + gptimer5: gptimer@5 { + reg = <5>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x100>; + }; + + gptimer6: gptimer@6 { + reg = <6>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x120>; + }; + + gptimer7: gptimer@7 { + reg = <7>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x140>; + }; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + rsc_tbl0: rsc_tbl0@20000000 { + reg = <0x20000000 0x400>; /*1KiB*/ + no-map; + }; + + rsc_tbl1: rsc_tbl0@20000400 { + reg = <0x20000400 0x400>; /*1KiB*/ + no-map; + }; + + sharc_internal_icc@20005000 { + reg = <0x20005000 0x20000>; /*128KiB*/ + no-map; + }; + }; + + scb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pads_system_config: adi-control@31004600 { + compatible = "adi,pads-system-config"; + reg = <0x31004600 0x100>; + status = "okay"; + }; + + dmc_pmu: dmc-pmu@31070000 { + compatible = "adi,dmc-pmu"; + reg = <0x31070000 0x200>; + status = "okay"; + }; + + sram-controller@31080000 { + compatible = "adi,sram-controller"; + reg = <0x31080000 0x100>; + /* adi,sram = <&sram0>, <&sram1>; */ + interrupts = ; + status = "disabled"; + }; + + gptimer_counter: gptimer-counters@0 { + compatible = "adi,gptimer-counter"; + status = "okay"; + }; + + rcu: rcu@3108c000 { + compatible = "adi,reset-controller"; + reg = <0x3108c000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + adi,enable-reboot; + status = "okay"; + }; + + sec: sec@31089000 { + compatible = "adi,system-event-controller"; + reg = <0x31089000 0x1000>; + adi,rcu = <&rcu>; + adi,sharc-cores = <2>; + status = "okay"; + }; + + tru: tru@3108a000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108a000 0x1000>; + adi,max-master-id = <182>; + adi,max-slave-id = <187>; + status = "okay"; + }; + + thermal: thermal@31016800 { + compatible = "adi,sc59x-thermal"; + reg = <0x31016800 0x100>; + #thermal-sensor-cells = <0>; + interrupt-parent = <&gic>; + interrupts = , + ; + adi,gain = <0x4>; /* 10-bit two's complement */ + adi,offset = <0x7D40>; /* Q9.7 fixed point */ + status = "disabled"; + }; + + hadc: hadc@31016000 { + compatible = "adi,hadc"; + reg = <0x31016000 0x100>; + interrupt-parent = <&gic>; + interrupts = ; + #iio-cells = <1>; + status = "okay"; + }; + + rtc0: rtc@310C8000 { + compatible = "adi,rtc2"; + reg = <0x310C8000 0x100>; + /*interrupts = ;*/ + calibration = /bits/ 8 <0>; + status = "disabled"; + }; + + uart0: uart@31003000 { + compatible = "adi,uart4"; + reg = <0x31003000 0x40>; + dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + adi,use-edbo; + status = "disabled"; + }; + + uart1: uart@31003400 { + compatible = "adi,uart4"; + reg = <0x31003400 0x40>; + dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + adi,use-edbo; + status = "disabled"; + }; + + uart2: uart@31003800 { + compatible = "adi,uart4"; + reg = <0x31003800 0x40>; + dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + adi,use-edbo; + status = "disabled"; + }; + + uart3: uart@31003C00 { + compatible = "adi,uart4"; + reg = <0x31003C00 0x40>; + dmas = <&dma_cluster2 53>, <&dma_cluster2 54>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + adi,use-edbo; + status = "disabled"; + }; + + can0: can@31000200 { + compatible = "adi,can"; + reg = <0x31000200 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + can1: can@31000a00 { + compatible = "adi,can"; + reg = <0x31000a00 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + i2c0: twi@31001400 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001400 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c1: twi@31001500 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001500 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c2: twi@31001600 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001600 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c3: twi@31001000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001000 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c4: twi@31001100 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001100 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c5: twi@31001200 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001200 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2s4: i2s@4 { + compatible = "adi,sc5xx-i2s-dai"; + status = "disabled"; + reg = <0x31002400 0x80>, <0x31002480 0x80>; + sport-channel = <4>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport4_dma_cluster 10>, <&sport4_dma_cluster 11>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK1>; + clock-names = "sclk"; + }; + + i2s0: i2s0@0 { + compatible = "adi,sc5xx-i2s-dai"; + status = "disabled"; + reg = <0x31002000 0x80>, <0x31002080 0x80>; + sport-channel = <0>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport0_dma_cluster 0>, <&sport0_dma_cluster 1>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK1>; + clock-names = "sclk"; + }; + + watchdog@0x31008000 { + compatible = "adi,watchdog"; + reg = <0x31008000 0x10>; + timeout-sec = <30>; + clocks = <&clk ADSP_SC598_CLK_CGU0_SCLK0>; + clock-names = "adi-watchdog"; + }; + + spi0: spi@0x3102e000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102e000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 22>, <&spi_cluster 23>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi1: spi@0x3102f000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102f000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 24>, <&spi_cluster 25>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + spi2: spi@0x31030000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31030000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 26>, <&spi_cluster 27>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + spi3: spi@0x31031000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31031000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 55>, <&spi_cluster 56>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC598_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + ospi: spi@31027000 { + compatible = "adi,sc5xx-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x31027000 0x1000>, + <0x60000000 0x10000000>; + interrupts = ; + clocks = <&clk ADSP_SC598_CLK_OSPI_REFCLK>; + cdns,is-decoded-cs; + cdns,fifo-depth = <128>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x00000000>; + status = "disabled"; + }; + + emac0: ethernet@0x31040000 { + compatible = "adi,dwmac", "snps,dwmac-4.20a", "snps,dwmac-5.20a"; + reg = <0x31040000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,mixed-burst; + snps,pbl = <8>; + snps,force_sf_dma_mode; + snps,perfect-filter-entries = <32>; + snps,tso = <1>; + clocks = <&clk ADSP_SC598_CLK_GIGE>; + clock-names = "stmmaceth"; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + emac1: ethernet@0x31042000 { + compatible = "adi,dwmac", "snps,dwmac-4.20a", "snps,dwmac-5.20a"; + reg = <0x31042000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,fixed-burst; + snps,pbl = <1>; + snps,force_thresh_dma_mode; + clocks = <&emac1_clkin>; + clock-names = "stmmaceth"; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + crc0: crc@310a5000 { + compatible = "adi,hmac-crc"; + reg = <0x310a5000 0xFF>; + interrupts = ; + dmas = <&crc_cluster 8>; + dma-names = "mdma_chan"; + crypto_crc_poly = <0x04C11DB7>; + status = "disabled"; + }; + + crc1: crc@310a6000 { + compatible = "adi,hmac-crc"; + reg = <0x310a6000 0xFF>; + interrupts = ; + dmas = <&crc_cluster 18>; + dma-names = "mdma_chan"; + crypto_crc_poly = <0x04C11DB7>; + status = "disabled"; + }; + + pinctrl0: pinctrl@31004600 { + compatible = "adi,adsp-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x31004600 0x400>; + adi,port-sizes = <16 16 16 16 16 16 16 16 7>; + }; + + sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310C9000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + sru_ctrl_dai1: sru-ctrl-dai1@0x310CA000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310CA000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + emac1_clkin: emac1-clkin@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <50000000>; + clock-output-names = "emac1_clkin"; + }; + + mmc0: mmc@310C7000 { + compatible = "snps,dwcmshc-sdhci"; + reg = <0x310C7000 0x1000>; + interrupts = ; /* Status */ + /*;*/ /* Wakeup */ + clocks = <&clk ADSP_SC598_CLK_EMMC>; + clock-names = "core"; + bus-width = <8>; + status = "disabled"; + }; + + gp_counter: cnt@3100B000 { + compatible = "adi,gp_counter"; + reg = <0x3100B000 0xFF>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + lp0: linkport@0 { + compatible = "linkport0"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + lp1: linkport@1 { + compatible = "linkport1"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + pint0: pint@31005000 { + compatible = "adi,adsp-pint"; + reg = <0x31005000 0xFF>; + interrupts = ; + }; + + pint1: pint@31005100 { + compatible = "adi,adsp-pint"; + reg = <0x31005100 0xFF>; + interrupts = ; + }; + + pint2: pint@31005200 { + compatible = "adi,adsp-pint"; + reg = <0x31005200 0xFF>; + interrupts = ; + }; + + pint3: pint@31005300 { + compatible = "adi,adsp-pint"; + reg = <0x31005300 0xFF>; + interrupts = ; + }; + + pint4: pint@31005400 { + compatible = "adi,adsp-pint"; + reg = <0x31005400 0xFF>; + interrupts = ; + }; + + pint5: pint@31005500 { + compatible = "adi,adsp-pint"; + reg = <0x31005500 0xFF>; + interrupts = ; + }; + + pint6: pint@31005600 { + compatible = "adi,adsp-pint"; + reg = <0x31005600 0xFF>; + interrupts = ; + }; + + pint7: pint@31005700 { + compatible = "adi,adsp-pint"; + reg = <0x31005700 0xFF>; + interrupts = ; + }; + + gpa: gport@0x31004000 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004000 0x7F>; + gpio-ranges = <&pinctrl0 0 0 16>; + adi,pint = <&pint0 1>; + status = "okay"; + }; + + gpb: gport@31004080 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004080 0x7F>; + gpio-ranges = <&pinctrl0 0 16 16>; + adi,pint = <&pint0 0>; + status = "okay"; + }; + + gpc: gport@31004100 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004100 0x7F>; + gpio-ranges = <&pinctrl0 0 32 16>; + adi,pint = <&pint2 1>; + status = "okay"; + }; + + gpd: gport@31004180 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004180 0x7F>; + gpio-ranges = <&pinctrl0 0 48 16>; + adi,pint = <&pint2 0>; + }; + + gpe: gport@31004200 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004200 0x7F>; + gpio-ranges = <&pinctrl0 0 64 16>; + adi,pint = <&pint4 1>; + }; + + gpf: gport@31004280 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004280 0x7F>; + gpio-ranges = <&pinctrl0 0 80 16>; + adi,pint = <&pint4 0>; + }; + + gpg: gport@31004300 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004300 0x7F>; + gpio-ranges = <&pinctrl0 0 96 16>; + adi,pint = <&pint6 1>; + }; + + gph: gport@31004380 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004380 0x7F>; + gpio-ranges = <&pinctrl0 0 112 16>; + adi,pint = <&pint6 0>; + }; + + gpi: gport@31004400 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004400 0x7F>; + gpio-ranges = <&pinctrl0 0 128 7>; + adi,pint = <&pint7 1>; + }; + + usb0_phy: usbphy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; + status = "disabled"; + }; + + usb0: usb@310c5000 { + compatible = "adi,adsp2159x-usbc", "snps,dwc2"; + reg = <0x310c5000 0x2000>; + interrupts = ; + phys = <&usb0_phy>; + phy-names = "usb2-phy"; + status = "disabled"; + }; + + pkte1: pkte@310CD000 { + compatible = "adi,pkte"; + reg = <0x310CD000 0x400>; + interrupts = ; + status = "disabled"; + }; + +// dma0: dma@0 { +// compatible = "adi,dma2"; +// reg = <0x31022000 0x7F>; +// interrupts = ; +// spu_securep_id = <66>; +// }; +// +// dma1: dma@1 { +// compatible = "adi,dma2"; +// reg = <0x31022080 0x7F>; +// interrupts = ; +// spu_securep_id = <67>; +// }; +// +// dma2: dma@2 { +// compatible = "adi,dma2"; +// reg = <0x31022100 0x7F>; +// interrupts = ; +// spu_securep_id = <68>; +// }; +// +// dma3: dma@3 { +// compatible = "adi,dma2"; +// reg = <0x31022180 0x7F>; +// interrupts = ; +// spu_securep_id = <69>; +// }; +// +// dma4: dma@4 { +// compatible = "adi,dma2"; +// reg = <0x31022200 0x7F>; +// interrupts = ; +// spu_securep_id = <70>; +// }; +// +// dma5: dma@5 { +// compatible = "adi,dma2"; +// reg = <0x31022280 0x7F>; +// interrupts = ; +// spu_securep_id = <71>; +// }; +// +// dma6: dma@6 { +// compatible = "adi,dma2"; +// reg = <0x31022300 0x7F>; +// interrupts = ; +// spu_securep_id = <72>; +// }; +// +// dma7: dma@7 { +// compatible = "adi,dma2"; +// reg = <0x31022380 0x7F>; +// interrupts = ; +// spu_securep_id = <73>; +// }; +// +// dma8: dma@8 { +// compatible = "adi,dma2"; +// reg = <0x310A7000 0x7F>; +// interrupts = ; +// spu_securep_id = <74>; +// }; +// +// dma9: dma@9 { +// compatible = "adi,dma2"; +// reg = <0x310A7080 0x7F>; +// interrupts = ; +// spu_securep_id = <75>; +// }; +// +// dma12: dma@012 { +// compatible = "adi,dma2"; +// reg = <0x31023100 0x7F>; +// interrupts = ; +// spu_securep_id = <78>; +// }; +// +// dma13: dma@13 { +// compatible = "adi,dma2"; +// reg = <0x31023180 0x7F>; +// interrupts = ; +// spu_securep_id = <79>; +// }; +// +// dma14: dma@14 { +// compatible = "adi,dma2"; +// reg = <0x31023200 0x7F>; +// interrupts = ; +// spu_securep_id = <80>; +// }; +// +// dma15: dma@15 { +// compatible = "adi,dma2"; +// reg = <0x31023280 0x7F>; +// interrupts = ; +// spu_securep_id = <81>; +// }; +// +// dma16: dma@16 { +// compatible = "adi,dma2"; +// reg = <0x31023300 0x7F>; +// interrupts = ; +// spu_securep_id = <82>; +// }; +// +// dma17: dma@17 { +// compatible = "adi,dma2"; +// reg = <0x31023380 0x7F>; +// interrupts = ; +// spu_securep_id = <83>; +// }; +// +// dma18: dma@18 { +// compatible = "adi,dma2"; +// reg = <0x310A7100 0x7F>; +// interrupts = ; +// spu_securep_id = <84>; +// }; +// +// dma19: dma@19 { +// compatible = "adi,dma2"; +// reg = <0x310A7180 0x7F>; +// interrupts = ; +// spu_securep_id = <85>; +// }; +// +// dma28: dma@28 { +// compatible = "adi,dma2"; +// reg = <0x31026400 0x7F>; +// interrupts = ; +// spu_securep_id = <94>; +// }; +// +// dma29: dma@29 { +// compatible = "adi,dma2"; +// reg = <0x31026480 0x7F>; +// interrupts = ; +// spu_securep_id = <95>; +// }; +// +// dma30: dma@30 { +// compatible = "adi,dma2"; +// reg = <0x30FFF000 0x7F>; +// interrupts = ; +// spu_securep_id = <96>; +// }; +// +// dma36: dma@36 { +// compatible = "adi,dma2"; +// reg = <0x30FFF080 0x7F>; +// interrupts = ; +// spu_securep_id = <99>; +// }; +// +// dma43: dma@43 { +// compatible = "adi,dma2"; +// reg = <0x3109B000 0x7F>; +// interrupts = ; +// spu_securep_id = <104>; +// }; +// +// dma44: dma@44 { +// compatible = "adi,dma2"; +// reg = <0x3109B080 0x7F>; +// interrupts = ; +// spu_securep_id = <105>; +// }; +// +// dma45: dma@45 { +// compatible = "adi,dma2"; +// reg = <0x310A7200 0x7F>; +// interrupts = ; +// spu_securep_id = <106>; +// }; +// +// dma46: dma@46 { +// compatible = "adi,dma2"; +// reg = <0x310A7280 0x7F>; +// interrupts = ; +// spu_securep_id = <107>; +// }; +// +// dma47: dma@47 { +// compatible = "adi,dma2"; +// reg = <0x310A7300 0x7F>; +// interrupts = ; +// spu_securep_id = <108>; +// }; +// +// dma48: dma@48 { +// compatible = "adi,dma2"; +// reg = <0x310A7380 0x7F>; +// interrupts = ; +// spu_securep_id = <109>; +// }; +// +// dma49: dma@49 { +// compatible = "adi,dma2"; +// reg = <0x310AC000 0x7F>; +// interrupts = ; +// spu_securep_id = <110>; +// }; +// +// dma50: dma@50 { +// compatible = "adi,dma2"; +// reg = <0x310AC080 0x7F>; +// interrupts = ; +// spu_securep_id = <111>; +// }; +// +// dma51: dma@51 { +// compatible = "adi,dma2"; +// reg = <0x3109C000 0x7F>; +// interrupts = ; +// spu_securep_id = <112>; +// }; +// +// dma52: dma@52 { +// compatible = "adi,dma2"; +// reg = <0x3109C080 0x7F>; +// interrupts = ; +// spu_securep_id = <113>; +// }; +// +// dma53: dma@53 { +// compatible = "adi,dma2"; +// reg = <0x31026380 0x7F>; +// interrupts = ; +// spu_securep_id = <114>; +// }; +// +// dma54: dma@54 { +// compatible = "adi,dma2"; +// reg = <0x31026300 0x7F>; +// interrupts = ; +// spu_securep_id = <115>; +// }; + + sport0_dma_cluster: dma@0x31022000 { + compatible = "adi,dma-controller"; + reg = <0x31022000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport0a: channel@0 { + adi,id = <0>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport0b: channel@1 { + adi,id = <1>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + sport4_dma_cluster: dma@0x31023000 { + compatible = "adi,dma-controller"; + reg = <0x31023000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport4a: channel@10 { + adi,id = <10>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport4b: channel@11 { + adi,id = <11>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + spi_cluster: dma@0x3102d000 { + compatible = "adi,dma-controller"; + reg = <0x3102d000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + spi0_tx: channel@22 { + adi,id = <22>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + spi0_rx: channel@23 { + adi,id = <23>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + spi1_tx: channel@24 { + adi,id = <24>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + spi1_rx: channel@25 { + adi,id = <25>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + spi2_tx: channel@26 { + adi,id = <26>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + spi2_rx: channel@27 { + adi,id = <27>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + spi3_tx: channel@55 { + adi,id = <55>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + + spi3_rx: channel@56 { + adi,id = <56>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + + }; + + crc_cluster: dma@0x310a7000 { + compatible = "adi,dma-controller"; + reg = <0x310a7000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + crc0_dma: channel@8 { /* MDMA0_SRC */ + adi,id = <8>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x0>; + }; + + crc1_dma: channel@18 { /* MDMA1_SRC */ + adi,id = <18>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + }; + + dma_cluster2: dma@0x31026000 { + compatible = "adi,dma-controller"; + reg = <0x31026000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + uart0_tx: channel@20 { + adi,id = <20>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + uart0_rx: channel@21 { + adi,id = <21>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + uart1_tx: channel@34 { + adi,id = <34>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + uart1_rx: channel@35 { + adi,id = <35>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + uart2_tx: channel@37 { + adi,id = <37>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + uart2_rx: channel@38 { + adi,id = <38>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + uart3_tx: channel@53 { + adi,id = <53>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + + uart3_rx: channel@54 { + adi,id = <54>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + }; + + mdma: dma@0x3109a000 { + compatible = "adi,mdma-controller"; + reg = <0x3109a000 0x1000>; + status = "okay"; + + sdma2: channel@40 { + adi,id = <40>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + + trng: rng@0x310D0000 { + compatible = "adi,sc5xx-trng"; + reg = <0x310D0000 0x74>, <0x310D8000 0x14>; + interrupts = ; + interrupt-names = "pkic0_irq"; + startup-cycles = <0xff>; + minref-cycles = <0x21>; + maxref-cycles = <0x22>; + alarm-thresh = <0xff>; + shdn-thresh = <0x04>; + poll-data = <0>; /* Use IRQ for data */ + status = "okay"; + }; + + }; +}; From d4da82adb8e3298d43dd39f1488199a057a877a2 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 11 Jul 2025 13:27:47 +0100 Subject: [PATCH 34/85] arm64: dts: adi: Support EV-SC598-SOM with EZLITE carrier Signed-off-by: Utsav Agarwal --- arch/arm64/boot/dts/adi/sc598-som-ezlite.dts | 212 +++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 arch/arm64/boot/dts/adi/sc598-som-ezlite.dts diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts new file mode 100644 index 00000000000000..ca42b7a4afddfe --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021 Analog Devices Incorporated + * Author: Nathan Barrett-Morrison + */ + +/dts-v1/; + +#include "sc598-som.dtsi" + +/ { + model = "ADI 64-bit SC598 SOM EZ Lite"; + compatible = "adi,sc598-som-ezlite", "adi,sc59x-64"; + + clocks { + compatible = "simple-bus"; + mclk: mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + clock-output-names = "mclk"; + }; + }; + + scb { + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s0>; + adi,codec = <&adau1372>; + }; + }; +}; + +&i2c2 { + gpio_expander: adp5588@30 { + compatible = "adi,adp5588-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x30>; + status = "okay"; + + usb-spi0 { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_spi0_en"; + }; + + usb-spi1 { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_spi1_en"; + }; + + usb-qspi-en { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_qspi_en"; + }; + + usb-qspi-reset { + gpio-hog; + gpios = <11 GPIO_ACTIVE_LOW>; + output-high; + line-name = "usb_qspi_reset"; + }; + + eth0-reset { + gpio-hog; + gpios = <12 GPIO_ACTIVE_LOW>; + output-low; + line-name = "eth0-reset"; + }; + + adau1372-pwrdwn { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-low; + line-name = "adau1372_pwrdwn"; + }; + + led1 { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led1-en"; + }; + + led2 { + gpio-hog; + gpios = <16 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led2-en"; + }; + + led3 { + gpio-hog; + gpios = <17 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led3-en"; + }; + }; + + adau1372: adau1372@0x3c { + compatible = "adi,adau1372"; + reg = <0x3c>; + clock-names = "mclk"; + clocks = <&mclk>; + }; +}; + +&emac0 { + snps,reset-active-low; + snps,reset-delays-us = <0 200 500>; + phy-handle = <&adin1300>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + snps,mtl-rx-config = <&emac0rxconfig>; + snps,mtl-tx-config = <&emac0txconfig>; + + emac0txconfig: tx-config { + snps,tx-queues-to-use = <3>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,dcb-algorithm; + }; + }; + + emac0rxconfig: rx-config { + snps,rx-queues-to-use = <1>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,dcb-algorithm; + }; + }; + + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + adin1300: ethernet-phy@0 { + reg = <0>; + }; + }; + +}; + +&emac1 { + status = "disabled"; +}; + +&sru_ctrl_dai0 { + status = "okay"; + + sru_dai0: sru_dai0_mux { + route { + sru-routing = + /* 1362 TX LRCLK */ + , /* set DAI0_PIN01 to input */ + , /* route DAI0_PIN01 to SPT0_AFS */ + + /* 1363 TX BCLK */ + , /* set DAI0_PIN02 to input */ + , /* route DAI0_PIN02 to SPT0_ACLK */ + + /* 1363 TX DAC_SDATA/MP0 */ + , /* set DAI0_PIN03 to output */ + , /* route SPT0_AD0 to DAI0_PIN03 */ + + /* 1362 RX LRCLK */ + , /* set DAI0_PIN01 to input */ + , /* route DAI0_PIN01 to SPT0_BFS */ + + /* 1363 RX BCLK */ + , /* set DAI0_PIN02 to input */ + , /* route DAI0_PIN02 to SPT0_BCLK */ + + /* 1363 RX ADC_SDATA0/MP1 */ + , /* set DAI0_PIN04 to input */ + , /* route DAI0_PIN04 to SPT0_BD0 */ + + /* 1363 RX ADC_SDATA1/MP6 */ + , /* set DAI0_PIN05 to input */ + ; /* route DAI0_PIN05 to SPT0_BD1 */ + }; + }; +}; + +&i2s0 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai0>; + status = "okay"; +}; From 8e40146bd14f470f42ade7d0850a74ebb6564bed Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 11:11:56 +0100 Subject: [PATCH 35/85] ARM: dts: adi: sc594: add device tree Co-developed-by: Utsav Agarwal Signed-off-by: Utsav Agarwal Co-developed-by: Caleb Ethridge Signed-off-by: Caleb Ethridge Signed-off-by: Philip Molloy --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/adi/Makefile | 3 + arch/arm/boot/dts/adi/sc594-som-ezkit.dts | 233 ++++ arch/arm/boot/dts/adi/sc594-som.dtsi | 504 ++++++++ arch/arm/boot/dts/adi/sc59x.dtsi | 1283 +++++++++++++++++++++ 5 files changed, 2024 insertions(+) create mode 100644 arch/arm/boot/dts/adi/Makefile create mode 100644 arch/arm/boot/dts/adi/sc594-som-ezkit.dts create mode 100644 arch/arm/boot/dts/adi/sc594-som.dtsi create mode 100644 arch/arm/boot/dts/adi/sc59x.dtsi diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index efe38eb2530164..8c1be990426c08 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 subdir-y += actions +subdir-y += adi subdir-y += airoha subdir-y += allwinner subdir-y += alphascale diff --git a/arch/arm/boot/dts/adi/Makefile b/arch/arm/boot/dts/adi/Makefile new file mode 100644 index 00000000000000..1b844da68e90a2 --- /dev/null +++ b/arch/arm/boot/dts/adi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SC5XX) += \ + sc594-som-ezkit.dtb diff --git a/arch/arm/boot/dts/adi/sc594-som-ezkit.dts b/arch/arm/boot/dts/adi/sc594-som-ezkit.dts new file mode 100644 index 00000000000000..f845a3b0ac36a8 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc594-som-ezkit.dts @@ -0,0 +1,233 @@ +/* + * Device tree for ADI sc594-som-ezkit board + * + * Copyright 2014 - 2020 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +/dts-v1/; + +#include "sc594-som.dtsi" + +/ { + model = "ADI sc594-som-ezkit"; + compatible = "adi,sc594-som-ezkit", "adi,sc59x"; + + scb { + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s4>; + adi,codec = <&adau1962>, <&adau1979>; + }; + }; +}; + +&i2c2 { + ssw1: gpio@22 { + compatible = "microchip,mcp23017"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x22>; + + eeprom { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-low; + line-name = "eeprom-en"; + }; + + pushbutton { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-low; /*output-high;*/ + line-name = "pushbutton-en"; + }; + + microsd { + gpio-hog; + gpios = <2 GPIO_ACTIVE_LOW>; + output-low; + line-name = "microsd-spi"; + }; + + adau-reset { + gpio-hog; + gpios = <5 GPIO_ACTIVE_LOW>; + output-low; + line-name = "adau1962-reset"; + }; + + adau1962 { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1962-en"; + }; + + adau1979 { + gpio-hog; + gpios = <7 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1979-en"; + }; + + octal { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-low; + line-name = "octal-spi-cs-en"; + }; + + spdif-dig { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-digital-en"; + }; + + spdif-opt { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-optical-en"; + }; + + audio-jack { + gpio-hog; + gpios = <11 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "audio-jack-sel"; + }; + + mlb { + gpio-hog; + gpios = <12 0x0>; + output-high; + line-name = "~mlb-en"; + }; + + eth1 { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-high; + line-name = "eth1-en"; + }; + + eth1-reset { + gpio-hog; + gpios = <14 GPIO_ACTIVE_LOW>; +/* USB0 lines are shared with Eth1 so Eth PHY must be held in reset + when using the USB */ + output-high; + line-name = "eth1-reset"; + }; + + gige-reset { + gpio-hog; + gpios = <15 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "gige-reset"; + }; + + }; + + adau1979: adau1979@11 { + compatible = "adi,adau1977"; + reg = <0x11>; + }; + + adau1962: adau1962@4 { + compatible = "adi,adau1962"; + reg = <0x4>; + }; + +}; + +&can0 { + pinctrl-names = "default"; + /*pinctrl-0 = <&can0_default>;*/ + phy-name = "tja1055"; + phy-gpios = <&gpb 8 0>, /* en PB8 */ + <&gpb 2 0x1>; /* stb PB2, GPIO_ACTIVE_LOW */ + status = "disabled"; +}; + +&can1 { + pinctrl-names = "default"; + /*pinctrl-0 = <&can1_default>;*/ + phy-name = "tja1145"; + phy-spibus = /bits/ 16 <0>; + phy-spiclk = <1000000>; + phy-spics = /bits/ 16 <44>; /* GPIO_PC12 */ + status = "disabled"; +}; + +&emac0 { + snps,reset-active-low; + snps,reset-delays-us = <0 200 500>; + phy-handle = <&dp83867>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + dp83867: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + }; + }; +}; + +&emac1 { + phy-handle = <&dp83848>; + phy-mode = "rmii"; + pinctrl-names = "default"; + pinctrl-0 = <ð1_default>; + status = "disabled"; + + mdio1 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + dp83848: ethernet-phy@1 { + reg = <1>; + }; + }; +}; + +&i2s4 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai1>; + status = "okay"; +}; + +&sru_ctrl_dai1 { + status = "okay"; + + sru_dai1: sru_dai1_mux { + route { + sru-routing = + , /* set DAI1_PIN0B to input */ + , /* route DAI1_PIN0B to SPT4_ACLK */ + , /* set DAI1_PIN04 to input */ + , /* route DAI1_PIN04 to SPT4_AFS */ + , /* set DAI1_PIN01 to output */ + , /* route SPT4_AD0 to DAI1_PIN01 */ + , /* set DAI1_PIN12 to input */ + , /* route DAI1_PIN12 to SPT4_BCLK */ + , /* set DAI1_PIN20 to input */ + , /* route DAI1_PIN20 to SPT4_BFS */ + , /* set DAI1_PIN06 to input */ + ; /* route DAI1_PIN06 to SPT4_BD0 */ + }; + }; +}; diff --git a/arch/arm/boot/dts/adi/sc594-som.dtsi b/arch/arm/boot/dts/adi/sc594-som.dtsi new file mode 100644 index 00000000000000..dfa87c9b407e25 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc594-som.dtsi @@ -0,0 +1,504 @@ +/* + * Device tree for ADI sc594-som-ezkit board + * + * Copyright 2014 - 2020 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "sc59x.dtsi" + +/ { + model = "ADI sc594-som-ezkit"; + compatible = "adi,sc594-som-ezkit", "adi,sc59x"; + + chosen { + stdout-path = &uart1; + bootargs = "root=/dev/mtdblock4 rw rootfstype=jffs2 earlyprintk=serial,uart0,115200 console=ttySC0,115200 vmalloc=512M mem=512M"; + }; + + aliases { + }; + + memory@80000000 { /*4 Gbit DDR*/ + device_type = "memory"; + reg = <0xa0000000 0x20000000>; + }; + + reserved-memory { + vdev0vrings: vdev0vring0@20080000 { + reg = <0x20080000 0x4000>; + no-map; + }; + + vdev0buffer: vdev0buffer@20084000 { + compatible = "shared-dma-pool"; + reg = <0x20084000 0x20000>; + no-map; + }; + + vdev1vrings: vdev0vring0@200A4000 { + reg = <0x200A4000 0x4000>; + no-map; + }; + + vdev1buffer: vdev0buffer@200A8000 { + compatible = "shared-dma-pool"; + reg = <0x200A8000 0x20000>; + no-map; + }; + }; + + scb { + +/* The button GPIO conflicts with OSPI0_D4 for this + button0: button@0 { + compatible = "adi,button-led"; + button_gpio = <48>; /* PD_00 + led_gpio = <35>; /* PC_03 / LED9 / LED1_KIT + }; +*/ + +/* This also appears to interfere with the OSPI somehow... + possible trace noise/interference? + button1: button@1 { + compatible = "adi,button-led"; + button_gpio = <112>; /* PH_00 + led_gpio = <34>; /* PC_02 / LED8 / LED2_KIT + }; +*/ + + sharc0: core1-rproc@28240000 { + compatible = "adi,remoteproc"; + reg = <0x28240000 0x160000>, + <0x20000000 0x200000>; + core-id = <1>; + core-irq = <74>; /* SOFT1 */ + firmware-name = "adi_adsp_core1_fw.ldr"; + interrupts = ; /* TRU0_SLV3 */ + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20000000 0x20200000>; + adi,rsc-table = <&rsc_tbl0>; + adi,verify = <1>; + adi,tru = <&tru>; + adi,tru-master-id = <140>; /* trigger master SOFT4 */ + status = "okay"; + }; + + sharc1: core2-rproc@28a40000 { + compatible = "adi,remoteproc"; + reg = <0x28a40000 0x160000>, + <0x20000000 0x200000>; + core-id = <2>; + core-irq = <75>; /* SOFT2 */ + firmware-name = "adi_adsp_core2_fw.ldr"; + interrupts = ; /* TRU0_SLV3 */ + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20000000 0x20200000>; + adi,rsc-table = <&rsc_tbl1>; + adi,verify = <1>; + adi,tru = <&tru>; + adi,tru-master-id = <141>; /* trigger master SOFT5 */ + status = "okay"; + }; + + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + status = "okay"; +}; + +&rtc0 { + status = "disabled"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_default>; + status = "disabled"; + + cs-gpios = <&gpc 12 GPIO_ACTIVE_LOW>, + <&gpc 0 GPIO_ACTIVE_LOW>; + + spidev@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "rohm,dh2228fv"; + spi-max-frequency = <5000000>; + reg = <0>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_default>; + status = "disabled"; + + cs-gpios = <&gpa 13 GPIO_ACTIVE_LOW>; + + spidev@0{ + compatible = "rohm,dh2228fv"; + spi-max-frequency = <5000000>; + reg = <0>; + }; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_quad>; + status = "okay"; + + cs-gpios = <&gpa 5 GPIO_ACTIVE_LOW>; + + flash: is25lp512@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "is25lp512", "jedec,spi-nor"; + spi-max-frequency = <5000000>; + reg = <0>; + spi-cpol; + spi-cpha; + spi-rx-bus-width = <4>; + /*adi,enable-dma;*/ + + qspi_0: partition@0 { + label = "U-Boot SPL"; + reg = <0x0 0x40000>; + }; + + qspi_1: partition@1 { + label = "U-Boot Proper"; + reg = <0x40000 0xC0000>; + }; + + qspi_2: partition@2 { + label = "U-Boot Environment"; + reg = <0x100000 0x20000>; + }; + + qspi_3: partition@3 { + label = "FIT Image"; + reg = <0x120000 0xF00000>; + }; + + qspi_4: partition@4 { + label = "JFFS2 Formatted RFS"; + reg = <0x1020000 0x2FE0000>; + }; + + }; + +}; + +&ospi { + pinctrl-names = "default"; + pinctrl-0 = <&ospi_default>; + + status = "disabled"; + + flash0: is25lx256@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "is25lx256", "jedec,spi-nor"; + reg = <0>; + + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + spi-max-frequency = <125000000>; + + cdns,read-delay = <4>; + cdns,tshsl-ns = <50>; + cdns,tsd2d-ns = <255>; + cdns,tchsh-ns = <8>; + cdns,tslch-ns = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + ospi_0: partition@0 { + label = "U-Boot SPL"; + reg = <0x0 0x40000>; + }; + + ospi_1: partition@1 { + label = "U-Boot Proper"; + reg = <0x40000 0xC0000>; + }; + + ospi_2: partition@2 { + label = "U-Boot Environment"; + reg = <0x100000 0x20000>; + }; + + ospi_3: partition@3 { + label = "FIT Image"; + reg = <0x120000 0xF00000>; + }; + + ospi_4: partition@4 { + label = "JFFS2 Formatted RFS"; + reg = <0x1020000 0xFE0000>; + }; + }; + }; +}; + +&i2c0 { + status = "disabled"; +}; + +&i2c1 { + status = "disabled"; +}; + +&i2c2 { + status = "okay"; + + ssw0: gpio@21 { + compatible = "microchip,mcp23017"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x21>; + + led1 { + gpio-hog; + gpios = <0 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "led1-en"; + }; + + led2 { + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "led2-en"; + }; + + led3 { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "led3-en"; + }; + + spi2flash-cs { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2flash-cs"; + }; + + spi2d2-d3 { + gpio-hog; + gpios = <4 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2d2-d3-en"; + }; + + uart0 { + gpio-hog; + gpios = <5 GPIO_ACTIVE_LOW>; + output-high; + line-name = "uart0-en"; + }; + + uart0-flow-en { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; + + ospiflash-cs { + gpio-hog; + gpios = <7 GPIO_ACTIVE_LOW>; + output-high; + line-name = "ospiflash-cs"; + }; + }; +}; + +&crc0 { + status = "disabled"; +}; + +&crc1 { + status = "disabled"; +}; + +&usb0_phy { + reset-gpios = <&gpg 11 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&usb0 { + dr_mode = "host"; + pinctrl-names = "default"; + pinctrl-0 = <&usbc0_default>; + status = "okay"; +}; + +&mmc0 { + /* wp-en-pin = <&ssw0 ? GPIO_ACTIVE_LOW>; SD_WP_EN */ + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_8bgrp>; + supports-highspeed; + status = "disabled"; +}; + +&sram_mmap { + status = "okay"; +}; + +&pinctrl0 { + uart0_default: uart0_default_pins { + pins { + pinmux = , + ; + }; + }; + uart0_hwflow: uart0_hwflow_pins { + pins { + pinmux = , + , + , + ; + }; + }; + spi0_default: spi0_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi1_default: spi1_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi2_quad: spi2_quad_pins { + pins { + pinmux = , + , + , + , + ; + }; + }; + ospi_default: ospi_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + i2c2_pins: i2c2_default_pins { + pins { + pinmux = , + ; + }; + }; + eth0_default: eth0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + eth1_default: eth1_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + ; + }; + }; + mmc0_8bgrp: mmc0_8bgrp_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + usbc0_default: usbc0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; +}; + +&tru { + rpmsg_to_a55: channel@0 { + adi,tru-master-id = <139>; /* trigger master SOFT3 */ + adi,tru-slave-id = <160>; /* TRU0_IRQ3 */ + }; + rpmsg_to_sharc0: channel@1 { + adi,tru-master-id = <140>; /* trigger master SOFT4 */ + adi,tru-slave-id = <164>; /* TRU0_IRQ7 */ + }; + rpmsg_to_sharc1: channel@2 { + adi,tru-master-id = <141>; /* trigger master SOFT5 */ + adi,tru-slave-id = <168>; /* TRU0_IRQ11 */ + }; +}; diff --git a/arch/arm/boot/dts/adi/sc59x.dtsi b/arch/arm/boot/dts/adi/sc59x.dtsi new file mode 100644 index 00000000000000..27de4e8a57c754 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc59x.dtsi @@ -0,0 +1,1283 @@ +/* + * Device tree header for ADI sc59x processor + * + * Copyright 2014 - 2018 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +#include +#include +#include + +/ { + model = "ADI sc59x"; + compatible = "adi,sc59x"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &uart0; + timer0 = &gptimer0; + timer1 = &gptimer1; + timer2 = &gptimer2; + timer3 = &gptimer3; + timer4 = &gptimer4; + timer5 = &gptimer5; + timer6 = &gptimer6; + timer7 = &gptimer7; + ethernet0 = &emac0; + ethernet1 = &emac1; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; +/* + can0 = &can0; + can1 = &can1; + rtc0 = &rtc0; +*/ + + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2s4 = &i2s4; + mmc0 = &mmc0; + sru0 = &sru_ctrl_dai0; + sru1 = &sru_ctrl_dai1; + }; + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a5"; + reg = <0x0>; + clocks = <&clk ADSP_SC594_CLK_ARM>; + }; + }; + + pmu { + compatible = "arm,cortex-a5-pmu"; + interrupts = ; + }; + + gic: interrupt-controller@310B2000 { + compatible = "arm,cortex-a5-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x310B2000 0x1000>, + <0x310B4000 0x100>; + }; + + L2: cache-controller@10000000 { + compatible = "arm,pl310-cache"; + reg = <0x10000000 0x1000>; + cache-level = <2>; + }; + + sram0: sram-icc@20000000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20025000 0x1B000>; + ranges = <0 0x20025000 0x1B000>; /*108KiB*/ + }; + + sram1: sram-reserved@20040000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20040000 0x40000>; + ranges = <0 0x20040000 0x40000>; /*256KiB*/ + }; + + sys_clkin0: sys-clkin0@1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin0"; + }; + + sys_clkin1: sys-clkin1@2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin1"; + }; + + clk: clocks@3108d000 { + compatible = "adi,sc594-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + gptimers: gptimers@31018000 { + compatible = "adi,sc5xx-gptimers"; + reg = <0x31018000 0x200>; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + gptimer0: gptimer@0 { + reg = <0>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x60>; + adi,is-clocksource; + adi,reset-timer; + }; + + gptimer1: gptimer@1 { + reg = <1>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x80>; + adi,is-clockevent; + adi,reset-timer; + }; + + gptimer2: gptimer@2 { + reg = <2>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xa0>; + }; + + gptimer3: gptimer@3 { + reg = <3>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xc0>; + }; + + gptimer4: gptimer@4 { + reg = <4>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xe0>; + }; + + gptimer5: gptimer@5 { + reg = <5>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x100>; + }; + + gptimer6: gptimer@6 { + reg = <6>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x120>; + }; + + gptimer7: gptimer@7 { + reg = <7>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x140>; + }; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + rsc_tbl0: rsc_tbl0@20000000 { + reg = <0x20000000 0x400>; /*1KiB*/ + no-map; + }; + + rsc_tbl1: rsc_tbl0@20000400 { + reg = <0x20000400 0x400>; /*1KiB*/ + no-map; + }; + + sram_B1_unused@20000800 { + reg = <0x20000800 0x4800>; /*18KiB*/ + no-map; + }; + + sharc_internal_icc@20005000 { + reg = <0x20005000 0x20000>; /*128KiB*/ + no-map; + }; + }; + + scb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pads_system_config: adi-control@31004600 { + compatible = "adi,pads-system-config"; + reg = <0x31004600 0x100>; + status = "okay"; + }; + + sram-controller@31080000 { + compatible = "adi,sram-controller"; + reg = <0x31080000 0x100>; + adi,sram = <&sram0>, <&sram1>; + interrupts = ; + status = "okay"; + }; + + sram_mmap: sram-mmap@0 { /* mmap from sram1 pool*/ + compatible = "adi,sram-mmap"; + adi,sram = <&sram1>; + status = "disabled"; + }; + + gptimer_counter: gptimer-counters@0 { + compatible = "adi,gptimer-counter"; + status = "okay"; + }; + + rcu: rcu@3108C000 { + compatible = "adi,reset-controller"; + reg = <0x3108C000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + adi,enable-reboot; + status = "okay"; + }; + + sec: sec@31089000 { + compatible = "adi,system-event-controller"; + reg = <0x31089000 0x1000>; + adi,rcu = <&rcu>; + adi,sharc-cores = <2>; + status = "okay"; + }; + + tru: tru@3108A000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108A000 0x1000>; + adi,max-master-id = <177>; + adi,max-slave-id = <188>; + status = "okay"; + }; + + rtc0: rtc@310C8000 { + compatible = "adi,rtc2"; + reg = <0x310C8000 0x100>; + /*interrupts = ;*/ + calibration = /bits/ 8 <0>; + status = "disabled"; + }; + + uart0: uart@31003000 { + compatible = "adi,uart4"; + reg = <0x31003000 0x40>; + dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart1: uart@31003400 { + compatible = "adi,uart4"; + reg = <0x31003400 0x40>; + dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart2: uart@31003800 { + compatible = "adi,uart4"; + reg = <0x31003800 0x40>; + dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + can0: can@31000200 { + compatible = "adi,can"; + reg = <0x31000200 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + can1: can@31000a00 { + compatible = "adi,can"; + reg = <0x31000a00 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + i2c0: twi@31001400 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001400 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c1: twi@31001500 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001500 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c2: twi@31001600 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001600 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2s4: i2s@4 { + compatible = "adi,sc5xx-i2s-dai"; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk"; + status = "disabled"; + reg = <0x31002400 0x80>, <0x31002480 0x80>; + sport-channel = <4>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport4_dma_cluster 10>, <&sport4_dma_cluster 11>; + dma-names = "tx", "rx"; + }; + + i2s0: i2s0@0 { + compatible = "adi,sc5xx-i2s-dai"; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "sclk"; + status = "disabled"; + reg = <0x31002000 0x80>, <0x31002080 0x80>; + sport-channel = <0>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport0_dma_cluster 0>, <&sport0_dma_cluster 1>; + dma-names = "tx", "rx"; + }; + + watchdog@31008000 { + compatible = "adi,watchdog"; + reg = <0x31008000 0x10>; + timeout-sec = <30>; + clocks = <&clk ADSP_SC594_CLK_CGU0_SCLK0>; + clock-names = "adi-watchdog"; + }; + + spi0: spi@3102e000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102e000 0xFF>; + interrupts = ; + dma-channel = <22>, <23>; + clocks = <&clk ADSP_SC594_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + spi1: spi@3102f000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102f000 0xFF>; + interrupts = ; + dma-channel = <24>, <25>; + dmas = <&spi_cluster 24>, <&spi_cluster 25>; + clocks = <&clk ADSP_SC594_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + spi2: spi@31030000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31030000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 26>, <&spi_cluster 27>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC594_CLK_SPI>; + clock-names = "spi"; + status = "disabled"; + }; + + ospi: spi@31027000 { + compatible = "adi,sc5xx-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x31027000 0x1000>, + <0x60000000 0x10000000>; + interrupts = ; + clocks = <&clk ADSP_SC594_CLK_OSPI>; + clock-names = "ospi"; + cdns,is-decoded-cs; + cdns,fifo-depth = <128>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x00000000>; + status = "disabled"; + }; + + emac0: ethernet@31040000 { + compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; + reg = <0x31040000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,pbl = <8>; + snps,perfect-filter-entries = <32>; + clocks = <&clk ADSP_SC594_CLK_GIGE>; + clock-names = "stmmaceth"; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + emac1: ethernet@31042000 { + compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; + reg = <0x31042000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,fixed-burst; + snps,burst_len = <0x4>; /* BLEN8 */ + snps,pbl = <1>; + snps,force_thresh_dma_mode; + clocks = <&clk ADSP_SC594_CLK_GIGE>; + clock-names = "stmmaceth"; + status = "disabled"; + }; + + crc0: crc@31001200 { + compatible = "adi,hmac-crc"; + reg = <0x31001200 0xFF>; + interrupts = ; + dma_channel = <8>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + crc1: crc@31001300 { + compatible = "adi,hmac-crc"; + reg = <0x31001300 0xFF>; + interrupts = ; + dma_channel = <18>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + pinctrl0: pinctrl@31004600 { + compatible = "adi,adsp-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x31004600 0x400>; + adi,port-sizes = <16 16 16 16 16 16 16 16 7>; + }; + + sru_ctrl_dai0: sru-ctrl-dai0@310C9000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310C9000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + sru_ctrl_dai1: sru-ctrl-dai1@310CA000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310CA000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + mmc0: mmc@31010000 { + compatible = "snps,dw-mshc"; + reg = <0x31010000 0xFFF>; + interrupts = ; + spu_securep_id = <58>; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <1024>; + status = "disabled"; + }; + + gp_counter: cnt@3100B000 { + compatible = "adi,gp_counter"; + reg = <0x3100B000 0xFF>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + lp0: linkport@0 { + compatible = "linkport0"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + lp1: linkport@1 { + compatible = "linkport1"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + pint0: pint@31005000 { + compatible = "adi,adsp-pint"; + reg = <0x31005000 0xFF>; + interrupts = ; + }; + + pint1: pint@31005100 { + compatible = "adi,adsp-pint"; + reg = <0x31005100 0xFF>; + interrupts = ; + }; + + pint2: pint@31005200 { + compatible = "adi,adsp-pint"; + reg = <0x31005200 0xFF>; + interrupts = ; + }; + + pint3: pint@31005300 { + compatible = "adi,adsp-pint"; + reg = <0x31005300 0xFF>; + interrupts = ; + }; + + pint4: pint@31005400 { + compatible = "adi,adsp-pint"; + reg = <0x31005400 0xFF>; + interrupts = ; + }; + + pint5: pint@31005500 { + compatible = "adi,adsp-pint"; + reg = <0x31005500 0xFF>; + interrupts = ; + }; + + pint6: pint@31005600 { + compatible = "adi,adsp-pint"; + reg = <0x31005600 0xFF>; + interrupts = ; + }; + + pint7: pint@31005700 { + compatible = "adi,adsp-pint"; + reg = <0x31005700 0xFF>; + interrupts = ; + }; + + gpa: gport@31004000 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004000 0x7F>; + gpio-ranges = <&pinctrl0 0 0 16>; + adi,pint = <&pint0 1>; + adi,gpio-base = <0>; + }; + + gpb: gport@31004080 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004080 0x7F>; + gpio-ranges = <&pinctrl0 0 16 16>; + adi,pint = <&pint0 0>; + adi,gpio-base = <16>; + }; + + gpc: gport@31004100 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004100 0x7F>; + gpio-ranges = <&pinctrl0 0 32 16>; + adi,pint = <&pint2 1>; + adi,gpio-base = <32>; + }; + + gpd: gport@31004180 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004180 0x7F>; + gpio-ranges = <&pinctrl0 0 48 16>; + adi,pint = <&pint2 0>; + adi,gpio-base = <48>; + }; + + gpe: gport@31004200 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004200 0x7F>; + gpio-ranges = <&pinctrl0 0 64 16>; + adi,pint = <&pint4 1>; + adi,gpio-base = <64>; + }; + + gpf: gport@31004280 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004280 0x7F>; + gpio-ranges = <&pinctrl0 0 80 16>; + adi,pint = <&pint4 0>; + adi,gpio-base = <80>; + }; + + gpg: gport@31004300 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004300 0x7F>; + gpio-ranges = <&pinctrl0 0 96 16>; + adi,pint = <&pint6 1>; + adi,gpio-base = <96>; + }; + + gph: gport@31004380 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004380 0x7F>; + gpio-ranges = <&pinctrl0 0 112 16>; + adi,pint = <&pint6 0>; + adi,gpio-base = <112>; + }; + + gpi: gport@31004400 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004400 0x7F>; + gpio-ranges = <&pinctrl0 0 128 7>; + adi,pint = <&pint7 1>; + adi,gpio-base = <128>; + }; + + usb0_phy: usbphy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; + status = "disabled"; + }; + + usb0: usb@310c5000 { + compatible = "adi,adsp2159x-usbc", "snps,dwc2"; + reg = <0x310c5000 0x4000>; + interrupts = ; + phys = <&usb0_phy>; + phy-names = "usb2-phy"; + status = "disabled"; + }; + +/* + dma0: dma@0 { + compatible = "adi,dma2"; + reg = <0x31022000 0x7F>; + interrupts = ; + spu_securep_id = <66>; + }; + + dma1: dma@1 { + compatible = "adi,dma2"; + reg = <0x31022080 0x7F>; + interrupts = ; + spu_securep_id = <67>; + }; + + dma2: dma@2 { + compatible = "adi,dma2"; + reg = <0x31022100 0x7F>; + interrupts = ; + spu_securep_id = <68>; + }; + + dma3: dma@3 { + compatible = "adi,dma2"; + reg = <0x31022180 0x7F>; + interrupts = ; + spu_securep_id = <69>; + }; + + dma4: dma@4 { + compatible = "adi,dma2"; + reg = <0x31022200 0x7F>; + interrupts = ; + spu_securep_id = <70>; + }; + + dma5: dma@5 { + compatible = "adi,dma2"; + reg = <0x31022280 0x7F>; + interrupts = ; + spu_securep_id = <71>; + }; + + dma6: dma@6 { + compatible = "adi,dma2"; + reg = <0x31022300 0x7F>; + interrupts = ; + spu_securep_id = <72>; + }; + + dma7: dma@7 { + compatible = "adi,dma2"; + reg = <0x31022380 0x7F>; + interrupts = ; + spu_securep_id = <73>; + }; + + dma8: dma@8 { + compatible = "adi,dma2"; + reg = <0x310A7000 0x7F>; + interrupts = ; + spu_securep_id = <74>; + }; + + dma9: dma@9 { + compatible = "adi,dma2"; + reg = <0x310A7080 0x7F>; + interrupts = ; + spu_securep_id = <75>; + }; + + dma10: dma@10 { + compatible = "adi,dma2"; + reg = <0x31023000 0x7F>; + interrupts = ; + spu_securep_id = <76>; + }; + + dma11: dma@11 { + compatible = "adi,dma2"; + reg = <0x31023080 0x7F>; + interrupts = ; + spu_securep_id = <77>; + }; + + dma12: dma@012 { + compatible = "adi,dma2"; + reg = <0x31023100 0x7F>; + interrupts = ; + spu_securep_id = <78>; + }; + + dma13: dma@13 { + compatible = "adi,dma2"; + reg = <0x31023180 0x7F>; + interrupts = ; + spu_securep_id = <79>; + }; + + dma14: dma@14 { + compatible = "adi,dma2"; + reg = <0x31023200 0x7F>; + interrupts = ; + spu_securep_id = <80>; + }; + + dma15: dma@15 { + compatible = "adi,dma2"; + reg = <0x31023280 0x7F>; + interrupts = ; + spu_securep_id = <81>; + }; + + dma16: dma@16 { + compatible = "adi,dma2"; + reg = <0x31023300 0x7F>; + interrupts = ; + spu_securep_id = <82>; + }; + + dma17: dma@17 { + compatible = "adi,dma2"; + reg = <0x31023380 0x7F>; + interrupts = ; + spu_securep_id = <83>; + }; + + dma18: dma@18 { + compatible = "adi,dma2"; + reg = <0x310A7100 0x7F>; + interrupts = ; + spu_securep_id = <84>; + }; + + dma19: dma@19 { + compatible = "adi,dma2"; + reg = <0x310A7180 0x7F>; + interrupts = ; + spu_securep_id = <85>; + }; + + dma20: dma@20 { + compatible = "adi,dma2"; + reg = <0x31026080 0x7F>; + interrupts = ; + spu_securep_id = <86>; + }; + + dma21: dma@21 { + compatible = "adi,dma2"; + reg = <0x31026000 0x7F>; + interrupts = ; + spu_securep_id = <87>; + }; + + dma22: dma@22 { + compatible = "adi,dma2"; + reg = <0x3102D000 0x7F>; + interrupts = ; + spu_securep_id = <88>; + }; + + dma23: dma@23 { + compatible = "adi,dma2"; + reg = <0x3102D080 0x7F>; + interrupts = ; + spu_securep_id = <89>; + }; + + dma24: dma@24 { + compatible = "adi,dma2"; + reg = <0x3102D100 0x7F>; + interrupts = ; + spu_securep_id = <90>; + }; + + dma25: dma@25 { + compatible = "adi,dma2"; + reg = <0x3102D180 0x7F>; + interrupts = ; + spu_securep_id = <91>; + }; + + dma26: dma@26 { + compatible = "adi,dma2"; + reg = <0x3102D200 0x7F>; + interrupts = ; + spu_securep_id = <92>; + }; + + dma27: dma@27 { + compatible = "adi,dma2"; + reg = <0x3102D280 0x7F>; + interrupts = ; + spu_securep_id = <93>; + }; + + dma28: dma@28 { + compatible = "adi,dma2"; + reg = <0x31026400 0x7F>; + interrupts = ; + spu_securep_id = <94>; + }; + + dma29: dma@29 { + compatible = "adi,dma2"; + reg = <0x31026480 0x7F>; + interrupts = ; + spu_securep_id = <95>; + }; + + dma30: dma@30 { + compatible = "adi,dma2"; + reg = <0x30FFF000 0x7F>; + interrupts = ; + spu_securep_id = <96>; + }; + + dma34: dma@34 { + compatible = "adi,dma2"; + reg = <0x31026180 0x7F>; + interrupts = ; + spu_securep_id = <97>; + }; + + dma35: dma@35 { + compatible = "adi,dma2"; + reg = <0x31026100 0x7F>; + interrupts = ; + spu_securep_id = <98>; + }; + + dma36: dma@36 { + compatible = "adi,dma2"; + reg = <0x30FFF080 0x7F>; + interrupts = ; + spu_securep_id = <99>; + }; + + dma37: dma@37 { + compatible = "adi,dma2"; + reg = <0x31026280 0x7F>; + interrupts = ; + spu_securep_id = <100>; + }; + + dma38: dma@38 { + compatible = "adi,dma2"; + reg = <0x31026200 0x7F>; + interrupts = ; + spu_securep_id = <101>; + }; + + dma39: dma@39 { + compatible = "adi,dma2"; + reg = <0x3109A000 0x7F>; + interrupts = ; + spu_securep_id = <102>; + }; + + dma40: dma@40 { + compatible = "adi,dma2"; + reg = <0x3109A080 0x7F>; + interrupts = ; + spu_securep_id = <103>; + }; + + dma43: dma@43 { + compatible = "adi,dma2"; + reg = <0x3109B000 0x7F>; + interrupts = ; + spu_securep_id = <104>; + }; + + dma44: dma@44 { + compatible = "adi,dma2"; + reg = <0x3109B080 0x7F>; + interrupts = ; + spu_securep_id = <105>; + }; + + dma45: dma@45 { + compatible = "adi,dma2"; + reg = <0x310A7200 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma46: dma@46 { + compatible = "adi,dma2"; + reg = <0x310A7280 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma47: dma@47 { + compatible = "adi,dma2"; + reg = <0x310A7300 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma48: dma@48 { + compatible = "adi,dma2"; + reg = <0x310A7380 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma49: dma@49 { + compatible = "adi,dma2"; + reg = <0x310AC000 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma50: dma@50 { + compatible = "adi,dma2"; + reg = <0x310AC080 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma51: dma@51 { + compatible = "adi,dma2"; + reg = <0x3109C000 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma52: dma@52 { + compatible = "adi,dma2"; + reg = <0x3109C080 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma53: dma@53 { + compatible = "adi,dma2"; + reg = <0x31026380 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma54: dma@54 { + compatible = "adi,dma2"; + reg = <0x31026300 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; +*/ + + spi_cluster: dma@3102D000 { + compatible = "adi,dma-controller"; + reg = <0x3102D000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + spi0_tx: channel@22 { + adi,id = <22>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + spi0_rx: channel@23 { + adi,id = <23>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + spi1_tx: channel@24 { + adi,id = <24>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + spi1_rx: channel@25 { + adi,id = <25>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + spi2_tx: channel@26 { + adi,id = <26>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + spi2_rx: channel@27 { + adi,id = <27>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + }; + + sport0_dma_cluster: dma@31022000 { + compatible = "adi,dma-controller"; + reg = <0x31022000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport0a: channel@0 { + adi,id = <0>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport0b: channel@1 { + adi,id = <1>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + sport4_dma_cluster: dma@31023000 { + compatible = "adi,dma-controller"; + reg = <0x31023000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport4a: channel@10 { + adi,id = <10>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport4b: channel@11 { + adi,id = <11>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + dma_cluster2: dma@31026000 { + compatible = "adi,dma-controller"; + reg = <0x31026000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + uart0_tx: channel@20 { + adi,id = <20>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + uart0_rx: channel@21 { + adi,id = <21>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + uart1_tx: channel@34 { + adi,id = <34>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + uart1_rx: channel@35 { + adi,id = <35>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + uart2_tx: channel@37 { + adi,id = <37>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + uart2_rx: channel@38 { + adi,id = <38>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + }; + + mdma: dma@3109a000 { + compatible = "adi,mdma-controller"; + reg = <0x3109a000 0x1000>; + status = "okay"; + + sdma2: channel@40 { + adi,id = <40>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + + trng: rng@310D0000 { + compatible = "adi,sc5xx-trng"; + reg = <0x310D0000 0x74>, <0x310D8000 0x14>; + interrupts = ; + interrupt-names = "pkic0_irq"; + startup-cycles = <0xff>; + minref-cycles = <0x21>; + maxref-cycles = <0x22>; + alarm-thresh = <0xff>; + shdn-thresh = <0x04>; + poll-data = <0>; /* Use IRQ for data */ + status = "okay"; + }; + + }; +}; From a2d98dda5878dc9337e2c4300e07e7425dfd0020 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 13:41:50 +0100 Subject: [PATCH 36/85] ARM: dts: adi: sc589: add device tree Signed-off-by: Utsav Agarwal Signed-off-by: Arturs Artamonovs --- arch/arm/boot/dts/adi/Makefile | 5 +- arch/arm/boot/dts/adi/sc589-mini.dts | 477 ++++++++++ arch/arm/boot/dts/adi/sc58x.dtsi | 1227 ++++++++++++++++++++++++++ 3 files changed, 1707 insertions(+), 2 deletions(-) create mode 100644 arch/arm/boot/dts/adi/sc589-mini.dts create mode 100644 arch/arm/boot/dts/adi/sc58x.dtsi diff --git a/arch/arm/boot/dts/adi/Makefile b/arch/arm/boot/dts/adi/Makefile index 1b844da68e90a2..7387ff81c0fb5b 100644 --- a/arch/arm/boot/dts/adi/Makefile +++ b/arch/arm/boot/dts/adi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_SC5XX) += \ - sc594-som-ezkit.dtb +dtb-$(CONFIG_ARCH_SC59X) += sc594-som-ezkit.dtb + +dtb-$(CONFIG_ARCH_SC58X) += sc589-mini.dtb diff --git a/arch/arm/boot/dts/adi/sc589-mini.dts b/arch/arm/boot/dts/adi/sc589-mini.dts new file mode 100644 index 00000000000000..ff6bfda31fcd63 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc589-mini.dts @@ -0,0 +1,477 @@ +/* + * Device tree for ADI sc589-mini board + * + * Copyright 2014 - 2018 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "sc58x.dtsi" + +/ { + model = "ADI sc589-mini"; + compatible = "adi,sc589-mini", "adi,sc58x"; + + aliases { + }; + + memory@C3000000 { + device_type = "memory"; + reg = <0xC3000000 0xF000000>; + }; + + reserved-memory { + vdev0vrings: vdev0vring0@200A0000 { + reg = <0x200A0000 0x4000>; + no-map; + }; + + vdev0buffer: vdev0buffer@200A4000 { + compatible = "shared-dma-pool"; + reg = <0x200A4000 0xC000>; + no-map; + }; + + vdev1vrings: vdev0vring0@200B0000 { + reg = <0x200B0000 0x4000>; + no-map; + }; + + vdev1buffer: vdev0buffer@200B4000 { + compatible = "shared-dma-pool"; + reg = <0x200B4000 0xC000>; + no-map; + }; + }; + + scb { + + button0: button@0 { + compatible = "adi,button-led"; + button_gpio = <80>; + led_gpio = <49>; + }; + button1: button@1 { + compatible = "adi,button-led"; + button_gpio = <81>; + led_gpio = <50>; + }; + + core1-rproc@0x28240000 { + compatible = "adi,remoteproc"; + reg = <0x28240000 0x160000>, + <0x20080000 0x40000>; + core-id = <1>; + core-irq = <76>; + firmware-name = "adi_adsp_core1_fw.ldr"; + interrupts = ; + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20080000 0x200C0000>; + adi,rsc-table = <&rsc_tbl0>; + adi,tru = <&tru>; + adi,tru-master-id = <97>; /* trigger master SOFT4 */ + }; + + core2-rproc@0x28a40000 { + compatible = "adi,remoteproc"; + reg = <0x28a40000 0x160000>, + <0x20080000 0x40000>; + core-id = <2>; + core-irq = <77>; + firmware-name = "adi_adsp_core2_fw.ldr"; + interrupts = ; + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20080000 0x200C0000>; + adi,rsc-table = <&rsc_tbl1>; + adi,tru = <&tru>; + adi,tru-master-id = <98>; /* trigger master SOFT5 */ + }; + + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s0_sport0>; + adi,codec = <&adau1761>; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_default>; + status = "okay"; + + cs-gpios = <&gpc 12 GPIO_ACTIVE_LOW>, + <&gpc 0 GPIO_ACTIVE_LOW>; + + spidev@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "rohm,dh2228fv"; + spi-max-frequency = <5000000>; + reg = <0>; + }; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_quad>; + status = "okay"; + + cs-gpios = <&gpc 6 GPIO_ACTIVE_LOW>; + + flash: mt25ql512@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "micron,mt25ql512", "jedec,spi-nor"; + spi-max-frequency = <5000000>; + reg = <0>; + + partition@0 { + label = "uboot spl (spi)"; + reg = <0x0 0x20000>; + }; + + partition@1 { + label = "uboot proper (spi)"; + reg = <0x20000 0xb0000>; + }; + + partition@2 { + label = "uboot env"; + reg = <0xd0000 0x10000>; + }; + + partition@3 { + label = "kernel (spi)"; + reg = <0x00e0000 0x0800000>; + }; + + partition@4 { + label = "root file system (spi)"; + reg = <0x08e0000 0x3720000>; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + status = "okay"; +}; + +&crc0 { + status = "okay"; +}; + +&crc1 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; + + adau1761: adau1761@0x38{ + compatible = "adi,adau1761"; + reg = <0x38>; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2s0_sport0 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai0>; + status = "okay"; +}; + +&emac0 { + snps,reset-gpio = <&gpb 7 0>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 1000000>; + phy-handle = <&dp83867>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + dp83867: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + }; + }; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb0 { + /* mode = <2>; Place OTG port into Device Mode */ + /* mode = <1>; Place OTG port into Host Mode */ + mode = <1>; + status = "okay"; +}; + +&usb1_phy { + status = "okay"; +}; + +&usb1 { + mode = <1>; /* Host port */ + status = "okay"; +}; + +&mmc0 { + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_4bgrp>; + supports-highspeed; + status = "okay"; +}; + +&sram_mmap { + status = "okay"; +}; + +&sru_ctrl_dai0 { + status = "okay"; + + sru_dai0: sru_dai0_mux { + route { + sru-routing = + , /* set DAI0_PIN01 for ADAU1761 DAC data as an output */ + , /* set DAI0_PIN02 for ADAU1761 ADC data as an input */ + , /* set DAI0_PIN03 for ADAU1761 CLK as an input */ + , /* set DAI0_PIN04 for ADAU1761 FS as an input */ + + , /* DAI0_PIN01 to SPT0_AD0 */ + , /* DAI0_PIN03 to SPT0_ACLK */ + , /* DAI0_PIN04 to SPT0_AFS */ + + , /* DAI0_PIN02 to SPT0_BD0 */ + , /* DAI0_PIN03 to SPT0_BCLK */ + ; /* DAI0_PIN04 to SPT0_BFS */ + }; + }; +}; + +&sru_ctrl_dai1 { + status = "okay"; +}; + +&tru { + rpmsg_to_a55: channel@0 { + adi,tru-master-id = <96>; /* trigger master SOFT3 */ + adi,tru-slave-id = <87>; /* TRU0_IRQ3 */ + }; + rpmsg_to_sharc0: channel@1 { + adi,tru-master-id = <97>; /* trigger master SOFT4 */ + adi,tru-slave-id = <91>; /* TRU0_IRQ7 */ + }; + rpmsg_to_sharc1: channel@2 { + adi,tru-master-id = <98>; /* trigger master SOFT5 */ + adi,tru-slave-id = <95>; /* TRU0_IRQ11 */ + }; +}; + +&pinctrl0 { + uart0_default: uart0_default_pins { + pins { + pinmux = , + ; + }; + }; + uart0_hwflow: uart0_hwflow_pins { + pins { + pinmux = , + , + , + ; + }; + }; + spi0_default: spi0_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi1_default: spi1_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi2_quad: spi2_quad_pins { + pins { + pinmux = , + , + , + , + ; + }; + }; + eth0_default: eth0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + mmc0_4bgrp: mmc0_4bgrp_pins { + pins { + pinmux = , + , + , + , + , + , + ; + }; + }; + ppi0_8b: ppi0_8b_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + ppi0_16b: ppi0_16b_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + ppi0_24b: ppi0_24b_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + lp0_default: lp0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + ; + }; + }; + lp1_default: lp1_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + ; + }; + }; + can0_default: can0_default_pins { + pins { + pinmux = , + ; + }; + }; + can1_default: can1_default_pins { + pins { + pinmux = , + ; + }; + }; +}; diff --git a/arch/arm/boot/dts/adi/sc58x.dtsi b/arch/arm/boot/dts/adi/sc58x.dtsi new file mode 100644 index 00000000000000..8fed5eae5c3976 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc58x.dtsi @@ -0,0 +1,1227 @@ +/* + * Device tree header for ADI sc58x processor + * + * Copyright 2014 - 2018 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +#include +#include +#include + +/ { + model = "ADI sc58x"; + compatible = "adi,sc58x"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &uart0; + timer0 = &gptimer0; + timer1 = &gptimer1; + timer2 = &gptimer2; + timer3 = &gptimer3; + timer4 = &gptimer4; + timer5 = &gptimer5; + timer6 = &gptimer6; + timer7 = &gptimer7; + ethernet0 = &emac0; + ethernet1 = &emac1; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + can0 = &can0; + can1 = &can1; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + rtc0 = &rtc0; + i2s0 = &i2s0; + mmc0 = &mmc0; + sru0 = &sru_ctrl_dai0; + sru1 = &sru_ctrl_dai1; + }; + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a5"; + reg = <0x0>; + clocks = <&clk ADSP_SC58X_CLK_ARM>; + }; + }; + + pmu { + compatible = "arm,cortex-a5-pmu"; + interrupts = ; + }; + + gic: interrupt-controller@310B2000 { + compatible = "arm,cortex-a5-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x310B2000 0x1000>, + <0x310B4000 0x100>; + }; + + L2: cache-controller@10000000 { + compatible = "arm,pl310-cache"; + reg = <0x10000000 0x1000>; + cache-level = <2>; + }; + + sram0: sram-icc@20084000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20084000 0x4000>; + ranges = <0 0x20084000 0x4000>; /* 16 KiB */ + }; + + sram1: sram-reserved@20088000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20088000 0x18000>; + ranges = <0 0x20088000 0x18000>; /* 96 KiB */ + }; + + sys_clkin0: sys-clkin0@1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin0"; + }; + + sys_clkin1: sys-clkin1@2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin1"; + }; + + clk: clocks@0x3108d000 { + compatible = "adi,sc58x-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + gptimers: gptimers@0x31001000 { + compatible = "adi,sc5xx-gptimers"; + reg = <0x31001000 0x200>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + gptimer0: gptimer@0 { + reg = <0>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x60>; + adi,is-clocksource; + adi,reset-timer; + }; + + gptimer1: gptimer@1 { + reg = <1>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x80>; + adi,is-clockevent; + adi,reset-timer; + }; + + gptimer2: gptimer@2 { + reg = <2>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xa0>; + }; + + gptimer3: gptimer@3 { + reg = <3>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xc0>; + }; + + gptimer4: gptimer@4 { + reg = <4>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xe0>; + }; + + gptimer5: gptimer@5 { + reg = <5>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x100>; + }; + + gptimer6: gptimer@6 { + reg = <6>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x120>; + }; + + gptimer7: gptimer@7 { + reg = <7>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x140>; + }; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + rsc_tbl0: rsc_tbl0@20080000 { + reg = <0x20080000 0x400>; /*1KiB*/ + no-map; + }; + + rsc_tbl1: rsc_tbl0@20000400 { + reg = <0x20080400 0x400>; /*1KiB*/ + no-map; + }; + + sram_B1_unused@20000800 { + reg = <0x20080800 0x3800>; /* <128 KiB*/ + no-map; + }; + }; + + scb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pads_system_config: adi-control@31004400 { + compatible = "adi,pads-system-config"; + reg = <0x31004400 0x100>; + status = "okay"; + }; + + sram-controller@31080000 { + compatible = "adi,sram-controller"; + reg = <0x31080000 0x100>; + adi,sram = <&sram0>, <&sram1>; + interrupts = ; + status = "okay"; + }; + + gptimer_counter: gptimer-counters@0 { + compatible = "adi,gptimer-counter"; + status = "okay"; + }; + + sram_mmap: sram-mmap@0 { /* mmap from sram1 pool*/ + compatible = "adi,sram-mmap"; + adi,sram = <&sram1>; + status = "disabled"; + }; + + rcu: rcu@0x3108B000 { + compatible = "adi,reset-controller"; + reg = <0x3108B000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + adi,enable-reboot; + status = "okay"; + }; + + sec: sec@0x31089000 { + compatible = "adi,system-event-controller"; + reg = <0x31089000 0x1000>; + adi,rcu = <&rcu>; + adi,sharc-cores = <2>; + status = "okay"; + }; + + tru: tru@0x3108a000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108a000 0x1000>; + adi,max-master-id = <139>; + adi,max-slave-id = <137>; + status = "okay"; + }; + + rtc0: rtc@0x310C8000 { + compatible = "adi,rtc2"; + reg = <0x310C8000 0x100>; + interrupts = ; + calibration = /bits/ 8 <0>; + status = "disabled"; + }; + + uart0: uart@0x31003000 { + compatible = "adi,uart4"; + reg = <0x31003000 0x40>; + dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart1: uart@0x31003400 { + compatible = "adi,uart4"; + reg = <0x31003400 0x40>; + dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart2: uart@0x31003800 { + compatible = "adi,uart4"; + reg = <0x31003800 0x40>; + dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + can0: can@0x31000200 { + compatible = "adi,can"; + reg = <0x31000200 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + can1: can@0x31000a00 { + compatible = "adi,can"; + reg = <0x31000a00 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + i2c0: twi@0x31001400 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001400 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c1: twi@0x31001500 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001500 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c2: twi@0x31001600 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001600 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2s0: i2s@0 { + compatible = "adi,sc5xx-i2s-dai"; + reg = <0x31002400 0x80>, <0x31002480 0x80>; + sport-channel = <4>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport_dma_cluster 10>, <&sport_dma_cluster 11>; + dma-names = "tx", "rx"; + iram = <&sram1>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK1>; + clock-names = "sclk"; + status = "disabled"; + }; + + i2s0_sport0: i2s-sport0@0 { + compatible = "adi,sc5xx-i2s-dai"; + reg = <0x31002000 0x80>, <0x31002080 0x80>; + sport-channel = <0>; + interrupt-names = "tx_status", "rx_status"; + interrupts = , ; + dmas = <&sport0_dma_cluster 0>, <&sport0_dma_cluster 1>; + dma-names = "tx", "rx"; + iram = <&sram1>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK1>; + clock-names = "sclk"; + status = "disabled"; + }; + + watchdog@0x31008000 { + compatible = "adi,watchdog"; + reg = <0x31008000 0x10>; + timeout-sec = <30>; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "adi-watchdog"; + }; + + spi0: spi@0x31042000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31042000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 22>, <&spi_cluster 23>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK1>; + clock-names = "spi"; + status = "disabled"; + }; + + spi1: spi@0x31043000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31043000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 24>, <&spi_cluster 25>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "spi"; + status = "disabled"; + }; + + spi2: spi@0x31044000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31044000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 26>, <&spi_cluster 27>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "spi"; + status = "disabled"; + }; + + emac0: ethernet@0x3100C000 { + compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; + reg = <0x3100C000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,mixed-burst; + snps,pbl = <8>; + snps,force_sf_dma_mode; + snps,perfect-filter-entries = <32>; + clocks = <&clk ADSP_SC58X_CLK_GIGE>; + clock-names = "stmmaceth"; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + emac1: ethernet@0x3100E000 { + compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; + reg = <0x3100E000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,fixed-burst; + snps,burst_len = <0x4>; /* BLEN8 */ + snps,pbl = <1>; + snps,force_thresh_dma_mode; + clocks = <&clk ADSP_SC58X_CLK_GIGE>; + clock-names = "stmmaceth"; + status = "disabled"; + }; + + crc0: crc@0x31001200 { + compatible = "adi,hmac-crc"; + reg = <0x31001200 0xFF>; + interrupts = ; + dma_channel = <8>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + crc1: crc@0x31001300 { + compatible = "adi,hmac-crc"; + reg = <0x31001300 0xFF>; + interrupts = ; + dma_channel = <18>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + pinctrl0: pinctrl@0x31004400 { + compatible = "adi,adsp-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x31004400 0x400>; + adi,port-sizes = <16 16 16 16 16 16 6>; + adi,no-drive-strength; + adi,no-pull-up-down; + }; + + sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310C9000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + sru_ctrl_dai1: sru-ctrl-dai1@0x310CB000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310CB000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + mmc0: mmc@0x31010000 { + compatible = "snps,dw-mshc"; + reg = <0x31010000 0xFFF>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <1024>; + clocks = <&clk ADSP_SC58X_CLK_SDIO>, + <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; + clock-names = "ciu", "biu"; + status = "disabled"; + }; + + lp0: linkport@0 { + compatible = "linkport0"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + lp1: linkport@1 { + compatible = "linkport1"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <1>; + status = "disabled"; + }; + + pint0: pint@0x31005000 { + compatible = "adi,adsp-pint"; + reg = <0x31005000 0xFF>; + interrupts = ; + }; + + pint1: pint@0x31005100 { + compatible = "adi,adsp-pint"; + reg = <0x31005100 0xFF>; + interrupts = ; + }; + + pint2: pint@0x31005200 { + compatible = "adi,adsp-pint"; + reg = <0x31005200 0xFF>; + interrupts = ; + }; + + pint3: pint@0x31005300 { + compatible = "adi,adsp-pint"; + reg = <0x31005300 0xFF>; + interrupts = ; + }; + + pint4: pint@0x31005400 { + compatible = "adi,adsp-pint"; + reg = <0x31005400 0xFF>; + interrupts = ; + }; + + pint5: pint@0x31005500 { + compatible = "adi,adsp-pint"; + reg = <0x31005500 0xFF>; + interrupts = ; + }; + + gpa: gport@0x31004000 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004000 0x7F>; + gpio-ranges = <&pinctrl0 0 0 16>; + adi,pint = <&pint0 1>; + adi,gpio-base = <0>; + }; + + gpb: gport@0x31004080 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004080 0x7F>; + gpio-ranges = <&pinctrl0 0 16 16>; + adi,pint = <&pint0 0>; + adi,gpio-base = <16>; + }; + + gpc: gport@0x31004100 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004100 0x7F>; + gpio-ranges = <&pinctrl0 0 32 16>; + adi,pint = <&pint2 1>; + adi,gpio-base = <32>; + }; + + gpd: gport@0x31004180 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004180 0x7F>; + gpio-ranges = <&pinctrl0 0 48 16>; + adi,pint = <&pint2 0>; + adi,gpio-base = <48>; + }; + + gpe: gport@0x31004200 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004200 0x7F>; + gpio-ranges = <&pinctrl0 0 64 16>; + adi,pint = <&pint4 1>; + adi,gpio-base = <64>; + }; + + gpf: gport@0x31004280 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004280 0x7F>; + gpio-ranges = <&pinctrl0 0 80 16>; + adi,pint = <&pint4 0>; + adi,gpio-base = <80>; + }; + + gpg: gport@0x31004300 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004300 0x7F>; + gpio-ranges = <&pinctrl0 0 96 6>; + adi,pint = <&pint5 0>; + adi,gpio-base = <96>; + }; + + usb0_phy: usb-phy@310c1390 { + compatible = "usb-nop-xceiv"; + reg = <0x310c1390 0x10>; + reg-names = "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + + usb0: usb@310c1000 { + compatible = "adi,musb"; + reg = <0x310c1000 0x390>; + reg-names = "mc"; + interrupts = , + ; + interrupt-names = "mc", "dma"; + spu_securep_id = <153>; + + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb0_phy>; + status = "disabled"; + }; + + usb1_phy: usb-phy@310c2390 { + compatible = "usb-nop-xceiv"; + reg = <0x310c2390 0x10>; + reg-names = "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + + usb1: usb@310c2000 { + compatible = "adi,musb"; + reg = <0x310c2000 0x390>; + reg-names = "mc"; + interrupts = , + ; + interrupt-names = "mc", "dma"; + spu_securep_id = <154>; + + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb1_phy>; + status = "disabled"; + }; + +/* + dma0: dma@0 { + compatible = "adi,dma2"; + reg = <0x31022000 0x7F>; + interrupts = ; + spu_securep_id = <66>; + }; + + dma1: dma@1 { + compatible = "adi,dma2"; + reg = <0x31022080 0x7F>; + interrupts = ; + spu_securep_id = <67>; + }; + + dma2: dma@2 { + compatible = "adi,dma2"; + reg = <0x31022100 0x7F>; + interrupts = ; + spu_securep_id = <68>; + }; + + dma3: dma@3 { + compatible = "adi,dma2"; + reg = <0x31022180 0x7F>; + interrupts = ; + spu_securep_id = <69>; + }; + + dma4: dma@4 { + compatible = "adi,dma2"; + reg = <0x31022200 0x7F>; + interrupts = ; + spu_securep_id = <70>; + }; + + dma5: dma@5 { + compatible = "adi,dma2"; + reg = <0x31022280 0x7F>; + interrupts = ; + spu_securep_id = <71>; + }; + + dma6: dma@6 { + compatible = "adi,dma2"; + reg = <0x31022300 0x7F>; + interrupts = ; + spu_securep_id = <72>; + }; + + dma7: dma@7 { + compatible = "adi,dma2"; + reg = <0x31022380 0x7F>; + interrupts = ; + spu_securep_id = <73>; + }; + + dma8: dma@8 { + compatible = "adi,dma2"; + reg = <0x31028000 0x7F>; + interrupts = ; + spu_securep_id = <88>; + }; + + dma9: dma@9 { + compatible = "adi,dma2"; + reg = <0x31028080 0x7F>; + interrupts = ; + spu_securep_id = <89>; + }; + + dma10: dma@10 { + compatible = "adi,dma2"; + reg = <0x31024000 0x7F>; + interrupts = ; + spu_securep_id = <74>; + }; + + dma11: dma@11 { + compatible = "adi,dma2"; + reg = <0x31024080 0x7F>; + interrupts = ; + spu_securep_id = <75>; + }; + + dma12: dma@012 { + compatible = "adi,dma2"; + reg = <0x31024100 0x7F>; + interrupts = ; + spu_securep_id = <76>; + }; + + dma13: dma@13 { + compatible = "adi,dma2"; + reg = <0x31024180 0x7F>; + interrupts = ; + spu_securep_id = <77>; + }; + + dma14: dma@14 { + compatible = "adi,dma2"; + reg = <0x31024200 0x7F>; + interrupts = ; + spu_securep_id = <78>; + }; + + dma15: dma@15 { + compatible = "adi,dma2"; + reg = <0x31024280 0x7F>; + interrupts = ; + spu_securep_id = <79>; + }; + + dma16: dma@16 { + compatible = "adi,dma2"; + reg = <0x31024300 0x7F>; + interrupts = ; + spu_securep_id = <80>; + }; + + dma17: dma@17 { + compatible = "adi,dma2"; + reg = <0x31024380 0x7F>; + interrupts = ; + spu_securep_id = <81>; + }; + + dma18: dma@18 { + compatible = "adi,dma2"; + reg = <0x31028100 0x7F>; + interrupts = ; + spu_securep_id = <90>; + }; + + dma19: dma@19 { + compatible = "adi,dma2"; + reg = <0x31028180 0x7F>; + interrupts = ; + spu_securep_id = <91>; + }; + + dma20: dma@20 { + compatible = "adi,dma2"; + reg = <0x31026080 0x7F>; + interrupts = ; + spu_securep_id = <83>; + }; + + dma21: dma@21 { + compatible = "adi,dma2"; + reg = <0x31026000 0x7F>; + interrupts = ; + spu_securep_id = <82>; + }; + + dma22: dma@22 { + compatible = "adi,dma2"; + reg = <0x31046000 0x7F>; + interrupts = ; + spu_securep_id = <101>; + }; + + dma23: dma@23 { + compatible = "adi,dma2"; + reg = <0x31046080 0x7F>; + interrupts = ; + spu_securep_id = <102>; + }; + + dma24: dma@24 { + compatible = "adi,dma2"; + reg = <0x31046100 0x7F>; + interrupts = ; + spu_securep_id = <103>; + }; + + dma25: dma@25 { + compatible = "adi,dma2"; + reg = <0x31046180 0x7F>; + interrupts = ; + spu_securep_id = <104>; + }; + + dma26: dma@26 { + compatible = "adi,dma2"; + reg = <0x31046200 0x7F>; + interrupts = ; + spu_securep_id = <105>; + }; + + dma27: dma@27 { + compatible = "adi,dma2"; + reg = <0x31046280 0x7F>; + interrupts = ; + spu_securep_id = <106>; + }; + + dma28: dma@28 { + compatible = "adi,dma2"; + reg = <0x31046300 0x7F>; + interrupts = ; + spu_securep_id = <107>; + }; + + dma29: dma@29 { + compatible = "adi,dma2"; + reg = <0x31046380 0x7F>; + interrupts = ; + spu_securep_id = <108>; + }; + + dma30: dma@30 { + compatible = "adi,dma2"; + reg = <0x30FFF000 0x7F>; + interrupts = ; + spu_securep_id = <5>; + }; + + dma31: dma@31 { + compatible = "adi,dma2"; + reg = <0x3102A100 0x7F>; + interrupts = ; + spu_securep_id = <94>; + }; + + dma32: dma@32 { + compatible = "adi,dma2"; + reg = <0x3102A000 0x7F>; + interrupts = ; + spu_securep_id = <92>; + }; + + dma33: dma@33 { + compatible = "adi,dma2"; + reg = <0x3102A080 0x7F>; + interrupts = ; + spu_securep_id = <93>; + }; + + dma34: dma@34 { + compatible = "adi,dma2"; + reg = <0x31026180 0x7F>; + interrupts = ; + spu_securep_id = <85>; + }; + + dma35: dma@35 { + compatible = "adi,dma2"; + reg = <0x31026100 0x7F>; + interrupts = ; + spu_securep_id = <84>; + }; + + dma36: dma@36 { + compatible = "adi,dma2"; + reg = <0x30FFF080 0x7F>; + interrupts = ; + spu_securep_id = <6>; + }; + + dma37: dma@37 { + compatible = "adi,dma2"; + reg = <0x31026280 0x7F>; + interrupts = ; + spu_securep_id = <87>; + }; + + dma38: dma@38 { + compatible = "adi,dma2"; + reg = <0x31026200 0x7F>; + interrupts = ; + spu_securep_id = <86>; + }; + + dma39: dma@39 { + compatible = "adi,dma2"; + reg = <0x3109A000 0x7F>; + interrupts = ; + spu_securep_id = <140>; + }; + + dma40: dma@40 { + compatible = "adi,dma2"; + reg = <0x3109A080 0x7F>; + interrupts = ; + spu_securep_id = <140>; + }; + + dma41: dma@41 { + compatible = "adi,dma2"; + reg = <0x3109C200 0x7F>; + interrupts = ; + spu_securep_id = <142>; + }; + + dma42: dma@42 { + compatible = "adi,dma2"; + reg = <0x3109C400 0x7F>; + interrupts = ; + spu_securep_id = <142>; + }; + + dma43: dma@43 { + compatible = "adi,dma2"; + reg = <0x3109B000 0x7F>; + interrupts = ; + spu_securep_id = <141>; + }; + + dma44: dma@44 { + compatible = "adi,dma2"; + reg = <0x3109B080 0x7F>; + interrupts = ; + spu_securep_id = <141>; + }; +*/ + + spi_cluster: dma@0x31046000 { + compatible = "adi,dma-controller"; + reg = <0x31046000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + spi0_tx: channel@22 { + adi,id = <22>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + spi0_rx: channel@23 { + adi,id = <23>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + spi1_tx: channel@24 { + adi,id = <24>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + spi1_rx: channel@25 { + adi,id = <25>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + spi2_tx: channel@26 { + adi,id = <26>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + spi2_rx: channel@27 { + adi,id = <27>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + }; + + sport_dma_cluster: dma@0x31024000 { + compatible = "adi,dma-controller"; + reg = <0x31024000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport4a: channel@10 { + adi,id = <10>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport4b: channel@11 { + adi,id = <11>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + sport0_dma_cluster: dma@0x31022000 { + compatible = "adi,dma-controller"; + reg = <0x31022000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + sport0a: channel@0 { + adi,id = <0>; + adi,src-offset = <0>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + + sport0b: channel@1 { + adi,id = <1>; + adi,src-offset = <0x80>; + adi,skip-interrupts = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + }; + }; + + dma_cluster2: dma@0x31026000 { + compatible = "adi,dma-controller"; + reg = <0x31026000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + uart0_tx: channel@20 { + adi,id = <20>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + uart0_rx: channel@21 { + adi,id = <21>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + uart1_tx: channel@34 { + adi,id = <34>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + uart1_rx: channel@35 { + adi,id = <35>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + uart2_tx: channel@37 { + adi,id = <37>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + uart2_rx: channel@38 { + adi,id = <38>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + }; + + mdma: dma@0x3109a000 { + compatible = "adi,mdma-controller"; + reg = <0x3109a000 0x1000>; + status = "okay"; + + sdma2: channel@40 { + adi,id = <40>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + + trng: rng@0x310D0000 { + compatible = "adi,sc5xx-trng"; + reg = <0x310D0000 0x74>, <0x310D8000 0x14>; + interrupts = ; + interrupt-names = "pkic0_irq"; + startup-cycles = <0xff>; + minref-cycles = <0x21>; + maxref-cycles = <0x22>; + alarm-thresh = <0xff>; + shdn-thresh = <0x04>; + poll-data = <0>; /* Use IRQ for data */ + status = "okay"; + }; + + }; +}; From 2ce0d1da977ff47106c528496b3450629311e59a Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:13:05 +0100 Subject: [PATCH 37/85] ARM: dts: adi: Support ADZS-SC573-EZLITE Signed-off-by: UtsavAgarwalADI --- arch/arm/boot/dts/adi/Makefile | 2 +- arch/arm/boot/dts/adi/sc573-ezkit.dts | 677 ++++++++++++++++++++ arch/arm/boot/dts/adi/sc57x.dtsi | 861 ++++++++++++++++++++++++++ 3 files changed, 1539 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boot/dts/adi/sc573-ezkit.dts create mode 100644 arch/arm/boot/dts/adi/sc57x.dtsi diff --git a/arch/arm/boot/dts/adi/Makefile b/arch/arm/boot/dts/adi/Makefile index 7387ff81c0fb5b..4bf3cbf8d8f72d 100644 --- a/arch/arm/boot/dts/adi/Makefile +++ b/arch/arm/boot/dts/adi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_SC59X) += sc594-som-ezkit.dtb - dtb-$(CONFIG_ARCH_SC58X) += sc589-mini.dtb +dtb-$(CONFIG_ARCH_SC57X) += sc573-ezkit.dtb diff --git a/arch/arm/boot/dts/adi/sc573-ezkit.dts b/arch/arm/boot/dts/adi/sc573-ezkit.dts new file mode 100644 index 00000000000000..9cb68a0e1786ee --- /dev/null +++ b/arch/arm/boot/dts/adi/sc573-ezkit.dts @@ -0,0 +1,677 @@ +/* + * Device tree for ADI sc573-ezkit board + * + * Copyright 2014 - 2018 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * todo list: + * - bushbutton GPIOs are all incorrect and commented out for now + */ + +/dts-v1/; + +#include +#include +#include +#include +#include +#include "sc57x.dtsi" + +/ { + model = "ADI sc573-ezkit"; + compatible = "adi,sc573-ezkit", "adi,sc57x"; + + aliases { + /* serial2 = &uart2; */ + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0xF000000>; + }; + + reserved-memory { + vdev0vrings: vdev0vring0@20080000 { + reg = <0x20080000 0x4000>; + no-map; + }; + + vdev0buffer: vdev0buffer@20084000 { + compatible = "shared-dma-pool"; + reg = <0x20084000 0x20000>; + no-map; + }; + + vdev1vrings: vdev0vring0@200A4000 { + reg = <0x200A4000 0x4000>; + no-map; + }; + + vdev1buffer: vdev0buffer@200A8000 { + compatible = "shared-dma-pool"; + reg = <0x200A8000 0x20000>; + no-map; + }; + }; + + scb { + button0: button@0 { + compatible = "adi,button-led"; +// en-pins = <&ssw1 2 GPIO_ACTIVE_LOW>, /* PUSHBUTTON1_EN */ +// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// button_gpio = <40>; +// led_gpio = <77>; + }; + button1: button@1 { + compatible = "adi,button-led"; +// en-pins = <&ssw1 1 GPIO_ACTIVE_LOW>, /* PUSHBUTTON2_EN */ +// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// button_gpio = <41>; +// led_gpio = <9>; + }; + button2: button@2 { + compatible = "adi,button-led"; +// en-pins = <&ssw1 0 GPIO_ACTIVE_LOW>, /* PUSHBUTTON3_EN */ +// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// button_gpio = <42>; +// led_gpio = <65>; + }; + + core1-rproc@0x3108C000 { + compatible = "adi,remoteproc"; + reg = <0x28240000 0x2000>, + <0x20000000 0x200000>; + core-id = <1>; + core-irq = <84>; + firmware-name = "adi_adsp_core1_fw.ldr"; + interrupts = ; + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20080000 0x200C0000>; + adi,rsc-table = <&rsc_tbl0>; + adi,tru = <&tru>; + adi,tru-master-id = <71>; /* trigger master SOFT4 */ + status = "okay"; + }; + + core2-rproc@0x3108C000 { + compatible = "adi,remoteproc"; + reg = <0x28A40000 0x2000>, + <0x20000000 0x200000>; + core-id = <2>; + core-irq = <85>; + firmware-name = "adi_adsp_core2_fw.ldr"; + interrupts = ; + adi,rcu = <&rcu>; + adi,l1-da = <0x240000 0x3a0000>; + adi,l2-da = <0x20080000 0x200C0000>; + adi,rsc-table = <&rsc_tbl1>; + adi,tru = <&tru>; + adi,tru-master-id = <72>; /* trigger master SOFT5 */ + status = "okay"; + }; + + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s0>; + adi,codec = <&adau1962>, <&adau1979>; + status = "okay"; + }; + }; +}; + +&tru { + mcapi_to_a5: channel@0 { + adi,tru-master-id = <70>; /* trigger master SOFT3 */ + adi,tru-slave-id = <71>; /* TRU0_IRQ3 */ + }; + mcapi_to_sharc0: channel@1 { + adi,tru-master-id = <71>; /* trigger master SOFT4 */ + adi,tru-slave-id = <75>; /* TRU0_IRQ7 */ + }; + mcapi_to_sharc1: channel@2 { + adi,tru-master-id = <72>; /* trigger master SOFT5 */ + adi,tru-slave-id = <79>; /* TRU0_IRQ11 */ + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_default>; + status = "okay"; + cs-gpios = <&gpc 6 GPIO_ACTIVE_LOW>; + + spidev@0 { + #address-cells = <1>; + size-cells = <1>; + /* this is actually being used to control a tja1145 can transceiver */ + compatible = "rohm,dh2228fv"; + spi-max-frequency = <5000000>; + reg = <0>; + }; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_quad>; + status = "okay"; + cs-gpios = <&gpb 15 GPIO_ACTIVE_LOW>; + + flash: w25q128@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "winbond,w25q128"; + spi-max-frequency = <50000000>; + reg = <0>; + spi-cpol; + spi-cpha; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partition@0 { + label = "uboot spl (spi)"; + reg = <0x0 0x20000>; + }; + + partition@1 { + label = "uboot proper (spi)"; + reg = <0x20000 0xb0000>; + }; + + partition@2 { + label = "uboot env"; + reg = <0xd0000 0x10000>; + }; + + partition@3 { + label = "kernel (spi)"; + reg = <0x00e0000 0x0600000>; + }; + + partition@4 { + label = "root file system (spi)"; + reg = <0x06e0000 0x0920000>; + }; + }; +}; + +&i2c0 { + status = "okay"; + + ssw0: gpio@0x21 { + compatible = "microchip,mcp23017"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x21>; + + eeprom-en { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-low; + line-name = "eeprom_en"; + }; + + uart0-flow-en { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; + + mlb3-en { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "mlb3-en"; + }; + + can0-en { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-low; + line-name = "can0-en"; + }; + + can1-en { + gpio-hog; + gpios = <7 GPIO_ACTIVE_LOW>; + output-low; + line-name = "can1-en"; + }; + + adau1962-en { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1962-en"; + }; + + adau1979-en { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-high; + line-name = "adau1979-en"; + }; + + sd-wp-en { + gpio-hog; + gpios = <11 GPIO_ACTIVE_LOW>; + output-low; + line-name = "sd-wp-en"; + }; + + spi2flash-cs-en { + gpio-hog; + gpios = <12 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2flash-cs-en"; + }; + + spi2d2-d3-en { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-high; + line-name = "spi2-d2-d3-en"; + }; + + spdif-optical-en { + gpio-hog; + gpios = <14 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-optical-en"; + }; + + spdif-digital-en { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-low; + line-name = "spdif-digital-en"; + }; + + }; + + ssw1: gpio@0x22 { + compatible = "microchip,mcp23017"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x22>; + + pushbutton3-en { + gpio-hog; + gpios = <0 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "pushbutton3-en"; + }; + + pushbutton2-en { + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "pushbutton2-en"; + }; + + pushbutton1-en { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "pushbutton1-en"; + }; + + leds-en { + gpio-hog; + gpios = <3 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "leds-en"; + }; + + flag0-loop { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "flag0-loop"; + }; + + flag1-loop { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "flag1-loop"; + }; + + flag2-loop { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "flag2-loop"; + }; + + flag3-loop { + gpio-hog; + gpios = <7 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "flag3-loop"; + }; + + adau1977-en { + gpio-hog; + gpios = <8 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "adau1977-en"; + }; + + adau1977-fault-rst-en { + gpio-hog; + gpios = <9 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "adau1977-fault-rst-en"; + }; + + thumbwheel-oe { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "thumbwheel-oe"; + }; + + engine-rpm-oe { + gpio-hog; + gpios = <11 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "engine-rpm-oe"; + }; + + }; + + adau1979: adau1979@0x11 { + compatible = "adi,adau1977"; + reg = <0x11>; + }; + + adau1962: adau1962@0x4 { + compatible = "adi,adau1962"; + reg = <0x4>; + reset-gpios = <&gpa 6 GPIO_ACTIVE_LOW>; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2s0 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai0>; + status = "okay"; +}; + +&crc0 { + status = "okay"; +}; + +&crc1 { + status = "okay"; +}; + +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_default>; + phy-name = "tja1055"; + phy-gpios = <&gpa 1 0>, /* en PA1 */ + <&gpa 2 0x1>; /* stb PA2, GPIO_ACTIVE_LOW */ + status = "okay"; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&can1_default>; + phy-name = "tja1145"; + phy-spibus = /bits/ 16 <0>; + phy-spiclk = <1000000>; + phy-spics = /bits/ 16 <38>; /* GPIO_PC6 */ + status = "okay"; +}; + +&emac0 { + snps,reset-gpio = <&gpa 5 0>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 10000>; + phy-handle = <&dp83867>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + dp83867: ethernet-phy@0 { + reg = <0>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + }; + }; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb0 { + /* mode = <2>; Place OTG port into Device Mode */ + /* mode = <1>; Place OTG port into Host Mode */ + mode = <1>; + status = "okay"; +}; + +&mmc0 { + wp-en-pin = <&ssw0 11 GPIO_ACTIVE_LOW>; /* SD_WP_EN */ + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_default>; + max-frequency = <18750000>; + status = "okay"; +}; + +&lp0 { + pinctrl-names = "default"; + pinctrl-0 = <&lp0_default>; + status = "okay"; +}; + +&lp1 { + pinctrl-names = "default"; + pinctrl-0 = <&lp1_default>; + status = "okay"; +}; + +&sram_mmap { + status = "okay"; +}; + +&pinctrl0 { + uart0_default: uart0_default_pins { + pins { + pinmux = , + ; + }; + }; + uart0_hwflow: uart0_hwflow_pins { + pins { + pinmux = , + , + , + ; + }; + }; + eth0_default: eth0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + spi0_default: spi0_default_pins { + pins { + pinmux = , + , + ; + }; + }; + spi2_quad: spi2_quad_pins { + pins { + pinmux = , + , + , + , + ; + }; + }; + can0_default: can0_default_pins { + pins { + pinmux = , + ; + + }; + }; + can1_default: can1_default_pins { + pins { + pinmux = , + ; + }; + }; + mmc0_default: mmc0_default_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + ppi0_8b: ppi0_8b_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + ppi0_16b: ppi0_16b_pins { + pins { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + }; + cnt0_default: cnt0_default_pins { + pinmux = , + , + ; + }; + lp0_default: lp0_default_pins { + pinmux = , + , + , + , + , + , + , + , + , + ; + }; + lp1_default: lp1_default_pins { + pinmux = , + , + , + , + , + , + , + , + , + ; + }; +}; + +&sru_ctrl_dai0 { + status = "okay"; + + sru_dai0: sru_dai0_mux { + route { + sru-routing = + , /* set DAI0_PIN02 to input */ + , /* route DAI0_PIN02 to SPT0_ACLK */ + , /* set DAI0_PIN04 to input */ + , /* route DAI0_PIN04 to SPT0_AFS */ + , /* set DAI0_PIN01 to output */ + , /* route SPT4_AD0 to DAI0_PIN01 */ + , /* set DAI0_PIN12 to input */ + , /* route DAI0_PIN12 to SPT0_BCLK */ + , /* set DAI0_PIN20 to input */ + , /* route DAI0_PIN20 to SPT0_BFS */ + , /* set DAI0_PIN06 to input */ + ; /* route DAI0_PIN06 to SPT0_BD0 */ + }; + }; +}; + +&sru_ctrl_dai1 { + status = "okay"; +}; + diff --git a/arch/arm/boot/dts/adi/sc57x.dtsi b/arch/arm/boot/dts/adi/sc57x.dtsi new file mode 100644 index 00000000000000..c109ce03a9c50e --- /dev/null +++ b/arch/arm/boot/dts/adi/sc57x.dtsi @@ -0,0 +1,861 @@ +/* + * Device tree header for ADI sc57x processor + * + * Copyright 2014 - 2018 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +#include +#include +#include + +/ { + model = "ADI sc57x"; + compatible = "adi,sc57x"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &uart0; + timer0 = &gptimer0; + timer1 = &gptimer1; + timer2 = &gptimer2; + timer3 = &gptimer3; + timer4 = &gptimer4; + timer5 = &gptimer5; + timer6 = &gptimer6; + timer7 = &gptimer7; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + can0 = &can0; + can1 = &can1; + ethernet0 = &emac0; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2s0 = &i2s0; + mmc0 = &mmc0; + sru0 = &sru_ctrl_dai0; + sru1 = &sru_ctrl_dai1; + }; + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a5"; + reg = <0x0>; + clocks = <&clk ADSP_SC57X_CLK_ARM>; + }; + }; + + pmu { + compatible = "arm,cortex-a5-pmu"; + interrupts = ; + }; + + gic: interrupt-controller@310B2000 { + compatible = "arm,cortex-a5-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x310B2000 0x1000>, + <0x310B4000 0x100>; + }; + + L2: cache-controller@10000000 { + compatible = "arm,pl310-cache"; + reg = <0x10000000 0x1000>; + cache-level = <2>; + }; + + sram0: sram-icc@20000000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20010000 0x1000>; + ranges = <0 0x20010000 0x1000>; /* 64 KiB */ + }; + + sram1: sram-reserved@20004000 { + compatible = "mmio-sram"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x20040000 0x40000>; + ranges = <0 0x20040000 0x40000>; /* 256 KiB */ + }; + + sys_clkin0: sys-clkin0@1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin0"; + }; + + sys_clkin1: sys-clkin1@2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + clock-output-names = "sys_clkin1"; + }; + + clk: clocks@0x3108d000 { + compatible = "adi,sc57x-clocks"; + reg = <0x3108d000 0x1000>, + <0x3108e000 0x1000>, + <0x3108f000 0x1000>; + #clock-cells = <1>; + clocks = <&sys_clkin0>, <&sys_clkin1>; + clock-names = "sys_clkin0", "sys_clkin1"; + status = "okay"; + }; + + gptimers: gptimers@0x31018000 { + compatible = "adi,sc5xx-gptimers"; + reg = <0x31018000 0x200>; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + gptimer0: gptimer@0 { + reg = <0>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x60>; + adi,is-clocksource; + adi,reset-timer; + }; + + gptimer1: gptimer@1 { + reg = <1>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x80>; + adi,is-clockevent; + adi,reset-timer; + }; + + gptimer2: gptimer@2 { + reg = <2>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xa0>; + }; + + gptimer3: gptimer@3 { + reg = <3>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xc0>; + }; + + gptimer4: gptimer@4 { + reg = <4>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0xe0>; + }; + + gptimer5: gptimer@5 { + reg = <5>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x100>; + }; + + gptimer6: gptimer@6 { + reg = <6>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x120>; + }; + + gptimer7: gptimer@7 { + reg = <7>; + interrupt-parent = <&gic>; + interrupts = ; + adi,offset = <0x140>; + }; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + rsc_tbl0: rsc_tbl0@20000000 { + reg = <0x20000000 0x400>; /*1KiB*/ + no-map; + }; + + rsc_tbl1: rsc_tbl0@20000400 { + reg = <0x20000400 0x400>; /*1KiB*/ + no-map; + }; + + sram_B1_unused@20000800 { + reg = <0x20000800 0x1F800>; /* <128 KiB*/ + no-map; + }; + }; + + scb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pads_system_config: adi-control@31004400 { + compatible = "adi,pads-system-config"; + reg = <0x31004400 0x100>; + status = "okay"; + }; + + sram-controller@31080000 { + compatible = "adi,sram-controller"; + reg = <0x31080000 0x100>; + adi,sram = <&sram0>, <&sram1>; + interrupts = ; + status = "okay"; + }; + + sram_mmap: sram-mmap@0 { /* mmap from sram1 pool*/ + compatible = "adi,sram-mmap"; + adi,sram = <&sram1>; + status = "disabled"; + }; + + gptimer_counter: gptimer-counters@0 { + compatible = "adi,gptimer-counter"; + status = "okay"; + }; + + rcu: rcu@0x3108B000 { + compatible = "adi,reset-controller"; + reg = <0x3108C000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + adi,enable-reboot; + status = "okay"; + }; + + sec: sec@0x31089000 { + compatible = "adi,system-event-controller"; + reg = <0x31089000 0x1000>; + adi,rcu = <&rcu>; + adi,sharc-cores = <2>; + status = "okay"; + }; + + tru: tru@0x3108a000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108a000 0x1000>; + adi,max-master-id = <126>; + adi,max-slave-id = <123>; + status = "okay"; + }; + + uart0: uart@0x31003000 { + compatible = "adi,uart4"; + reg = <0x31003000 0x40>; + dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart1: uart@0x31003400 { + compatible = "adi,uart4"; + reg = <0x31003400 0x40>; + dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + uart2: uart@0x31003800 { + compatible = "adi,uart4"; + reg = <0x31003800 0x40>; + dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; + dma-names = "tx", "rx"; + interrupt-parent = <&gic>; + interrupt-names = "tx", "rx", "status"; + interrupts = , + , + ; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c0: twi@0x31001400 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001400 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c1: twi@0x31001500 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001500 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2c2: twi@0x31001600 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,twi"; + reg = <0x31001600 0xFF>; + interrupts = ; + clock-khz = <100>; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk0"; + status = "disabled"; + }; + + i2s0: i2s@0 { + compatible = "adi,sc5xx-i2s-dai"; + reg = <0x31002000 0x80>, <0x31002080 0x80>; + interrupts = , + ;/* SPORT0 */ + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "sclk"; + dmas = <&sport_cluster0 0>, <&sport_cluster0 1>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + watchdog@0x31008000 { + compatible = "adi,watchdog"; + reg = <0x31008000 0x10>; + timeout-sec = <30>; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "adi-watchdog"; + }; + + emac0: ethernet@0x3100C000 { + compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; + reg = <0x3100C000 0x2000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + snps,mixed-burst; + snps,pbl = <8>; + snps,force_sf_dma_mode; + snps,perfect-filter-entries = <32>; + clocks = <&clk ADSP_SC57X_CLK_GIGE>; + clock-names = "stmmaceth"; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + spi0: spi@0x3102E000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102E000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 22>, <&spi_cluster 23>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK1>; + clock-names = "spi"; + status = "disabled"; + }; + + spi1: spi@0x3102F000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x3102F000 0xFF>; + interrupts = ; + dmas = <&spi_cluster 24>, <&spi_cluster 25>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK1>; + clock-names = "spi"; + status = "disabled"; + }; + + spi2: spi@0x31044000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,spi3"; + reg = <0x31044000 0xFF>; + interrupts = ; + dmas = <&spi_cluster2 26>, <&spi_cluster2 27>; + dma-names = "tx", "rx"; + clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK1>; + clock-names = "spi"; + status = "disabled"; + }; + + crc0: crc@0x310A5000 { + compatible = "adi,hmac-crc"; + reg = <0x310A5000 0xFF>; + interrupts = ; + dma_channel = <8>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + crc1: crc@0x310A6000 { + compatible = "adi,hmac-crc"; + reg = <0x310A6000 0xFF>; + interrupts = ; + dma_channel = <18>; + crypto_crc_poly = <0x5c5c5c5c>; + status = "disabled"; + }; + + can0: can@0x31000200 { + compatible = "adi,can"; + reg = <0x31000200 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + can1: can@0x31000a00 { + compatible = "adi,can"; + reg = <0x31000a00 0x5FF>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + status = "disabled"; + }; + + pinctrl0: pinctrl@0x31004400 { + compatible = "adi,adsp-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x31004400 0x400>; + adi,port-sizes = <16 16 16 16 16 12>; + adi,no-drive-strength; + /* + * @todo fix pinctrl driver: + * PUE/PDE is supported but the register locations are different + */ + adi,no-pull-up-down; + }; + + sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310C9000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + sru_ctrl_dai1: sru-ctrl-dai1@0x310CB000 { + compatible = "adi,adsp-sru-ctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x310CB000 0x224>; + adi,system-config = <&pads_system_config>; + status = "disabled"; + }; + + mmc0: mmc@0x31010000 { + compatible = "snps,dw-mshc"; + reg = <0x31010000 0xFFF>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <1024>; + clocks = <&clk ADSP_SC57X_CLK_SDIO>, + <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; + clock-names = "ciu", "biu"; + status = "disabled"; + }; + + lp0: linkport@0 { + compatible = "linkport0"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <2>; + status = "disabled"; + }; + + lp1: linkport@1 { + compatible = "linkport1"; + interrupt-parent = <&gic>; + interrupts = , + ; + clock-div = <2>; + status = "disabled"; + }; + + pint0: pint@0x31005000 { + compatible = "adi,adsp-pint"; + reg = <0x31005000 0xFF>; + interrupts = ; + }; + + pint1: pint@0x31005100 { + compatible = "adi,adsp-pint"; + reg = <0x31005100 0xFF>; + interrupts = ; + }; + + pint2: pint@0x31005200 { + compatible = "adi,adsp-pint"; + reg = <0x31005200 0xFF>; + interrupts = ; + }; + + pint3: pint@0x31005300 { + compatible = "adi,adsp-pint"; + reg = <0x31005300 0xFF>; + interrupts = ; + }; + + pint4: pint@0x31005400 { + compatible = "adi,adsp-pint"; + reg = <0x31005400 0xFF>; + interrupts = ; + }; + + gpa: gport@0x31004000 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004000 0x7F>; + gpio-ranges = <&pinctrl0 0 0 16>; + adi,pint = <&pint0 1>; + adi,gpio-base = <0>; + }; + + gpb: gport@0x31004080 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004080 0x7F>; + gpio-ranges = <&pinctrl0 0 16 16>; + adi,pint = <&pint0 0>; + adi,gpio-base = <16>; + }; + + gpc: gport@0x31004100 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004100 0x7F>; + gpio-ranges = <&pinctrl0 0 32 16>; + adi,pint = <&pint2 1>; + adi,gpio-base = <32>; + }; + + gpd: gport@0x31004180 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004180 0x7F>; + gpio-ranges = <&pinctrl0 0 48 16>; + adi,pint = <&pint2 0>; + adi,gpio-base = <48>; + }; + + gpe: gport@0x31004200 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004200 0x7F>; + gpio-ranges = <&pinctrl0 0 64 16>; + adi,pint = <&pint4 1>; + adi,gpio-base = <64>; + }; + + gpf: gport@0x31004280 { + compatible = "adi,adsp-port-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x31004280 0x7F>; + gpio-ranges = <&pinctrl0 0 80 12>; + adi,pint = <&pint4 0>; + adi,gpio-base = <80>; + }; + + usb0_phy: usb-phy@310c1390 { + compatible = "usb-nop-xceiv"; + reg = <0x310c1390 0x10>; + reg-names = "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + + usb0: usb@310c1000 { + compatible = "adi,musb"; + reg = <0x310c1000 0x390>; + reg-names = "mc"; + interrupts = , + ; + interrupt-names = "mc", "dma"; + spu_securep_id = <109>; + + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb0_phy>; + status = "disabled"; + }; + + sport_cluster0: dma@0x31022000 { + compatible = "adi,dma-controller"; + reg = <0x31022000 0x400>; + status = "okay"; + #dma-cells = <1>; + + sport0_a: channel@0 { + adi,id = <0>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + sport0_b: channel@1 { + adi,id = <1>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + sport1_a: channel@2 { + adi,id = <2>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + sport1_b: channel@3 { + adi,id = <3>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + sport2_a: channel@4 { + adi,id = <4>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + sport2_b: channel@5 { + adi,id = <5>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + sport3_a: channel@6 { + adi,id = <6>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x300>; + }; + + sport3_b: channel@7 { + adi,id = <7>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x380>; + }; + + }; + + spi_cluster: dma@0x3102B000 { + compatible = "adi,dma-controller"; + reg = <0x3102B000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + spi0_tx: channel@22 { + adi,id = <22>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + spi0_rx: channel@23 { + adi,id = <23>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + spi1_tx: channel@24 { + adi,id = <24>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + spi1_rx: channel@25 { + adi,id = <25>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + }; + + spi_cluster2: dma@0x31046000 { + compatible = "adi,dma-controller"; + reg = <0x31046000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + spi2_tx: channel@26 { + adi,id = <26>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + + spi2_rx: channel@27 { + adi,id = <27>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + }; + + dma_cluster2: dma@0x31026000 { + compatible = "adi,dma-controller"; + reg = <0x31026000 0x1000>; + status = "okay"; + #dma-cells = <1>; + + uart0_tx: channel@20 { + adi,id = <20>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x80>; + }; + + uart0_rx: channel@21 { + adi,id = <21>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0>; + }; + + uart1_tx: channel@34 { + adi,id = <34>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x180>; + }; + + uart1_rx: channel@35 { + adi,id = <35>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x100>; + }; + + uart2_tx: channel@37 { + adi,id = <37>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x280>; + }; + + uart2_rx: channel@38 { + adi,id = <38>; + interrupts = , + ; + interrupt-names = "complete", "error"; + adi,src-offset = <0x200>; + }; + }; + + mdma: dma@0x3109b000 { + compatible = "adi,mdma-controller"; + reg = <0x3109b000 0x1000>; + status = "okay"; + + mdma3: channel@43 { + adi,id = <43>; + // The destination interrupts are used for primary complete detection + interrupts = , + , + , + ; + interrupt-names = "complete", "error", "complete2", "error2"; + adi,src-offset = <0>; + adi,dest-offset = <0x80>; + }; + }; + + trng: rng@0x310D0000 { + compatible = "adi,sc5xx-trng"; + reg = <0x310D0000 0x74>, <0x310D8000 0x14>; + interrupts = ; + interrupt-names = "pkic0_irq"; + startup-cycles = <0xff>; + minref-cycles = <0x21>; + maxref-cycles = <0x22>; + alarm-thresh = <0xff>; + shdn-thresh = <0x04>; + poll-data = <0>; /* Use IRQ for data */ + status = "okay"; + }; + + }; +}; From 3c140ed3e940bfdfb84b83a6f5fc84b9630a5a5f Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Thu, 29 May 2025 15:28:48 +0100 Subject: [PATCH 38/85] ARM: dts: adi: Support ADI EV-SC594-SOM with EZLITE carrier Adding device tree support for sc594-som-ezlite Signed-off-by: UtsavAgarwalADI --- arch/arm/boot/dts/adi/Makefile | 2 +- arch/arm/boot/dts/adi/sc594-som-ezlite.dts | 183 +++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boot/dts/adi/sc594-som-ezlite.dts diff --git a/arch/arm/boot/dts/adi/Makefile b/arch/arm/boot/dts/adi/Makefile index 4bf3cbf8d8f72d..9ab2aeb874ade3 100644 --- a/arch/arm/boot/dts/adi/Makefile +++ b/arch/arm/boot/dts/adi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_SC59X) += sc594-som-ezkit.dtb +dtb-$(CONFIG_ARCH_SC59X) += sc594-som-ezkit.dtb sc594-som-ezlite.dtb dtb-$(CONFIG_ARCH_SC58X) += sc589-mini.dtb dtb-$(CONFIG_ARCH_SC57X) += sc573-ezkit.dtb diff --git a/arch/arm/boot/dts/adi/sc594-som-ezlite.dts b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts new file mode 100644 index 00000000000000..fceec8844a44b9 --- /dev/null +++ b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts @@ -0,0 +1,183 @@ +/* + * Device tree for ADI sc594-som-ezlite board + * + * Copyright 2014 - 2020 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + */ + +/dts-v1/; + +#include "sc594-som.dtsi" + +/ { + model = "ADI sc594-som-ezlite"; + compatible = "adi,sc594-som-ezlite", "adi,sc59x"; + + clocks { + compatible = "simple-bus"; + mclk: mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + clock-output-names = "mclk"; + }; + }; + + scb { + sound { + compatible = "adi,sc5xx-asoc-card"; + adi,cpu-dai = <&i2s0>; + adi,codec = <&adau1372>; + }; + }; +}; + +&i2c2 { + gpio_expander: adp5588@30 { + compatible = "adi,adp5588-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x30>; + status = "okay"; + + usb-spi0 { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_spi0_en"; + }; + + usb-spi1 { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_spi1_en"; + }; + + usb-qspi-en { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-low; + line-name = "usb_qspi_en"; + }; + + usb-qspi-reset { + gpio-hog; + gpios = <11 GPIO_ACTIVE_LOW>; + output-high; + line-name = "usb_qspi_reset"; + }; + + eth0-reset { + gpio-hog; + gpios = <12 GPIO_ACTIVE_LOW>; + output-low; + line-name = "eth0-reset"; + }; + + adau1372-pwrdwn { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-low; + line-name = "adau1372_pwrdwn"; + }; + + led1 { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led1-en"; + }; + + led2 { + gpio-hog; + gpios = <16 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led2-en"; + }; + + led3 { + gpio-hog; + gpios = <17 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led3-en"; + }; + }; + + adau1372: adau1372@0x3c { + compatible = "adi,adau1372"; + reg = <0x3c>; + clock-names = "mclk"; + clocks = <&mclk>; + }; + +}; + +&emac0 { + snps,reset-active-low; + snps,reset-delays-us = <0 200 500>; + phy-handle = <&adin1300>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + adin1300: ethernet-phy@0 { + reg = <0>; + }; + }; +}; + +&emac1 { + status = "disabled"; +}; + +&sru_ctrl_dai0 { + status = "okay"; + + sru_dai0: sru_dai0_mux { + route { + sru-routing = + /* 1362 TX LRCLK */ + , /* set DAI0_PIN01 to input */ + , /* route DAI0_PIN01 to SPT0_AFS */ + + /* 1363 TX BCLK */ + , /* set DAI0_PIN02 to input */ + , /* route DAI0_PIN02 to SPT0_ACLK */ + + /* 1363 TX DAC_SDATA/MP0 */ + , /* set DAI0_PIN03 to output */ + , /* route SPT0_AD0 to DAI0_PIN03 */ + + /* 1362 RX LRCLK */ + , /* set DAI0_PIN01 to input */ + , /* route DAI0_PIN01 to SPT0_BFS */ + + /* 1363 RX BCLK */ + , /* set DAI0_PIN02 to input */ + , /* route DAI0_PIN02 to SPT0_BCLK */ + + /* 1363 RX ADC_SDATA0/MP1 */ + , /* set DAI0_PIN04 to input */ + , /* route DAI0_PIN04 to SPT0_BD0 */ + + /* 1363 RX ADC_SDATA1/MP6 */ + , /* set DAI0_PIN05 to input */ + ; /* route DAI0_PIN05 to SPT0_BD1 */ + }; + }; +}; + +&i2s0 { + pinctrl-names = "default"; + pinctrl-0 = <&sru_dai0>; + status = "okay"; +}; + From 8882cb6e10bc4337e6a5aac527299265f3bc658b Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:29:10 +0100 Subject: [PATCH 39/85] [ADI] ARM: sc573-ezkit_defconfig: Support ADZS-SC573-EZLITE The only evaluation board for the ADSP-SC573 was named EZKIT and EZLITE in different places Signed-off-by: UtsavAgarwalADI --- arch/arm/configs/sc573-ezkit_defconfig | 138 +++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 arch/arm/configs/sc573-ezkit_defconfig diff --git a/arch/arm/configs/sc573-ezkit_defconfig b/arch/arm/configs/sc573-ezkit_defconfig new file mode 100644 index 00000000000000..f02d212c309f02 --- /dev/null +++ b/arch/arm/configs/sc573-ezkit_defconfig @@ -0,0 +1,138 @@ +CONFIG_LOCALVERSION="" +CONFIG_SYSVIPC=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_ARCH_SC5XX=y +CONFIG_ARCH_SC57X=y +CONFIG_MACH_SC573_EZKIT=y +CONFIG_ARM_THUMBEE=y +CONFIG_HZ_250=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPUFREQ_DT=y +# CONFIG_SUSPEND is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_SWAP is not set +# CONFIG_COMPACTION is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_DNS_RESOLVER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SRAM=y +CONFIG_ADI_SRAM_MMAP_V7=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_NETDEVICES=y +CONFIG_STMMAC_ETH=y +CONFIG_DP83867_PHY=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_SERIO_LIBPS2=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_ADI_UART4=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ADI=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=m +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_ADI=y +CONFIG_PINCTRL_MCP23S08=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +CONFIG_GPIO_SYSFS=y +# CONFIG_HWMON is not set +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1979=y +CONFIG_SND_SC5XX_ADAU1962=y +CONFIG_USB=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_ADI=y +CONFIG_USB_INVENTRA_DMA=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_HID=m +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_REMOTEPROC=y +CONFIG_ADI_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_ADI_ADSP_IRQ=y +CONFIG_EXT4_FS=y +# CONFIG_DNOTIFY is not set +CONFIG_AUTOFS4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_DEV_ADI_CRC=y +CONFIG_CRC_CCITT=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_PAGEALLOC=y +# CONFIG_FTRACE is not set +CONFIG_ARM_PTDUMP_DEBUGFS=y +CONFIG_EARLY_PRINTK=y From e59c5c830d015ea1d47ce0a70b1ed5c543880d77 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Wed, 10 Sep 2025 15:57:21 +0200 Subject: [PATCH 40/85] [ADI] ARM: sc589-mini_defconfig: Support ADZS-SC589-MINI Signed-off-by: Utsav Agarwal --- arch/arm/configs/sc589-mini_defconfig | 143 ++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 arch/arm/configs/sc589-mini_defconfig diff --git a/arch/arm/configs/sc589-mini_defconfig b/arch/arm/configs/sc589-mini_defconfig new file mode 100644 index 00000000000000..a9504c27068d67 --- /dev/null +++ b/arch/arm/configs/sc589-mini_defconfig @@ -0,0 +1,143 @@ +CONFIG_LOCALVERSION="" +CONFIG_SYSVIPC=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_USELIB=y +CONFIG_GENERIC_IRQ_DEBUGFS=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_EVENTFD is not set +# CONFIG_AIO is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_ARCH_SC5XX=y +CONFIG_ARCH_SC58X=y +CONFIG_MACH_SC589_MINI=y +CONFIG_ARM_THUMBEE=y +CONFIG_HZ_250=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPUFREQ_DT=y +# CONFIG_SUSPEND is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_SWAP is not set +# CONFIG_COMPACTION is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_INET_DIAG is not set +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SRAM=y +CONFIG_ADI_SRAM_MMAP_V7=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_STMMAC_ETH=y +CONFIG_DP83867_PHY=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_MISC=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_ADI_UART4=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ADI=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_ADI=y +CONFIG_PINCTRL_MCP23S08=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +# CONFIG_HWMON is not set +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1761=m +CONFIG_USB=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_ADI=y +CONFIG_USB_INVENTRA_DMA=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_HID=m +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_PLTFM=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_ADI2=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_REMOTEPROC=y +CONFIG_ADI_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_ADI_ADSP_IRQ=y +CONFIG_EXT4_FS=y +# CONFIG_DNOTIFY is not set +CONFIG_AUTOFS4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_ADI_CRC=y +CONFIG_CRC_CCITT=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_PAGEALLOC=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_FTRACE is not set +CONFIG_DEBUG_LL=y +CONFIG_EARLY_PRINTK=y From c49e57fb8a19d8d7f8f8885fff667c7c0bb022e6 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Wed, 10 Sep 2025 16:22:13 +0200 Subject: [PATCH 41/85] [ADI] ARM: sc594-som-ezkit_defconfig: Support EV-SC594-SOM with EZKIT carrier Signed-off-by: Utsav Agarwal --- arch/arm/configs/sc594-som-ezkit_defconfig | 151 +++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 arch/arm/configs/sc594-som-ezkit_defconfig diff --git a/arch/arm/configs/sc594-som-ezkit_defconfig b/arch/arm/configs/sc594-som-ezkit_defconfig new file mode 100644 index 00000000000000..137f1163bf94ac --- /dev/null +++ b/arch/arm/configs/sc594-som-ezkit_defconfig @@ -0,0 +1,151 @@ +CONFIG_LOCALVERSION="-yocto-standard" +CONFIG_SYSVIPC=y +CONFIG_USELIB=y +CONFIG_GENERIC_IRQ_DEBUGFS=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_ARCH_SC5XX=y +CONFIG_ARCH_SC59X=y +CONFIG_MACH_SC594_SOM=y +CONFIG_ARM_THUMBEE=y +CONFIG_HZ_250=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPUFREQ_DT=y +# CONFIG_SUSPEND is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_MQPRIO=y +CONFIG_DNS_RESOLVER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SRAM=y +CONFIG_ADI_SRAM_MMAP_V7=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_STMMAC_ETH=y +CONFIG_DP83848_PHY=y +CONFIG_DP83867_PHY=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_MISC=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ADI=y +CONFIG_TTY_PRINTK=y +CONFIG_BUTTON_LED=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=m +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_ADI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_MCP23S08=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +CONFIG_GPIO_SYSFS=y +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_FB=m +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1979=y +CONFIG_SND_SC5XX_ADAU1962=y +CONFIG_USB=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC2=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_HID=m +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +CONFIG_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_GENERIC_PHY=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRC_CCITT=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_FS=y +# CONFIG_FTRACE is not set +CONFIG_ADI_REMOTEPROC=y From 13460854c83008d34259478691692af20421de6a Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Thu, 29 May 2025 15:34:34 +0100 Subject: [PATCH 42/85] [ADI] ARM: sc594-som-ezlite_defconfig: Support ADI EV-SC594-SOM with EZLITE carrier Signed-off-by: Utsav Agarwal --- arch/arm/configs/sc594-som-ezlite_defconfig | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 arch/arm/configs/sc594-som-ezlite_defconfig diff --git a/arch/arm/configs/sc594-som-ezlite_defconfig b/arch/arm/configs/sc594-som-ezlite_defconfig new file mode 100644 index 00000000000000..350bf80c5121ce --- /dev/null +++ b/arch/arm/configs/sc594-som-ezlite_defconfig @@ -0,0 +1,148 @@ +CONFIG_LOCALVERSION="" +CONFIG_SYSVIPC=y +CONFIG_USELIB=y +CONFIG_GENERIC_IRQ_DEBUGFS=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_ARCH_SC5XX=y +CONFIG_ARCH_SC59X=y +CONFIG_MACH_SC594_SOM=y +CONFIG_ARM_THUMBEE=y +CONFIG_HZ_250=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPUFREQ_DT=y +# CONFIG_SUSPEND is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_MQPRIO=y +CONFIG_DNS_RESOLVER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SRAM=y +CONFIG_ADI_SRAM_MMAP_V7=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_STMMAC_ETH=y +CONFIG_DP83848_PHY=y +CONFIG_DP83867_PHY=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_MISC=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ADI=y +CONFIG_TTY_PRINTK=y +CONFIG_BUTTON_LED=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=m +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_ADI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_MCP23S08=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +CONFIG_GPIO_ADP5588=y +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_FB=m +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1372=y +CONFIG_USB=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC2=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_HID=m +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +# CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +CONFIG_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_GENERIC_PHY=y +CONFIG_EXT4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRC_CCITT=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_FS=y +# CONFIG_FTRACE is not set From 7cc8349e9c43f0d00d6b1ca9d9c1f940d51e3b96 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 14:39:49 +0200 Subject: [PATCH 43/85] [ADI] arm64: sc598-som-ezkit_defconfig: Support ADI EV-SC598-SOM with EZKIT carrier Signed-off-by: Philip Molloy --- arch/arm64/configs/sc598-som-ezkit_defconfig | 369 +++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 arch/arm64/configs/sc598-som-ezkit_defconfig diff --git a/arch/arm64/configs/sc598-som-ezkit_defconfig b/arch/arm64/configs/sc598-som-ezkit_defconfig new file mode 100644 index 00000000000000..5c1f506fc4769b --- /dev/null +++ b/arch/arm64/configs/sc598-som-ezkit_defconfig @@ -0,0 +1,369 @@ +CONFIG_LOCALVERSION="" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_JIT=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=12 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12 +CONFIG_NUMA_BALANCING=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_ELF_CORE is not set +# CONFIG_BASE_FULL is not set +# CONFIG_KALLSYMS is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_SC59X_64=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_HOTPLUG_CPU=y +CONFIG_NUMA=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_CNP is not set +# CONFIG_ARM64_PTR_AUTH is not set +# CONFIG_SUSPEND is not set +CONFIG_PM=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=y +CONFIG_ACPI_CPPC_CPUFREQ=m +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ACPI=y +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_FAN is not set +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64_CE=m +CONFIG_CRYPTO_SHA3_ARM64=m +CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IPV6 is not set +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_IP_SET=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_MQPRIO=y +# CONFIG_WIRELESS is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_ALLOW_DEV_COREDUMP is not set +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +# CONFIG_PNP_DEBUG_MESSAGES is not set +# CONFIG_XEN_BLKDEV_FRONTEND is not set +CONFIG_SRAM=y +CONFIG_EEPROM_AT25=m +CONFIG_ADI_SRAM_MMAP=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_LITEX is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_WLAN is not set +# CONFIG_XEN_NETDEV_FRONTEND is not set +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_EVDEV=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set +CONFIG_HW_RANDOM_ADI=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_SPIDEV=y +# CONFIG_PTP_1588_CLOCK_KVM is not set +CONFIG_PINCTRL_MCP23S08=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_ADSP_SC5XX=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +CONFIG_GPIO_ADI_ADSP_PORT=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +# CONFIG_POWER_SUPPLY_HWMON is not set +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_MFD_HI6421_PMIC=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1979=y +CONFIG_SND_SC5XX_ADAU1962=y +CONFIG_SND_SC5XX_SHARC_ALSA_CARD=y +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +# CONFIG_HID is not set +# CONFIG_USB_HID is not set +CONFIG_USB=y +# CONFIG_USB_OTG is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_TYPEC=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +CONFIG_MMC_DW=y +CONFIG_MMC_CQHCI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +CONFIG_MV_XOR_V2=y +CONFIG_PL330_DMA=y +CONFIG_VFIO=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +# CONFIG_VHOST_MENU is not set +# CONFIG_SURFACE_PLATFORMS is not set +CONFIG_COMMON_CLK_ADI_SC598=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_HWSPINLOCK=y +CONFIG_ARM_MHU=y +CONFIG_PLATFORM_MHU=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_REMOTEPROC=y +CONFIG_ADI_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_IIO=y +CONFIG_ADI_HADC=y +CONFIG_ADI_ADSP_IRQ=y +CONFIG_PHY_XGENE=y +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_HISI_PMU=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_ECDH=m +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_ADI_CRC=y +CONFIG_CRYPTO_DEV_ADI_HASH=y +CONFIG_CRYPTO_DEV_ADI_SKCIPHER=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_IRQ_POLL=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_BLK_DEV_IO_TRACE=y +# CONFIG_STRICT_DEVMEM is not set +CONFIG_MEMTEST=y +CONFIG_BLK_DEV_INTEGRITY_T10=y +CONFIG_SCSI_MOD=y +CONFIG_SCSI_COMMON=y +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +CONFIG_SCSI_PROC_FS=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_BSG=y +CONFIG_USB_STORAGE=y +CONFIG_SG_POOL=y +CONFIG_RPMSG_CTRL=y \ No newline at end of file From 3896d6c72b48d8bdc91292c52c4a6963c58a4c30 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 11 Jul 2025 13:27:42 +0100 Subject: [PATCH 44/85] [ADI] arm64: sc598-som-ezlite_defconfig: Support EV-SC598-SOM with EZLITE carrier Signed-off-by: Utsav Agarwal --- arch/arm64/configs/sc598-som-ezlite_defconfig | 357 ++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 arch/arm64/configs/sc598-som-ezlite_defconfig diff --git a/arch/arm64/configs/sc598-som-ezlite_defconfig b/arch/arm64/configs/sc598-som-ezlite_defconfig new file mode 100644 index 00000000000000..e246aa27375f8d --- /dev/null +++ b/arch/arm64/configs/sc598-som-ezlite_defconfig @@ -0,0 +1,357 @@ +CONFIG_LOCALVERSION="" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_JIT=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=12 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12 +CONFIG_NUMA_BALANCING=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_ELF_CORE is not set +# CONFIG_BASE_FULL is not set +# CONFIG_KALLSYMS is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_SC59X_64=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_HOTPLUG_CPU=y +CONFIG_NUMA=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_CNP is not set +# CONFIG_ARM64_PTR_AUTH is not set +# CONFIG_SUSPEND is not set +CONFIG_PM=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=y +CONFIG_ACPI_CPPC_CPUFREQ=m +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ACPI=y +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_FAN is not set +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64_CE=m +CONFIG_CRYPTO_SHA3_ARM64=m +CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IPV6 is not set +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_IP_SET=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_MQPRIO=y +# CONFIG_WIRELESS is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_ALLOW_DEV_COREDUMP is not set +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +# CONFIG_PNP_DEBUG_MESSAGES is not set +# CONFIG_XEN_BLKDEV_FRONTEND is not set +CONFIG_SRAM=y +CONFIG_EEPROM_AT25=m +CONFIG_ADI_SRAM_MMAP=y +CONFIG_ADI_SRAM_CONTROLLER=y +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_LITEX is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_WLAN is not set +# CONFIG_XEN_NETDEV_FRONTEND is not set +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_EVDEV=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set +CONFIG_HW_RANDOM_ADI=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ADI_TWI=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_SPIDEV=y +# CONFIG_PTP_1588_CLOCK_KVM is not set +CONFIG_PINCTRL_MCP23S08=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_ADSP_SC5XX=y +CONFIG_SRUCTRL_ADSP_SC5XX=y +CONFIG_GPIO_ADI_ADSP_PORT=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +# CONFIG_POWER_SUPPLY_HWMON is not set +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_ADI_WATCHDOG=y +CONFIG_MFD_HI6421_PMIC=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SC5XX_PCM=y +CONFIG_SND_SC5XX_ADAU1372=y +CONFIG_SND_SC5XX_SHARC_ALSA_CARD=y +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +# CONFIG_HID is not set +# CONFIG_USB_HID is not set +CONFIG_USB=y +CONFIG_USB_OTG=y +CONFIG_CRYPTO_SHA256=m +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_TYPEC=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +CONFIG_MMC_DW=y +CONFIG_MMC_CQHCI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_ADI_DMA=y +CONFIG_MV_XOR_V2=y +CONFIG_PL330_DMA=y +CONFIG_VFIO=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +# CONFIG_VHOST_MENU is not set +# CONFIG_SURFACE_PLATFORMS is not set +CONFIG_COMMON_CLK_ADI_SC598=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_HWSPINLOCK=y +CONFIG_ARM_MHU=y +CONFIG_PLATFORM_MHU=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_REMOTEPROC=y +CONFIG_ADI_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_IIO=y +CONFIG_ADI_HADC=y +CONFIG_ADI_ADSP_IRQ=y +CONFIG_PHY_XGENE=y +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_HISI_PMU=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_ECDH=m +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_IRQ_POLL=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_BLK_DEV_IO_TRACE=y +# CONFIG_STRICT_DEVMEM is not set +CONFIG_MEMTEST=y +CONFIG_RPMSG_CTRL=y +CONFIG_GPIO_ADP5588=y From c0fe3af3df3af1265172f7e789b199a7ec51750e Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Thu, 11 Dec 2025 16:41:14 +0100 Subject: [PATCH 45/85] soc: adi: Only build system drivers for ADSP architecture SoC specific code should not be built for all platforms. Therefore only build it for ADSP 32/64-bit architectures. In the future system.c and system.c and pads_system.c should be replaced with the corresponding mainline frameworks. Signed-off-by: Philip Molloy --- drivers/soc/adi/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/adi/Makefile b/drivers/soc/adi/Makefile index 49413f4b83f003..58cd4bc51a82ee 100644 --- a/drivers/soc/adi/Makefile +++ b/drivers/soc/adi/Makefile @@ -2,9 +2,9 @@ # todo modularize; already depends on CONFIG_ARCH_SC59X_64 though -obj-y += pads_system.o system.o - +obj-$(CONFIG_ARCH_SC59X_64) += pads_system.o system.o obj-$(CONFIG_ARCH_SC59X_64) += mach-sc59x/ obj-$(CONFIG_ARCH_SC59X_64) += mach-sc5xx/ +obj-$(CONFIG_ARCH_SC5XX) += pads_system.o system.o obj-$(CONFIG_ARCH_SC5XX) += mach-sc5xx/ From b783c742fe57544c1d9fbee89d32051058a1adc6 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Fri, 12 Dec 2025 15:54:53 +0000 Subject: [PATCH 46/85] MAINTAINERS: Add entry for ADI ADSP-SC5xx SoC support Add maintainer entry for Analog Devices ADSP-SC5xx series So Signed-off-by: Arturs Artamonovs --- MAINTAINERS | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e8f06145fb54c9..7380a4c8f2163f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2288,6 +2288,43 @@ F: include/dt-bindings/reset/actions,* F: include/linux/soc/actions/ N: owl +ARM/ADI SoC Support +M: Arturs Artamonovs +M: Utsav Agarwal +M: Vasileios Bimpikas +L: linux@analog.com +S: Maintained +F: Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml +F: Documentation/devicetree/bindings/usb/adi,musb.yaml +F: arch/arm/boot/dts/adi/* +F: arch/arm/configs/sc5*_defconfig +F: arch/arm/mach-sc5xx/ +F: arch/arm64/boot/dts/adi/* +F: arch/arm64/configs/sc5*_defconfig +F: drivers/clk/adi/* +F: drivers/clocksource/timer-adi-adsp-sc5xx.c +F: drivers/dma/adi-dma.c +F: drivers/dma/adi-dma.h +F: drivers/gpio/gpio-adi-adsp-port.c +F: drivers/i2c/busses/i2c-adi-twi.c +F: drivers/irqchip/irq-adi-adsp.c +F: drivers/misc/adi/ +F: drivers/net/ethernet/stmicro/stmmac/dwmac-adi.c +F: drivers/pinctrl/adi/ +F: drivers/rpmsg/adi_rpmsg.c +F: drivers/soc/adi/ +F: drivers/spi/spi-adi.c +F: drivers/tty/serial/adi_uart4.c +F: drivers/usb/musb/adi.c +F: drivers/usb/musb/adi.h +F: drivers/watchdog/adi_wdt.c +F: include/dt-bindings/clock/adi-sc5xx-clock.h +F: include/dt-bindings/pinctrl/adi-adsp-sru.h +F: include/dt-bindings/pinctrl/adi-adsp.h +F: include/linux/soc/adi/ +F: include/sound/sc5xx-dai.h +F: sound/soc/adi/sc5xx* + ARM/AIROHA SOC SUPPORT M: Matthias Brugger M: AngeloGioacchino Del Regno From bee4b8789c6db543b6000d15412a4fa4e14df03e Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 6 Aug 2025 18:39:16 +0200 Subject: [PATCH 47/85] arm: sc5xx: remove -yocto-standard from LOCALVERSION Signed-off-by: Philip Molloy --- arch/arm/configs/sc594-som-ezkit_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/sc594-som-ezkit_defconfig b/arch/arm/configs/sc594-som-ezkit_defconfig index 137f1163bf94ac..95ee136225b73a 100644 --- a/arch/arm/configs/sc594-som-ezkit_defconfig +++ b/arch/arm/configs/sc594-som-ezkit_defconfig @@ -1,4 +1,4 @@ -CONFIG_LOCALVERSION="-yocto-standard" +CONFIG_LOCALVERSION="" CONFIG_SYSVIPC=y CONFIG_USELIB=y CONFIG_GENERIC_IRQ_DEBUGFS=y From 0741004630869b27fa748191e96c67212ea515b1 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Mon, 12 Jan 2026 09:03:43 +0100 Subject: [PATCH 48/85] arm64: defconfig: sc598-som-ezkit: enable UBI/UBIFS Enable UBI and UBIFS support in the SC598 SOM EZ-KIT defconfig. Signed-off-by: Ozan Durgut --- arch/arm64/configs/sc598-som-ezkit_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/sc598-som-ezkit_defconfig b/arch/arm64/configs/sc598-som-ezkit_defconfig index 5c1f506fc4769b..d1a590c1139cb5 100644 --- a/arch/arm64/configs/sc598-som-ezkit_defconfig +++ b/arch/arm64/configs/sc598-som-ezkit_defconfig @@ -125,6 +125,7 @@ CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_ARM_SCPI_PROTOCOL=y CONFIG_EFI_CAPSULE_LOADER=y CONFIG_MTD=y +CONFIG_MTD_UBI=y CONFIG_MTD_BLOCK=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_DENALI_DT=y @@ -318,6 +319,7 @@ CONFIG_HUGETLBFS=y CONFIG_CONFIGFS_FS=y CONFIG_EFIVAR_FS=y CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y From fdead2775982ab17a69fe2b7f71fa0683a3c0f82 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Mon, 12 Jan 2026 09:04:44 +0100 Subject: [PATCH 49/85] arm64: defconfig: sc598-som-ezkit: enable ADP5588 Enable the ADP5588 keyboard driver which is required on SC598 SOM Rev. E boards. Signed-off-by: Ozan Durgut --- arch/arm64/configs/sc598-som-ezkit_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/sc598-som-ezkit_defconfig b/arch/arm64/configs/sc598-som-ezkit_defconfig index d1a590c1139cb5..c86b70b01a5f43 100644 --- a/arch/arm64/configs/sc598-som-ezkit_defconfig +++ b/arch/arm64/configs/sc598-som-ezkit_defconfig @@ -186,6 +186,7 @@ CONFIG_STMMAC_ETH=y # CONFIG_XEN_NETDEV_FRONTEND is not set CONFIG_INPUT_FF_MEMLESS=y CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADP5588=y # CONFIG_SERIO_SERPORT is not set CONFIG_LEGACY_PTY_COUNT=16 CONFIG_SERIAL_8250=y From 92c7df6e43e548cdc01fec90a3b15c4459d0f216 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Mon, 15 Dec 2025 18:21:05 +0000 Subject: [PATCH 50/85] dt-bindings: soc: adi: Add PADS system config binding for SC5XX Document the PADS system configuration register binding. Provides voltage and endian selection controls for SC5XX peripheral drivers. Signed-off-by: Arturs Artamonovs --- .../soc/adi/adi,pads-system-config.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/adi/adi,pads-system-config.yaml diff --git a/Documentation/devicetree/bindings/soc/adi/adi,pads-system-config.yaml b/Documentation/devicetree/bindings/soc/adi/adi,pads-system-config.yaml new file mode 100644 index 00000000000000..bba2e5509a9558 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/adi/adi,pads-system-config.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/adi/adi,pads-system-config.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices PADS-related system config for SC5XX processor family + +maintainers: + - Arturs Artamonovs + - Utsav Agarwal + +description: + Allows other drivers to control the PADS-related system config register. + This register ties into many drivers and adds silicon controls for items + like voltage selection and endian selection. + +properties: + compatible: + enum: + - adi,pads-system-config + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pads_system_config: adi-control@31004600 { + compatible = "adi,pads-system-config"; + reg = <0x31004600 0x100>; + }; + }; + + emac0: ethernet@31040000 { + reg = <0x31040000 0x2000>; + adi,system-config = <&pads_system_config>; + }; From 19ed9daa8fc6c629fc2e2b6548128c6e9323e528 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Mon, 15 Dec 2025 18:21:44 +0000 Subject: [PATCH 51/85] dt-bindings: soc: adi: Add reset controller config binding for SC5XX Document the Reset Control Unit (RCU) binding for managing SHARC and ARM core start/stop/reset operations on SC5XX processors Signed-off-by: Arturs Artamonovs --- .../soc/adi/adi,reset-controller.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml diff --git a/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml b/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml new file mode 100644 index 00000000000000..9194a7c905ca30 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/adi/adi,reset-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Reset Controller for SC5XX processor family + +maintainers: + - Arturs Artamonovs + - Utsav Agarwal + +description: + SHARC and ARM core reset control unit for starting/stopping/resetting + processors + +properties: + compatible: + enum: + - adi,reset-controller + + reg: + maxItems: 1 + + adi,sharc-min: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Minimum valid SHARC core ID/count + minimum: 0 + + adi,sharc-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximum valid SHARC core ID/count + maximum: 2 + +required: + - compatible + - reg + - adi,sharc-min + - adi,sharc-max + +additionalProperties: false + +examples: + - | + rcu: rcu@3108c000 { + compatible = "adi,reset-controller"; + reg = <0x3108c000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + }; From 62257854115f86b78bdc11a2844b5d6bf5d7d90f Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Mon, 15 Dec 2025 18:22:22 +0000 Subject: [PATCH 52/85] dt-bindings: soc: adi: Add rpmsg config binding for SC5XX Document the RPMSG binding for ARM-SHARC inter-core communication on SC598 processors using TRU-based signaling and shared memory. Signed-off-by: Arturs Artamonovs --- .../bindings/soc/adi/adi,rpmsg-SC598.yaml | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml diff --git a/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml b/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml new file mode 100644 index 00000000000000..d7a2d4273e8f03 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/adi/adi,rpmsg-SC598.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices RPMSG Driver for SC5XX processor family + +maintainers: + - Arturs Artamonovs + - Utsav Agarwal + +description: | + Describes device tree binding for adi rpmsg driver + required when rpmsg communication is needed and remote core isn't + started by adi,remoteproc. + The size of vdev-vring specifies how many message buffers are allocated + for tx and rx (combined) overriding the default number specified in + MAX_RPMSG_NUM_BUFS. The memory-region size must match (or be larger) selected + number of messages times MAX_RPMSG_BUF_SIZE (default 512). + E.g. for 1024 message buffers (512 for rx and 512 for tx): + vdev-vring size = 0x0000a000 + memory-region size = 0x00080000 + + Selection table for MAX_RPMSG_BUF_SIZE=512 (default): + + rpmsg | | + buffers | vdev-vring | memory-region + --------------------------------- + 256 | 0x00004000 | 0x00020000 + 512 | 0x00006000 | 0x00040000 + 1024 | 0x0000a000 | 0x00080000 + 2048 | 0x00010000 | 0x00100000 + 4096 | 0x0001e000 | 0x00200000 + 8192 | 0x00038000 | 0x00400000 + 16384 | 0x0006c000 | 0x00800000 + 32768 | 0x000d4000 | 0x01000000 + 65536 | 0x001a4000 | 0x02000000 + 131072 | 0x00344000 | 0x04000000 + 262144 | 0x00684000 | 0x08000000 + 524288 | 0x00d04000 | 0x10000000 + 1048576 | 0x01a04000 | 0x20000000 + 2097152 | 0x03404000 | 0x40000000 + +properties: + compatible: + enum: + - adi,rpmsg-SC598 + + reg: + maxItems: 1 + + core-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: SHARC core number + + adi,rcu: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to Remote Control Unit + + adi,rsc-table: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to resource table memory region shared with SHARC core + + adi,tru: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to Trigger Routing Unit for ICC interrupts + + interrupts: + maxItems: 1 + description: ICC interrupt for rpmsg communication + + adi,tru-master-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: ICC interrupt number to notify remote core + + vdev-vring: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + phandle to reserved memory region for rpmsg vdev0vrings, + if not specified allocates buffer from DMA pool. + + memory-region: + maxItems: 1 + description: | + phandle to reserved memory for rpmsg message buffers, + if not specified allocates buffer from DMA pool. + + +required: + - compatible + - core-id + - adi,rcu + - adi,rsc-table + - adi,tru + - interrupts + - adi,tru-master-id + +additionalProperties: false + +examples: + - | + #include + #include + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vdev0vrings: vdev0vring0@20080000 { + reg = <0x20080000 0x4000>; + no-map; + }; + + vdev0buffer: vdev0buffer@20084000 { + compatible = "shared-dma-pool"; + reg = <0x20084000 0x20000>; + no-map; + }; + }; + + rcu: rcu@3108c000 { + compatible = "adi,reset-controller"; + reg = <0x3108c000 0x1000>; + adi,sharc-min = <1>; + adi,sharc-max = <2>; + }; + + tru: tru@3108a000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108a000 0x1000>; + adi,max-master-id = <182>; + adi,max-slave-id = <187>; + }; + + rsc_tbl0: rsc-tbl@20081000 { + reg = <0x20081000 0x1000>; + }; + + core0-rpmsg@28240000 { + compatible = "adi,rpmsg-SC598"; + reg = <0x28240000 0x1000>; + core-id = <1>; + adi,rcu = <&rcu>; + adi,rsc-table = <&rsc_tbl0>; + interrupts = ; + adi,tru = <&tru>; + adi,tru-master-id = <135>; + vdev-vring = <&vdev0vrings>; + memory-region = <&vdev0buffer>; + }; From cc33611209ee22fcb43462692a5077a78f55c186 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Mon, 15 Dec 2025 18:22:50 +0000 Subject: [PATCH 53/85] dt-bindings: soc: adi: Add system event controller config binding for SC5XX Document the System Event Controller (SEC) binding, which provides interrupt control for SHARC cores on SC5XX processors. Signed-off-by: Arturs Artamonovs --- .../soc/adi/adi,system-event-controller.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/adi/adi,system-event-controller.yaml diff --git a/Documentation/devicetree/bindings/soc/adi/adi,system-event-controller.yaml b/Documentation/devicetree/bindings/soc/adi/adi,system-event-controller.yaml new file mode 100644 index 00000000000000..9c3f6a37bf0ab0 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/adi/adi,system-event-controller.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/adi/adi,system-event-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices System Event Controller for SC5XX processor family + +maintainers: + - Arturs Artamonovs + - Utsav Agarwal + +description: + This is the interrupt controller for the SHARC cores on the SC5XX family. + +properties: + compatible: + enum: + - adi,system-event-controller + + reg: + maxItems: 1 + + adi,rcu: + $ref: /schemas/types.yaml#/definitions/phandle + description: Associated reset control unit + + adi,sharc-cores: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of SHARC cores available + +required: + - compatible + - reg + - adi,rcu + - adi,sharc-cores + +additionalProperties: false + +examples: + - | + sec: sec@31089000 { + compatible = "adi,system-event-controller"; + reg = <0x31089000 0x1000>; + adi,rcu = <&rcu>; + adi,sharc-cores = <2>; + }; From d25ccf9516730e5c46c68f4cb14e58c419851758 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Mon, 15 Dec 2025 18:23:19 +0000 Subject: [PATCH 54/85] dt-bindings: soc: adi: Add trigger routing unit config binding for SC5XX Document the Trigger Routing Unit (TRU) binding for mapping trigger masters to slaves, enabling inter-core communication on SC5XX. Signed-off-by: Arturs Artamonovs --- .../soc/adi/adi,reset-controller.yaml | 4 +- .../bindings/soc/adi/adi,rpmsg-SC598.yaml | 37 ++------ .../soc/adi/adi,trigger-routing-unit.yaml | 85 +++++++++++++++++++ 3 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/adi/adi,trigger-routing-unit.yaml diff --git a/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml b/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml index 9194a7c905ca30..a37b0e6867cb9c 100644 --- a/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml +++ b/Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml @@ -25,11 +25,13 @@ properties: adi,sharc-min: $ref: /schemas/types.yaml#/definitions/uint32 description: Minimum valid SHARC core ID/count - minimum: 0 + minimum: 1 + maximum: 2 adi,sharc-max: $ref: /schemas/types.yaml#/definitions/uint32 description: Maximum valid SHARC core ID/count + minimum: 1 maximum: 2 required: diff --git a/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml b/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml index d7a2d4273e8f03..9d38723a42005c 100644 --- a/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml +++ b/Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml @@ -11,36 +11,9 @@ maintainers: - Utsav Agarwal description: | - Describes device tree binding for adi rpmsg driver - required when rpmsg communication is needed and remote core isn't - started by adi,remoteproc. - The size of vdev-vring specifies how many message buffers are allocated - for tx and rx (combined) overriding the default number specified in - MAX_RPMSG_NUM_BUFS. The memory-region size must match (or be larger) selected - number of messages times MAX_RPMSG_BUF_SIZE (default 512). - E.g. for 1024 message buffers (512 for rx and 512 for tx): - vdev-vring size = 0x0000a000 - memory-region size = 0x00080000 - - Selection table for MAX_RPMSG_BUF_SIZE=512 (default): - - rpmsg | | - buffers | vdev-vring | memory-region - --------------------------------- - 256 | 0x00004000 | 0x00020000 - 512 | 0x00006000 | 0x00040000 - 1024 | 0x0000a000 | 0x00080000 - 2048 | 0x00010000 | 0x00100000 - 4096 | 0x0001e000 | 0x00200000 - 8192 | 0x00038000 | 0x00400000 - 16384 | 0x0006c000 | 0x00800000 - 32768 | 0x000d4000 | 0x01000000 - 65536 | 0x001a4000 | 0x02000000 - 131072 | 0x00344000 | 0x04000000 - 262144 | 0x00684000 | 0x08000000 - 524288 | 0x00d04000 | 0x10000000 - 1048576 | 0x01a04000 | 0x20000000 - 2097152 | 0x03404000 | 0x40000000 + This rpmsg driver, used when the firmware loaded before Linux Kernel + starts. Allocates memmory according rpmsg message max settings + both for vrings and buffer. properties: compatible: @@ -76,13 +49,13 @@ properties: vdev-vring: $ref: /schemas/types.yaml#/definitions/phandle - description: | + description: phandle to reserved memory region for rpmsg vdev0vrings, if not specified allocates buffer from DMA pool. memory-region: maxItems: 1 - description: | + description: phandle to reserved memory for rpmsg message buffers, if not specified allocates buffer from DMA pool. diff --git a/Documentation/devicetree/bindings/soc/adi/adi,trigger-routing-unit.yaml b/Documentation/devicetree/bindings/soc/adi/adi,trigger-routing-unit.yaml new file mode 100644 index 00000000000000..318fb2a9304a35 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/adi/adi,trigger-routing-unit.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/adi/adi,trigger-routing-unit.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Trigger Routing Unit for SC5XX processor family + +maintainers: + - Arturs Artamonovs + - Utsav Agarwal + +description: + Used for ICC between SHARC and ARM cores. + + The TRU provides system-level sequence control without core intervention. + The TRU maps trigger masters (generators of triggers) to trigger slaves + (receivers of triggers). Slave endpoints can be configured to respond to + triggers in various ways. Multiple TRUs may be provided in a + multiprocessor system to create a trigger network. Common applications + enabled by the TRU include + +properties: + compatible: + enum: + - adi,trigger-routing-unit + + reg: + maxItems: 1 + + adi,max-master-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Max Trigger Master ID + maximum: 1024 + + adi,max-slave-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Max Trigger Slave ID + maximum: 1024 + +patternProperties: + "^channel-[0-9]+$": + description: Trigger Routing Channel to Map Master/Slave + type: object + properties: + adi,tru-master-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Trigger Routing Master ID + adi,tru-slave-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Trigger Routing Slave ID + required: + - adi,tru-master-id + - adi,tru-slave-id + additionalProperties: false + +required: + - compatible + - reg + - adi,max-master-id + - adi,max-slave-id + +additionalProperties: false + +examples: + - | + tru: tru@3108a000 { + compatible = "adi,trigger-routing-unit"; + reg = <0x3108a000 0x1000>; + adi,max-master-id = <182>; + adi,max-slave-id = <187>; + + rpmsg_to_a55: channel-0 { + adi,tru-master-id = <134>; /* trigger master SOFT3 */ + adi,tru-slave-id = <160>; /* TRU0_IRQ3 */ + }; + rpmsg_to_sharc0: channel-1 { + adi,tru-master-id = <135>; /* trigger master SOFT4 */ + adi,tru-slave-id = <164>; /* TRU0_IRQ7 */ + }; + rpmsg_to_sharc1: channel-2 { + adi,tru-master-id = <136>; /* trigger master SOFT5 */ + adi,tru-slave-id = <168>; /* TRU0_IRQ11 */ + }; + }; From 71b9727b58162bb80a6923773bc9bbd83b45478b Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Tue, 13 Jan 2026 13:11:55 +0000 Subject: [PATCH 55/85] MAINTAINERS: Add ADI SC5XX SoC device tree binding files Add file entries for the recently introduced ADI SC5XX SoC device tree bindings including PADS system config, reset controller, rpmsg, system event controller, and trigger routing unit. Signed-off-by: Arturs Artamonovs --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7380a4c8f2163f..2d803e1dd9bc87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2294,6 +2294,11 @@ M: Utsav Agarwal M: Vasileios Bimpikas L: linux@analog.com S: Maintained +F: Documentation/devicetree/bindings/soc/adi/adi,pads-system-config.yaml +F: Documentation/devicetree/bindings/soc/adi/adi,reset-controller.yaml +F: Documentation/devicetree/bindings/soc/adi/adi,rpmsg-SC598.yaml +F: Documentation/devicetree/bindings/soc/adi/adi,system-event-controller.yaml +F: Documentation/devicetree/bindings/soc/adi/adi,trigger-routing-unit.yaml F: Documentation/devicetree/bindings/clock/adi,sc5xx-clocks.yaml F: Documentation/devicetree/bindings/usb/adi,musb.yaml F: arch/arm/boot/dts/adi/* From 8aa9394dc688d2318408503e6aaa18b204818c36 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Mon, 8 Dec 2025 09:51:08 +0100 Subject: [PATCH 56/85] ARM: dts: adi: rename GPIO expanders Rename GPIO expander labels across ADI SC57x/SC59x device trees to descriptive identifiers. Signed-off-by: Ozan Durgut --- arch/arm/boot/dts/adi/sc573-ezkit.dts | 18 +++++++++--------- arch/arm/boot/dts/adi/sc594-som-ezkit.dts | 2 +- arch/arm/boot/dts/adi/sc594-som-ezlite.dts | 2 +- arch/arm/boot/dts/adi/sc594-som.dtsi | 4 ++-- arch/arm64/boot/dts/adi/sc598-som-ezkit.dts | 4 ++-- arch/arm64/boot/dts/adi/sc598-som-ezlite.dts | 2 +- arch/arm64/boot/dts/adi/sc598-som.dtsi | 10 +++++----- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/arch/arm/boot/dts/adi/sc573-ezkit.dts b/arch/arm/boot/dts/adi/sc573-ezkit.dts index 9cb68a0e1786ee..a2350e5ff04522 100644 --- a/arch/arm/boot/dts/adi/sc573-ezkit.dts +++ b/arch/arm/boot/dts/adi/sc573-ezkit.dts @@ -58,22 +58,22 @@ scb { button0: button@0 { compatible = "adi,button-led"; -// en-pins = <&ssw1 2 GPIO_ACTIVE_LOW>, /* PUSHBUTTON1_EN */ -// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// en-pins = <&gpio_expander1 2 GPIO_ACTIVE_LOW>, /* PUSHBUTTON1_EN */ +// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ // button_gpio = <40>; // led_gpio = <77>; }; button1: button@1 { compatible = "adi,button-led"; -// en-pins = <&ssw1 1 GPIO_ACTIVE_LOW>, /* PUSHBUTTON2_EN */ -// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// en-pins = <&gpio_expander1 1 GPIO_ACTIVE_LOW>, /* PUSHBUTTON2_EN */ +// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ // button_gpio = <41>; // led_gpio = <9>; }; button2: button@2 { compatible = "adi,button-led"; -// en-pins = <&ssw1 0 GPIO_ACTIVE_LOW>, /* PUSHBUTTON3_EN */ -// <&ssw1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ +// en-pins = <&gpio_expander1 0 GPIO_ACTIVE_LOW>, /* PUSHBUTTON3_EN */ +// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ // button_gpio = <42>; // led_gpio = <65>; }; @@ -205,7 +205,7 @@ &i2c0 { status = "okay"; - ssw0: gpio@0x21 { + gpio_expander0: gpio@0x21 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -297,7 +297,7 @@ }; - ssw1: gpio@0x22 { + gpio_expander1: gpio@0x22 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -477,7 +477,7 @@ }; &mmc0 { - wp-en-pin = <&ssw0 11 GPIO_ACTIVE_LOW>; /* SD_WP_EN */ + wp-en-pin = <&gpio_expander0 11 GPIO_ACTIVE_LOW>; /* SD_WP_EN */ bus-width = <4>; pinctrl-names = "default"; pinctrl-0 = <&mmc0_default>; diff --git a/arch/arm/boot/dts/adi/sc594-som-ezkit.dts b/arch/arm/boot/dts/adi/sc594-som-ezkit.dts index f845a3b0ac36a8..7ebfbcef2c9bc4 100644 --- a/arch/arm/boot/dts/adi/sc594-som-ezkit.dts +++ b/arch/arm/boot/dts/adi/sc594-som-ezkit.dts @@ -25,7 +25,7 @@ }; &i2c2 { - ssw1: gpio@22 { + crr_gpio_expander: gpio@22 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; diff --git a/arch/arm/boot/dts/adi/sc594-som-ezlite.dts b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts index fceec8844a44b9..eebbddb9af8338 100644 --- a/arch/arm/boot/dts/adi/sc594-som-ezlite.dts +++ b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts @@ -35,7 +35,7 @@ }; &i2c2 { - gpio_expander: adp5588@30 { + crr_gpio_expander: adp5588@30 { compatible = "adi,adp5588-gpio"; gpio-controller; #gpio-cells = <2>; diff --git a/arch/arm/boot/dts/adi/sc594-som.dtsi b/arch/arm/boot/dts/adi/sc594-som.dtsi index dfa87c9b407e25..c000736c61e45a 100644 --- a/arch/arm/boot/dts/adi/sc594-som.dtsi +++ b/arch/arm/boot/dts/adi/sc594-som.dtsi @@ -268,7 +268,7 @@ &i2c2 { status = "okay"; - ssw0: gpio@21 { + som_gpio_expander: gpio@21 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -353,7 +353,7 @@ }; &mmc0 { - /* wp-en-pin = <&ssw0 ? GPIO_ACTIVE_LOW>; SD_WP_EN */ + /* wp-en-pin = <&som_gpio_expander ? GPIO_ACTIVE_LOW>; SD_WP_EN */ bus-width = <4>; pinctrl-names = "default"; pinctrl-0 = <&mmc0_8bgrp>; diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts index 0664d45eccce4a..809fa9d09b09f4 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts @@ -75,7 +75,7 @@ }; &i2c2 { - ssw1: gpio@22 { + crr_gpio_expander: gpio@22 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -198,7 +198,7 @@ adau1962: adau1962@4 { compatible = "adi,adau1962"; reg = <0x4>; - reset-gpios = <&ssw1 5 GPIO_ACTIVE_LOW>; + reset-gpios = <&crr_gpio_expander 5 GPIO_ACTIVE_LOW>; }; }; diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts index ca42b7a4afddfe..cfb0baa0b4e790 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts @@ -31,7 +31,7 @@ }; &i2c2 { - gpio_expander: adp5588@30 { + crr_gpio_expander: adp5588@30 { compatible = "adi,adp5588-gpio"; gpio-controller; #gpio-cells = <2>; diff --git a/arch/arm64/boot/dts/adi/sc598-som.dtsi b/arch/arm64/boot/dts/adi/sc598-som.dtsi index 5eddbf5287d1de..79b3bdbd3adfa3 100644 --- a/arch/arm64/boot/dts/adi/sc598-som.dtsi +++ b/arch/arm64/boot/dts/adi/sc598-som.dtsi @@ -153,8 +153,8 @@ }; &uart0 { - /* enable-pin = <&ssw0 5 GPIO_ACTIVE_LOW>; UART0_EN */ - /* hwflow-en-pin = <&ssw0 6 GPIO_ACTIVE_LOW>; UART0_FLOW_EN */ + /* enable-pin = <&som_gpio_expander 5 GPIO_ACTIVE_LOW>; UART0_EN */ + /* hwflow-en-pin = <&som_gpio_expander 6 GPIO_ACTIVE_LOW>; UART0_FLOW_EN */ pinctrl-names = "default"; pinctrl-0 = <&uart0_default>; status = "okay"; @@ -255,7 +255,7 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins>; - ssw0: gpio@20 { + som_gpio_expander: gpio@20 { compatible = "microchip,mcp23018"; gpio-controller; #gpio-cells = <2>; @@ -263,9 +263,9 @@ status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&ssw0pullups>; + pinctrl-0 = <&som_gpio_expanderpullups>; - ssw0pullups: pinmux { + som_gpio_expanderpullups: pinmux { bias-pull-up; pins = "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio8", "gpio9"; From 97789e2c555f19360e1244e77769044e3a06d601 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 16 Dec 2025 09:10:23 +0100 Subject: [PATCH 57/85] ARM: dts: adi: fix unit-address-format warnings Signed-off-by: Ozan Durgut --- arch/arm/boot/dts/adi/sc573-ezkit.dts | 12 +-- arch/arm/boot/dts/adi/sc57x.dtsi | 82 +++++++++--------- arch/arm/boot/dts/adi/sc589-mini.dts | 6 +- arch/arm/boot/dts/adi/sc58x.dtsi | 90 ++++++++++---------- arch/arm/boot/dts/adi/sc594-som-ezlite.dts | 2 +- arch/arm64/boot/dts/adi/sc598-som-ezlite.dts | 2 +- arch/arm64/boot/dts/adi/sc59x-64.dtsi | 34 ++++---- 7 files changed, 114 insertions(+), 114 deletions(-) diff --git a/arch/arm/boot/dts/adi/sc573-ezkit.dts b/arch/arm/boot/dts/adi/sc573-ezkit.dts index a2350e5ff04522..601b6f198e0661 100644 --- a/arch/arm/boot/dts/adi/sc573-ezkit.dts +++ b/arch/arm/boot/dts/adi/sc573-ezkit.dts @@ -78,7 +78,7 @@ // led_gpio = <65>; }; - core1-rproc@0x3108C000 { + core1-rproc@3108C000 { compatible = "adi,remoteproc"; reg = <0x28240000 0x2000>, <0x20000000 0x200000>; @@ -95,7 +95,7 @@ status = "okay"; }; - core2-rproc@0x3108C000 { + core2-rproc@3108C000 { compatible = "adi,remoteproc"; reg = <0x28A40000 0x2000>, <0x20000000 0x200000>; @@ -205,7 +205,7 @@ &i2c0 { status = "okay"; - gpio_expander0: gpio@0x21 { + gpio_expander0: gpio@21 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -297,7 +297,7 @@ }; - gpio_expander1: gpio@0x22 { + gpio_expander1: gpio@22 { compatible = "microchip,mcp23017"; gpio-controller; #gpio-cells = <2>; @@ -389,12 +389,12 @@ }; - adau1979: adau1979@0x11 { + adau1979: adau1979@11 { compatible = "adi,adau1977"; reg = <0x11>; }; - adau1962: adau1962@0x4 { + adau1962: adau1962@4 { compatible = "adi,adau1962"; reg = <0x4>; reset-gpios = <&gpa 6 GPIO_ACTIVE_LOW>; diff --git a/arch/arm/boot/dts/adi/sc57x.dtsi b/arch/arm/boot/dts/adi/sc57x.dtsi index c109ce03a9c50e..f2e59f73dc1c88 100644 --- a/arch/arm/boot/dts/adi/sc57x.dtsi +++ b/arch/arm/boot/dts/adi/sc57x.dtsi @@ -107,7 +107,7 @@ clock-output-names = "sys_clkin1"; }; - clk: clocks@0x3108d000 { + clk: clocks@3108d000 { compatible = "adi,sc57x-clocks"; reg = <0x3108d000 0x1000>, <0x3108e000 0x1000>, @@ -118,7 +118,7 @@ status = "okay"; }; - gptimers: gptimers@0x31018000 { + gptimers: gptimers@31018000 { compatible = "adi,sc5xx-gptimers"; reg = <0x31018000 0x200>; clocks = <&clk ADSP_SC57X_CLK_CGU0_SCLK0>; @@ -239,7 +239,7 @@ status = "okay"; }; - rcu: rcu@0x3108B000 { + rcu: rcu@3108B000 { compatible = "adi,reset-controller"; reg = <0x3108C000 0x1000>; adi,sharc-min = <1>; @@ -248,7 +248,7 @@ status = "okay"; }; - sec: sec@0x31089000 { + sec: sec@31089000 { compatible = "adi,system-event-controller"; reg = <0x31089000 0x1000>; adi,rcu = <&rcu>; @@ -256,7 +256,7 @@ status = "okay"; }; - tru: tru@0x3108a000 { + tru: tru@3108a000 { compatible = "adi,trigger-routing-unit"; reg = <0x3108a000 0x1000>; adi,max-master-id = <126>; @@ -264,7 +264,7 @@ status = "okay"; }; - uart0: uart@0x31003000 { + uart0: uart@31003000 { compatible = "adi,uart4"; reg = <0x31003000 0x40>; dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; @@ -279,7 +279,7 @@ status = "disabled"; }; - uart1: uart@0x31003400 { + uart1: uart@31003400 { compatible = "adi,uart4"; reg = <0x31003400 0x40>; dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; @@ -294,7 +294,7 @@ status = "disabled"; }; - uart2: uart@0x31003800 { + uart2: uart@31003800 { compatible = "adi,uart4"; reg = <0x31003800 0x40>; dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; @@ -309,7 +309,7 @@ status = "disabled"; }; - i2c0: twi@0x31001400 { + i2c0: twi@31001400 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -321,7 +321,7 @@ status = "disabled"; }; - i2c1: twi@0x31001500 { + i2c1: twi@31001500 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -333,7 +333,7 @@ status = "disabled"; }; - i2c2: twi@0x31001600 { + i2c2: twi@31001600 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -357,7 +357,7 @@ status = "disabled"; }; - watchdog@0x31008000 { + watchdog@31008000 { compatible = "adi,watchdog"; reg = <0x31008000 0x10>; timeout-sec = <30>; @@ -365,7 +365,7 @@ clock-names = "adi-watchdog"; }; - emac0: ethernet@0x3100C000 { + emac0: ethernet@3100C000 { compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; reg = <0x3100C000 0x2000>; interrupt-parent = <&gic>; @@ -381,7 +381,7 @@ status = "disabled"; }; - spi0: spi@0x3102E000 { + spi0: spi@3102E000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -394,7 +394,7 @@ status = "disabled"; }; - spi1: spi@0x3102F000 { + spi1: spi@3102F000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -407,7 +407,7 @@ status = "disabled"; }; - spi2: spi@0x31044000 { + spi2: spi@31044000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -420,7 +420,7 @@ status = "disabled"; }; - crc0: crc@0x310A5000 { + crc0: crc@310A5000 { compatible = "adi,hmac-crc"; reg = <0x310A5000 0xFF>; interrupts = ; @@ -429,7 +429,7 @@ status = "disabled"; }; - crc1: crc@0x310A6000 { + crc1: crc@310A6000 { compatible = "adi,hmac-crc"; reg = <0x310A6000 0xFF>; interrupts = ; @@ -438,7 +438,7 @@ status = "disabled"; }; - can0: can@0x31000200 { + can0: can@31000200 { compatible = "adi,can"; reg = <0x31000200 0x5FF>; interrupt-parent = <&gic>; @@ -448,7 +448,7 @@ status = "disabled"; }; - can1: can@0x31000a00 { + can1: can@31000a00 { compatible = "adi,can"; reg = <0x31000a00 0x5FF>; interrupt-parent = <&gic>; @@ -458,7 +458,7 @@ status = "disabled"; }; - pinctrl0: pinctrl@0x31004400 { + pinctrl0: pinctrl@31004400 { compatible = "adi,adsp-pinctrl"; #address-cells = <1>; #size-cells = <1>; @@ -472,7 +472,7 @@ adi,no-pull-up-down; }; - sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + sru_ctrl_dai0: sru-ctrl-dai0@310C9000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -481,7 +481,7 @@ status = "disabled"; }; - sru_ctrl_dai1: sru-ctrl-dai1@0x310CB000 { + sru_ctrl_dai1: sru-ctrl-dai1@310CB000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -490,7 +490,7 @@ status = "disabled"; }; - mmc0: mmc@0x31010000 { + mmc0: mmc@31010000 { compatible = "snps,dw-mshc"; reg = <0x31010000 0xFFF>; interrupts = ; @@ -521,37 +521,37 @@ status = "disabled"; }; - pint0: pint@0x31005000 { + pint0: pint@31005000 { compatible = "adi,adsp-pint"; reg = <0x31005000 0xFF>; interrupts = ; }; - pint1: pint@0x31005100 { + pint1: pint@31005100 { compatible = "adi,adsp-pint"; reg = <0x31005100 0xFF>; interrupts = ; }; - pint2: pint@0x31005200 { + pint2: pint@31005200 { compatible = "adi,adsp-pint"; reg = <0x31005200 0xFF>; interrupts = ; }; - pint3: pint@0x31005300 { + pint3: pint@31005300 { compatible = "adi,adsp-pint"; reg = <0x31005300 0xFF>; interrupts = ; }; - pint4: pint@0x31005400 { + pint4: pint@31005400 { compatible = "adi,adsp-pint"; reg = <0x31005400 0xFF>; interrupts = ; }; - gpa: gport@0x31004000 { + gpa: gport@31004000 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -561,7 +561,7 @@ adi,gpio-base = <0>; }; - gpb: gport@0x31004080 { + gpb: gport@31004080 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -571,7 +571,7 @@ adi,gpio-base = <16>; }; - gpc: gport@0x31004100 { + gpc: gport@31004100 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -581,7 +581,7 @@ adi,gpio-base = <32>; }; - gpd: gport@0x31004180 { + gpd: gport@31004180 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -591,7 +591,7 @@ adi,gpio-base = <48>; }; - gpe: gport@0x31004200 { + gpe: gport@31004200 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -601,7 +601,7 @@ adi,gpio-base = <64>; }; - gpf: gport@0x31004280 { + gpf: gport@31004280 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -636,7 +636,7 @@ status = "disabled"; }; - sport_cluster0: dma@0x31022000 { + sport_cluster0: dma@31022000 { compatible = "adi,dma-controller"; reg = <0x31022000 0x400>; status = "okay"; @@ -708,7 +708,7 @@ }; - spi_cluster: dma@0x3102B000 { + spi_cluster: dma@3102B000 { compatible = "adi,dma-controller"; reg = <0x3102B000 0x1000>; status = "okay"; @@ -747,7 +747,7 @@ }; }; - spi_cluster2: dma@0x31046000 { + spi_cluster2: dma@31046000 { compatible = "adi,dma-controller"; reg = <0x31046000 0x1000>; status = "okay"; @@ -770,7 +770,7 @@ }; }; - dma_cluster2: dma@0x31026000 { + dma_cluster2: dma@31026000 { compatible = "adi,dma-controller"; reg = <0x31026000 0x1000>; status = "okay"; @@ -825,7 +825,7 @@ }; }; - mdma: dma@0x3109b000 { + mdma: dma@3109b000 { compatible = "adi,mdma-controller"; reg = <0x3109b000 0x1000>; status = "okay"; @@ -843,7 +843,7 @@ }; }; - trng: rng@0x310D0000 { + trng: rng@310D0000 { compatible = "adi,sc5xx-trng"; reg = <0x310D0000 0x74>, <0x310D8000 0x14>; interrupts = ; diff --git a/arch/arm/boot/dts/adi/sc589-mini.dts b/arch/arm/boot/dts/adi/sc589-mini.dts index ff6bfda31fcd63..524b67e979f8e3 100644 --- a/arch/arm/boot/dts/adi/sc589-mini.dts +++ b/arch/arm/boot/dts/adi/sc589-mini.dts @@ -64,7 +64,7 @@ led_gpio = <50>; }; - core1-rproc@0x28240000 { + core1-rproc@28240000 { compatible = "adi,remoteproc"; reg = <0x28240000 0x160000>, <0x20080000 0x40000>; @@ -80,7 +80,7 @@ adi,tru-master-id = <97>; /* trigger master SOFT4 */ }; - core2-rproc@0x28a40000 { + core2-rproc@28a40000 { compatible = "adi,remoteproc"; reg = <0x28a40000 0x160000>, <0x20080000 0x40000>; @@ -179,7 +179,7 @@ &i2c0 { status = "okay"; - adau1761: adau1761@0x38{ + adau1761: adau1761@38{ compatible = "adi,adau1761"; reg = <0x38>; }; diff --git a/arch/arm/boot/dts/adi/sc58x.dtsi b/arch/arm/boot/dts/adi/sc58x.dtsi index 8fed5eae5c3976..e07eeec5e1e775 100644 --- a/arch/arm/boot/dts/adi/sc58x.dtsi +++ b/arch/arm/boot/dts/adi/sc58x.dtsi @@ -109,7 +109,7 @@ clock-output-names = "sys_clkin1"; }; - clk: clocks@0x3108d000 { + clk: clocks@3108d000 { compatible = "adi,sc58x-clocks"; reg = <0x3108d000 0x1000>, <0x3108e000 0x1000>, @@ -120,7 +120,7 @@ status = "okay"; }; - gptimers: gptimers@0x31001000 { + gptimers: gptimers@31001000 { compatible = "adi,sc5xx-gptimers"; reg = <0x31001000 0x200>; clocks = <&clk ADSP_SC58X_CLK_CGU0_SCLK0>; @@ -241,7 +241,7 @@ status = "disabled"; }; - rcu: rcu@0x3108B000 { + rcu: rcu@3108B000 { compatible = "adi,reset-controller"; reg = <0x3108B000 0x1000>; adi,sharc-min = <1>; @@ -250,7 +250,7 @@ status = "okay"; }; - sec: sec@0x31089000 { + sec: sec@31089000 { compatible = "adi,system-event-controller"; reg = <0x31089000 0x1000>; adi,rcu = <&rcu>; @@ -258,7 +258,7 @@ status = "okay"; }; - tru: tru@0x3108a000 { + tru: tru@3108a000 { compatible = "adi,trigger-routing-unit"; reg = <0x3108a000 0x1000>; adi,max-master-id = <139>; @@ -266,7 +266,7 @@ status = "okay"; }; - rtc0: rtc@0x310C8000 { + rtc0: rtc@310C8000 { compatible = "adi,rtc2"; reg = <0x310C8000 0x100>; interrupts = ; @@ -274,7 +274,7 @@ status = "disabled"; }; - uart0: uart@0x31003000 { + uart0: uart@31003000 { compatible = "adi,uart4"; reg = <0x31003000 0x40>; dmas = <&dma_cluster2 20>, <&dma_cluster2 21>; @@ -289,7 +289,7 @@ status = "disabled"; }; - uart1: uart@0x31003400 { + uart1: uart@31003400 { compatible = "adi,uart4"; reg = <0x31003400 0x40>; dmas = <&dma_cluster2 34>, <&dma_cluster2 35>; @@ -304,7 +304,7 @@ status = "disabled"; }; - uart2: uart@0x31003800 { + uart2: uart@31003800 { compatible = "adi,uart4"; reg = <0x31003800 0x40>; dmas = <&dma_cluster2 37>, <&dma_cluster2 38>; @@ -319,7 +319,7 @@ status = "disabled"; }; - can0: can@0x31000200 { + can0: can@31000200 { compatible = "adi,can"; reg = <0x31000200 0x5FF>; interrupt-parent = <&gic>; @@ -329,7 +329,7 @@ status = "disabled"; }; - can1: can@0x31000a00 { + can1: can@31000a00 { compatible = "adi,can"; reg = <0x31000a00 0x5FF>; interrupt-parent = <&gic>; @@ -339,7 +339,7 @@ status = "disabled"; }; - i2c0: twi@0x31001400 { + i2c0: twi@31001400 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -351,7 +351,7 @@ status = "disabled"; }; - i2c1: twi@0x31001500 { + i2c1: twi@31001500 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -363,7 +363,7 @@ status = "disabled"; }; - i2c2: twi@0x31001600 { + i2c2: twi@31001600 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,twi"; @@ -403,7 +403,7 @@ status = "disabled"; }; - watchdog@0x31008000 { + watchdog@31008000 { compatible = "adi,watchdog"; reg = <0x31008000 0x10>; timeout-sec = <30>; @@ -411,7 +411,7 @@ clock-names = "adi-watchdog"; }; - spi0: spi@0x31042000 { + spi0: spi@31042000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -424,7 +424,7 @@ status = "disabled"; }; - spi1: spi@0x31043000 { + spi1: spi@31043000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -437,7 +437,7 @@ status = "disabled"; }; - spi2: spi@0x31044000 { + spi2: spi@31044000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -450,7 +450,7 @@ status = "disabled"; }; - emac0: ethernet@0x3100C000 { + emac0: ethernet@3100C000 { compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; reg = <0x3100C000 0x2000>; interrupt-parent = <&gic>; @@ -466,7 +466,7 @@ status = "disabled"; }; - emac1: ethernet@0x3100E000 { + emac1: ethernet@3100E000 { compatible = "adi,dwmac", "snps,dwmac-3.710", "snps,dwmac"; reg = <0x3100E000 0x2000>; interrupt-parent = <&gic>; @@ -481,7 +481,7 @@ status = "disabled"; }; - crc0: crc@0x31001200 { + crc0: crc@31001200 { compatible = "adi,hmac-crc"; reg = <0x31001200 0xFF>; interrupts = ; @@ -490,7 +490,7 @@ status = "disabled"; }; - crc1: crc@0x31001300 { + crc1: crc@31001300 { compatible = "adi,hmac-crc"; reg = <0x31001300 0xFF>; interrupts = ; @@ -499,7 +499,7 @@ status = "disabled"; }; - pinctrl0: pinctrl@0x31004400 { + pinctrl0: pinctrl@31004400 { compatible = "adi,adsp-pinctrl"; #address-cells = <1>; #size-cells = <1>; @@ -509,7 +509,7 @@ adi,no-pull-up-down; }; - sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + sru_ctrl_dai0: sru-ctrl-dai0@310C9000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -518,7 +518,7 @@ status = "disabled"; }; - sru_ctrl_dai1: sru-ctrl-dai1@0x310CB000 { + sru_ctrl_dai1: sru-ctrl-dai1@310CB000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -527,7 +527,7 @@ status = "disabled"; }; - mmc0: mmc@0x31010000 { + mmc0: mmc@31010000 { compatible = "snps,dw-mshc"; reg = <0x31010000 0xFFF>; interrupts = ; @@ -558,43 +558,43 @@ status = "disabled"; }; - pint0: pint@0x31005000 { + pint0: pint@31005000 { compatible = "adi,adsp-pint"; reg = <0x31005000 0xFF>; interrupts = ; }; - pint1: pint@0x31005100 { + pint1: pint@31005100 { compatible = "adi,adsp-pint"; reg = <0x31005100 0xFF>; interrupts = ; }; - pint2: pint@0x31005200 { + pint2: pint@31005200 { compatible = "adi,adsp-pint"; reg = <0x31005200 0xFF>; interrupts = ; }; - pint3: pint@0x31005300 { + pint3: pint@31005300 { compatible = "adi,adsp-pint"; reg = <0x31005300 0xFF>; interrupts = ; }; - pint4: pint@0x31005400 { + pint4: pint@31005400 { compatible = "adi,adsp-pint"; reg = <0x31005400 0xFF>; interrupts = ; }; - pint5: pint@0x31005500 { + pint5: pint@31005500 { compatible = "adi,adsp-pint"; reg = <0x31005500 0xFF>; interrupts = ; }; - gpa: gport@0x31004000 { + gpa: gport@31004000 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -604,7 +604,7 @@ adi,gpio-base = <0>; }; - gpb: gport@0x31004080 { + gpb: gport@31004080 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -614,7 +614,7 @@ adi,gpio-base = <16>; }; - gpc: gport@0x31004100 { + gpc: gport@31004100 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -624,7 +624,7 @@ adi,gpio-base = <32>; }; - gpd: gport@0x31004180 { + gpd: gport@31004180 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -634,7 +634,7 @@ adi,gpio-base = <48>; }; - gpe: gport@0x31004200 { + gpe: gport@31004200 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -644,7 +644,7 @@ adi,gpio-base = <64>; }; - gpf: gport@0x31004280 { + gpf: gport@31004280 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -654,7 +654,7 @@ adi,gpio-base = <80>; }; - gpg: gport@0x31004300 { + gpg: gport@31004300 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -1031,7 +1031,7 @@ }; */ - spi_cluster: dma@0x31046000 { + spi_cluster: dma@31046000 { compatible = "adi,dma-controller"; reg = <0x31046000 0x1000>; status = "okay"; @@ -1086,7 +1086,7 @@ }; }; - sport_dma_cluster: dma@0x31024000 { + sport_dma_cluster: dma@31024000 { compatible = "adi,dma-controller"; reg = <0x31024000 0x1000>; status = "okay"; @@ -1111,7 +1111,7 @@ }; }; - sport0_dma_cluster: dma@0x31022000 { + sport0_dma_cluster: dma@31022000 { compatible = "adi,dma-controller"; reg = <0x31022000 0x1000>; status = "okay"; @@ -1136,7 +1136,7 @@ }; }; - dma_cluster2: dma@0x31026000 { + dma_cluster2: dma@31026000 { compatible = "adi,dma-controller"; reg = <0x31026000 0x1000>; status = "okay"; @@ -1191,7 +1191,7 @@ }; }; - mdma: dma@0x3109a000 { + mdma: dma@3109a000 { compatible = "adi,mdma-controller"; reg = <0x3109a000 0x1000>; status = "okay"; @@ -1209,7 +1209,7 @@ }; }; - trng: rng@0x310D0000 { + trng: rng@310D0000 { compatible = "adi,sc5xx-trng"; reg = <0x310D0000 0x74>, <0x310D8000 0x14>; interrupts = ; diff --git a/arch/arm/boot/dts/adi/sc594-som-ezlite.dts b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts index eebbddb9af8338..edd3bbb321dc5a 100644 --- a/arch/arm/boot/dts/adi/sc594-som-ezlite.dts +++ b/arch/arm/boot/dts/adi/sc594-som-ezlite.dts @@ -106,7 +106,7 @@ }; }; - adau1372: adau1372@0x3c { + adau1372: adau1372@3c { compatible = "adi,adau1372"; reg = <0x3c>; clock-names = "mclk"; diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts index cfb0baa0b4e790..630a2069c15fe7 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts @@ -102,7 +102,7 @@ }; }; - adau1372: adau1372@0x3c { + adau1372: adau1372@3c { compatible = "adi,adau1372"; reg = <0x3c>; clock-names = "mclk"; diff --git a/arch/arm64/boot/dts/adi/sc59x-64.dtsi b/arch/arm64/boot/dts/adi/sc59x-64.dtsi index 66249bcd9a3508..ef5aee84fc129a 100644 --- a/arch/arm64/boot/dts/adi/sc59x-64.dtsi +++ b/arch/arm64/boot/dts/adi/sc59x-64.dtsi @@ -467,7 +467,7 @@ clock-names = "sclk"; }; - watchdog@0x31008000 { + watchdog@31008000 { compatible = "adi,watchdog"; reg = <0x31008000 0x10>; timeout-sec = <30>; @@ -475,7 +475,7 @@ clock-names = "adi-watchdog"; }; - spi0: spi@0x3102e000 { + spi0: spi@3102e000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -486,7 +486,7 @@ status = "disabled"; }; - spi1: spi@0x3102f000 { + spi1: spi@3102f000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -499,7 +499,7 @@ status = "disabled"; }; - spi2: spi@0x31030000 { + spi2: spi@31030000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -512,7 +512,7 @@ status = "disabled"; }; - spi3: spi@0x31031000 { + spi3: spi@31031000 { #address-cells = <1>; #size-cells = <0>; compatible = "adi,spi3"; @@ -540,7 +540,7 @@ status = "disabled"; }; - emac0: ethernet@0x31040000 { + emac0: ethernet@31040000 { compatible = "adi,dwmac", "snps,dwmac-4.20a", "snps,dwmac-5.20a"; reg = <0x31040000 0x2000>; interrupt-parent = <&gic>; @@ -557,7 +557,7 @@ status = "disabled"; }; - emac1: ethernet@0x31042000 { + emac1: ethernet@31042000 { compatible = "adi,dwmac", "snps,dwmac-4.20a", "snps,dwmac-5.20a"; reg = <0x31042000 0x2000>; interrupt-parent = <&gic>; @@ -600,7 +600,7 @@ adi,port-sizes = <16 16 16 16 16 16 16 16 7>; }; - sru_ctrl_dai0: sru-ctrl-dai0@0x310C9000 { + sru_ctrl_dai0: sru-ctrl-dai0@310C9000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -609,7 +609,7 @@ status = "disabled"; }; - sru_ctrl_dai1: sru-ctrl-dai1@0x310CA000 { + sru_ctrl_dai1: sru-ctrl-dai1@310CA000 { compatible = "adi,adsp-sru-ctrl"; #address-cells = <1>; #size-cells = <1>; @@ -711,7 +711,7 @@ interrupts = ; }; - gpa: gport@0x31004000 { + gpa: gport@31004000 { compatible = "adi,adsp-port-gpio"; gpio-controller; #gpio-cells = <2>; @@ -1055,7 +1055,7 @@ // spu_securep_id = <115>; // }; - sport0_dma_cluster: dma@0x31022000 { + sport0_dma_cluster: dma@31022000 { compatible = "adi,dma-controller"; reg = <0x31022000 0x1000>; status = "okay"; @@ -1080,7 +1080,7 @@ }; }; - sport4_dma_cluster: dma@0x31023000 { + sport4_dma_cluster: dma@31023000 { compatible = "adi,dma-controller"; reg = <0x31023000 0x1000>; status = "okay"; @@ -1105,7 +1105,7 @@ }; }; - spi_cluster: dma@0x3102d000 { + spi_cluster: dma@3102d000 { compatible = "adi,dma-controller"; reg = <0x3102d000 0x1000>; status = "okay"; @@ -1177,7 +1177,7 @@ }; - crc_cluster: dma@0x310a7000 { + crc_cluster: dma@310a7000 { compatible = "adi,dma-controller"; reg = <0x310a7000 0x1000>; status = "okay"; @@ -1200,7 +1200,7 @@ }; }; - dma_cluster2: dma@0x31026000 { + dma_cluster2: dma@31026000 { compatible = "adi,dma-controller"; reg = <0x31026000 0x1000>; status = "okay"; @@ -1271,7 +1271,7 @@ }; }; - mdma: dma@0x3109a000 { + mdma: dma@3109a000 { compatible = "adi,mdma-controller"; reg = <0x3109a000 0x1000>; status = "okay"; @@ -1289,7 +1289,7 @@ }; }; - trng: rng@0x310D0000 { + trng: rng@310D0000 { compatible = "adi,sc5xx-trng"; reg = <0x310D0000 0x74>, <0x310D8000 0x14>; interrupts = ; From f0d26adecb2688e1b2367e658a99cccfbf06c62c Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Thu, 25 Dec 2025 13:44:13 +0300 Subject: [PATCH 58/85] ARM: sc573: Rename EZKIT board to EZLITE Renaming the SC573 EZKIT board to EZLITE across the device tree, defconfig, board file, and related Kconfig/Makefile entries to match with release naming. Signed-off-by: Ozan Durgut --- arch/arm/boot/dts/adi/Makefile | 2 +- arch/arm/boot/dts/adi/{sc573-ezkit.dts => sc573-ezlite.dts} | 6 +++--- .../{sc573-ezkit_defconfig => sc573-ezlite_defconfig} | 2 +- arch/arm/mach-sc5xx/Kconfig | 6 +++--- arch/arm/mach-sc5xx/Makefile | 2 +- arch/arm/mach-sc5xx/Makefile.boot | 2 +- arch/arm/mach-sc5xx/{sc57x-ezkit.c => sc57x-ezlite.c} | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) rename arch/arm/boot/dts/adi/{sc573-ezkit.dts => sc573-ezlite.dts} (99%) rename arch/arm/configs/{sc573-ezkit_defconfig => sc573-ezlite_defconfig} (99%) rename arch/arm/mach-sc5xx/{sc57x-ezkit.c => sc57x-ezlite.c} (93%) diff --git a/arch/arm/boot/dts/adi/Makefile b/arch/arm/boot/dts/adi/Makefile index 9ab2aeb874ade3..fae5792eb6ee17 100644 --- a/arch/arm/boot/dts/adi/Makefile +++ b/arch/arm/boot/dts/adi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_SC59X) += sc594-som-ezkit.dtb sc594-som-ezlite.dtb dtb-$(CONFIG_ARCH_SC58X) += sc589-mini.dtb -dtb-$(CONFIG_ARCH_SC57X) += sc573-ezkit.dtb +dtb-$(CONFIG_ARCH_SC57X) += sc573-ezlite.dtb diff --git a/arch/arm/boot/dts/adi/sc573-ezkit.dts b/arch/arm/boot/dts/adi/sc573-ezlite.dts similarity index 99% rename from arch/arm/boot/dts/adi/sc573-ezkit.dts rename to arch/arm/boot/dts/adi/sc573-ezlite.dts index 601b6f198e0661..da4ad94d479ddb 100644 --- a/arch/arm/boot/dts/adi/sc573-ezkit.dts +++ b/arch/arm/boot/dts/adi/sc573-ezlite.dts @@ -1,5 +1,5 @@ /* - * Device tree for ADI sc573-ezkit board + * Device tree for ADI sc573-ezlite board * * Copyright 2014 - 2018 Analog Devices Inc. * @@ -19,8 +19,8 @@ #include "sc57x.dtsi" / { - model = "ADI sc573-ezkit"; - compatible = "adi,sc573-ezkit", "adi,sc57x"; + model = "ADI sc573-ezlite"; + compatible = "adi,sc573-ezlite", "adi,sc57x"; aliases { /* serial2 = &uart2; */ diff --git a/arch/arm/configs/sc573-ezkit_defconfig b/arch/arm/configs/sc573-ezlite_defconfig similarity index 99% rename from arch/arm/configs/sc573-ezkit_defconfig rename to arch/arm/configs/sc573-ezlite_defconfig index f02d212c309f02..588403c935a7ba 100644 --- a/arch/arm/configs/sc573-ezkit_defconfig +++ b/arch/arm/configs/sc573-ezlite_defconfig @@ -20,7 +20,7 @@ CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_ARCH_SC5XX=y CONFIG_ARCH_SC57X=y -CONFIG_MACH_SC573_EZKIT=y +CONFIG_MACH_SC573_EZLITE=y CONFIG_ARM_THUMBEE=y CONFIG_HZ_250=y CONFIG_CPU_FREQ=y diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index d982ed8496f69b..048a2af3fdbd1f 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -63,9 +63,9 @@ config ARCH_SC57X help This enables support for ADI ADSP SC-57x SoCs -config MACH_SC573_EZKIT - bool "ADI SC573 EZKIT board" +config MACH_SC573_EZLITE + bool "ADI SC573 EZLITE board" depends on ARCH_SC57X help Say 'Y' here if you want your kernel to run on the ADI - SC573-EZKIT board. + SC573-EZLITE board. diff --git a/arch/arm/mach-sc5xx/Makefile b/arch/arm/mach-sc5xx/Makefile index 74c3f17e77b307..a7f674f64c8b44 100644 --- a/arch/arm/mach-sc5xx/Makefile +++ b/arch/arm/mach-sc5xx/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) obj-$(CONFIG_ARCH_SC5XX) := core.o spu.o -obj-$(CONFIG_ARCH_SC57X) += sc57x-ezkit.o +obj-$(CONFIG_ARCH_SC57X) += sc57x-ezlite.o obj-$(CONFIG_ARCH_SC58X) += sc58x-ezkit.o obj-$(CONFIG_ARCH_SC59X) += sc59x-ezkit.o diff --git a/arch/arm/mach-sc5xx/Makefile.boot b/arch/arm/mach-sc5xx/Makefile.boot index 4448a4ccfd675b..87d3ec91e33e73 100644 --- a/arch/arm/mach-sc5xx/Makefile.boot +++ b/arch/arm/mach-sc5xx/Makefile.boot @@ -1,6 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -ifeq ($(CONFIG_MACH_SC573_EZKIT),y) +ifeq ($(CONFIG_MACH_SC573_EZLITE),y) zreladdr-y += 0x82008000 params_phys-y := 0x82000100 endif diff --git a/arch/arm/mach-sc5xx/sc57x-ezkit.c b/arch/arm/mach-sc5xx/sc57x-ezlite.c similarity index 93% rename from arch/arm/mach-sc5xx/sc57x-ezkit.c rename to arch/arm/mach-sc5xx/sc57x-ezlite.c index c8e51b11191d35..6608f8be115331 100644 --- a/arch/arm/mach-sc5xx/sc57x-ezkit.c +++ b/arch/arm/mach-sc5xx/sc57x-ezlite.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Machine entries for the sc573 ezkit + * Machine entries for the sc573 ezlite * * (C) Copyright 2022 - Analog Devices, Inc. * @@ -59,7 +59,7 @@ static void __init sc57x_init(void) sc5xx_init_ethernet(); } -DT_MACHINE_START(SC57X_DT, "SC57x-EZKIT (Device Tree Support)") +DT_MACHINE_START(SC57X_DT, "SC57x-EZLITE (Device Tree Support)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_early = sc57x_init_early, From 44042207d07d203519f8808ad963ef88614424da Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Fri, 16 Jan 2026 12:45:53 +0100 Subject: [PATCH 59/85] clk: adi: Rename SC573-EZKIT board to EZLITE Renaming the SC573 EZKIT board to EZLITE to match with release naming. Signed-off-by: Ozan Durgut --- drivers/clk/adi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index 7c18a2195cfd42..4fef2d6b799e7c 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -3,9 +3,9 @@ config COMMON_CLK_ADI_SC57X bool "ADI SC573 clock driver" depends on ARCH_SC5XX || COMPILE_TEST help - This enables the ADI SC573-ezkit clock driver. The driver provides - support for the clock controller on the ADI SC573-ezkit SoC. - If you are using the ADI SC573-ezkit SoC, say Y here. + This enables the ADI SC573-ezlite clock driver. The driver provides + support for the clock controller on the ADI SC573-ezlite SoC. + If you are using the ADI SC573-ezlite SoC, say Y here. If unsure, say N. config COMMON_CLK_ADI_SC58X From 31e1f6abb61667a809eb3dfb4a6bea45fb938cb2 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Fri, 16 Jan 2026 15:04:08 +0100 Subject: [PATCH 60/85] .github: top-level: rename sc573 defconfig Update the CI workflow to build the SC573 EZLITE defconfig instead of the SC573 EZKIT to match with release naming. Signed-off-by: Ozan Durgut --- .github/workflows/top-level.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/top-level.yml b/.github/workflows/top-level.yml index abb0b419de1235..a3797edf5bad01 100644 --- a/.github/workflows/top-level.yml +++ b/.github/workflows/top-level.yml @@ -87,14 +87,14 @@ jobs: https://raw.githubusercontent.com/${{ github.repository }}/ci/ci/runner_env.sh source ./runner_env.sh assert_labels - build_gcc_arm_sc573-ezkit_defconfig: + build_gcc_arm_sc573-ezlite_defconfig: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit with: compiler: "gcc" arch: "arm" - defconfig: "sc573-ezkit_defconfig" + defconfig: "sc573-ezlite_defconfig" auto_from_range: false build_gcc_arm_sc589-mini_defconfig: needs: [assert_checks] @@ -144,7 +144,7 @@ jobs: assert_build_adsp: runs-on: [self-hosted, repo-only] needs: - - build_gcc_arm_sc573-ezkit_defconfig + - build_gcc_arm_sc573-ezlite_defconfig - build_gcc_arm_sc589-mini_defconfig - build_gcc_arm_sc594-som-ezkit_defconfig - build_gcc_arm_sc594-som-ezlite_defconfig @@ -153,13 +153,13 @@ jobs: steps: - name: Assert env: - job_warn_build_gcc_arm_sc573-ezkit_defconfig: ${{needs.build_gcc_arm_sc573-ezkit_defconfig.outputs.warn}} + job_warn_build_gcc_arm_sc573-ezlite_defconfig: ${{needs.build_gcc_arm_sc573-ezlite_defconfig.outputs.warn}} job_warn_build_gcc_arm_sc589-mini_defconfig: ${{needs.build_gcc_arm_sc589-mini_defconfig.outputs.warn}} job_warn_build_gcc_arm_sc594-som-ezkit_defconfig: ${{needs.build_gcc_arm_sc594-som-ezkit_defconfig.outputs.warn}} job_warn_build_gcc_arm_sc594-som-ezlite_defconfig: ${{needs.build_gcc_arm_sc594-som-ezlite_defconfig.outputs.warn}} job_warn_build_gcc_aarch64_sc598-som-ezkit_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezkit_defconfig.outputs.warn}} job_warn_build_gcc_aarch64_sc598-som-ezlite_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezlite_defconfig.outputs.warn}} - job_fail_build_gcc_arm_sc573-ezkit_defconfig: ${{needs.build_gcc_arm_sc573-ezkit_defconfig.outputs.fail}} + job_fail_build_gcc_arm_sc573-ezlite_defconfig: ${{needs.build_gcc_arm_sc573-ezlite_defconfig.outputs.fail}} job_fail_build_gcc_arm_sc589-mini_defconfig: ${{needs.build_gcc_arm_sc589-mini_defconfig.outputs.fail}} job_fail_build_gcc_arm_sc594-som-ezkit_defconfig: ${{needs.build_gcc_arm_sc594-som-ezkit_defconfig.outputs.fail}} job_fail_build_gcc_arm_sc594-som-ezlite_defconfig: ${{needs.build_gcc_arm_sc594-som-ezlite_defconfig.outputs.fail}} From fd68560f36e5c71705ec7897f7635b9077bd8ef4 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Fri, 16 Jan 2026 16:09:21 +0100 Subject: [PATCH 61/85] ARM: mach-sc5xx: Allow building with COMPILE_TEST Add COMPILE_TEST to the dependencies of the SC5xx SoC and board Kconfig symbols. Currently, these symbols strictly depend on ARCH_SC5XX. This prevents them from being selected during build testing on other architectures or generic configurations. Signed-off-by: Ozan Durgut --- arch/arm/mach-sc5xx/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-sc5xx/Kconfig b/arch/arm/mach-sc5xx/Kconfig index 048a2af3fdbd1f..8c9718fe64f295 100644 --- a/arch/arm/mach-sc5xx/Kconfig +++ b/arch/arm/mach-sc5xx/Kconfig @@ -23,7 +23,7 @@ menuconfig ARCH_SC5XX config ARCH_SC59X bool "ADI SC59x SoCs (Cortex-A5-based)" - depends on ARCH_SC5XX + depends on ARCH_SC5XX || COMPILE_TEST select COMMON_CLK_ADI_SC594 help This enables support for 32-bit Cortex-A5-based ADI ADSP SC-59X @@ -32,7 +32,7 @@ config ARCH_SC59X config MACH_SC594_SOM bool "ADI SC594-SOM board" - depends on ARCH_SC59X + depends on ARCH_SC59X || COMPILE_TEST select MIGHT_HAVE_PCI select HIGHMEM select HIGHPTE @@ -42,7 +42,7 @@ config MACH_SC594_SOM config ARCH_SC58X bool "ADI SC58x SoCs (Cortex-A5-based)" - depends on ARCH_SC5XX + depends on ARCH_SC5XX || COMPILE_TEST select COMMON_CLK_ADI_SC58X help This enables support for 32-bit Cortex-A5-based ADI ADSP SC-58X @@ -50,7 +50,7 @@ config ARCH_SC58X config MACH_SC589_MINI bool "ADI sc589-mini board" - depends on ARCH_SC58X + depends on ARCH_SC58X || COMPILE_TEST select MIGHT_HAVE_PCI help Say 'Y' here if you want your kernel to run on the ADI @@ -58,14 +58,14 @@ config MACH_SC589_MINI config ARCH_SC57X bool "ADI SC57x SoCs" - depends on ARCH_SC5XX + depends on ARCH_SC5XX || COMPILE_TEST select COMMON_CLK_ADI_SC57X help This enables support for ADI ADSP SC-57x SoCs config MACH_SC573_EZLITE bool "ADI SC573 EZLITE board" - depends on ARCH_SC57X + depends on ARCH_SC57X || COMPILE_TEST help Say 'Y' here if you want your kernel to run on the ADI SC573-EZLITE board. From 265c1be3e6ed5c0f205cfe672e12dc67bba14cae Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Fri, 16 Jan 2026 12:10:40 +0100 Subject: [PATCH 62/85] ARM: dts: adi: add Rev E support to SC598-SOM Add revision-specific DTS files layered on top of sc598-som.dtsi to support Rev D and E. Signed-off-by: Ozan Durgut --- arch/arm64/boot/dts/adi/sc598-som-ezkit.dts | 2 +- arch/arm64/boot/dts/adi/sc598-som-ezlite.dts | 2 +- arch/arm64/boot/dts/adi/sc598-som-revD.dtsi | 121 +++++++++++++++++++ arch/arm64/boot/dts/adi/sc598-som-revE.dtsi | 120 ++++++++++++++++++ arch/arm64/boot/dts/adi/sc598-som.dtsi | 117 ------------------ 5 files changed, 243 insertions(+), 119 deletions(-) create mode 100644 arch/arm64/boot/dts/adi/sc598-som-revD.dtsi create mode 100644 arch/arm64/boot/dts/adi/sc598-som-revE.dtsi diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts index 809fa9d09b09f4..0dd95064d4c372 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts @@ -5,7 +5,7 @@ /dts-v1/; -#include "sc598-som.dtsi" +#include "sc598-som-revE.dtsi" / { model = "ADI 64-bit SC598 SOM EZ Kit"; diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts index 630a2069c15fe7..cb857c671bc560 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezlite.dts @@ -5,7 +5,7 @@ /dts-v1/; -#include "sc598-som.dtsi" +#include "sc598-som-revE.dtsi" / { model = "ADI 64-bit SC598 SOM EZ Lite"; diff --git a/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi b/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi new file mode 100644 index 00000000000000..78ec419b142426 --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Analog Devices Incorporated + */ + +/dts-v1/; + +#include "sc598-som.dtsi" + +&i2c2 { + som_gpio_expander: mcp23018@20 { + compatible = "microchip,mcp23018"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + drive-pullups; + + led-ds1 { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led1-en"; + }; + + led-ds2 { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led-ds2"; + }; + + led-ds3 { + gpio-hog; + gpios = <2 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led-ds3"; + }; + + som-flash-d2d3 { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-flash-d2d3-en"; + }; + + som-flash-cs { + gpio-hog; + gpios = <4 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-flash-cs-en"; + }; + + uart0 { + gpio-hog; + gpios = <5 GPIO_ACTIVE_LOW>; + output-high; + line-name = "uart0-en"; + }; + + uart0-flow-en { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; + + som-emmc { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-emmc-en"; + }; + + crr-sdcard { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "crr-sdcard-en"; + }; + }; +}; + +&spi2 { + som_flash: is25lp512@0 { + compatible = "jedec,spi-nor", "is25lp512"; + reg = <0>; + spi-rx-bus-width = <4>; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qspi_0@0 { + label = "u-boot-spl"; + reg = <0x00000000 0x00040000>; + }; + + qspi_1@40000 { + label = "u-boot"; + reg = <0x00040000 0x000C0000>; + }; + + qspi_2@100000 { + label = "dtb"; + reg = <0x00100000 0x00010000>; + }; + + qspi_3@110000 { + label = "kernel"; + reg = <0x00110000 0x02000000>; + }; + + qspi_4@2110000 { + label = "rootfs"; + reg = <0x02110000 0x01EF0000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi b/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi new file mode 100644 index 00000000000000..0b620f2b0ec812 --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Analog Devices Incorporated + */ + +/dts-v1/; + +#include "sc598-som.dtsi" + +&i2c2 { + som_gpio_expander: adp5587@34 { + compatible = "adi,adp5587"; + reg = <0x34>; + gpio-controller; + #gpio-cells = <2>; + + uart0 { + gpio-hog; + gpios = <0 GPIO_ACTIVE_LOW>; + output-high; + line-name = "uart0-en"; + }; + + uart0-flow { + gpio-hog; + gpios = <1 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; + + som-flash-d2d3 { + gpio-hog; + gpios = <2 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-flash-d2d3-en"; + }; + + som-flash-cs { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-flash-cs-en"; + }; + + som-emmc { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-high; + line-name = "som-emmc-en"; + }; + + crr-sdcard { + gpio-hog; + gpios = <9 GPIO_ACTIVE_LOW>; + output-low; + line-name = "crr-sdcard-en"; + }; + + led-ds3 { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led-ds3"; + }; + + led-ds2 { + gpio-hog; + gpios = <16 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led-ds2"; + }; + + led-ds1 { + gpio-hog; + gpios = <17 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led-ds1"; + }; + }; +}; + +&spi2 { + som_flash: is25lp01g@0 { + compatible = "jedec,spi-nor", "is25lp01g"; + reg = <0>; + spi-rx-bus-width = <4>; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qspi_0@0 { + label = "u-boot-spl"; + reg = <0x00000000 0x00040000>; + }; + + qspi_1@40000 { + label = "u-boot"; + reg = <0x00040000 0x000C0000>; + }; + + qspi_2@100000 { + label = "dtb"; + reg = <0x00100000 0x00010000>; + }; + + qspi_3@110000 { + label = "kernel"; + reg = <0x00110000 0x02000000>; + }; + + qspi_4@2110000 { + label = "rootfs"; + reg = <0x02110000 0x05EF0000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/adi/sc598-som.dtsi b/arch/arm64/boot/dts/adi/sc598-som.dtsi index 79b3bdbd3adfa3..b2a65303af286b 100644 --- a/arch/arm64/boot/dts/adi/sc598-som.dtsi +++ b/arch/arm64/boot/dts/adi/sc598-som.dtsi @@ -203,43 +203,6 @@ pinctrl-0 = <&spi2_quad>; status = "okay"; cs-gpios = <&gpa 5 GPIO_ACTIVE_LOW>; - - flash: is25lp512@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "is25lp512", "jedec,spi-nor"; - reg = <0>; - spi-cpha; - spi-cpol; - spi-max-frequency = <25000000>; - spi-rx-bus-width = <4>; - - qspi_0: partition@0 { - label = "U-Boot SPL"; - reg = <0x0 0x40000>; - }; - - qspi_1: partition@1 { - label = "U-Boot Proper"; - reg = <0x40000 0xC0000>; - }; - - qspi_2: partition@2 { - label = "U-Boot Environment"; - reg = <0x100000 0x20000>; - }; - - qspi_3: partition@3 { - label = "FIT Image"; - reg = <0x120000 0xF00000>; - }; - - qspi_4: partition@4 { - label = "JFFS2 Formatted RFS"; - reg = <0x1020000 0x2FE0000>; - }; - - }; }; &i2c0 { @@ -254,86 +217,6 @@ status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins>; - - som_gpio_expander: gpio@20 { - compatible = "microchip,mcp23018"; - gpio-controller; - #gpio-cells = <2>; - reg = <0x20>; - status = "okay"; - - pinctrl-names = "default"; - pinctrl-0 = <&som_gpio_expanderpullups>; - - som_gpio_expanderpullups: pinmux { - bias-pull-up; - pins = "gpio0", "gpio1", "gpio2", "gpio3", - "gpio4", "gpio5", "gpio6", "gpio8", "gpio9"; - }; - - led1 { - gpio-hog; - gpios = <0 GPIO_ACTIVE_LOW>; - output-high; - line-name = "led1-en"; - }; - - led2 { - gpio-hog; - gpios = <1 GPIO_ACTIVE_LOW>; - output-high; - line-name = "led2-en"; - }; - - led3 { - gpio-hog; - gpios = <2 GPIO_ACTIVE_LOW>; - output-high; - line-name = "led3-en"; - }; - - spi2d2-d3 { - gpio-hog; - gpios = <3 GPIO_ACTIVE_LOW>; - output-high; - line-name = "spi2d2-d3-en"; - }; - - spi2flash-cs { - gpio-hog; - gpios = <4 GPIO_ACTIVE_LOW>; - output-high; - line-name = "spi2flash-cs"; - }; - - uart0 { - gpio-hog; - gpios = <5 GPIO_ACTIVE_LOW>; - output-high; - line-name = "uart0-en"; - }; - - uart0-flow-en { - gpio-hog; - gpios = <6 GPIO_ACTIVE_LOW>; - output-low; - line-name = "uart0-flow-en"; - }; - - emmc { - gpio-hog; - gpios = <8 GPIO_ACTIVE_LOW>; - output-high; - line-name = "emmc-en"; - }; - - emmc-som-en { - gpio-hog; - gpios = <9 GPIO_ACTIVE_LOW>; - output-low; - line-name = "emmc-som-en"; - }; - }; }; &mmc0{ From 97aeeb14bf6e85e4eba19f2889d5eb8a64fef7d7 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Thu, 29 Jan 2026 22:19:14 +0100 Subject: [PATCH 63/85] ASoC: axi-i2s: Convert remove callback to return void Update the driver to match the new `void` return type and remove the useless `return 0;`. The `remove` callback in `struct platform_driver` has transitioned to returning `void` instead of `int`. This transition started with commit 5c5a7680e67b ("platform: Provide a remove callback that returns no value"), continued with commit 0edb555a65d1 ("platform: Make platform_driver::remove() return void"), and was finalized in commit e70140ba0d2b ("Get rid of "remove_new" relic from platform driver struct"). Fixes: f72ed8b8cd25 ("sound: soc: adi: Add ALSA support for ADSP-SC598") Signed-off-by: Philip Molloy --- sound/soc/adi/axi-i2s.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index 6fdec9cc4d555b..41f89384f8fd74 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -274,13 +274,11 @@ static int axi_i2s_probe(struct platform_device *pdev) return ret; } -static int axi_i2s_dev_remove(struct platform_device *pdev) +static void axi_i2s_dev_remove(struct platform_device *pdev) { struct axi_i2s *i2s = platform_get_drvdata(pdev); clk_disable_unprepare(i2s->clk); - - return 0; } static const struct of_device_id axi_i2s_of_match[] = { From bc7589da399c48d3b8c9c3aa9d58714b5b1b5a39 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Sun, 1 Feb 2026 13:16:06 +0100 Subject: [PATCH 64/85] remoteproc: Drop COMPILE_TEST from ADI driver adi_remoteproc.c includes arch specific code under linux/soc/ that is not enabled with COMPILE_TEST making it impossible to build the driver with COMPILE_TEST enabled Signed-off-by: Philip Molloy --- drivers/remoteproc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 2d95b9d17207e3..2a1124493d4ffc 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -380,7 +380,7 @@ config XLNX_R5_REMOTEPROC config ADI_REMOTEPROC tristate "ADI remoteproc support" - depends on ARCH_SC59X_64 || ARCH_SC5XX || COMPILE_TEST + depends on ARCH_SC59X_64 || ARCH_SC5XX help Say y here to support ADI's remote processors (SHARC DSP Core onSC5XX) via the remote processor framework. From 85b36d6095559d8e14a5fc68a169c1662aa143c4 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 4 Feb 2026 12:50:28 +0100 Subject: [PATCH 65/85] clk: adi: Refactor Makefile and Kconfig for shared PLL code Introduce a hidden Kconfig symbol COMMON_CLK_ADI_PLL to handle the shared PLL code in clk-adi-pll.c. Previously, the Makefile attempted to build clk-adi-pll.o based on architecture flags (ARCH_SC5XX, ARCH_SC59X_64), which is fragile and incorrect if multiple drivers are enabled or if they are built as modules. Update the specific clock drivers (SC57X, SC58X, SC594, SC598) to select COMMON_CLK_ADI_PLL, and update the Makefile to build clk-adi-pll.o only when this symbol is selected. Signed-off-by: Philip Molloy --- drivers/clk/adi/Kconfig | 7 +++++++ drivers/clk/adi/Makefile | 12 +++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index 4fef2d6b799e7c..2e57dfb11aeeee 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -1,7 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_PLL + bool + config COMMON_CLK_ADI_SC57X bool "ADI SC573 clock driver" depends on ARCH_SC5XX || COMPILE_TEST + select COMMON_CLK_ADI_PLL help This enables the ADI SC573-ezlite clock driver. The driver provides support for the clock controller on the ADI SC573-ezlite SoC. @@ -11,6 +15,7 @@ config COMMON_CLK_ADI_SC57X config COMMON_CLK_ADI_SC58X bool "ADI SC589 clock driver" depends on ARCH_SC5XX || COMPILE_TEST + select COMMON_CLK_ADI_PLL help This enables the ADI SC589 clock driver. The driver provides support for the clock controller on the ADI SC589 SoC. @@ -20,6 +25,7 @@ config COMMON_CLK_ADI_SC58X config COMMON_CLK_ADI_SC594 bool "ADI SC594 clock driver" depends on ARCH_SC5XX || COMPILE_TEST + select COMMON_CLK_ADI_PLL help This enables the ADI SC594 clock driver. The driver provides support for the clock controller on the ADI SC594 SoC. @@ -29,6 +35,7 @@ config COMMON_CLK_ADI_SC594 config COMMON_CLK_ADI_SC598 bool "ADI SC598 clock driver" depends on ARCH_SC59X_64 || COMPILE_TEST + select COMMON_CLK_ADI_PLL help This enables the ADI SC594 clock driver. The driver provides support for the clock controller on the ADI SC598 SoC. diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile index 4dbb2095a8dc08..79e31a1a9f0e16 100644 --- a/drivers/clk/adi/Makefile +++ b/drivers/clk/adi/Makefile @@ -1,9 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -obj-$(CONFIG_ARCH_SC5XX) += clk-adi-pll.o -obj-$(CONFIG_ARCH_SC59X_64) += clk-adi-pll.o - -obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o -obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o -obj-$(CONFIG_COMMON_CLK_ADI_SC58X) += clk-adi-sc58x.o -obj-$(CONFIG_COMMON_CLK_ADI_SC57X) += clk-adi-sc57x.o +obj-$(CONFIG_COMMON_CLK_ADI_PLL) += clk-adi-pll.o +obj-$(CONFIG_COMMON_CLK_ADI_SC598) += clk-adi-sc598.o +obj-$(CONFIG_COMMON_CLK_ADI_SC594) += clk-adi-sc594.o +obj-$(CONFIG_COMMON_CLK_ADI_SC58X) += clk-adi-sc58x.o +obj-$(CONFIG_COMMON_CLK_ADI_SC57X) += clk-adi-sc57x.o From d9f8601b6c51f24d8854f83e06dd44d626c8f086 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 4 Feb 2026 16:38:55 +0100 Subject: [PATCH 66/85] soc: adi: Support building with COMPILE_TEST Enable COMPILE_TEST for ADI system configuration and PADS drivers. This allows these drivers to be built on other architectures for testing purposes, increasing build coverage. Signed-off-by: Philip Molloy --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 1 + drivers/soc/Kconfig | 1 + drivers/soc/adi/Kconfig | 18 ++++++++++++++++++ drivers/soc/adi/Makefile | 7 ++----- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 drivers/soc/adi/Kconfig diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index e40489b1cbff72..6adfacf7a2e8e1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -62,6 +62,7 @@ config DWMAC_ADI tristate "ADI DWMAC support" default ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X depends on OF && (ARCH_SC59X_64 || ARCH_SC59X || ARCH_SC58X || ARCH_SC57X || COMPILE_TEST) + select ADI_SYSTEM_CONFIG help Support for ethernet controller on SC5XX SOCs. diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index a2d65adffb8052..d1b9dd17a189ca 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/adi/Kconfig" source "drivers/soc/amlogic/Kconfig" source "drivers/soc/apple/Kconfig" source "drivers/soc/aspeed/Kconfig" diff --git a/drivers/soc/adi/Kconfig b/drivers/soc/adi/Kconfig new file mode 100644 index 00000000000000..cdd2a79057de4f --- /dev/null +++ b/drivers/soc/adi/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config ADI_SYSTEM_CONFIG + bool "ADI System Config" if COMPILE_TEST + default ARCH_SC59X_64 || ARCH_SC5XX + select REGMAP_MMIO + help + This enables support for the ADI System Config system, which + provides a regmap interface to system configuration registers. + +config ADI_PADS_SYSTEM + tristate "ADI PADS System Config driver" + default ARCH_SC59X_64 || ARCH_SC5XX + depends on ARCH_SC59X_64 || ARCH_SC5XX || COMPILE_TEST + select ADI_SYSTEM_CONFIG + help + This enables support for the ADI PADS System Config driver, + which manages pin configuration and other system settings. diff --git a/drivers/soc/adi/Makefile b/drivers/soc/adi/Makefile index 58cd4bc51a82ee..7d93149c037ae1 100644 --- a/drivers/soc/adi/Makefile +++ b/drivers/soc/adi/Makefile @@ -1,10 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# todo modularize; already depends on CONFIG_ARCH_SC59X_64 though - -obj-$(CONFIG_ARCH_SC59X_64) += pads_system.o system.o +obj-$(CONFIG_ADI_SYSTEM_CONFIG) += system.o +obj-$(CONFIG_ADI_PADS_SYSTEM) += pads_system.o obj-$(CONFIG_ARCH_SC59X_64) += mach-sc59x/ obj-$(CONFIG_ARCH_SC59X_64) += mach-sc5xx/ - -obj-$(CONFIG_ARCH_SC5XX) += pads_system.o system.o obj-$(CONFIG_ARCH_SC5XX) += mach-sc5xx/ From 05062a98bda32e18d3f3cadc61cb01745ecd360a Mon Sep 17 00:00:00 2001 From: Caleb Ethridge Date: Thu, 19 Feb 2026 15:15:58 -0500 Subject: [PATCH 67/85] ARM: dts: adi: Update SPI and OSPI partitions in device tree Reconfiguring the partitions in the SPI and OSPI to remove the DTB partition and give the extra space to the rootfs partition. Signed-off-by: Caleb Ethridge --- arch/arm/boot/dts/adi/sc573-ezlite.dts | 15 +++---- arch/arm/boot/dts/adi/sc589-mini.dts | 15 +++---- arch/arm/boot/dts/adi/sc594-som.dtsi | 46 ++++++++------------- arch/arm64/boot/dts/adi/sc598-som-ezkit.dts | 19 ++++----- arch/arm64/boot/dts/adi/sc598-som-revD.dtsi | 15 +++---- arch/arm64/boot/dts/adi/sc598-som-revE.dtsi | 15 +++---- 6 files changed, 45 insertions(+), 80 deletions(-) diff --git a/arch/arm/boot/dts/adi/sc573-ezlite.dts b/arch/arm/boot/dts/adi/sc573-ezlite.dts index da4ad94d479ddb..2fe7971b09f644 100644 --- a/arch/arm/boot/dts/adi/sc573-ezlite.dts +++ b/arch/arm/boot/dts/adi/sc573-ezlite.dts @@ -176,27 +176,22 @@ spi-rx-bus-width = <4>; partition@0 { - label = "uboot spl (spi)"; + label = "u-boot-spl"; reg = <0x0 0x20000>; }; partition@1 { - label = "uboot proper (spi)"; + label = "u-boot"; reg = <0x20000 0xb0000>; }; partition@2 { - label = "uboot env"; - reg = <0xd0000 0x10000>; + label = "kernel"; + reg = <0x00d0000 0x610000>; }; partition@3 { - label = "kernel (spi)"; - reg = <0x00e0000 0x0600000>; - }; - - partition@4 { - label = "root file system (spi)"; + label = "rootfs"; reg = <0x06e0000 0x0920000>; }; }; diff --git a/arch/arm/boot/dts/adi/sc589-mini.dts b/arch/arm/boot/dts/adi/sc589-mini.dts index 524b67e979f8e3..32a6e685282cc3 100644 --- a/arch/arm/boot/dts/adi/sc589-mini.dts +++ b/arch/arm/boot/dts/adi/sc589-mini.dts @@ -136,27 +136,22 @@ reg = <0>; partition@0 { - label = "uboot spl (spi)"; + label = "u-boot-spl"; reg = <0x0 0x20000>; }; partition@1 { - label = "uboot proper (spi)"; + label = "u-boot"; reg = <0x20000 0xb0000>; }; partition@2 { - label = "uboot env"; - reg = <0xd0000 0x10000>; + label = "kernel"; + reg = <0x00d0000 0x810000>; }; partition@3 { - label = "kernel (spi)"; - reg = <0x00e0000 0x0800000>; - }; - - partition@4 { - label = "root file system (spi)"; + label = "rootfs"; reg = <0x08e0000 0x3720000>; }; }; diff --git a/arch/arm/boot/dts/adi/sc594-som.dtsi b/arch/arm/boot/dts/adi/sc594-som.dtsi index c000736c61e45a..e8ef70a0f003e9 100644 --- a/arch/arm/boot/dts/adi/sc594-som.dtsi +++ b/arch/arm/boot/dts/adi/sc594-som.dtsi @@ -174,28 +174,23 @@ /*adi,enable-dma;*/ qspi_0: partition@0 { - label = "U-Boot SPL"; + label = "u-boot-spl"; reg = <0x0 0x40000>; }; qspi_1: partition@1 { - label = "U-Boot Proper"; - reg = <0x40000 0xC0000>; + label = "u-boot"; + reg = <0x40000 0xc0000>; }; - qspi_2: partition@2 { - label = "U-Boot Environment"; - reg = <0x100000 0x20000>; + qspi_2: partition@3 { + label = "kernel"; + reg = <0x00100000 0xf00000>; }; - qspi_3: partition@3 { - label = "FIT Image"; - reg = <0x120000 0xF00000>; - }; - - qspi_4: partition@4 { - label = "JFFS2 Formatted RFS"; - reg = <0x1020000 0x2FE0000>; + qspi_3: partition@4 { + label = "rootfs"; + reg = <0x1000000 0x3000000>; }; }; @@ -230,28 +225,23 @@ #size-cells = <1>; ospi_0: partition@0 { - label = "U-Boot SPL"; + label = "u-boot-spl"; reg = <0x0 0x40000>; }; ospi_1: partition@1 { - label = "U-Boot Proper"; - reg = <0x40000 0xC0000>; - }; - - ospi_2: partition@2 { - label = "U-Boot Environment"; - reg = <0x100000 0x20000>; + label = "u-boot"; + reg = <0x40000 0xc0000>; }; - ospi_3: partition@3 { - label = "FIT Image"; - reg = <0x120000 0xF00000>; + ospi_2: partition@3 { + label = "kernel"; + reg = <0x100000 0xf00000>; }; - ospi_4: partition@4 { - label = "JFFS2 Formatted RFS"; - reg = <0x1020000 0xFE0000>; + ospi_3: partition@4 { + label = "rootfs"; + reg = <0x01000000 0x1000000>; }; }; }; diff --git a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts index 0dd95064d4c372..a5cd60dac23832 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts +++ b/arch/arm64/boot/dts/adi/sc598-som-ezkit.dts @@ -47,28 +47,23 @@ #size-cells = <1>; ospi_0: partition@0 { - label = "U-Boot SPL"; + label = "u-boot-spl"; reg = <0x0 0x40000>; }; ospi_1: partition@1 { - label = "U-Boot Proper"; - reg = <0x40000 0xC0000>; + label = "u-boot"; + reg = <0x40000 0xc0000>; }; ospi_2: partition@2 { - label = "U-Boot Environment"; - reg = <0x100000 0x20000>; + label = "kernel"; + reg = <0x100000 0xf00000>; }; ospi_3: partition@3 { - label = "FIT Image"; - reg = <0x120000 0xF00000>; - }; - - ospi_4: partition@4 { - label = "JFFS2 Formatted RFS"; - reg = <0x1020000 0xFE0000>; + label = "rootfs"; + reg = <0x1000000 0x1000000>; }; }; }; diff --git a/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi b/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi index 78ec419b142426..ce641c888db135 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi +++ b/arch/arm64/boot/dts/adi/sc598-som-revD.dtsi @@ -94,27 +94,22 @@ qspi_0@0 { label = "u-boot-spl"; - reg = <0x00000000 0x00040000>; + reg = <0x00000000 0x40000>; }; qspi_1@40000 { label = "u-boot"; - reg = <0x00040000 0x000C0000>; + reg = <0x00040000 0xc0000>; }; qspi_2@100000 { - label = "dtb"; - reg = <0x00100000 0x00010000>; - }; - - qspi_3@110000 { label = "kernel"; - reg = <0x00110000 0x02000000>; + reg = <0x00100000 0xf00000>; }; - qspi_4@2110000 { + qspi_3@1000000 { label = "rootfs"; - reg = <0x02110000 0x01EF0000>; + reg = <0x01000000 0x3000000>; }; }; }; diff --git a/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi b/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi index 0b620f2b0ec812..46bb19af093e81 100644 --- a/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi +++ b/arch/arm64/boot/dts/adi/sc598-som-revE.dtsi @@ -93,27 +93,22 @@ qspi_0@0 { label = "u-boot-spl"; - reg = <0x00000000 0x00040000>; + reg = <0x00000000 0x40000>; }; qspi_1@40000 { label = "u-boot"; - reg = <0x00040000 0x000C0000>; + reg = <0x00040000 0xC0000>; }; qspi_2@100000 { - label = "dtb"; - reg = <0x00100000 0x00010000>; - }; - - qspi_3@110000 { label = "kernel"; - reg = <0x00110000 0x02000000>; + reg = <0x00100000 0x2000000>; }; - qspi_4@2110000 { + qspi_3@2100000 { label = "rootfs"; - reg = <0x02110000 0x05EF0000>; + reg = <0x02100000 0x5ef0000>; }; }; }; From dd54e79498173b05e6e7c08e3e1044fddc9fac46 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Thu, 5 Mar 2026 17:47:22 +0100 Subject: [PATCH 68/85] ARM: dts: adi: Remove dead adi,button-led nodes Remove unused button-led device tree nodes from sc573-ezlite, and sc594-som boards. Signed-off-by: Ozan Durgut --- arch/arm/boot/dts/adi/sc573-ezlite.dts | 25 ------------------------- arch/arm/boot/dts/adi/sc594-som.dtsi | 18 ------------------ 2 files changed, 43 deletions(-) diff --git a/arch/arm/boot/dts/adi/sc573-ezlite.dts b/arch/arm/boot/dts/adi/sc573-ezlite.dts index 2fe7971b09f644..181804eeb9d59c 100644 --- a/arch/arm/boot/dts/adi/sc573-ezlite.dts +++ b/arch/arm/boot/dts/adi/sc573-ezlite.dts @@ -4,9 +4,6 @@ * Copyright 2014 - 2018 Analog Devices Inc. * * Licensed under the GPL-2 or later. - * - * todo list: - * - bushbutton GPIOs are all incorrect and commented out for now */ /dts-v1/; @@ -56,28 +53,6 @@ }; scb { - button0: button@0 { - compatible = "adi,button-led"; -// en-pins = <&gpio_expander1 2 GPIO_ACTIVE_LOW>, /* PUSHBUTTON1_EN */ -// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ -// button_gpio = <40>; -// led_gpio = <77>; - }; - button1: button@1 { - compatible = "adi,button-led"; -// en-pins = <&gpio_expander1 1 GPIO_ACTIVE_LOW>, /* PUSHBUTTON2_EN */ -// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ -// button_gpio = <41>; -// led_gpio = <9>; - }; - button2: button@2 { - compatible = "adi,button-led"; -// en-pins = <&gpio_expander1 0 GPIO_ACTIVE_LOW>, /* PUSHBUTTON3_EN */ -// <&gpio_expander1 3 GPIO_ACTIVE_LOW>; /* LEDS_EN */ -// button_gpio = <42>; -// led_gpio = <65>; - }; - core1-rproc@3108C000 { compatible = "adi,remoteproc"; reg = <0x28240000 0x2000>, diff --git a/arch/arm/boot/dts/adi/sc594-som.dtsi b/arch/arm/boot/dts/adi/sc594-som.dtsi index e8ef70a0f003e9..932fff67dd240a 100644 --- a/arch/arm/boot/dts/adi/sc594-som.dtsi +++ b/arch/arm/boot/dts/adi/sc594-som.dtsi @@ -57,24 +57,6 @@ }; scb { - -/* The button GPIO conflicts with OSPI0_D4 for this - button0: button@0 { - compatible = "adi,button-led"; - button_gpio = <48>; /* PD_00 - led_gpio = <35>; /* PC_03 / LED9 / LED1_KIT - }; -*/ - -/* This also appears to interfere with the OSPI somehow... - possible trace noise/interference? - button1: button@1 { - compatible = "adi,button-led"; - button_gpio = <112>; /* PH_00 - led_gpio = <34>; /* PC_02 / LED8 / LED2_KIT - }; -*/ - sharc0: core1-rproc@28240000 { compatible = "adi,remoteproc"; reg = <0x28240000 0x160000>, From 64156b12c2964e13470348fd5bf26699c4837309 Mon Sep 17 00:00:00 2001 From: Caleb Ethridge Date: Thu, 26 Feb 2026 15:44:43 -0500 Subject: [PATCH 69/85] ARM: dts: adi: Remove hardcoded bootargs Removing these hardcoded bootargs since they are not needed, bootargs are passed from U-boot for ADSP platforms. Signed-off-by: Caleb Ethridge --- arch/arm/boot/dts/adi/sc594-som.dtsi | 1 - arch/arm64/boot/dts/adi/sc598-som.dtsi | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm/boot/dts/adi/sc594-som.dtsi b/arch/arm/boot/dts/adi/sc594-som.dtsi index 932fff67dd240a..2e5539429d6bbb 100644 --- a/arch/arm/boot/dts/adi/sc594-som.dtsi +++ b/arch/arm/boot/dts/adi/sc594-som.dtsi @@ -21,7 +21,6 @@ chosen { stdout-path = &uart1; - bootargs = "root=/dev/mtdblock4 rw rootfstype=jffs2 earlyprintk=serial,uart0,115200 console=ttySC0,115200 vmalloc=512M mem=512M"; }; aliases { diff --git a/arch/arm64/boot/dts/adi/sc598-som.dtsi b/arch/arm64/boot/dts/adi/sc598-som.dtsi index b2a65303af286b..7d6d5f1fad2a47 100644 --- a/arch/arm64/boot/dts/adi/sc598-som.dtsi +++ b/arch/arm64/boot/dts/adi/sc598-som.dtsi @@ -14,7 +14,6 @@ / { chosen { stdout-path = &uart1; - bootargs = "root=/dev/mtdblock4 rw rootfstype=jffs2 earlycon=adi_uart,0x31003000 console=ttySC0,115200 mem=224M"; }; memory@90000000 { From 3ca55d86e0da111c732e2b94254aa18f10634274 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 1 Apr 2026 15:18:25 +0200 Subject: [PATCH 70/85] [ADI] Hardcode ADI org and repository name Always fetch from the official public ADI org, even when forked Signed-off-by: Philip Molloy --- .github/workflows/top-level.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/top-level.yml b/.github/workflows/top-level.yml index a3797edf5bad01..ccc28d9a448d4a 100644 --- a/.github/workflows/top-level.yml +++ b/.github/workflows/top-level.yml @@ -84,7 +84,7 @@ jobs: job_fail_build_gcc_arm: ${{needs.build_gcc_arm.outputs.fail}} run: | curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -L -o runner_env.sh \ - https://raw.githubusercontent.com/${{ github.repository }}/ci/ci/runner_env.sh + https://raw.githubusercontent.com/analogdevicesinc/linux/ci/ci/runner_env.sh source ./runner_env.sh assert_labels build_gcc_arm_sc573-ezlite_defconfig: @@ -167,6 +167,6 @@ jobs: job_fail_build_gcc_aarch64_sc598-som-ezlite_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezlite_defconfig.outputs.fail}} run: | curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -L -o runner_env.sh \ - https://raw.githubusercontent.com/${{ github.repository }}/ci/ci/runner_env.sh + https://raw.githubusercontent.com/analogdevicesinc/linux/ci/ci/runner_env.sh source ./runner_env.sh assert_labels From a92162bdf3dba053d69f57f235b6b3f6efe6f7c9 Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Wed, 1 Apr 2026 19:22:22 +0100 Subject: [PATCH 71/85] serial: adi_uart4: enable COMPILE_TEST in Kconfig Add COMPILE_TEST to SERIAL_ADI_UART4 so CI build jobs can build the driver. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 57ae138ffa9504..3dcdb4b3b03449 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -477,7 +477,7 @@ config SERIAL_SA1100_CONSOLE config SERIAL_ADI_UART4 tristate "ADI uart4 serial port support" - depends on ARCH_SC59X = y || ARCH_SC58X = y || ARCH_SC57X = y || ARCH_SC59X_64 = y + depends on ARCH_SC59X = y || ARCH_SC58X = y || ARCH_SC57X = y || ARCH_SC59X_64 = y || COMPILE_TEST select SERIAL_CORE select SERIAL_CORE_CONSOLE help From d0da80bc6bc0533e1bb1dbdf75107ce360ce438e Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Wed, 1 Apr 2026 19:40:04 +0100 Subject: [PATCH 72/85] serial: adi_uart4: add missing MODULE_LICENSE Add the missing MODULE_LICENSE() declaration so CI jobs can build the driver. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index adb56ad8631a13..19a520a9b07d2e 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -1375,3 +1375,6 @@ static int __init adi_uart_early_console_setup(struct earlycon_device *device, } EARLYCON_DECLARE(adi_uart, adi_uart_early_console_setup); + +MODULE_DESCRIPTION("ADI UART4 serial driver"); +MODULE_LICENSE("GPL"); From 3263fa164afb9a4de9e962572e41cc1404b53fb1 Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Thu, 2 Apr 2026 20:24:26 +0100 Subject: [PATCH 73/85] serial: adi_uart4: drop redundant platform driver owner Remove the explicit .owner assignment from adi_uart4_serial_driver to address the coccicheck warning: "No need to set .owner here. The core will do it." Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index 19a520a9b07d2e..5aeceb25bd9f3f 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -1293,7 +1293,6 @@ static struct platform_driver adi_uart4_serial_driver = { .resume = adi_uart4_serial_resume, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = adi_uart_dt_match, }, }; From 119892b76a0fb2b45e3988cf81b61a9030a36240 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Thu, 9 Apr 2026 09:59:35 -0700 Subject: [PATCH 74/85] drivers: tty: serial: Add missing CONFIG_SERIAL_EARLYCON for ADI UART4 Enable early console support for ADI UART4. Currently it is included in the kernel command line arguments for ADSP Linux but not enabled via Kconfig. This makes it so the ADI UART4 driver selects and enables earlycon support by default and will be available as an early console option during boot. Signed-off-by: Brandon Hurst --- drivers/tty/serial/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 3dcdb4b3b03449..a5bb7b33650c56 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -488,6 +488,7 @@ config SERIAL_ADI_UART4_CONSOLE depends on SERIAL_ADI_UART4 default y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help If you have enabled the ADI UART4 serial port, you can make it the console by answering Y to this option. From fdd14bdac2f765edca0db00e7b554de2d333550f Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Wed, 8 Apr 2026 09:29:13 +0200 Subject: [PATCH 75/85] [ADI] ARM: dts: adi: Add support for SC598-HTOL board Add device tree support for SC598 HTOL SBC based on the reference board designs. Signed-off-by: Ozan Durgut --- arch/arm64/boot/dts/adi/Makefile | 2 +- arch/arm64/boot/dts/adi/sc598-htol.dts | 137 +++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/boot/dts/adi/sc598-htol.dts diff --git a/arch/arm64/boot/dts/adi/Makefile b/arch/arm64/boot/dts/adi/Makefile index 10f843a4a73406..083dca112fc674 100644 --- a/arch/arm64/boot/dts/adi/Makefile +++ b/arch/arm64/boot/dts/adi/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -dtb-$(CONFIG_ARCH_SC59X_64) += sc598-som-ezkit.dtb sc598-som-ezlite.dtb +dtb-$(CONFIG_ARCH_SC59X_64) += sc598-som-ezkit.dtb sc598-som-ezlite.dtb sc598-htol.dtb diff --git a/arch/arm64/boot/dts/adi/sc598-htol.dts b/arch/arm64/boot/dts/adi/sc598-htol.dts new file mode 100644 index 00000000000000..fdd95d9ce516bd --- /dev/null +++ b/arch/arm64/boot/dts/adi/sc598-htol.dts @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2026 - Analog Devices, Inc. + */ + +/dts-v1/; + +#include "sc598-som.dtsi" + +/ { + model = "ADI SC598-HTOL"; + compatible = "adi,sc598-htol", "adi,sc59x-64"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_default>; + linux,rs485-enabled-at-boot-time; + rs485-rts-active-low; + rts-gpios = <&gpd 1 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_default>; + status = "okay"; +}; + +&pinctrl0 { + uart1_default: uart1_pins { + pins { + pinmux = , + ; + }; + }; + + uart2_default: uart2_pins { + pins { + pinmux = , + ; + }; + }; +}; + +&gpb { + uart0-en { + gpio-hog; + gpios = <13 GPIO_ACTIVE_LOW>; + output-high; + line-name = "uart0-en"; + }; + + uart0-flow-en { + gpio-hog; + gpios = <14 GPIO_ACTIVE_LOW>; + output-low; + line-name = "uart0-flow-en"; + }; +}; + +&gpe { + led1 { + gpio-hog; + gpios = <15 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led1"; + }; + + led2 { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-high; + line-name = "led2"; + }; +}; + +&emac0 { + phy-handle = <&adin1300>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð0_default>; + status = "okay"; + + mdio0 { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + adin1300: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + }; +}; + +&mmc0 { + status = "disabled"; +}; + +&spi2 { + som_flash: is25lp01g@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "issi,is25lp01g", "jedec,spi-nor"; + reg = <0>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qspi_0@0 { + label = "u-boot-spl"; + reg = <0x00000000 0x40000>; + }; + + qspi_1@40000 { + label = "u-boot"; + reg = <0x00040000 0xC0000>; + }; + + qspi_2@100000 { + label = "kernel"; + reg = <0x00100000 0x2000000>; + }; + + qspi_3@2100000 { + label = "rootfs"; + reg = <0x02100000 0x5ef0000>; + }; + }; + }; +}; From 713b2ccd69e2bb1a62e0ea57a2625deec037165e Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Fri, 17 Apr 2026 16:21:39 +0100 Subject: [PATCH 76/85] serial: adi_uart4: introduce devm_kzalloc() and clean up error paths The driver allocates struct adi_uart4_serial_port with kzalloc() in probe(), but several probe error paths return after the port has been allocated and published in adi_uart4_serial_ports[] without freeing it. Convert the port allocation to devm_kzalloc() so the memory is device managed meaning it is released automatically on probe failure and device detach. While doing that, route the post allocation probe failures through a common error path so adi_uart4_serial_ports[uartid] is cleared before returning. This avoids leaving a stale global port pointer behind after a failed probe. Also move dev_set_drvdata() to the successful uart_add_one_port() path so drvdata is not set for a port that failed to register. This change: - replaces kzalloc() with devm_kzalloc() in probe() - removes the manual kfree() from the probe error path - removes the manual kfree() from adi_uart4_serial_remove() - converts several direct probe returns to use the common cleanup path - renames out_error_unmap to out_error - propagates the devm_clk_get() error with PTR_ERR() - sets drvdata only after uart_add_one_port() succeeds Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index 5aeceb25bd9f3f..e1bdbffd1cd848 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -1152,7 +1152,7 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) } if (adi_uart4_serial_ports[uartid] == NULL) { - uart = kzalloc(sizeof(*uart), GFP_KERNEL); + uart = devm_kzalloc(dev, sizeof(*uart), GFP_KERNEL); if (!uart) return -ENOMEM; @@ -1160,8 +1160,10 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) uart->dev = &pdev->dev; uart->clk = devm_clk_get(dev, "sclk0"); - if (IS_ERR(uart->clk)) - return -ENODEV; + if (IS_ERR(uart->clk)) { + ret = PTR_ERR(uart->clk); + goto out_error; + } spin_lock_init(&uart->port.lock); uart->port.uartclk = clk_get_rate(uart->clk); @@ -1175,14 +1177,15 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) if (res == NULL) { dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); ret = -ENOENT; - goto out_error_unmap; + goto out_error; } uart->port.membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!uart->port.membase) { dev_err(&pdev->dev, "Cannot map uart IO\n"); - return -ENXIO; + ret = -ENXIO; + goto out_error; } uart->tx_dma_channel = tx_dma_channel; @@ -1203,7 +1206,7 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) uart); if (ret) { dev_err(dev, "Unable to attach UART RX int\n"); - return ret; + goto out_error; } ret = devm_request_threaded_irq(dev, uart->tx_irq, @@ -1211,7 +1214,7 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) uart); if (ret) { dev_err(dev, "Unable to attach UART TX int\n"); - return ret; + goto out_error; } } @@ -1241,25 +1244,24 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) if (IS_ERR(uart->hwflow_en_pin)) { dev_err(dev, "hwflow-en required in peripheral hwflow mode\n"); - return PTR_ERR(uart->hwflow_en_pin); + ret = PTR_ERR(uart->hwflow_en_pin); + goto out_error; } } } uart = adi_uart4_serial_ports[uartid]; uart->port.dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, uart); ret = uart_add_one_port(&adi_uart4_serial_reg, &uart->port); - if (!ret) + if (!ret) { + dev_set_drvdata(&pdev->dev, uart); return 0; - - if (uart) { -out_error_unmap: - adi_uart4_serial_ports[uartid] = NULL; - kfree(uart); } +out_error: + adi_uart4_serial_ports[uartid] = NULL; + return ret; } @@ -1276,7 +1278,6 @@ static void adi_uart4_serial_remove(struct platform_device *pdev) dma_release_channel(uart->tx_dma_channel); dma_release_channel(uart->rx_dma_channel); } - kfree(uart); } } From 3e3a42121f0f49190603def011ae0f370c1b478b Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Fri, 17 Apr 2026 18:02:04 +0100 Subject: [PATCH 77/85] serial: adi_uart4: release DMA channels on probe() failure The driver requests TX and RX DMA channels during adi_uart4_serial_probe(), but several probe failure paths return without releasing them. Add a common probe cleanup path that releases any successfully requested DMA channels before returning an error. Ensure that alias lookup failure, allocation failure, and post allocation probe failures go through that cleanup path. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index e1bdbffd1cd848..0f3df0951661c7 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -1148,13 +1148,15 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", uartid); ret = -ENODEV; - return ret; + goto out_dma_release; } if (adi_uart4_serial_ports[uartid] == NULL) { uart = devm_kzalloc(dev, sizeof(*uart), GFP_KERNEL); - if (!uart) - return -ENOMEM; + if (!uart) { + ret = -ENOMEM; + goto out_dma_release; + } adi_uart4_serial_ports[uartid] = uart; uart->dev = &pdev->dev; @@ -1261,6 +1263,11 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) out_error: adi_uart4_serial_ports[uartid] = NULL; +out_dma_release: + if (!IS_ERR(tx_dma_channel)) + dma_release_channel(tx_dma_channel); + if (!IS_ERR(rx_dma_channel)) + dma_release_channel(rx_dma_channel); return ret; } From d9a658b88f6234c6c51dbbc9f917fd0844add12c Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Fri, 17 Apr 2026 18:24:38 +0100 Subject: [PATCH 78/85] serial: adi_uart4: validate serial alias id range The driver indexes adi_uart4_serial_ports[] using the serial alias id returned by of_alias_get_id(), but does not verify that the id is within the valid port range. Add a bounds check and reject alias ids greater than or equal to ADI_UART_NR_PORTS before indexing the port array. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index 0f3df0951661c7..ad292aa18c0854 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -1151,6 +1151,12 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) goto out_dma_release; } + if (uartid >= ADI_UART_NR_PORTS) { + dev_err(dev, "serial id %d out of range\n", uartid); + ret = -EINVAL; + goto out_dma_release; + } + if (adi_uart4_serial_ports[uartid] == NULL) { uart = devm_kzalloc(dev, sizeof(*uart), GFP_KERNEL); if (!uart) { From db7c2592b76da89fb9b232039caea268d345e9e6 Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Mon, 27 Apr 2026 16:31:55 +0100 Subject: [PATCH 79/85] serial: adi_uart4: require built in driver for console SERIAL_ADI_UART4_CONSOLE selects SERIAL_EARLYCON, but only depended on SERIAL_ADI_UART4. This allowed the UART driver and serial core to be built as modules while earlycon was built into vmlinux, causing a link failure: earlycon.c:(.init.text+0x1e0): undefined reference to `uart_parse_earlycon' Require SERIAL_ADI_UART4=y for console support so earlycon is only enabled when the ADI UART4 driver and serial core are built into the kernel. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a5bb7b33650c56..b66829cee7cfbe 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -485,7 +485,7 @@ config SERIAL_ADI_UART4 config SERIAL_ADI_UART4_CONSOLE bool "Console on ADI uart4 serial port" - depends on SERIAL_ADI_UART4 + depends on SERIAL_ADI_UART4=y default y select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON From 9485eb8c4fa3b36e1442b63b7a8ad924b8f9b09e Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Wed, 6 May 2026 13:58:44 +0100 Subject: [PATCH 80/85] serial: adi-uart4: avoid jiffies timeout under spin_lock_irqsave() adi_uart4_serial_set_termios() applies new termios settings such as baud rate, parity and stop bits. Before reprogramming the UART registers, it waits for any active TX to complete so the settings are not changed mid transfer. That wait is done while holding a spin lock with interrupts disabled via spin_lock_irqsave(). The driver has a safety mechanism in the loop where it waits for a maximum of 10 ms: timeout = jiffies + msecs_to_jiffies(10); while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) if (time_after(jiffies, timeout)) { dev_warn(port->dev, "timeout waiting for TX buffer empty\n"); break; } However this approach is problematic because on single core systems, jiffies may not advance while interrupts are disabled (jiffies is advanced through timer interrupts), making the timeout approach unreliable if TX does not drain for some reason. To confirm this behaviour on single core systems I wrote a small program to validate if jiffies was being advanced on the ADSP-SC589-MINI and it did not while holding the spin lock. Use readl_poll_timeout_atomic() instead, which is suitable for atomic contexts and is suitable when timekeeping is disabled. This fix also removes the duplicated TX empty wait loop which seems to be an accidental copy+paste issue. Signed-off-by: Qasim Ijaz --- drivers/tty/serial/adi_uart4.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index ad292aa18c0854..df6e438a54c7d9 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -744,7 +745,8 @@ static void adi_uart4_serial_set_termios(struct uart_port *port, unsigned long flags; unsigned int baud, quot; unsigned int ier, lcr = 0; - unsigned long timeout; + u32 val; + int ret; if (uart->hwflow_mode == ADI_UART_HWFLOW_PERI) termios->c_cflag |= CRTSCTS; @@ -818,23 +820,13 @@ static void adi_uart4_serial_set_termios(struct uart_port *port, quot = uart_get_divisor(port, baud); } - /* Wait till the transfer buffer is empty */ - timeout = jiffies + msecs_to_jiffies(10); - while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, - "timeout waiting for TX buffer empty\n"); - break; - } - - /* Wait till the transfer buffer is empty */ - timeout = jiffies + msecs_to_jiffies(10); - while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, - "timeout waiting for TX buffer empty\n"); - break; - } + /* Wait up to 10 ms for any active TX to complete. */ + ret = readl_poll_timeout_atomic(uart->port.membase + OFFSET_CTL, val, + (!(val & UCEN) || + (UART_GET_LSR(uart) & TEMT)), + 1, 10000); + if (ret) + dev_warn(port->dev, "timeout waiting for TX buffer empty\n"); /* Disable UART */ ier = UART_GET_IER(uart); From 34a84fbdfc05dfa4cc722841467e0ffdf674bb3d Mon Sep 17 00:00:00 2001 From: Vasileios Bimpikas Date: Thu, 7 May 2026 10:57:24 +0100 Subject: [PATCH 81/85] serial: adi_uart4: fix NULL dereference and error path resource leaks in startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc-fanalyzer flagged a NULL dereference on `desc` — dmaengine_prep_dma_cyclic() can return NULL and we just blindly assign desc->callback without checking. While fixing that I noticed all the error paths in adi_uart4_serial_startup() are broken too. Every early return after clk_prepare_enable() leaks the clock, and later failures dont clean up DMA buffers or mappings. Fixed by adding goto-based cleanup like upstream does (same pattern as atmel_serial.c). Each failure point now unwinds exactly what was allocated before it. Also added a dev_err so we actually get a log message if cyclic prep fails. Signed-off-by: Vasileios Bimpikas --- drivers/tty/serial/adi_uart4.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index df6e438a54c7d9..32448508bcbe93 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -651,8 +651,10 @@ static int adi_uart4_serial_startup(struct uart_port *port) */ uart->rx_dma_buf.buf = dmam_alloc_coherent(uart->dev, UART_XMIT_SIZE, &uart->rx_dma_phy, GFP_KERNEL); - if (!uart->rx_dma_buf.buf) - return -ENOMEM; + if (!uart->rx_dma_buf.buf) { + ret = -ENOMEM; + goto err_disable_clk; + } uart->rx_dma_buf.head = 0; uart->rx_dma_buf.tail = 0; @@ -666,12 +668,17 @@ static int adi_uart4_serial_startup(struct uart_port *port) ret = dmaengine_slave_config(uart->rx_dma_channel, &dma_config); if (ret) { dev_err(uart->dev, "Error configuring RX DMA channel\n"); - return -EINVAL; + goto err_free_rx_dma_buf; } desc = dmaengine_prep_dma_cyclic(uart->rx_dma_channel, uart->rx_dma_phy, UART_XMIT_SIZE, DMA_RX_XCOUNT, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(uart->dev, "Error preparing RX DMA cyclic\n"); + ret = -ENODEV; + goto err_free_rx_dma_buf; + } desc->callback = adi_uart4_serial_dma_rx; desc->callback_param = uart; @@ -690,8 +697,10 @@ static int adi_uart4_serial_startup(struct uart_port *port) tport->xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); - if (dma_mapping_error(uart->dev, uart->tx_dma_phy)) - return -ENOMEM; + if (dma_mapping_error(uart->dev, uart->tx_dma_phy)) { + ret = -ENOMEM; + goto err_stop_rx_dma; + } uart->port.fifosize = UART_XMIT_SIZE; @@ -705,7 +714,7 @@ static int adi_uart4_serial_startup(struct uart_port *port) ret = dmaengine_slave_config(uart->tx_dma_channel, &dma_config); if (ret) { dev_err(uart->dev, "Error configuring TX DMA channel\n"); - return -EINVAL; + goto err_unmap_tx_dma; } } @@ -717,6 +726,19 @@ static int adi_uart4_serial_startup(struct uart_port *port) UART_SET_IER(uart, ERBFI); return 0; + +err_unmap_tx_dma: + dma_unmap_single(uart->dev, uart->tx_dma_phy, UART_XMIT_SIZE, + DMA_TO_DEVICE); +err_stop_rx_dma: + timer_delete(&uart->rx_dma_timer); + dmaengine_terminate_sync(uart->rx_dma_channel); +err_free_rx_dma_buf: + dmam_free_coherent(uart->dev, UART_XMIT_SIZE, + uart->rx_dma_buf.buf, uart->rx_dma_phy); +err_disable_clk: + clk_disable_unprepare(uart->clk); + return ret; } static void adi_uart4_serial_shutdown(struct uart_port *port) From 8b9437189fff4eb596ef9bbc4ff2a1735e42418d Mon Sep 17 00:00:00 2001 From: Vasileios Bimpikas Date: Thu, 7 May 2026 13:33:36 +0100 Subject: [PATCH 82/85] serial: adi_uart4: fix unbind/rebind crash and resource cleanup - Remove __init from console_setup/get_options. These get called on rebind via register_console and the code was already freed after boot. - Move clk_prepare_enable from startup() to probe() so the clock is on before uart_add_one_port re-registers the console. Without this, console_putchar spins forever on THRE with the clock gated. - Use timer_delete_sync() instead of timer_delete() to avoid use-after-free if the rx timeout handler is still running when we free the DMA buffer. Signed-off-by: Vasileios Bimpikas --- drivers/tty/serial/adi_uart4.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/adi_uart4.c b/drivers/tty/serial/adi_uart4.c index 32448508bcbe93..097c85b67d3350 100644 --- a/drivers/tty/serial/adi_uart4.c +++ b/drivers/tty/serial/adi_uart4.c @@ -640,10 +640,6 @@ static int adi_uart4_serial_startup(struct uart_port *port) uart->tx_done = 1; - ret = clk_prepare_enable(uart->clk); - if (ret) - return ret; - if (!IS_ERR(uart->tx_dma_channel)) { /* RX channel: * - src_addr is not configured because we're attached to @@ -653,7 +649,7 @@ static int adi_uart4_serial_startup(struct uart_port *port) &uart->rx_dma_phy, GFP_KERNEL); if (!uart->rx_dma_buf.buf) { ret = -ENOMEM; - goto err_disable_clk; + return ret; } uart->rx_dma_buf.head = 0; @@ -731,13 +727,11 @@ static int adi_uart4_serial_startup(struct uart_port *port) dma_unmap_single(uart->dev, uart->tx_dma_phy, UART_XMIT_SIZE, DMA_TO_DEVICE); err_stop_rx_dma: - timer_delete(&uart->rx_dma_timer); + timer_delete_sync(&uart->rx_dma_timer); dmaengine_terminate_sync(uart->rx_dma_channel); err_free_rx_dma_buf: dmam_free_coherent(uart->dev, UART_XMIT_SIZE, uart->rx_dma_buf.buf, uart->rx_dma_phy); -err_disable_clk: - clk_disable_unprepare(uart->clk); return ret; } @@ -754,10 +748,8 @@ static void adi_uart4_serial_shutdown(struct uart_port *port) DMA_TO_DEVICE); dmam_free_coherent(uart->dev, UART_XMIT_SIZE, uart->rx_dma_buf.buf, uart->rx_dma_phy); - timer_delete(&uart->rx_dma_timer); + timer_delete_sync(&uart->rx_dma_timer); } - - clk_disable_unprepare(uart->clk); } static void adi_uart4_serial_set_termios(struct uart_port *port, @@ -1011,7 +1003,7 @@ static void adi_uart4_serial_console_putchar(struct uart_port *port, UART_PUT_CHAR(uart, ch); } -static void __init +static void adi_uart4_serial_console_get_options(struct adi_uart4_serial_port *uart, int *baud, int *parity, int *bits) { @@ -1058,7 +1050,7 @@ adi_uart4_serial_console_write(struct console *co, const char *s, } -static int __init +static int adi_uart4_serial_console_setup(struct console *co, char *options) { struct adi_uart4_serial_port *uart; @@ -1275,6 +1267,10 @@ static int adi_uart4_serial_probe(struct platform_device *pdev) uart = adi_uart4_serial_ports[uartid]; uart->port.dev = &pdev->dev; + ret = clk_prepare_enable(uart->clk); + if (ret) + goto out_error; + ret = uart_add_one_port(&adi_uart4_serial_reg, &uart->port); if (!ret) { dev_set_drvdata(&pdev->dev, uart); @@ -1299,6 +1295,7 @@ static void adi_uart4_serial_remove(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, NULL); if (uart) { uart_remove_one_port(&adi_uart4_serial_reg, &uart->port); + clk_disable_unprepare(uart->clk); adi_uart4_serial_ports[uart->port.line] = NULL; if (!IS_ERR(uart->tx_dma_channel)) { From 818d8d9b82458070151afd0df4e472f70f5c0857 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 15 May 2026 14:34:52 +0200 Subject: [PATCH 83/85] [ADI] .github: top-level: Synchronize with xlnx-main Synchronize with a14dd7c6dea23b7d727afd1c3a820e747e5ba832 on xlnx-main including: - Remove paths-ignore for push and pull request triggers - Add the concurrency group with cancel-in-progress: true to optimize resource usage by cancelling redundant runs - Restrict push events to the analogdevicesinc repository owner - Explicitly define read permission for all build and check jobs - Upload to Cloudsmith, which includes SBOM generation - Use the consolidated summary output from dependency jobs - Switch from assert_labels to assert_job_summary - Remove the unused ref_branch input from the checks job Signed-off-by: Philip Molloy --- .github/workflows/top-level.yml | 123 +++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 40 deletions(-) diff --git a/.github/workflows/top-level.yml b/.github/workflows/top-level.yml index ccc28d9a448d4a..550c8f4cdcc489 100644 --- a/.github/workflows/top-level.yml +++ b/.github/workflows/top-level.yml @@ -5,29 +5,26 @@ on: push: branches: - 'adsp-[0-9]+.[0-9]+*-y' - paths-ignore: - - '.github/**' - - 'ci/**' - - 'docs/**' pull_request: - branches: - - 'adsp-[0-9]+.[0-9]+*-y' - paths-ignore: - - '.github/**' - - 'ci/**' - - 'docs/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true jobs: checks: uses: analogdevicesinc/linux/.github/workflows/checks.yml@ci + if: (github.event_name != 'push' || github.repository_owner == 'analogdevicesinc') secrets: inherit - with: - ref_branch: "mirror/next/linux-next/master" + permissions: + contents: read build_gcc_x86_64: uses: analogdevicesinc/linux/.github/workflows/build.yml@ci needs: [checks] if: needs.checks.outputs.fatal != 'true' secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "x86" @@ -37,6 +34,8 @@ jobs: needs: [checks] if: needs.checks.outputs.fatal != 'true' secrets: inherit + permissions: + contents: read with: compiler: "llvm" arch: "x86" @@ -44,17 +43,20 @@ jobs: checks: true build_gcc_aarch64: uses: analogdevicesinc/linux/.github/workflows/build.yml@ci - needs: [checks] - if: needs.checks.outputs.fatal != 'true' + if: (github.event_name != 'push' || github.repository_owner == 'analogdevicesinc') secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm64" defconfig: "adi_ci_defconfig" build_gcc_arm: uses: analogdevicesinc/linux/.github/workflows/build.yml@ci - needs: [checks] + if: (github.event_name != 'push' || github.repository_owner == 'analogdevicesinc') secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm" @@ -62,6 +64,8 @@ jobs: checks: true assert_checks: runs-on: [self-hosted, repo-only] + permissions: + contents: read needs: - checks - build_gcc_x86_64 @@ -72,25 +76,38 @@ jobs: steps: - name: Assert env: - job_warn_checks: ${{needs.checks.outputs.warn}} - job_warn_build_gcc_x86_64: ${{needs.build_gcc_x86_64.outputs.warn}} - job_warn_build_llvm_x86_64: ${{needs.build_llvm_x86_64.outputs.warn}} - job_warn_build_gcc_aarch64: ${{needs.build_gcc_aarch64.outputs.warn}} - job_warn_build_gcc_arm: ${{needs.build_gcc_arm.outputs.warn}} - job_fail_checks: ${{needs.checks.outputs.fail}} - job_fail_build_gcc_x86_64: ${{needs.build_gcc_x86_64.outputs.fail}} - job_fail_build_llvm_x86_64: ${{needs.build_llvm_x86_64.outputs.fail}} - job_fail_build_gcc_aarch64: ${{needs.build_gcc_aarch64.outputs.fail}} - job_fail_build_gcc_arm: ${{needs.build_gcc_arm.outputs.fail}} + job_checks: ${{needs.checks.outputs.summary}} + job_build_gcc_x86_64: ${{needs.build_gcc_x86_64.outputs.summary}} + job_build_llvm_x86_64: ${{needs.build_llvm_x86_64.outputs.summary}} + job_build_gcc_aarch64: ${{needs.build_gcc_aarch64.outputs.summary}} + job_build_gcc_arm: ${{needs.build_gcc_arm.outputs.summary}} run: | curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -L -o runner_env.sh \ https://raw.githubusercontent.com/analogdevicesinc/linux/ci/ci/runner_env.sh source ./runner_env.sh - assert_labels + assert_job_summary + + deploy_cloudsmith_checks: + needs: [assert_checks] + if: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} + uses: analogdevicesinc/linux/.github/workflows/upload-to-cloudsmith.yml@ci + secrets: + CLOUDSMITH_SERVICE_SLUG: ${{ secrets.CLOUDSMITH_SERVICE_SLUG }} + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} + permissions: + id-token: write + contents: write + actions: read + with: + artifacts: > + adi_ci_defconfig-* + build_gcc_arm_sc573-ezlite_defconfig: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm" @@ -100,6 +117,8 @@ jobs: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm" @@ -109,6 +128,8 @@ jobs: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm" @@ -118,6 +139,8 @@ jobs: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm" @@ -127,6 +150,8 @@ jobs: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm64" @@ -136,13 +161,17 @@ jobs: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit + permissions: + contents: read with: compiler: "gcc" arch: "arm64" defconfig: "sc598-som-ezlite_defconfig" auto_from_range: false - assert_build_adsp: + assert_build: runs-on: [self-hosted, repo-only] + permissions: + contents: read needs: - build_gcc_arm_sc573-ezlite_defconfig - build_gcc_arm_sc589-mini_defconfig @@ -153,20 +182,34 @@ jobs: steps: - name: Assert env: - job_warn_build_gcc_arm_sc573-ezlite_defconfig: ${{needs.build_gcc_arm_sc573-ezlite_defconfig.outputs.warn}} - job_warn_build_gcc_arm_sc589-mini_defconfig: ${{needs.build_gcc_arm_sc589-mini_defconfig.outputs.warn}} - job_warn_build_gcc_arm_sc594-som-ezkit_defconfig: ${{needs.build_gcc_arm_sc594-som-ezkit_defconfig.outputs.warn}} - job_warn_build_gcc_arm_sc594-som-ezlite_defconfig: ${{needs.build_gcc_arm_sc594-som-ezlite_defconfig.outputs.warn}} - job_warn_build_gcc_aarch64_sc598-som-ezkit_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezkit_defconfig.outputs.warn}} - job_warn_build_gcc_aarch64_sc598-som-ezlite_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezlite_defconfig.outputs.warn}} - job_fail_build_gcc_arm_sc573-ezlite_defconfig: ${{needs.build_gcc_arm_sc573-ezlite_defconfig.outputs.fail}} - job_fail_build_gcc_arm_sc589-mini_defconfig: ${{needs.build_gcc_arm_sc589-mini_defconfig.outputs.fail}} - job_fail_build_gcc_arm_sc594-som-ezkit_defconfig: ${{needs.build_gcc_arm_sc594-som-ezkit_defconfig.outputs.fail}} - job_fail_build_gcc_arm_sc594-som-ezlite_defconfig: ${{needs.build_gcc_arm_sc594-som-ezlite_defconfig.outputs.fail}} - job_fail_build_gcc_aarch64_sc598-som-ezkit_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezkit_defconfig.outputs.fail}} - job_fail_build_gcc_aarch64_sc598-som-ezlite_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezlite_defconfig.outputs.fail}} + job_build_gcc_arm_sc573-ezlite_defconfig: ${{needs.build_gcc_arm_sc573-ezlite_defconfig.outputs.summary}} + job_build_gcc_arm_sc589-mini_defconfig: ${{needs.build_gcc_arm_sc589-mini_defconfig.outputs.summary}} + job_build_gcc_arm_sc594-som-ezkit_defconfig: ${{needs.build_gcc_arm_sc594-som-ezkit_defconfig.outputs.summary}} + job_build_gcc_arm_sc594-som-ezlite_defconfig: ${{needs.build_gcc_arm_sc594-som-ezlite_defconfig.outputs.summary}} + job_build_gcc_aarch64_sc598-som-ezkit_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezkit_defconfig.outputs.summary}} + job_build_gcc_aarch64_sc598-som-ezlite_defconfig: ${{needs.build_gcc_aarch64_sc598-som-ezlite_defconfig.outputs.summary}} run: | curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -L -o runner_env.sh \ https://raw.githubusercontent.com/analogdevicesinc/linux/ci/ci/runner_env.sh source ./runner_env.sh - assert_labels + assert_job_summary + + deploy_cloudsmith_build: + needs: [assert_build] + if: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} + uses: analogdevicesinc/linux/.github/workflows/upload-to-cloudsmith.yml@ci + secrets: + CLOUDSMITH_SERVICE_SLUG: ${{ secrets.CLOUDSMITH_SERVICE_SLUG }} + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} + permissions: + id-token: write + contents: write + actions: read + with: + artifacts: > + sc573-ezlite_defconfig-* + sc589-mini_defconfig-* + sc594-som-ezkit_defconfig-* + sc594-som-ezlite_defconfig-* + sc598-som-ezkit_defconfig-* + sc598-som-ezlite_defconfig-* From fdb4c1106aa5ad8aa0bec0e225fe2fe942fec7e4 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 19 May 2026 12:31:16 +0200 Subject: [PATCH 84/85] [ADI] .github: top-level: Remove generic x86 builds Signed-off-by: Philip Molloy --- .github/workflows/top-level.yml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/.github/workflows/top-level.yml b/.github/workflows/top-level.yml index 550c8f4cdcc489..47f9bcedc55688 100644 --- a/.github/workflows/top-level.yml +++ b/.github/workflows/top-level.yml @@ -18,29 +18,6 @@ jobs: secrets: inherit permissions: contents: read - build_gcc_x86_64: - uses: analogdevicesinc/linux/.github/workflows/build.yml@ci - needs: [checks] - if: needs.checks.outputs.fatal != 'true' - secrets: inherit - permissions: - contents: read - with: - compiler: "gcc" - arch: "x86" - defconfig: "adi_ci_defconfig" - build_llvm_x86_64: - uses: analogdevicesinc/linux/.github/workflows/build.yml@ci - needs: [checks] - if: needs.checks.outputs.fatal != 'true' - secrets: inherit - permissions: - contents: read - with: - compiler: "llvm" - arch: "x86" - defconfig: "adi_ci_defconfig" - checks: true build_gcc_aarch64: uses: analogdevicesinc/linux/.github/workflows/build.yml@ci if: (github.event_name != 'push' || github.repository_owner == 'analogdevicesinc') @@ -68,8 +45,6 @@ jobs: contents: read needs: - checks - - build_gcc_x86_64 - - build_llvm_x86_64 - build_gcc_aarch64 - build_gcc_arm @@ -77,8 +52,6 @@ jobs: - name: Assert env: job_checks: ${{needs.checks.outputs.summary}} - job_build_gcc_x86_64: ${{needs.build_gcc_x86_64.outputs.summary}} - job_build_llvm_x86_64: ${{needs.build_llvm_x86_64.outputs.summary}} job_build_gcc_aarch64: ${{needs.build_gcc_aarch64.outputs.summary}} job_build_gcc_arm: ${{needs.build_gcc_arm.outputs.summary}} run: | From e99cb64e435f8036de662336914487f103d1283f Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 19 May 2026 12:32:59 +0200 Subject: [PATCH 85/85] [ADI] .github: top-level: Run checks on SC598 EZ-KIT build The generic arm and arm64 builds that intelligently enable Kconfig options based on what is changed fail because of issues with how certain ADSP drivers call into arch specific code. To not block all ADSP defconfigs from building make the SC598 EZ-KIT build independent and enable checks for it rather than the generic ARM build. Signed-off-by: Philip Molloy --- .github/workflows/top-level.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/top-level.yml b/.github/workflows/top-level.yml index 47f9bcedc55688..724b5fd161cd53 100644 --- a/.github/workflows/top-level.yml +++ b/.github/workflows/top-level.yml @@ -30,6 +30,7 @@ jobs: defconfig: "adi_ci_defconfig" build_gcc_arm: uses: analogdevicesinc/linux/.github/workflows/build.yml@ci + needs: [checks] if: (github.event_name != 'push' || github.repository_owner == 'analogdevicesinc') secrets: inherit permissions: @@ -38,7 +39,6 @@ jobs: compiler: "gcc" arch: "arm" defconfig: "adi_ci_defconfig" - checks: true assert_checks: runs-on: [self-hosted, repo-only] permissions: @@ -120,7 +120,6 @@ jobs: defconfig: "sc594-som-ezlite_defconfig" auto_from_range: false build_gcc_aarch64_sc598-som-ezkit_defconfig: - needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci secrets: inherit permissions: @@ -130,6 +129,7 @@ jobs: arch: "arm64" defconfig: "sc598-som-ezkit_defconfig" auto_from_range: false + checks: true build_gcc_aarch64_sc598-som-ezlite_defconfig: needs: [assert_checks] uses: analogdevicesinc/linux/.github/workflows/build.yml@ci