From cfffc69534dc7e7ced2465dcb9b9518f372ac6a8 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 17 Sep 2025 12:37:52 +0200 Subject: [PATCH 01/44] 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 6c6d11536b42ec..c38ce553bc7b9b 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -292,6 +292,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 2fec6bd981872e98dd9790694393a0d5c47b259c Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:17:26 +0200 Subject: [PATCH 02/44] 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 ead08c003444f5c8b332f21957e3e91a67f77f7a Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:10:40 +0200 Subject: [PATCH 03/44] clock: Add driver for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/clk/Kconfig | 9 + 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 ++++++++++++++++ 5 files changed, 645 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/Kconfig b/drivers/clk/Kconfig index 299bc678ed1b9f..70d1c86de01fd1 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -42,6 +42,15 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config COMMON_CLK_ADI_SC598 + bool "Clock driver for ADI SC598 SoCs" + depends on OF && ARCH_SC59X_64 + help + This driver supports the system clocks on Analog Devices SC598-series + SoCs. It includes CGU and CDU clocks and supports gating unused clocks. + Modifying PLL configuration is not supported; that must be done prior + to booting the kernel. Clock dividers after the PLLs may be configured. + config CLK_HSDK bool "PLL Driver for HSDK platform" depends on ARC_SOC_HSDK || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fb8878a5d7d93d..6c1d6243afba2a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -94,6 +94,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..640b9aa101d4f0 --- /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 (IS_ERR(cgu0)) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (IS_ERR(cgu1)) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (IS_ERR(cdu)) { + pr_err("Unable to remap CDU address (resource 2)\n"); + return; + } + + pll3 = of_iomap(np, 3); + if (IS_ERR(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 9a513c8ac0f430c47de9905c8f679f67f5e33e38 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 17 Sep 2025 12:38:19 +0200 Subject: [PATCH 04/44] 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 4cf4d1b0869c4bf1bcb8bcf2181a5ed6a60c6970 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 16:57:41 +0200 Subject: [PATCH 05/44] 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 d9ec1e69e42831..52c5522c105081 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 @@ -464,7 +470,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 ad6a03c052ec4a..e1a349ebcce908 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 449db7ed6bf05dda85affc21817fa5e455fac229 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 17:55:15 +0200 Subject: [PATCH 06/44] 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 22743785299eda..8e9414e098e572 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -89,5 +89,7 @@ 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 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 8fc72661ce1dfa1371746b199c42abda8a48313f Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:00:40 +0200 Subject: [PATCH 07/44] spi: Add v3 SPI controller support for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-adi.c | 876 ++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spidev.c | 16 +- 4 files changed, 892 insertions(+), 7 deletions(-) create mode 100644 drivers/spi/spi-adi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 82379721740480..c46a9917ce9bf7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -192,6 +192,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 a9b1bc259b68d1..76db334c8a2710 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -133,6 +133,7 @@ obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.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_QSPI) += spi-stm32-qspi.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o diff --git a/drivers/spi/spi-adi.c b/drivers/spi/spi-adi.c new file mode 100644 index 00000000000000..3dc8345e29ca55 --- /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_master(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 (!controller->dma_tx) { + dev_err(dev, "Could not get TX DMA channel\n"); + return -ENOENT; + } + + controller->dma_rx = dma_request_chan(dev, "rx"); + if (!controller->dma_rx) { + dev_err(dev, "Could not get RX DMA channel\n"); + ret = -ENOENT; + 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 653f82984216c3..3dc0a4e6265d56 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -75,7 +75,7 @@ struct spidev_data { /* TX/RX buffers are NULL unless this device is open (users > 0) */ struct mutex buf_lock; - unsigned users; + unsigned int users; u8 *tx_buffer; u8 *rx_buffer; u32 speed_hz; @@ -84,7 +84,7 @@ struct spidev_data { static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); -static unsigned bufsiz = 4096; +static unsigned int bufsiz = 4096; module_param(bufsiz, uint, S_IRUGO); MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); @@ -208,13 +208,13 @@ spidev_write(struct file *filp, const char __user *buf, } static int spidev_message(struct spidev_data *spidev, - struct spi_ioc_transfer *u_xfers, unsigned n_xfers) + struct spi_ioc_transfer *u_xfers, unsigned int n_xfers) { struct spi_message msg; struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; - unsigned n, total, tx_total, rx_total; + unsigned int n, total, tx_total, rx_total; u8 *tx_buf, *rx_buf; int status = -EFAULT; @@ -330,7 +330,7 @@ static int spidev_message(struct spidev_data *spidev, static struct spi_ioc_transfer * spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, - unsigned *n_ioc) + unsigned int *n_ioc) { u32 tmp; @@ -359,7 +359,7 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct spi_device *spi; struct spi_controller *ctlr; u32 tmp; - unsigned n_ioc; + unsigned int n_ioc; struct spi_ioc_transfer *ioc; /* Check type and command number */ @@ -525,7 +525,7 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd, int retval = 0; struct spidev_data *spidev; struct spi_device *spi; - unsigned n_ioc, n; + unsigned int n_ioc, n; struct spi_ioc_transfer *ioc; u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg); @@ -711,6 +711,7 @@ static const struct spi_device_id spidev_spi_ids[] = { { .name = "spi-authenta" }, { .name = "em3581" }, { .name = "si3210" }, + { .name = "generic-spidev" }, {}, }; MODULE_DEVICE_TABLE(spi, spidev_spi_ids); @@ -741,6 +742,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 f03a38531167792bad87e52e25196996f3079203 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:06:41 +0200 Subject: [PATCH 08/44] 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 d93cd4f722b401..dd746c0c1a5291 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -147,6 +147,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" depends on OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1429e8c0229b92..3584ab8c08344d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -24,6 +24,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 4b3f6c84bf69fff35b518c7df68ccd603551b936 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:08:50 +0200 Subject: [PATCH 09/44] 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 6b3ba7e5723aa1..5cb9063957cfc8 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -470,7 +470,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 @@ -518,6 +518,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 ecc07c50f2a0fe..1a89ed96088dbf 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -48,6 +48,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_CPM) += i2c-cpm.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 f201db07f66bd1f25c9a8f7439c051967277afbf Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Mon, 15 Sep 2025 18:12:03 +0200 Subject: [PATCH 10/44] serial: Add UART driver for SC5xx SoCs Signed-off-by: Philip Molloy --- drivers/tty/serial/Kconfig | 19 +- drivers/tty/serial/Makefile | 1 + drivers/tty/serial/adi_uart4.c | 1380 ++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1402 insertions(+), 1 deletion(-) create mode 100644 drivers/tty/serial/adi_uart4.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 28e4beeabf8f37..d20d0b06be3949 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -471,6 +471,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 @@ -771,7 +788,7 @@ config SERIAL_CPM depends on CPM2 || CPM1 select SERIAL_CORE help - This driver supports the SCC and SMC serial ports on Motorola + This driver supports the SCC and SMC serial ports on Motorola embedded PowerPC that contain a CPM1 (8xx) or CPM2 (8xxx) config SERIAL_CPM_CONSOLE diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 6ff74f0a9530c4..a7c05acee57e1c 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..9389b7723ff0de --- /dev/null +++ b/drivers/tty/serial/adi_uart4.c @@ -0,0 +1,1380 @@ +// 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); + uart->port.icount.tx += uart->tx_count; + + if (!kfifo_is_empty(&tport->xmit_fifo)) { + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + } + + /* + * Advance fifo to match the dma write + */ + uart_xmit_advance(&uart->port, uart->tx_count); + 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); + del_timer(&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; + kfree(uart); + if (IS_ERR(uart->tx_dma_channel)) + return; + + dma_release_channel(uart->tx_dma_channel); + dma_release_channel(uart->rx_dma_channel); + } +} + +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 ee43182510fb29d2709ee1f90c4d00801e5b1816 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 11:54:23 +0200 Subject: [PATCH 11/44] 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 d82bcab233a1b0..94a58ceaa71894 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -84,6 +84,15 @@ config ALPINE_MSI select PCI_MSI 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 e3679ec2b9f76e..a6bd5bbe89979a 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 1a00283a24a3da8f0a779aa6e45148505f9a0638 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 12:56:22 +0200 Subject: [PATCH 12/44] 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..03408eeb460b3e --- /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: + - | + 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"; + }; + + - | + 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"; + }; + + - | + clk: clocks@0x3108d000 { + 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"; + }; + + - | + clk: clocks@0x3108d000 { + 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 1da565cd2a56a3aba0a287920d23faac9382214d Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:19:21 +0200 Subject: [PATCH 13/44] 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 a249bb67fdbbc0f44145d19e40623c7c61c27be5 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:26:15 +0200 Subject: [PATCH 14/44] pinctrl: Add support for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/pinctrl/Kconfig | 2 + drivers/pinctrl/Makefile | 2 + drivers/pinctrl/adi/Kconfig | 15 + drivers/pinctrl/adi/Makefile | 3 + drivers/pinctrl/adi/pinctrl-adsp.c | 915 +++++++++++++++++++++++++ include/dt-bindings/pinctrl/adi-adsp.h | 25 + 6 files changed, 962 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 354536de564b67..e58333118371d9 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -587,6 +587,8 @@ config PINCTRL_MLXBF3 each pin. This driver can also be built as a module called pinctrl-mlxbf3. + +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 97823f52b972a3..2640fcf06c498a 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -57,6 +57,8 @@ obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.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..337e4ddc3b6d59 --- /dev/null +++ b/drivers/pinctrl/adi/Kconfig @@ -0,0 +1,15 @@ +# 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. \ No newline at end of file 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 2dbe28387b215c282ff5090efa9bbbcf15b6085e Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 13:51:10 +0200 Subject: [PATCH 15/44] pinctrl: Add SRU control driver for ADSP-SC5xx Signed-off-by: Philip Molloy --- drivers/pinctrl/adi/Kconfig | 16 +- 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, 2490 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 337e4ddc3b6d59..fc214de653d6e9 100644 --- a/drivers/pinctrl/adi/Kconfig +++ b/drivers/pinctrl/adi/Kconfig @@ -6,10 +6,24 @@ 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) select PINCTRL_ADSP help Say Y here to enable the ADSP-SC5XX pinctrl driver. This is required for - correct peripheral functionality on the SoC. \ No newline at end of file + 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. \ No newline at end of file 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 e3683d5539f0aeadc3d74865b2de29e0492d022a Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 17:27:05 +0200 Subject: [PATCH 16/44] 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 202397be76d803..90dab448a8a6c6 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" @@ -1115,7 +1117,6 @@ config HZ_500 config HZ_1000 bool "1000 Hz" - endchoice config HZ @@ -1501,7 +1502,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 @@ -1643,7 +1644,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 aafebf145738ab..3810b5f0c4003d 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 eebf57e6264569da66f9f7fd67da45c24b465fd5 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Tue, 16 Sep 2025 17:31:41 +0200 Subject: [PATCH 17/44] soc: adi: Add initial support for SC5xx SoCs Signed-off-by: Philip Molloy --- drivers/soc/Makefile | 1 + drivers/soc/adi/Makefile | 10 + drivers/soc/adi/mach-sc59x/Kconfig | 77 +++++++ 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 | 342 ++++++++++++++++++++++++++++ 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 +++++++++++++++++++ 13 files changed, 1541 insertions(+) create mode 100644 drivers/soc/adi/Makefile create mode 100644 drivers/soc/adi/mach-sc59x/Kconfig 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 2037a8695cb289..d1787d9a748c73 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/Kconfig b/drivers/soc/adi/mach-sc59x/Kconfig new file mode 100644 index 00000000000000..52eb7cc5731d58 --- /dev/null +++ b/drivers/soc/adi/mach-sc59x/Kconfig @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)-or-later + +menu "sc59x 64-bit platform type" + depends on ARCH_SC59X_64 + +config ARCH_SC59X_SLAVECORE_COUNT + int "Slave core count in sc59x" + range 0 1 + default 1 + help + This sets the slave core count in sc59x processor. + +choice + prompt "SC59X Core Clock Divider" + default SC59X_CCLK_DIV_2 + help + This sets the frequency of the core. It can be 1, 2, 4 or 8 + Core Frequency = (PLL frequency) / (this setting) + +config SC59X_CCLK_DIV_1 + bool "1" + +config SC59X_CCLK_DIV_2 + bool "2" + +config SC59X_CCLK_DIV_4 + bool "4" + +config SC59X_CCLK_DIV_8 + bool "8" +endchoice + +config SCLK_DIV + int "System Clock Divider" + range 1 15 + default 4 + help + This sets the frequency of the system clock and provides the + source from which SCLK0 and SCLK1 are derived. + This can be between 1 and 15 + System Clock = (PLL frequency) / (this setting) + +config SCLK0_DIV + int "System Clock0 Divider" + range 1 15 + default 4 + help + This sets the frequency of the system clock0. + This can be between 1 and 15 + System Clock0 = (System Clock) / (this setting) + +config SCLK1_DIV + int "System Clock1 Divider" + range 1 15 + default 2 + help + This sets the frequency of the system clock1. + This can be between 1 and 15 + System Clock1 = (System Clock) / (this setting) + +config ARCH_HEADER_IN_MACH + bool + default y + help + Define the architecture specific head file path for + ADI genenric peripheral drivers. + +config MACH_SC59X_DT + bool "Support sc59x platforms from device tree" + default y + select USE_OF + help + Include support for sc59x based platforms using the device tree for + discovery. Note: This is under development and not all peripherals + can be supported with this machine file + +endmenu 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..1d994f9a0f0960 --- /dev/null +++ b/drivers/soc/adi/mach-sc5xx/rcu.c @@ -0,0 +1,342 @@ +// 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) + 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 e735589627f5a1ee919f7f456ff7d4fd5c576f7d Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 16:23:47 +0200 Subject: [PATCH 18/44] 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 aeab9c82472471d7d37c188db681fecb4ec20dab Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 15:55:37 +0200 Subject: [PATCH 19/44] 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 8440a852851150769d79b4f47e8a2c27701accec Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 26 May 2025 11:17:28 +0100 Subject: [PATCH 20/44] 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 f05ea67821f63f4b66b2f1c4e69e6315c677933d Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 26 May 2025 12:01:12 +0100 Subject: [PATCH 21/44] 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..c4f7ec4b9456e5 --- /dev/null +++ b/arch/arm/mach-sc5xx/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +obj-y := 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 c76215287061c96a904095f16d462ee25a6ebaea Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 12 Sep 2025 12:28:05 +0200 Subject: [PATCH 22/44] clk: adi: Add clock driver for ADSP-SC594 Signed-off-by: Philip Molloy --- arch/arm/mach-sc5xx/Kconfig | 2 - drivers/clk/Kconfig | 1 + drivers/clk/adi/Kconfig | 16 +++ drivers/clk/adi/Makefile | 2 + drivers/clk/adi/clk-adi-sc594.c | 231 ++++++++++++++++++++++++++++++++ 5 files changed, 250 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 70d1c86de01fd1..35f47f118851b3 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -484,6 +484,7 @@ config COMMON_CLK_SP7021 by the driver. 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..9fc4177f0ff37a --- /dev/null +++ b/drivers/clk/adi/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC594 + bool "ADI SC594 clock driver" + 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" + 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..4d1450eb8f4259 --- /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 (IS_ERR(cgu0)) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (IS_ERR(cgu1)) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (IS_ERR(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 83edad101d7a5a083d1129e253331f6456b0dbed Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 13:52:34 +0100 Subject: [PATCH 23/44] clk: adi: Add clock driver for ADSP-SC589 Only supports the ADZS-SC589-MINI Signed-off-by: Utsav Agarwal --- drivers/clk/adi/Kconfig | 8 ++ drivers/clk/adi/Makefile | 1 + drivers/clk/adi/clk-adi-sc58x.c | 217 ++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 drivers/clk/adi/clk-adi-sc58x.c diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index 9fc4177f0ff37a..5d1ea7cc87f80e 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -1,4 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC58X + bool "ADI SC589 clock driver" + 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" help 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..06bf9afc9d3d55 --- /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 (IS_ERR(cgu0)) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (IS_ERR(cgu1)) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (IS_ERR(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 3a79b6fc3e5f50f83428032fba4779080c98f632 Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:19:57 +0100 Subject: [PATCH 24/44] clk: adi: Add clock driver for ADSP-SC573 Signed-off-by: UtsavAgarwalADI --- drivers/clk/adi/Kconfig | 8 ++ drivers/clk/adi/Makefile | 1 + drivers/clk/adi/clk-adi-sc57x.c | 202 ++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 drivers/clk/adi/clk-adi-sc57x.c diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig index 5d1ea7cc87f80e..1e3be7f2603c95 100644 --- a/drivers/clk/adi/Kconfig +++ b/drivers/clk/adi/Kconfig @@ -1,4 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_ADI_SC57X + bool "ADI SC573 clock driver" + 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" help 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..65460445a0e6af --- /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 (IS_ERR(cgu0)) { + pr_err("Unable to remap CGU0 address (resource 0)\n"); + return; + } + + cgu1 = of_iomap(np, 1); + if (IS_ERR(cgu1)) { + pr_err("Unable to remap CGU1 address (resource 1)\n"); + return; + } + + cdu = of_iomap(np, 2); + if (IS_ERR(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 cf5590d35e10f4bade5ea663b04856f9a0c14113 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 12 Sep 2025 13:30:36 +0200 Subject: [PATCH 25/44] 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 05cc07b8f48c03..067bb0b2e7d6d3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -57,6 +57,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 c2f0e91f6bf83d..36f520baaadcea 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..19d8c55a201811 --- /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_new = 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 d3b008214f3533ee66dcc95c56edfb01f96c31cd Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Fri, 29 Mar 2024 16:00:10 +0000 Subject: [PATCH 26/44] 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 955e4e38477e6f..b4de5f19ff4b71 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 @@ -377,6 +377,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 5ff4e2fee4abd3..79ce0b9c164459 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.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 267ed73ed79b3ad01e19fff49861cb7bbbe0fec6 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Thu, 4 Apr 2024 16:23:33 +0100 Subject: [PATCH 27/44] 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 --- 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 3fe7e2a9bd294d..6e6abfdc65d871 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -628,4 +628,5 @@ source "drivers/misc/uacce/Kconfig" source "drivers/misc/pvpanic/Kconfig" source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" +source "drivers/misc/adi/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a9f94525e1819d..e2578f75dbe3de 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -72,3 +72,4 @@ obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_NSM) += nsm.o obj-$(CONFIG_MARVELL_CN10K_DPI) += mrvl_cn10k_dpi.o obj-y += keba/ +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..c45bae643778ff --- /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_new = 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 3aaa56d2a24b58347e8b3dd62e9414f55bd6df70 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Tue, 9 Apr 2024 11:37:44 +0100 Subject: [PATCH 28/44] 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 49a66f77cdc5ab27ba2d21e19802fd9e0a834e2a Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 12 Sep 2025 13:31:21 +0200 Subject: [PATCH 29/44] 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 Signed-off-by: Utsav Agarwal --- include/sound/sc5xx-dai.h | 212 ++++ sound/soc/adi/Kconfig | 97 ++ sound/soc/adi/Makefile | 14 +- sound/soc/adi/axi-i2s.c | 5 +- sound/soc/adi/axi-spdif.c | 5 +- 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 | 10 +- sound/soc/codecs/adau1962-i2c.c | 67 ++ sound/soc/codecs/adau1962.c | 836 +++++++++++++++ sound/soc/codecs/adau1962.h | 37 + 28 files changed, 7369 insertions(+), 9 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 0236dc5b4e9f0f..3d871807627274 100644 --- a/sound/soc/adi/Kconfig +++ b/sound/soc/adi/Kconfig @@ -19,3 +19,100 @@ config SND_SOC_ADI_AXI_SPDIF select REGMAP_MMIO 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. 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..950726df973545 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -161,7 +161,6 @@ static struct snd_soc_dai_driver axi_i2s_dai = { static const struct snd_soc_component_driver axi_i2s_component = { .name = "axi-i2s", - .legacy_dai_naming = 1, }; static const struct regmap_config axi_i2s_regmap_config = { @@ -274,11 +273,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/axi-spdif.c b/sound/soc/adi/axi-spdif.c index 5581134201a36a..e75911e794e11b 100644 --- a/sound/soc/adi/axi-spdif.c +++ b/sound/soc/adi/axi-spdif.c @@ -167,7 +167,6 @@ static struct snd_soc_dai_driver axi_spdif_dai = { static const struct snd_soc_component_driver axi_spdif_component = { .name = "axi-spdif", - .legacy_dai_naming = 1, }; static const struct regmap_config axi_spdif_regmap_config = { @@ -239,11 +238,13 @@ static int axi_spdif_probe(struct platform_device *pdev) return ret; } -static void axi_spdif_dev_remove(struct platform_device *pdev) +static int axi_spdif_dev_remove(struct platform_device *pdev) { struct axi_spdif *spdif = platform_get_drvdata(pdev); clk_disable_unprepare(spdif->clk); + + return 0; } static const struct of_device_id axi_spdif_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..40b13e296dcae9 --- /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_CBM_CFM; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + slots = 4; + rx_mask = 0xf; + break; + case 8: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + 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_CBM_CFM; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + slots = 4; + rx_mask = 0xf; + break; + case 8: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + 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_CBM_CFM; + break; + case 1: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + slots = 16; + rx_mask = 0x1; + break; + case 4: /* TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + 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_CBM_CFM; + 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_CBM_CFM, + }, +#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..a73b2cbc1919f8 --- /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_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + 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 7092842480ef17..fd4a763b23e869 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -522,6 +522,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 54cbc3feae3277..5d79e09b08e048 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 @@ -79,7 +81,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o snd-soc-cs35l56-i2c-y := cs35l56-i2c.o snd-soc-cs35l56-spi-y := cs35l56-spi.o snd-soc-cs35l56-sdw-y := cs35l56-sdw.o -snd-soc-cs40l50-objs := cs40l50-codec.o +snd-soc-cs40l50-y := cs40l50-codec.o snd-soc-cs42l42-y := cs42l42.o snd-soc-cs42l42-i2c-y := cs42l42-i2c.o snd-soc-cs42l42-sdw-y := cs42l42-sdw.o @@ -324,8 +326,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o snd-soc-wcd9335-y := wcd9335.o snd-soc-wcd934x-y := wcd934x.o -snd-soc-wcd937x-objs := wcd937x.o -snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o +snd-soc-wcd937x-y := wcd937x.o +snd-soc-wcd937x-sdw-y := wcd937x-sdw.o snd-soc-wcd938x-y := wcd938x.o snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o @@ -430,6 +432,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..cee818ebeaa559 --- /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_CBS_CFS: + adau1962->master = false; + break; + case SND_SOC_DAIFMT_CBM_CFM: + 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 08e3feeb85a081d25ff37a6b9546cddd8dc9c282 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Thu, 11 Apr 2024 11:17:15 +0100 Subject: [PATCH 30/44] 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 684b9fe84fff5b..53b373a51d6805 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -817,6 +817,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 ab6f2b41e38e64..be1c16310edb27 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -99,6 +99,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 b29f0b6755cf3ddbbcf8b907d5060822c7812659 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 23 May 2025 14:23:16 +0100 Subject: [PATCH 31/44] 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 dd746c0c1a5291..2d14b6edf5da53 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -151,6 +151,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 373f8f5053340d577b3da38dbb7c08d078bb8e47 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Tue, 15 Jul 2025 15:52:39 +0100 Subject: [PATCH 32/44] 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. Signed-off-by: Utsav Agarwal --- .../devicetree/bindings/usb/adi,musb.yaml | 83 ++++ 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, 539 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..51f932a2e0c49a --- /dev/null +++ b/Documentation/devicetree/bindings/usb/adi,musb.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/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: 1 + + interrupt-names: + maxItems: 1 + + spu_securep_id: + $ref: /schemas/types.yaml#/definitions/uint32 + + mentor,multipoint: + $ref: /schemas/types.yaml#/definitions/uint32 + + mentor,num-eps: + $ref: /schemas/types.yaml#/definitions/uint32 + + mentor,ram-bits: + maxItems: 1 + + mentor,power: + $ref: /schemas/types.yaml#/definitions/uint32 + + 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: + - | + 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 9a8cf3de061700..c5efadbb0aa060 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -104,6 +104,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 @@ -145,7 +151,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..02d9d4dee3588e --- /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_new = 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 ab1b039952cd8bd52f59128fb0544900eaf7809f Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 16:35:03 +0200 Subject: [PATCH 33/44] 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 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 21cd3a87f38530..225fc22eb9fc01 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..928a4e24b9b8b3 --- /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 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..61a160a4bbaf55 --- /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_HIGH>; + output-low; + line-name = "eeprom-en"; + }; + + pushbutton { + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + 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_HIGH>; + 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_HIGH>; + output-high; + 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..347be49f82d4e3 --- /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_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"; + }; + + 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 51730e5b0491f53eb5dcd8727c9589bfae0f9f9e Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 11 Jul 2025 13:27:47 +0100 Subject: [PATCH 34/44] 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 5ec5bcac6b1348fe8ef5d29a6695bb55e5995f99 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 11:11:56 +0100 Subject: [PATCH 35/44] ARM: dts: adi: sc594: add device tree Signed-off-by: Utsav Agarwal --- 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..fff7c3db0fb35e --- /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_HIGH>; + output-low; + line-name = "eeprom-en"; + }; + + pushbutton { + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + 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_HIGH>; + 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 bfabfb8204226682cf0edd56a313c716c1d97d35 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Mon, 19 May 2025 13:41:50 +0100 Subject: [PATCH 36/44] 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 cd8940dde3ea8f7d1a9378a0dd43ccfcca049ac9 Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:13:05 +0100 Subject: [PATCH 37/44] 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 76433f15b3275e8e48f77e52989ac23959f08d35 Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Thu, 29 May 2025 15:28:48 +0100 Subject: [PATCH 38/44] 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 f73ed6d8460a37a72bf090e3ebab31996ad6a1c1 Mon Sep 17 00:00:00 2001 From: UtsavAgarwalADI Date: Mon, 26 May 2025 11:29:10 +0100 Subject: [PATCH 39/44] [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 bee9802f3f5c6fe577b33922d95cde2b9fbc31a5 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Wed, 10 Sep 2025 15:57:21 +0200 Subject: [PATCH 40/44] [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 e5e4cdbf8d99e717ecbd5d4d388f2bf1479ddd8c Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Wed, 10 Sep 2025 16:22:13 +0200 Subject: [PATCH 41/44] [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 bbabec58b9c34bfe87e9a7a6e81b0b6276e5e5a0 Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Thu, 29 May 2025 15:34:34 +0100 Subject: [PATCH 42/44] [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 5410a4f0c6fa1840acf4ac1ddbc0b3cc03851bff Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Wed, 10 Sep 2025 14:39:49 +0200 Subject: [PATCH 43/44] [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 5f6e4407f5e26cbf51c08f37055dbde7a79080dd Mon Sep 17 00:00:00 2001 From: Utsav Agarwal Date: Fri, 11 Jul 2025 13:27:42 +0100 Subject: [PATCH 44/44] [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