realtek: remove patches, files and config for 6.6

Remove all files etc. for 6.6 because 6.12 is default now.

Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/19139
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Jonas Jelonek 2025-06-22 09:48:01 +00:00 committed by Robert Marko
parent 1cd68e915d
commit 12f13b227c
62 changed files with 0 additions and 30341 deletions

View file

@ -1,85 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/realtek,rtl8300-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek Timer Device Tree Bindings
maintainers:
- Markus Stockhausen <markus.stockhausen@gmx.de>
description: |
The Realtek SOCs of the RTL83XX and RTL93XX series have at least 5 known
timers with corresponding interrupt lines . Their speed is derived from the
Lexra Bus (LXB) by dividers. Each timer has a block of 4 control registers in
the address range 0xb800xxxx with following start offsets.
RTL83XX: 0x3100, 0x3110, 0x3120, 0x3130, 0x3140
RTL93XX: 0x3200, 0x3210, 0x3220, 0x3230, 0x3240
properties:
compatible:
items:
- enum:
- realtek,rtl8380-timer
- realtek,rtl8390-timer
- realtek,rtl9300-timer
- const: realtek,otto-timer
reg:
minItems: 5
maxItems: 5
description:
List of timer register addresses.
interrupts:
minItems: 5
maxItems: 5
description:
List of timer interrupts.
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
examples:
- |
timer0: timer@3100 {
compatible = "realtek,rtl8380-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
- |
timer0: timer@3100 {
compatible = "realtek,rtl8390-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
- |
timer0: timer@3200 {
compatible = "realtek,rtl9300-timer", "realtek,otto-timer";
reg = <0x3200 0x10>, <0x3210 0x10>, <0x3220 0x10>,
<0x3230 0x10>, <0x3240 0x10>;
interrupt-parent = <&intc>;
interrupts = <7 4>, <8 4>, <9 4>, <10 4>, <11 4>;
clocks = <&ccu CLK_LXB>;
};
...

View file

@ -1,29 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef RTL838X_IOREMAP_H_
#define RTL838X_IOREMAP_H_
static inline int is_rtl838x_internal_registers(phys_addr_t offset)
{
/* IO-Block */
if (offset >= 0xb8000000 && offset < 0xb9000000)
return 1;
/* Switch block */
if (offset >= 0xbb000000 && offset < 0xbc000000)
return 1;
return 0;
}
static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size,
unsigned long flags)
{
if (is_rtl838x_internal_registers(offset))
return (void __iomem *)offset;
return NULL;
}
static inline int plat_iounmap(const volatile void __iomem *addr)
{
return is_rtl838x_internal_registers((unsigned long)addr);
}
#endif

View file

@ -1,415 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
* Copyright (C) 2020 B. Koblitz
*/
#ifndef _MACH_RTL838X_H_
#define _MACH_RTL838X_H_
#include <asm/types.h>
/*
* Register access macros
*/
#define RTL838X_SW_BASE ((volatile void *) 0xBB000000)
#define rtl83xx_r32(reg) readl(reg)
#define rtl83xx_w32(val, reg) writel(val, reg)
#define rtl83xx_w32_mask(clear, set, reg) rtl83xx_w32((rtl83xx_r32(reg) & ~(clear)) | (set), reg)
#define rtl83xx_r8(reg) readb(reg)
#define rtl83xx_w8(val, reg) writeb(val, reg)
#define sw_r32(reg) readl(RTL838X_SW_BASE + reg)
#define sw_w32(val, reg) writel(val, RTL838X_SW_BASE + reg)
#define sw_w32_mask(clear, set, reg) \
sw_w32((sw_r32(reg) & ~(clear)) | (set), reg)
#define sw_r64(reg) ((((u64)readl(RTL838X_SW_BASE + reg)) << 32) | \
readl(RTL838X_SW_BASE + reg + 4))
#define sw_w64(val, reg) do { \
writel((u32)((val) >> 32), RTL838X_SW_BASE + reg); \
writel((u32)((val) & 0xffffffff), \
RTL838X_SW_BASE + reg + 4); \
} while (0)
/*
* SPRAM
*/
#define RTL838X_ISPRAM_BASE 0x0
#define RTL838X_DSPRAM_BASE 0x0
/*
* IRQ Controller
*/
#define RTL838X_IRQ_CPU_BASE 0
#define RTL838X_IRQ_CPU_NUM 8
#define RTL838X_IRQ_ICTL_BASE (RTL838X_IRQ_CPU_BASE + RTL838X_IRQ_CPU_NUM)
#define RTL838X_IRQ_ICTL_NUM 32
#define RTL83XX_IRQ_UART0 31
#define RTL83XX_IRQ_UART1 30
#define RTL83XX_IRQ_TC0 29
#define RTL83XX_IRQ_TC1 28
#define RTL83XX_IRQ_OCPTO 27
#define RTL83XX_IRQ_HLXTO 26
#define RTL83XX_IRQ_SLXTO 25
#define RTL83XX_IRQ_NIC 24
#define RTL83XX_IRQ_GPIO_ABCD 23
#define RTL83XX_IRQ_GPIO_EFGH 22
#define RTL83XX_IRQ_RTC 21
#define RTL83XX_IRQ_SWCORE 20
#define RTL83XX_IRQ_WDT_IP1 19
#define RTL83XX_IRQ_WDT_IP2 18
#define RTL9300_UART1_IRQ 31
#define RTL9300_UART0_IRQ 30
#define RTL9300_USB_H2_IRQ 28
#define RTL9300_NIC_IRQ 24
#define RTL9300_SWCORE_IRQ 23
#define RTL9300_GPIO_ABC_IRQ 13
#define RTL9300_TC4_IRQ 11
#define RTL9300_TC3_IRQ 10
#define RTL9300_TC2_IRQ 9
#define RTL9300_TC1_IRQ 8
#define RTL9300_TC0_IRQ 7
/*
* MIPS32R2 counter
*/
#define RTL838X_COMPARE_IRQ (RTL838X_IRQ_CPU_BASE + 7)
/*
* ICTL
* Base address 0xb8003000UL
*/
#define RTL838X_ICTL1_IRQ (RTL838X_IRQ_CPU_BASE + 2)
#define RTL838X_ICTL2_IRQ (RTL838X_IRQ_CPU_BASE + 3)
#define RTL838X_ICTL3_IRQ (RTL838X_IRQ_CPU_BASE + 4)
#define RTL838X_ICTL4_IRQ (RTL838X_IRQ_CPU_BASE + 5)
#define RTL838X_ICTL5_IRQ (RTL838X_IRQ_CPU_BASE + 6)
#define GIMR (0x00)
#define UART0_IE (1 << 31)
#define UART1_IE (1 << 30)
#define TC0_IE (1 << 29)
#define TC1_IE (1 << 28)
#define OCPTO_IE (1 << 27)
#define HLXTO_IE (1 << 26)
#define SLXTO_IE (1 << 25)
#define NIC_IE (1 << 24)
#define GPIO_ABCD_IE (1 << 23)
#define GPIO_EFGH_IE (1 << 22)
#define RTC_IE (1 << 21)
#define WDT_IP1_IE (1 << 19)
#define WDT_IP2_IE (1 << 18)
#define GISR (0x04)
#define UART0_IP (1 << 31)
#define UART1_IP (1 << 30)
#define TC0_IP (1 << 29)
#define TC1_IP (1 << 28)
#define OCPTO_IP (1 << 27)
#define HLXTO_IP (1 << 26)
#define SLXTO_IP (1 << 25)
#define NIC_IP (1 << 24)
#define GPIO_ABCD_IP (1 << 23)
#define GPIO_EFGH_IP (1 << 22)
#define RTC_IP (1 << 21)
#define WDT_IP1_IP (1 << 19)
#define WDT_IP2_IP (1 << 18)
/* Interrupt Routing Selection */
#define UART0_RS 2
#define UART1_RS 1
#define TC0_RS 5
#define TC1_RS 1
#define OCPTO_RS 1
#define HLXTO_RS 1
#define SLXTO_RS 1
#define NIC_RS 4
#define GPIO_ABCD_RS 4
#define GPIO_EFGH_RS 4
#define RTC_RS 4
#define SWCORE_RS 3
#define WDT_IP1_RS 4
#define WDT_IP2_RS 5
/* Interrupt IRQ Assignments */
#define UART0_IRQ 31
#define UART1_IRQ 30
#define TC0_IRQ 29
#define TC1_IRQ 28
#define OCPTO_IRQ 27
#define HLXTO_IRQ 26
#define SLXTO_IRQ 25
#define NIC_IRQ 24
#define GPIO_ABCD_IRQ 23
#define GPIO_EFGH_IRQ 22
#define RTC_IRQ 21
#define SWCORE_IRQ 20
#define WDT_IP1_IRQ 19
#define WDT_IP2_IRQ 18
#define SYSTEM_FREQ 200000000
#define RTL838X_UART0_BASE ((volatile void *)(0xb8002000UL))
#define RTL838X_UART0_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */
#define RTL838X_UART0_FREQ (SYSTEM_FREQ - RTL838X_UART0_BAUD * 24)
#define RTL838X_UART0_MAPBASE 0x18002000UL
#define RTL838X_UART0_MAPSIZE 0x100
#define RTL838X_UART0_IRQ UART0_IRQ
#define RTL838X_UART1_BASE ((volatile void *)(0xb8002100UL))
#define RTL838X_UART1_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */
#define RTL838X_UART1_FREQ (SYSTEM_FREQ - RTL838X_UART1_BAUD * 24)
#define RTL838X_UART1_MAPBASE 0x18002100UL
#define RTL838X_UART1_MAPSIZE 0x100
#define RTL838X_UART1_IRQ UART1_IRQ
#define UART0_RBR (RTL838X_UART0_BASE + 0x000)
#define UART0_THR (RTL838X_UART0_BASE + 0x000)
#define UART0_DLL (RTL838X_UART0_BASE + 0x000)
#define UART0_IER (RTL838X_UART0_BASE + 0x004)
#define UART0_DLM (RTL838X_UART0_BASE + 0x004)
#define UART0_IIR (RTL838X_UART0_BASE + 0x008)
#define UART0_FCR (RTL838X_UART0_BASE + 0x008)
#define UART0_LCR (RTL838X_UART0_BASE + 0x00C)
#define UART0_MCR (RTL838X_UART0_BASE + 0x010)
#define UART0_LSR (RTL838X_UART0_BASE + 0x014)
#define UART1_RBR (RTL838X_UART1_BASE + 0x000)
#define UART1_THR (RTL838X_UART1_BASE + 0x000)
#define UART1_DLL (RTL838X_UART1_BASE + 0x000)
#define UART1_IER (RTL838X_UART1_BASE + 0x004)
#define UART1_DLM (RTL838X_UART1_BASE + 0x004)
#define UART1_IIR (RTL838X_UART1_BASE + 0x008)
#define UART1_FCR (RTL838X_UART1_BASE + 0x008)
#define UART1_LCR (RTL838X_UART1_BASE + 0x00C)
#define UART1_MCR (RTL838X_UART1_BASE + 0x010)
#define UART1_LSR (RTL838X_UART1_BASE + 0x014)
/*
* Memory Controller
*/
#define MC_MCR 0xB8001000
#define MC_MCR_VAL 0x00000000
#define MC_DCR 0xB8001004
#define MC_DCR0_VAL 0x54480000
#define MC_DTCR 0xB8001008
#define MC_DTCR_VAL 0xFFFF05C0
/*
* GPIO
*/
#define GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003500)
#define RTL838X_GPIO_PABC_CNR (GPIO_CTRL_REG_BASE + 0x0)
#define RTL838X_GPIO_PABC_TYPE (GPIO_CTRL_REG_BASE + 0x04)
#define RTL838X_GPIO_PABC_DIR (GPIO_CTRL_REG_BASE + 0x8)
#define RTL838X_GPIO_PABC_DATA (GPIO_CTRL_REG_BASE + 0xc)
#define RTL838X_GPIO_PABC_ISR (GPIO_CTRL_REG_BASE + 0x10)
#define RTL838X_GPIO_PAB_IMR (GPIO_CTRL_REG_BASE + 0x14)
#define RTL838X_GPIO_PC_IMR (GPIO_CTRL_REG_BASE + 0x18)
#define RTL930X_GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003300)
#define RTL930X_GPIO_PABCD_DIR (RTL930X_GPIO_CTRL_REG_BASE + 0x8)
#define RTL930X_GPIO_PABCD_DAT (RTL930X_GPIO_CTRL_REG_BASE + 0xc)
#define RTL930X_GPIO_PABCD_ISR (RTL930X_GPIO_CTRL_REG_BASE + 0x10)
#define RTL930X_GPIO_PAB_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x14)
#define RTL930X_GPIO_PCD_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x18)
#define RTL838X_MODEL_NAME_INFO (0x00D4)
#define RTL839X_MODEL_NAME_INFO (0x0FF0)
#define RTL93XX_MODEL_NAME_INFO (0x0004)
#define RTL931X_CHIP_INFO_ADDR (0x0008)
#define RTL838X_LED_GLB_CTRL (0xA000)
#define RTL839X_LED_GLB_CTRL (0x00E4)
#define RTL9302_LED_GLB_CTRL (0xcc00)
#define RTL930X_LED_GLB_CTRL (0xCC00)
#define RTL931X_LED_GLB_CTRL (0x0600)
#define RTL838X_EXT_GPIO_DIR (0xA08C)
#define RTL839X_EXT_GPIO_DIR (0x0214)
#define RTL838X_EXT_GPIO_DATA (0xA094)
#define RTL839X_EXT_GPIO_DATA (0x021c)
#define RTL838X_EXT_GPIO_INDRT_ACCESS (0xA09C)
#define RTL839X_EXT_GPIO_INDRT_ACCESS (0x0224)
#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0)
#define RTL838X_DMY_REG5 (0x0144)
#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0)
#define RTL838X_GMII_INTF_SEL (0x1000)
#define RTL838X_IO_DRIVING_ABILITY_CTRL (0x1010)
#define RTL838X_GPIO_A7 31
#define RTL838X_GPIO_A6 30
#define RTL838X_GPIO_A5 29
#define RTL838X_GPIO_A4 28
#define RTL838X_GPIO_A3 27
#define RTL838X_GPIO_A2 26
#define RTL838X_GPIO_A1 25
#define RTL838X_GPIO_A0 24
#define RTL838X_GPIO_B7 23
#define RTL838X_GPIO_B6 22
#define RTL838X_GPIO_B5 21
#define RTL838X_GPIO_B4 20
#define RTL838X_GPIO_B3 19
#define RTL838X_GPIO_B2 18
#define RTL838X_GPIO_B1 17
#define RTL838X_GPIO_B0 16
#define RTL838X_GPIO_C7 15
#define RTL838X_GPIO_C6 14
#define RTL838X_GPIO_C5 13
#define RTL838X_GPIO_C4 12
#define RTL838X_GPIO_C3 11
#define RTL838X_GPIO_C2 10
#define RTL838X_GPIO_C1 9
#define RTL838X_GPIO_C0 8
#define RTL838X_INT_RW_CTRL (0x0058)
#define RTL838X_EXT_VERSION (0x00D0)
#define RTL838X_PLL_CML_CTRL (0x0FF8)
#define RTL838X_STRAP_DBG (0x100C)
/*
* Reset
*/
#define RGCR (0x1E70)
#define RTL838X_RST_GLB_CTRL_0 (0x003c)
#define RTL838X_RST_GLB_CTRL_1 (0x0040)
#define RTL839X_RST_GLB_CTRL (0x0014)
#define RTL930X_RST_GLB_CTRL_0 (0x000c)
#define RTL931X_RST_GLB_CTRL (0x0400)
/* LED control by switch */
#define RTL838X_LED_MODE_SEL (0x1004)
#define RTL838X_LED_MODE_CTRL (0xA004)
#define RTL838X_LED_P_EN_CTRL (0xA008)
/* LED control by software */
#define RTL838X_LED_SW_CTRL (0xA00C)
#define RTL839X_LED_SW_CTRL (0xA00C)
#define RTL838X_LED_SW_P_EN_CTRL (0xA010)
#define RTL839X_LED_SW_P_EN_CTRL (0x012C)
#define RTL838X_LED0_SW_P_EN_CTRL (0xA010)
#define RTL839X_LED0_SW_P_EN_CTRL (0x012C)
#define RTL838X_LED1_SW_P_EN_CTRL (0xA014)
#define RTL839X_LED1_SW_P_EN_CTRL (0x0130)
#define RTL838X_LED2_SW_P_EN_CTRL (0xA018)
#define RTL839X_LED2_SW_P_EN_CTRL (0x0134)
#define RTL838X_LED_SW_P_CTRL (0xA01C)
#define RTL838X_LED_SW_P_CTRL_PORT(p) (RTL838X_LED_SW_P_CTRL + (((p) << 2)))
#define RTL839X_LED_SW_P_CTRL (0x0144)
#define RTL839X_MAC_EFUSE_CTRL (0x02ac)
/*
* MDIO via Realtek's SMI interface
*/
#define RTL838X_SMI_GLB_CTRL (0xa100)
#define RTL838X_SMI_ACCESS_PHY_CTRL_0 (0xa1b8)
#define RTL838X_SMI_ACCESS_PHY_CTRL_1 (0xa1bc)
#define RTL838X_SMI_ACCESS_PHY_CTRL_2 (0xa1c0)
#define RTL838X_SMI_ACCESS_PHY_CTRL_3 (0xa1c4)
#define RTL838X_SMI_PORT0_5_ADDR_CTRL (0xa1c8)
#define RTL838X_SMI_POLL_CTRL (0xa17c)
#define RTL839X_SMI_GLB_CTRL (0x03f8)
#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc)
#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC)
#define RTL839X_PHYREG_CTRL (0x03E0)
#define RTL839X_PHYREG_PORT_CTRL (0x03E4)
#define RTL839X_PHYREG_DATA_CTRL (0x03F0)
#define RTL839X_PHYREG_MMD_CTRL (0x3F4)
#define RTL930X_SMI_GLB_CTRL (0xCA00)
#define RTL930X_SMI_POLL_CTRL (0xca90)
#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
#define RTL930X_SMI_PORT0_5_ADDR (0xCB80)
#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70)
#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74)
#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78)
#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C)
#define RTL931X_SMI_GLB_CTRL1 (0x0CBC)
#define RTL931X_SMI_GLB_CTRL0 (0x0CC0)
#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC)
#define RTL931X_SMI_PORT_ADDR (0x0C74)
#define RTL931X_SMI_PORT_POLLING_SEL (0x0C9C)
#define RTL9310_SMI_PORT_POLLING_CTRL (0x0CCC)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10)
#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14)
#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18)
#define RTL931X_MAC_L2_GLOBAL_CTRL2 (0x1358)
#define RTL931X_MAC_L2_GLOBAL_CTRL1 (0x5548)
/* Switch interrupts */
#define RTL838X_IMR_GLB (0x1100)
#define RTL838X_IMR_PORT_LINK_STS_CHG (0x1104)
#define RTL838X_ISR_GLB_SRC (0x1148)
#define RTL838X_ISR_PORT_LINK_STS_CHG (0x114C)
#define RTL839X_IMR_GLB (0x0064)
#define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068)
#define RTL839X_ISR_GLB_SRC (0x009c)
#define RTL839X_ISR_PORT_LINK_STS_CHG (0x00a0)
#define RTL930X_IMR_GLB (0xC628)
#define RTL930X_IMR_PORT_LINK_STS_CHG (0xC62C)
#define RTL930X_ISR_GLB (0xC658)
#define RTL930X_ISR_PORT_LINK_STS_CHG (0xC660)
/* IMR_GLB does not exit on RTL931X */
#define RTL931X_IMR_PORT_LINK_STS_CHG (0x126C)
#define RTL931X_ISR_GLB_SRC (0x12B4)
#define RTL931X_ISR_PORT_LINK_STS_CHG (0x12B8)
/* Definition of family IDs */
#define RTL8389_FAMILY_ID (0x8389)
#define RTL8328_FAMILY_ID (0x8328)
#define RTL8390_FAMILY_ID (0x8390)
#define RTL8350_FAMILY_ID (0x8350)
#define RTL8380_FAMILY_ID (0x8380)
#define RTL8330_FAMILY_ID (0x8330)
#define RTL9300_FAMILY_ID (0x9300)
#define RTL9310_FAMILY_ID (0x9310)
/* SPI Support */
#define RTL931X_SPI_CTRL0 (0x103C)
/* Basic SoC Features */
#define RTL838X_CPU_PORT 28
#define RTL839X_CPU_PORT 52
#define RTL930X_CPU_PORT 28
#define RTL931X_CPU_PORT 56
struct rtl83xx_soc_info {
unsigned char *name;
unsigned int id;
unsigned int family;
unsigned char *compatible;
volatile void *sw_base;
volatile void *icu_base;
int cpu_port;
};
/* rtl83xx-related functions used across subsystems */
int rtl838x_smi_wait_op(int timeout);
int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val);
#endif /* _MACH_RTL838X_H_ */

View file

@ -1,5 +0,0 @@
#
# Makefile for the rtl838x specific parts of the kernel
#
obj-y := setup.o prom.o

View file

@ -1,5 +0,0 @@
#
# Realtek RTL838x SoCs
#
cflags-$(CONFIG_MACH_REALTEK_RTL) += -I$(srctree)/arch/mips/include/asm/mach-rtl838x/
load-$(CONFIG_MACH_REALTEK_RTL) += 0xffffffff80100000

View file

@ -1,226 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* prom.c
* Early intialization code for the Realtek RTL838X SoC
*
* based on the original BSP by
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
* Copyright (C) 2020 B. Koblitz
*
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <asm/bootinfo.h>
#include <asm/addrspace.h>
#include <asm/page.h>
#include <asm/cpu.h>
#include <asm/fw/fw.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include <asm/mips-cps.h>
#include <mach-rtl83xx.h>
extern char arcs_cmdline[];
struct rtl83xx_soc_info soc_info;
const void *fdt;
#ifdef CONFIG_MIPS_MT_SMP
extern const struct plat_smp_ops vsmp_smp_ops;
static struct plat_smp_ops rtl_smp_ops;
static void rtl_init_secondary(void)
{
#ifndef CONFIG_CEVT_R4K
/*
* These devices are low on resources. There might be the chance that CEVT_R4K
* is not enabled in kernel build. Nevertheless the timer and interrupt 7 might
* be active by default after startup of secondary VPE. With no registered
* handler that leads to continuous unhandeled interrupts. In this case disable
* counting (DC) in the core and confirm a pending interrupt.
*/
write_c0_cause(read_c0_cause() | CAUSEF_DC);
write_c0_compare(0);
#endif /* CONFIG_CEVT_R4K */
/*
* Enable all CPU interrupts, as everything is managed by the external
* controller. TODO: Standard vsmp_init_secondary() has special treatment for
* Malta if external GIC is available. Maybe we need this too.
*/
if (mips_gic_present())
pr_warn("%s: GIC present. Maybe interrupt enabling required.\n", __func__);
else
set_c0_status(ST0_IM);
}
#endif /* CONFIG_MIPS_MT_SMP */
const char *get_system_type(void)
{
return soc_info.name;
}
void __init prom_free_prom_memory(void)
{
}
void __init device_tree_init(void)
{
if (!fdt_check_header(&__appended_dtb)) {
fdt = &__appended_dtb;
pr_info("Using appended Device Tree.\n");
}
initial_boot_params = (void *)fdt;
unflatten_and_copy_device_tree();
/* delay cpc & smp probing to allow devicetree access */
mips_cpc_probe();
if (!register_cps_smp_ops())
return;
#ifdef CONFIG_MIPS_MT_SMP
if (cpu_has_mipsmt) {
rtl_smp_ops = vsmp_smp_ops;
rtl_smp_ops.init_secondary = rtl_init_secondary;
register_smp_ops(&rtl_smp_ops);
return;
}
#endif
register_up_smp_ops();
}
static void __init identify_rtl9302(void)
{
switch (sw_r32(RTL93XX_MODEL_NAME_INFO) & 0xfffffff0) {
case 0x93020810:
soc_info.name = "RTL9302A 12x2.5G";
break;
case 0x93021010:
soc_info.name = "RTL9302B 8x2.5G";
break;
case 0x93021810:
soc_info.name = "RTL9302C 16x2.5G";
break;
case 0x93022010:
soc_info.name = "RTL9302D 24x2.5G";
break;
case 0x93020800:
soc_info.name = "RTL9302A";
break;
case 0x93021000:
soc_info.name = "RTL9302B";
break;
case 0x93021800:
soc_info.name = "RTL9302C";
break;
case 0x93022000:
soc_info.name = "RTL9302D";
break;
case 0x93023001:
soc_info.name = "RTL9302F";
break;
default:
soc_info.name = "RTL9302";
}
}
void __init prom_init(void)
{
uint32_t model;
model = sw_r32(RTL838X_MODEL_NAME_INFO);
pr_info("RTL838X model is %x\n", model);
model = model >> 16 & 0xFFFF;
if ((model != 0x8328) && (model != 0x8330) && (model != 0x8332)
&& (model != 0x8380) && (model != 0x8382)) {
model = sw_r32(RTL839X_MODEL_NAME_INFO);
pr_info("RTL839X model is %x\n", model);
model = model >> 16 & 0xFFFF;
}
if ((model & 0x8390) != 0x8380 && (model & 0x8390) != 0x8390) {
model = sw_r32(RTL93XX_MODEL_NAME_INFO);
pr_info("RTL93XX model is %x\n", model);
model = model >> 16 & 0xFFFF;
}
soc_info.id = model;
switch (model) {
case 0x8328:
soc_info.name = "RTL8328";
soc_info.family = RTL8328_FAMILY_ID;
break;
case 0x8332:
soc_info.name = "RTL8332";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8380:
soc_info.name = "RTL8380";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8382:
soc_info.name = "RTL8382";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8390:
soc_info.name = "RTL8390";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8391:
soc_info.name = "RTL8391";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8392:
soc_info.name = "RTL8392";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8393:
soc_info.name = "RTL8393";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x9301:
soc_info.name = "RTL9301";
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9302:
identify_rtl9302();
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9303:
soc_info.name = "RTL9303";
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9311:
soc_info.name = "RTL9311";
soc_info.family = RTL9310_FAMILY_ID;
break;
case 0x9313:
soc_info.name = "RTL9313";
soc_info.family = RTL9310_FAMILY_ID;
break;
default:
soc_info.name = "DEFAULT";
soc_info.family = 0;
}
pr_info("SoC Type: %s\n", get_system_type());
/*
* fw_arg2 is be the pointer to the environment. Some devices (e.g. HP JG924A) hand
* over other than expected kernel boot arguments. Something like 0xfffdffff looks
* suspicous. Do extra cleanup for fw_init_cmdline() to avoid a hang during boot.
*/
if (fw_arg2 >= CKSEG2)
fw_arg2 = 0;
fw_init_cmdline();
}

View file

@ -1,102 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Setup for the Realtek RTL838X SoC:
* Memory, Timer and Serial
*
* Copyright (C) 2020 B. Koblitz
* based on the original BSP by
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
*
*/
#include <linux/console.h>
#include <linux/init.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/of_fdt.h>
#include <linux/irqchip.h>
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include "mach-rtl83xx.h"
extern struct rtl83xx_soc_info soc_info;
void __init plat_mem_setup(void)
{
void *dtb;
set_io_port_base(KSEG1);
dtb = get_fdt();
if (!dtb)
panic("no dtb found");
/*
* Load the devicetree. This causes the chosen node to be
* parsed resulting in our memory appearing
*/
__dt_setup_arch(dtb);
}
static void plat_time_init_fallback(void)
{
struct device_node *np;
u32 freq = 500000000;
np = of_find_node_by_name(NULL, "cpus");
if (!np) {
pr_err("Missing 'cpus' DT node, using default frequency.");
} else {
if (of_property_read_u32(np, "frequency", &freq) < 0)
pr_err("No 'frequency' property in DT, using default.");
else
pr_info("CPU frequency from device tree: %dMHz", freq / 1000000);
of_node_put(np);
}
mips_hpt_frequency = freq / 2;
}
void __init plat_time_init(void)
{
/*
* Initialization routine resembles generic MIPS plat_time_init() with
* lazy error handling. The final fallback is only needed until we have
* converted all device trees to new clock syntax.
*/
struct device_node *np;
struct clk *clk;
of_clk_init(NULL);
mips_hpt_frequency = 0;
np = of_get_cpu_node(0, NULL);
if (!np) {
pr_err("Failed to get CPU node\n");
} else {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk));
} else {
mips_hpt_frequency = clk_get_rate(clk) / 2;
clk_put(clk);
}
}
if (!mips_hpt_frequency)
plat_time_init_fallback();
timer_probe();
}
void __init arch_init_irq(void)
{
irqchip_init();
}

View file

@ -1,19 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig COMMON_CLK_REALTEK
bool "Support for Realtek's clock controllers"
depends on MACH_REALTEK_RTL
if COMMON_CLK_REALTEK
config COMMON_CLK_RTL83XX
bool "Clock driver for Realtek RTL83XX"
depends on MACH_REALTEK_RTL
select SRAM
help
This driver adds support for the Realtek RTL83xx series basic clocks.
This includes chips in the RTL838x series, such as RTL8380, RTL8381,
RTL832, as well as chips from the RTL839x series, such as RTL8390,
RT8391, RTL8392, RTL8393 and RTL8396.
endif

View file

@ -1,2 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_COMMON_CLK_RTL83XX) += clk-rtl83xx.o clk-rtl838x-sram.o clk-rtl839x-sram.o

View file

@ -1,149 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL838X SRAM clock setters
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
#include <dt-bindings/clock/rtl83xx-clk.h>
#include "clk-rtl83xx.h"
#define rGLB $t0
#define rCTR $t1
#define rMSK $t2
#define rSLP $t3
#define rTMP $t4
.set noreorder
.globl rtcl_838x_dram_start
rtcl_838x_dram_start:
/*
* Functions start here and should avoid access to normal memory. REMARK! Do not forget about
* stack pointer and dirty caches that might interfere.
*/
.globl rtcl_838x_dram_set_rate
.ent rtcl_838x_dram_set_rate
rtcl_838x_dram_set_rate:
#ifdef CONFIG_RTL838X
li rCTR, RTL_SW_CORE_BASE
addiu rGLB, rCTR, RTL838X_PLL_GLB_CTRL
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, pre_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, pre_mem
nop
pre_lxb:
ori rSLP, $0, RTL838X_GLB_CTRL_LXB_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_LXB_CTRL0
b main_set
ori rMSK, $0, RTL838X_GLB_CTRL_EN_LXB_PLL_MASK
pre_mem:
/* simple 64K data cache flush to avoid unexpected memory access */
li rMSK, RTL_SRAM_BASE
li rTMP, 2048
pre_flush:
lw $0, 0(rMSK)
addiu rMSK, rMSK, 32
addiu rTMP, rTMP, -1
bne rTMP, $0, pre_flush
lw $0, -4(rMSK)
ori rSLP, $0, RTL838X_GLB_CTRL_MEM_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_MEM_CTRL0
b main_set
ori rMSK, $0, RTL838X_GLB_CTRL_EN_MEM_PLL_MASK
pre_cpu:
/* switch CPU to LXB clock */
ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK
nor rMSK, rMSK, $0
sync
lw rTMP, 0(rGLB)
and rTMP, rTMP, rMSK
sw rTMP, 0(rGLB)
sync
ori rSLP, $0, RTL838X_GLB_CTRL_CPU_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_CPU_CTRL0
ori rMSK, $0, RTL838X_GLB_CTRL_EN_CPU_PLL_MASK
main_set:
/* disable PLL */
nor rMSK, rMSK, 0
sync
lw rTMP, 0(rGLB)
sync
and rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* set new PLL values */
sync
sw $a1, 0(rCTR)
sw $a2, 4(rCTR)
sync
/* enable PLL (will reset it and clear ready status) */
nor rMSK, rMSK, 0
sync
lw rTMP, 0(rGLB)
sync
or rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait for PLL to become ready */
wait_ready:
lw rTMP, 0(rGLB)
and rTMP, rTMP, rSLP
bne rTMP, $0, wait_ready
sync
/* branch to post processing */
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, post_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, post_mem
nop
post_lxb:
jr $ra
nop
post_mem:
jr $ra
nop
post_cpu:
/* stabilize clock to avoid crash, empirically determined */
ori rSLP, $0, 0x3000
wait_cpu:
bnez rSLP, wait_cpu
addiu rSLP, rSLP, -1
/* switch CPU to PLL clock */
ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK
sync
lw rTMP, 0(rGLB)
or rTMP, rTMP, rMSK
sw rTMP, 0(rGLB)
sync
jr $ra
nop
#else /* !CONFIG_RTL838X */
jr $ra
nop
#endif
.end rtcl_838x_dram_set_rate
/*
* End marker. Do not delete.
*/
.word RTL_SRAM_MARKER
.globl rtcl_838x_dram_size
rtcl_838x_dram_size:
.word .-rtcl_838x_dram_start

View file

@ -1,141 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL839X SRAM clock setters
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
#include <asm/mipsregs.h>
#include <dt-bindings/clock/rtl83xx-clk.h>
#include "clk-rtl83xx.h"
#define rGLB $t0
#define rCTR $t1
#define rMSK $t2
#define rSLP1 $t3
#define rSLP2 $t4
#define rSLP3 $t5
#define rTMP $t6
#define rCP0 $t7
.set noreorder
.globl rtcl_839x_dram_start
rtcl_839x_dram_start:
/*
* Functions start here and should avoid access to normal memory. REMARK! Do not forget about
* stack pointer and dirty caches that might interfere.
*/
.globl rtcl_839x_dram_set_rate
.ent rtcl_839x_dram_set_rate
rtcl_839x_dram_set_rate:
#ifdef CONFIG_RTL839X
/* disable MIPS 34K branch and return prediction */
mfc0 rCP0, CP0_CONFIG, 7
ori rTMP, rCP0, 0xc
mtc0 rTMP, CP0_CONFIG, 7
li rCTR, RTL_SW_CORE_BASE
addiu rGLB, rCTR, RTL839X_PLL_GLB_CTRL
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, pre_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, pre_mem
nop
pre_lxb:
li rSLP1, 0x400000
li rSLP2, 0x400000
li rSLP3, 0x400000
addiu rCTR, rCTR, RTL839X_PLL_LXB_CTRL0
b main_set
ori rMSK, $0, RTL839X_GLB_CTRL_LXB_CLKSEL_MASK
pre_mem:
/* try to avoid memory access with simple 64K data cache flush */
li rMSK, RTL_SRAM_BASE
li rTMP, 2048
pre_flush:
lw $0, 0(rMSK)
addiu rMSK, rMSK, 32
addiu rTMP, rTMP, -1
bne rTMP, $0, pre_flush
lw $0, -4(rMSK)
li rSLP1, 0x10000
li rSLP2, 0x10000
li rSLP3, 0x10000
addiu rCTR, rCTR, RTL839X_PLL_MEM_CTRL0
b main_set
ori rMSK, $0, RTL839X_GLB_CTRL_MEM_CLKSEL_MASK
pre_cpu:
li rSLP1, 0x1000
li rSLP2, 0x1000
li rSLP3, 0x200
addiu rCTR, rCTR, RTL839X_PLL_CPU_CTRL0
ori rMSK, $0, RTL839X_GLB_CTRL_CPU_CLKSEL_MASK
main_set:
/* switch to fixed clock */
sync
lw rTMP, 0(rGLB)
sync
or rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait until fixed clock in use */
or rTMP, rSLP1, $0
wait_fixclock:
bnez rTMP, wait_fixclock
addiu rTMP, rTMP, -1
/* set new PLL values */
sync
sw $a1, 0(rCTR)
sw $a2, 4(rCTR)
sync
/* wait for value takeover */
or rTMP, rSLP2, $0
wait_pll:
bnez rTMP, wait_pll
addiu rTMP, rTMP, -1
/* switch back to PLL clock*/
nor rMSK, rMSK, $0
sync
lw rTMP, 0(rGLB)
sync
and rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait until PLL clock in use */
or rTMP, rSLP3, $0
wait_pllclock:
bnez rTMP, wait_pllclock
addiu rTMP, rTMP, -1
/* restore branch prediction */
mtc0 rCP0, CP0_CONFIG, 7
jr $ra
nop
#else /* !CONFIG_RTL839X */
jr $ra
nop
#endif
.end rtcl_839x_dram_set_rate
/*
* End marker. Do not delete.
*/
.word RTL_SRAM_MARKER
.globl rtcl_839x_dram_size
rtcl_839x_dram_size:
.word .-rtcl_839x_dram_start

View file

@ -1,770 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Realtek RTL83XX clock driver
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*
* This driver provides basic clock support for the central core clock unit (CCU) and its PLLs
* inside the RTL838X and RTL8389X SOC. Currently CPU, memory and LXB clock information can be
* accessed. To make use of the driver add the following devices and configurations at the
* appropriate locations to the DT.
*
* #include <dt-bindings/clock/rtl83xx-clk.h>
*
* sram0: sram@9f000000 {
* compatible = "mmio-sram";
* reg = <0x9f000000 0x18000>;
* #address-cells = <1>;
* #size-cells = <1>;
* ranges = <0 0x9f000000 0x18000>;
* };
*
* osc: oscillator {
* compatible = "fixed-clock";
* #clock-cells = <0>;
* clock-frequency = <25000000>;
* };
*
* ccu: clock-controller {
* compatible = "realtek,rtl8380-clock";
* #clock-cells = <1>;
* clocks = <&osc>;
* clock-names = "ref_clk";
* };
*
*
* The SRAM part is needed to be able to set clocks. When changing clocks the code must not run
* from DRAM. Otherwise system might freeze. Take care to adjust CCU compatibility, SRAM address
* and size to the target SOC device. Afterwards one can access/identify the clocks in the other
* DT devices with <&ccu CLK_CPU>, <&ccu CLK_MEM> or <&ccu CLK_LXB>. Additionally the clocks can
* be used inside the kernel with
*
* cpu_clk = clk_get(NULL, "cpu_clk");
* mem_clk = clk_get(NULL, "mem_clk");
* lxb_clk = clk_get(NULL, "lxb_clk");
*
* This driver can be directly used by the DT based cpufreq driver (CONFIG_CPUFREQ_DT) if CPU
* references the right clock and sane operating points (OPP) are provided. E.g.
*
* cpu@0 {
* compatible = "mips,mips4KEc";
* reg = <0>;
* clocks = <&ccu CLK_CPU>;
* operating-points-v2 = <&cpu_opp_table>;
* };
*
* cpu_opp_table: opp-table-0 {
* compatible = "operating-points-v2";
* opp-shared;
* opp00 {
* opp-hz = /bits/ 64 <425000000>;
* };
* ...
* }
*/
#include <asm/cacheflush.h>
#include <asm/mipsmtregs.h>
#include <dt-bindings/clock/rtl83xx-clk.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-rtl83xx.h"
#define read_sw(reg) ioread32(((void *)RTL_SW_CORE_BASE) + reg)
#define read_soc(reg) ioread32(((void *)RTL_SOC_BASE) + reg)
#define write_sw(val, reg) iowrite32(val, ((void *)RTL_SW_CORE_BASE) + reg)
#define write_soc(val, reg) iowrite32(val, ((void *)RTL_SOC_BASE) + reg)
/*
* some hardware specific definitions
*/
#define SOC_RTL838X 0
#define SOC_RTL839X 1
#define SOC_COUNT 2
#define MEM_DDR1 1
#define MEM_DDR2 2
#define MEM_DDR3 3
#define REG_CTRL0 0
#define REG_CTRL1 1
#define REG_COUNT 2
#define OSC_RATE 25000000
static const int rtcl_regs[SOC_COUNT][REG_COUNT][CLK_COUNT] = {
{
{ RTL838X_PLL_CPU_CTRL0, RTL838X_PLL_MEM_CTRL0, RTL838X_PLL_LXB_CTRL0 },
{ RTL838X_PLL_CPU_CTRL1, RTL838X_PLL_MEM_CTRL1, RTL838X_PLL_LXB_CTRL1 },
}, {
{ RTL839X_PLL_CPU_CTRL0, RTL839X_PLL_MEM_CTRL0, RTL839X_PLL_LXB_CTRL0 },
{ RTL839X_PLL_CPU_CTRL1, RTL839X_PLL_MEM_CTRL1, RTL839X_PLL_LXB_CTRL1 },
}
};
#define RTCL_REG_SET(_rate, _ctrl0, _ctrl1) \
{ \
.rate = _rate, \
.ctrl0 = _ctrl0, \
.ctrl1 = _ctrl1, \
}
struct rtcl_reg_set {
unsigned int rate;
unsigned int ctrl0;
unsigned int ctrl1;
};
/*
* The following configuration tables are valid operation points for their
* corresponding PLLs. The magic numbers are precalculated mulitpliers and
* dividers to keep the driver simple. They also provide rates outside the
* allowed physical specifications. E.g. DDR3 memory has a lower limit of 303
* MHz or the CPU might get unstable if set to anything above its startup
* frequency. Additionally the Realtek SOCs tend to expect CPU speed larger
* than MEM speed larger than LXB speed. The caller or DT configuration must
* take care that only valid operating points are selected.
*/
static const struct rtcl_reg_set rtcl_838x_cpu_reg_set[] = {
RTCL_REG_SET(300000000, 0x045c8, 0x1414530e),
RTCL_REG_SET(325000000, 0x04648, 0x1414530e),
RTCL_REG_SET(350000000, 0x046c8, 0x1414530e),
RTCL_REG_SET(375000000, 0x04748, 0x1414530e),
RTCL_REG_SET(400000000, 0x045c8, 0x0c14530e),
RTCL_REG_SET(425000000, 0x04628, 0x0c14530e),
RTCL_REG_SET(450000000, 0x04688, 0x0c14530e),
RTCL_REG_SET(475000000, 0x046e8, 0x0c14530e),
RTCL_REG_SET(500000000, 0x04748, 0x0c14530e),
RTCL_REG_SET(525000000, 0x047a8, 0x0c14530e),
RTCL_REG_SET(550000000, 0x04808, 0x0c14530e),
RTCL_REG_SET(575000000, 0x04868, 0x0c14530e),
RTCL_REG_SET(600000000, 0x048c8, 0x0c14530e),
RTCL_REG_SET(625000000, 0x04928, 0x0c14530e)
};
static const struct rtcl_reg_set rtcl_838x_mem_reg_set[] = {
RTCL_REG_SET(200000000, 0x041bc, 0x14018C80),
RTCL_REG_SET(225000000, 0x0417c, 0x0c018C80),
RTCL_REG_SET(250000000, 0x041ac, 0x0c018C80),
RTCL_REG_SET(275000000, 0x0412c, 0x04018C80),
RTCL_REG_SET(300000000, 0x0414c, 0x04018c80),
RTCL_REG_SET(325000000, 0x0416c, 0x04018c80),
RTCL_REG_SET(350000000, 0x0418c, 0x04018c80),
RTCL_REG_SET(375000000, 0x041ac, 0x04018c80)
};
static const struct rtcl_reg_set rtcl_838x_lxb_reg_set[] = {
RTCL_REG_SET(100000000, 0x043c8, 0x001ad30e),
RTCL_REG_SET(125000000, 0x043c8, 0x001ad30e),
RTCL_REG_SET(150000000, 0x04508, 0x1c1ad30e),
RTCL_REG_SET(175000000, 0x04508, 0x1c1ad30e),
RTCL_REG_SET(200000000, 0x047c8, 0x001ad30e)
};
static const struct rtcl_reg_set rtcl_839x_cpu_reg_set[] = {
RTCL_REG_SET(400000000, 0x0414c, 0x00000005),
RTCL_REG_SET(425000000, 0x041ec, 0x00000006),
RTCL_REG_SET(450000000, 0x0417c, 0x00000005),
RTCL_REG_SET(475000000, 0x0422c, 0x00000006),
RTCL_REG_SET(500000000, 0x041ac, 0x00000005),
RTCL_REG_SET(525000000, 0x0426c, 0x00000006),
RTCL_REG_SET(550000000, 0x0412c, 0x00000004),
RTCL_REG_SET(575000000, 0x042ac, 0x00000006),
RTCL_REG_SET(600000000, 0x0414c, 0x00000004),
RTCL_REG_SET(625000000, 0x042ec, 0x00000006),
RTCL_REG_SET(650000000, 0x0416c, 0x00000004),
RTCL_REG_SET(675000000, 0x04324, 0x00000006),
RTCL_REG_SET(700000000, 0x0418c, 0x00000004),
RTCL_REG_SET(725000000, 0x0436c, 0x00000006),
RTCL_REG_SET(750000000, 0x0438c, 0x00000006),
RTCL_REG_SET(775000000, 0x043ac, 0x00000006),
RTCL_REG_SET(800000000, 0x043cc, 0x00000006),
RTCL_REG_SET(825000000, 0x043ec, 0x00000006),
RTCL_REG_SET(850000000, 0x0440c, 0x00000006)
};
static const struct rtcl_reg_set rtcl_839x_mem_reg_set[] = {
RTCL_REG_SET(100000000, 0x041cc, 0x00000000),
RTCL_REG_SET(125000000, 0x041ac, 0x00000007),
RTCL_REG_SET(150000000, 0x0414c, 0x00000006),
RTCL_REG_SET(175000000, 0x0418c, 0x00000006),
RTCL_REG_SET(200000000, 0x041cc, 0x00000006),
RTCL_REG_SET(225000000, 0x0417c, 0x00000005),
RTCL_REG_SET(250000000, 0x041ac, 0x00000005),
RTCL_REG_SET(275000000, 0x0412c, 0x00000004),
RTCL_REG_SET(300000000, 0x0414c, 0x00000004),
RTCL_REG_SET(325000000, 0x0416c, 0x00000004),
RTCL_REG_SET(350000000, 0x0418c, 0x00000004),
RTCL_REG_SET(375000000, 0x041ac, 0x00000004),
RTCL_REG_SET(400000000, 0x041cc, 0x00000004)
};
static const struct rtcl_reg_set rtcl_839x_lxb_reg_set[] = {
RTCL_REG_SET(50000000, 0x1414c, 0x00000003),
RTCL_REG_SET(100000000, 0x0814c, 0x00000003),
RTCL_REG_SET(150000000, 0x0414c, 0x00000003),
RTCL_REG_SET(200000000, 0x0414c, 0x00000007)
};
struct rtcl_rtab_set {
int count;
const struct rtcl_reg_set *rset;
};
#define RTCL_RTAB_SET(_rset) \
{ \
.count = ARRAY_SIZE(_rset), \
.rset = _rset, \
}
static const struct rtcl_rtab_set rtcl_rtab_set[SOC_COUNT][CLK_COUNT] = {
{
RTCL_RTAB_SET(rtcl_838x_cpu_reg_set),
RTCL_RTAB_SET(rtcl_838x_mem_reg_set),
RTCL_RTAB_SET(rtcl_838x_lxb_reg_set)
}, {
RTCL_RTAB_SET(rtcl_839x_cpu_reg_set),
RTCL_RTAB_SET(rtcl_839x_mem_reg_set),
RTCL_RTAB_SET(rtcl_839x_lxb_reg_set)
}
};
#define RTCL_ROUND_SET(_min, _max, _step) \
{ \
.min = _min, \
.max = _max, \
.step = _step, \
}
struct rtcl_round_set {
unsigned long min;
unsigned long max;
unsigned long step;
};
static const struct rtcl_round_set rtcl_round_set[SOC_COUNT][CLK_COUNT] = {
{
RTCL_ROUND_SET(300000000, 625000000, 25000000),
RTCL_ROUND_SET(200000000, 375000000, 25000000),
RTCL_ROUND_SET(100000000, 200000000, 25000000)
}, {
RTCL_ROUND_SET(400000000, 850000000, 25000000),
RTCL_ROUND_SET(100000000, 400000000, 25000000),
RTCL_ROUND_SET(50000000, 200000000, 50000000)
}
};
static const int rtcl_divn3[] = { 2, 3, 4, 6 };
static const int rtcl_xdiv[] = { 2, 4, 2 };
/*
* module data structures
*/
#define RTCL_CLK_INFO(_idx, _name, _pname, _dname) \
{ \
.idx = _idx, \
.name = _name, \
.parent_name = _pname, \
.display_name = _dname, \
}
struct rtcl_clk_info {
unsigned int idx;
const char *name;
const char *parent_name;
const char *display_name;
};
struct rtcl_clk {
struct clk_hw hw;
unsigned int idx;
unsigned long min;
unsigned long max;
unsigned long rate;
unsigned long startup;
};
static const struct rtcl_clk_info rtcl_clk_info[CLK_COUNT] = {
RTCL_CLK_INFO(CLK_CPU, "cpu_clk", "ref_clk", "CPU"),
RTCL_CLK_INFO(CLK_MEM, "mem_clk", "ref_clk", "MEM"),
RTCL_CLK_INFO(CLK_LXB, "lxb_clk", "ref_clk", "LXB")
};
struct rtcl_dram {
int type;
int buswidth;
};
struct rtcl_sram {
int *pmark;
unsigned long vbase;
};
struct rtcl_ccu {
spinlock_t lock;
unsigned int soc;
struct rtcl_sram sram;
struct rtcl_dram dram;
struct device_node *np;
struct platform_device *pdev;
struct rtcl_clk clks[CLK_COUNT];
};
struct rtcl_ccu *rtcl_ccu;
#define rtcl_hw_to_clk(_hw) container_of(_hw, struct rtcl_clk, hw)
/*
* SRAM relocatable assembler functions. The dram() parts point to normal kernel
* memory while the sram() parts are the same functions but relocated to SRAM.
*/
extern void rtcl_838x_dram_start(void);
extern int rtcl_838x_dram_size;
extern void (*rtcl_838x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
static void (*rtcl_838x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
extern void rtcl_839x_dram_start(void);
extern int rtcl_839x_dram_size;
extern void (*rtcl_839x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
static void (*rtcl_839x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
/*
* clock setter/getter functions
*/
static unsigned long rtcl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
unsigned int ctrl0, ctrl1, div1, div2, cmu_ncode_in;
unsigned int cmu_sel_prediv, cmu_sel_div4, cmu_divn2, cmu_divn2_selb, cmu_divn3_sel;
if ((clk->idx >= CLK_COUNT) || (!rtcl_ccu) || (rtcl_ccu->soc >= SOC_COUNT))
return 0;
ctrl0 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL0][clk->idx]);
ctrl1 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL1][clk->idx]);
cmu_sel_prediv = 1 << RTL_PLL_CTRL0_CMU_SEL_PREDIV(ctrl0);
cmu_sel_div4 = RTL_PLL_CTRL0_CMU_SEL_DIV4(ctrl0) ? 4 : 1;
cmu_ncode_in = RTL_PLL_CTRL0_CMU_NCODE_IN(ctrl0) + 4;
cmu_divn2 = RTL_PLL_CTRL0_CMU_DIVN2(ctrl0) + 4;
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
if ((ctrl0 == 0) && (ctrl1 == 0) && (clk->idx == CLK_LXB))
return 200000000;
cmu_divn2_selb = RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1);
cmu_divn3_sel = rtcl_divn3[RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)];
break;
case SOC_RTL839X:
cmu_divn2_selb = RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1);
cmu_divn3_sel = rtcl_divn3[RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)];
break;
}
div1 = cmu_divn2_selb ? cmu_divn3_sel : cmu_divn2;
div2 = rtcl_xdiv[clk->idx];
return (((parent_rate / 16) * cmu_ncode_in) / (div1 * div2)) *
cmu_sel_prediv * cmu_sel_div4 * 16;
}
static int rtcl_838x_set_rate(int clk_idx, const struct rtcl_reg_set *reg)
{
unsigned long irqflags;
/*
* Runtime of this function (including locking)
* CPU: up to 14000 cycles / up to 56 us at 250 MHz (half default speed)
*/
spin_lock_irqsave(&rtcl_ccu->lock, irqflags);
rtcl_838x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1);
spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags);
return 0;
}
static int rtcl_839x_set_rate(int clk_idx, const struct rtcl_reg_set *reg)
{
unsigned long vpflags;
unsigned long irqflags;
/*
* Runtime of this function (including locking)
* CPU: up to 31000 cycles / up to 89 us at 350 MHz (half default speed)
*/
spin_lock_irqsave(&rtcl_ccu->lock, irqflags);
vpflags = dvpe();
rtcl_839x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1);
evpe(vpflags);
spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags);
return 0;
}
static int rtcl_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
{
int tab_idx;
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
const struct rtcl_rtab_set *rtab = &rtcl_rtab_set[rtcl_ccu->soc][clk->idx];
const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx];
if ((parent_rate != OSC_RATE) || (!rtcl_ccu->sram.vbase))
return -EINVAL;
/*
* Currently we do not know if SRAM is stable on these devices. Maybe someone
* changes memory in this region and does not care about proper allocation. So
* check if something might go wrong.
*/
if (unlikely(*rtcl_ccu->sram.pmark != RTL_SRAM_MARKER)) {
dev_err(&rtcl_ccu->pdev->dev, "SRAM code lost\n");
return -EINVAL;
}
tab_idx = (rate - round->min) / round->step;
if ((tab_idx < 0) || (tab_idx >= rtab->count) || (rtab->rset[tab_idx].rate != rate))
return -EINVAL;
rtcl_ccu->clks[clk->idx].rate = rate;
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
return rtcl_838x_set_rate(clk->idx, &rtab->rset[tab_idx]);
case SOC_RTL839X:
return rtcl_839x_set_rate(clk->idx, &rtab->rset[tab_idx]);
}
return -ENXIO;
}
static long rtcl_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
{
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
unsigned long rrate = max(clk->min, min(clk->max, rate));
const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx];
rrate = ((rrate + (round->step >> 1)) / round->step) * round->step;
rrate -= (rrate > clk->max) ? round->step : 0;
rrate += (rrate < clk->min) ? round->step : 0;
return rrate;
}
/*
* Initialization functions to register the CCU and its clocks
*/
#define RTCL_SRAM_FUNC(SOC, PBASE, FN) ({ \
rtcl_##SOC##_sram_##FN = ((void *)&rtcl_##SOC##_dram_##FN - \
(void *)&rtcl_##SOC##_dram_start) + \
(void *)PBASE; })
static const struct clk_ops rtcl_clk_ops = {
.set_rate = rtcl_set_rate,
.round_rate = rtcl_round_rate,
.recalc_rate = rtcl_recalc_rate,
};
static int rtcl_ccu_create(struct device_node *np)
{
int soc;
if (of_device_is_compatible(np, "realtek,rtl8380-clock"))
soc = SOC_RTL838X;
else if (of_device_is_compatible(np, "realtek,rtl8390-clock"))
soc = SOC_RTL839X;
else
return -ENXIO;
rtcl_ccu = kzalloc(sizeof(*rtcl_ccu), GFP_KERNEL);
if (IS_ERR(rtcl_ccu))
return -ENOMEM;
rtcl_ccu->np = np;
rtcl_ccu->soc = soc;
rtcl_ccu->dram.type = RTL_MC_MCR_DRAMTYPE(read_soc(RTL_MC_MCR));
rtcl_ccu->dram.buswidth = RTL_MC_DCR_BUSWIDTH(read_soc(RTL_MC_DCR));
spin_lock_init(&rtcl_ccu->lock);
return 0;
}
static int rtcl_register_clkhw(int clk_idx)
{
int ret;
struct clk *clk;
struct clk_init_data hw_init = { };
struct rtcl_clk *rclk = &rtcl_ccu->clks[clk_idx];
struct clk_parent_data parent_data = { .fw_name = rtcl_clk_info[clk_idx].parent_name };
rclk->idx = clk_idx;
rclk->hw.init = &hw_init;
hw_init.num_parents = 1;
hw_init.ops = &rtcl_clk_ops;
hw_init.parent_data = &parent_data;
hw_init.name = rtcl_clk_info[clk_idx].name;
ret = of_clk_hw_register(rtcl_ccu->np, &rclk->hw);
if (ret)
return ret;
clk_hw_register_clkdev(&rclk->hw, rtcl_clk_info[clk_idx].name, NULL);
clk = clk_get(NULL, rtcl_clk_info[clk_idx].name);
rclk->startup = clk_get_rate(clk);
clk_put(clk);
switch (clk_idx) {
case CLK_CPU:
rclk->min = rtcl_round_set[rtcl_ccu->soc][clk_idx].min;
rclk->max = rtcl_round_set[rtcl_ccu->soc][clk_idx].max;
break;
default:
/*
* TODO: This driver supports PLL reclocking and nothing else. Additional
* required steps for non CPU PLLs are missing. E.g. if we want to change memory
* clocks the right way we must adapt a lot of other settings. This includes
* MCR and DTRx timing registers (0xb80001000, 0xb8001008, ...) and a DLL reset
* so that hardware operates in the allowed limits. This is far too complex
* without official support. Avoid this for now.
*/
rclk->min = rclk->max = rclk->startup;
break;
}
return 0;
}
static struct clk_hw *rtcl_get_clkhw(struct of_phandle_args *clkspec, void *prv)
{
unsigned int idx = clkspec->args[0];
if (idx >= CLK_COUNT) {
pr_err("%s: Invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return &rtcl_ccu->clks[idx].hw;
}
static int rtcl_ccu_register_clocks(void)
{
int clk_idx, ret;
for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
ret = rtcl_register_clkhw(clk_idx);
if (ret) {
pr_err("%s: Couldn't register %s clock\n",
__func__, rtcl_clk_info[clk_idx].display_name);
goto err_hw_unregister;
}
}
ret = of_clk_add_hw_provider(rtcl_ccu->np, rtcl_get_clkhw, rtcl_ccu);
if (ret) {
pr_err("%s: Couldn't register clock provider of %s\n",
__func__, of_node_full_name(rtcl_ccu->np));
goto err_hw_unregister;
}
return 0;
err_hw_unregister:
for (--clk_idx; clk_idx >= 0; --clk_idx)
clk_hw_unregister(&rtcl_ccu->clks[clk_idx].hw);
return ret;
}
static int rtcl_init_sram(void)
{
struct gen_pool *sram_pool;
phys_addr_t sram_pbase;
unsigned long sram_vbase;
struct device_node *node;
struct platform_device *pdev = NULL;
void *dram_start;
int dram_size;
const char *wrn = ", rate setting disabled.\n";
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
dram_start = &rtcl_838x_dram_start;
dram_size = rtcl_838x_dram_size;
break;
case SOC_RTL839X:
dram_start = &rtcl_839x_dram_start;
dram_size = rtcl_839x_dram_size;
break;
default:
return -ENXIO;
}
for_each_compatible_node(node, NULL, "mmio-sram") {
pdev = of_find_device_by_node(node);
if (pdev) {
of_node_put(node);
break;
}
}
if (!pdev) {
dev_warn(&rtcl_ccu->pdev->dev, "no SRAM device found%s", wrn);
return -ENXIO;
}
sram_pool = gen_pool_get(&pdev->dev, NULL);
if (!sram_pool) {
dev_warn(&rtcl_ccu->pdev->dev, "SRAM pool unavailable%s", wrn);
goto err_put_device;
}
sram_vbase = gen_pool_alloc(sram_pool, dram_size);
if (!sram_vbase) {
dev_warn(&rtcl_ccu->pdev->dev, "can not allocate SRAM%s", wrn);
goto err_put_device;
}
sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_vbase);
memcpy((void *)sram_pbase, dram_start, dram_size);
flush_icache_range((unsigned long)sram_pbase, (unsigned long)(sram_pbase + dram_size));
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
RTCL_SRAM_FUNC(838x, sram_pbase, set_rate);
break;
case SOC_RTL839X:
RTCL_SRAM_FUNC(839x, sram_pbase, set_rate);
break;
}
rtcl_ccu->sram.pmark = (int *)((void *)sram_pbase + (dram_size - 4));
rtcl_ccu->sram.vbase = sram_vbase;
return 0;
err_put_device:
put_device(&pdev->dev);
return -ENXIO;
}
static void rtcl_ccu_log_early(void)
{
char meminfo[80], clkinfo[255], msg[255] = "rtl83xx-clk: initialized";
sprintf(meminfo, " (%d Bit DDR%d)", rtcl_ccu->dram.buswidth, rtcl_ccu->dram.type);
for (int clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
sprintf(clkinfo, ", %s %lu MHz", rtcl_clk_info[clk_idx].display_name,
rtcl_ccu->clks[clk_idx].startup / 1000000);
if (clk_idx == CLK_MEM)
strcat(clkinfo, meminfo);
strcat(msg, clkinfo);
}
pr_info("%s\n", msg);
}
static void rtcl_ccu_log_late(void)
{
struct rtcl_clk *rclk;
bool overclock = false;
char clkinfo[80], msg[255] = "rate setting enabled";
for (int clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
rclk = &rtcl_ccu->clks[clk_idx];
overclock |= rclk->max > rclk->startup;
sprintf(clkinfo, ", %s %lu-%lu MHz", rtcl_clk_info[clk_idx].display_name,
rclk->min / 1000000, rclk->max / 1000000);
strcat(msg, clkinfo);
}
if (overclock)
strcat(msg, ", OVERCLOCK AT OWN RISK");
dev_info(&rtcl_ccu->pdev->dev, "%s\n", msg);
}
/*
* Early registration: This module provides core startup clocks that are needed
* for generic SOC init and for further builtin devices (e.g. UART). Register
* asap via clock framework.
*/
static void __init rtcl_probe_early(struct device_node *np)
{
if (rtcl_ccu_create(np))
return;
if (rtcl_ccu_register_clocks())
kfree(rtcl_ccu);
else
rtcl_ccu_log_early();
}
CLK_OF_DECLARE_DRIVER(rtl838x_clk, "realtek,rtl8380-clock", rtcl_probe_early);
CLK_OF_DECLARE_DRIVER(rtl839x_clk, "realtek,rtl8390-clock", rtcl_probe_early);
/*
* Late registration: Finally register as normal platform driver. At this point
* we can make use of other modules like SRAM.
*/
static const struct of_device_id rtcl_dt_ids[] = {
{ .compatible = "realtek,rtl8380-clock" },
{ .compatible = "realtek,rtl8390-clock" },
{}
};
static int rtcl_probe_late(struct platform_device *pdev)
{
int ret;
if (!rtcl_ccu) {
dev_err(&pdev->dev, "early initialization not run");
return -ENXIO;
}
rtcl_ccu->pdev = pdev;
ret = rtcl_init_sram();
if (ret)
return ret;
rtcl_ccu_log_late();
return 0;
}
static struct platform_driver rtcl_platform_driver = {
.driver = {
.name = "rtl83xx-clk",
.of_match_table = rtcl_dt_ids,
},
.probe = rtcl_probe_late,
};
static int __init rtcl_init_subsys(void)
{
return platform_driver_register(&rtcl_platform_driver);
}
/*
* The driver does not know when SRAM module has finally loaded. With an
* arch_initcall() we might overtake SRAM initialization. Be polite and give the
* system a little more time.
*/
subsys_initcall(rtcl_init_subsys);

View file

@ -1,75 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL83XX clock headers
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
/*
* Switch registers (e.g. PLL)
*/
#define RTL_SW_CORE_BASE (0xbb000000)
#define RTL838X_PLL_GLB_CTRL (0x0fc0)
#define RTL838X_PLL_CPU_CTRL0 (0x0fc4)
#define RTL838X_PLL_CPU_CTRL1 (0x0fc8)
#define RTL838X_PLL_LXB_CTRL0 (0x0fd0)
#define RTL838X_PLL_LXB_CTRL1 (0x0fd4)
#define RTL838X_PLL_MEM_CTRL0 (0x0fdc)
#define RTL838X_PLL_MEM_CTRL1 (0x0fe0)
#define RTL839X_PLL_GLB_CTRL (0x0024)
#define RTL839X_PLL_CPU_CTRL0 (0x0028)
#define RTL839X_PLL_CPU_CTRL1 (0x002c)
#define RTL839X_PLL_LXB_CTRL0 (0x0038)
#define RTL839X_PLL_LXB_CTRL1 (0x003c)
#define RTL839X_PLL_MEM_CTRL0 (0x0048)
#define RTL839X_PLL_MEM_CTRL1 (0x004c)
#define RTL_PLL_CTRL0_CMU_SEL_PREDIV(v) (((v) >> 0) & 0x3)
#define RTL_PLL_CTRL0_CMU_SEL_DIV4(v) (((v) >> 2) & 0x1)
#define RTL_PLL_CTRL0_CMU_NCODE_IN(v) (((v) >> 4) & 0xff)
#define RTL_PLL_CTRL0_CMU_DIVN2(v) (((v) >> 12) & 0xff)
#define RTL838X_GLB_CTRL_EN_CPU_PLL_MASK (1 << 0)
#define RTL838X_GLB_CTRL_EN_LXB_PLL_MASK (1 << 1)
#define RTL838X_GLB_CTRL_EN_MEM_PLL_MASK (1 << 2)
#define RTL838X_GLB_CTRL_CPU_PLL_READY_MASK (1 << 8)
#define RTL838X_GLB_CTRL_LXB_PLL_READY_MASK (1 << 9)
#define RTL838X_GLB_CTRL_MEM_PLL_READY_MASK (1 << 10)
#define RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK (1 << 12)
#define RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 26) & 0x1)
#define RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 27) & 0x3)
#define RTL839X_GLB_CTRL_CPU_CLKSEL_MASK (1 << 11)
#define RTL839X_GLB_CTRL_MEM_CLKSEL_MASK (1 << 12)
#define RTL839X_GLB_CTRL_LXB_CLKSEL_MASK (1 << 13)
#define RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 2) & 0x1)
#define RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 0) & 0x3)
/*
* Core registers (e.g. memory controller)
*/
#define RTL_SOC_BASE (0xB8000000)
#define RTL_MC_MCR (0x1000)
#define RTL_MC_DCR (0x1004)
#define RTL_MC_DTR0 (0x1008)
#define RTL_MC_DTR1 (0x100c)
#define RTL_MC_DTR2 (0x1010)
#define RTL_MC_DMCR (0x101c)
#define RTL_MC_DACCR (0x1500)
#define RTL_MC_DCDR (0x1060)
#define RTL_MC_MCR_DRAMTYPE(v) ((((v) >> 28) & 0xf) + 1)
#define RTL_MC_DCR_BUSWIDTH(v) (8 << (((v) >> 24) & 0xf))
/*
* Other stuff
*/
#define RTL_SRAM_MARKER (0x5eaf00d5)
#define RTL_SRAM_BASE (0x9f000000)

View file

@ -1,286 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/cpuhotplug.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include "timer-of.h"
#define RTTM_DATA 0x0
#define RTTM_CNT 0x4
#define RTTM_CTRL 0x8
#define RTTM_INT 0xc
#define RTTM_CTRL_ENABLE BIT(28)
#define RTTM_INT_PENDING BIT(16)
#define RTTM_INT_ENABLE BIT(20)
/*
* The Otto platform provides multiple 28 bit timers/counters with the following
* operating logic. If enabled the timer counts up. Per timer one can set a
* maximum counter value as an end marker. If end marker is reached the timer
* fires an interrupt. If the timer "overflows" by reaching the end marker or
* by adding 1 to 0x0fffffff the counter is reset to 0. When this happens and
* the timer is in operating mode COUNTER it stops. In mode TIMER it will
* continue to count up.
*/
#define RTTM_CTRL_COUNTER 0
#define RTTM_CTRL_TIMER BIT(24)
#define RTTM_BIT_COUNT 28
#define RTTM_MIN_DELTA 8
#define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28)
/*
* Timers are derived from the LXB clock frequency. Usually this is a fixed
* multiple of the 25 MHz oscillator. The 930X SOC is an exception from that.
* Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its
* base. The only meaningful frequencies we can achieve from that are 175.000
* MHz and 153.125 MHz. The greatest common divisor of all explained possible
* speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency.
*/
#define RTTM_TICKS_PER_SEC 3125000
struct rttm_cs {
struct timer_of to;
struct clocksource cs;
};
/* Simple internal register functions */
static inline void rttm_set_counter(void __iomem *base, unsigned int counter)
{
iowrite32(counter, base + RTTM_CNT);
}
static inline unsigned int rttm_get_counter(void __iomem *base)
{
return ioread32(base + RTTM_CNT);
}
static inline void rttm_set_period(void __iomem *base, unsigned int period)
{
iowrite32(period, base + RTTM_DATA);
}
static inline void rttm_disable_timer(void __iomem *base)
{
iowrite32(0, base + RTTM_CTRL);
}
static inline void rttm_enable_timer(void __iomem *base, u32 mode, u32 divisor)
{
iowrite32(RTTM_CTRL_ENABLE | mode | divisor, base + RTTM_CTRL);
}
static inline void rttm_ack_irq(void __iomem *base)
{
iowrite32(ioread32(base + RTTM_INT) | RTTM_INT_PENDING, base + RTTM_INT);
}
static inline void rttm_enable_irq(void __iomem *base)
{
iowrite32(RTTM_INT_ENABLE, base + RTTM_INT);
}
static inline void rttm_disable_irq(void __iomem *base)
{
iowrite32(0, base + RTTM_INT);
}
/* Aggregated control functions for kernel clock framework */
#define RTTM_DEBUG(base) \
pr_debug("------------- %s %d %08x\n", __func__, \
smp_processor_id(), (u32)base)
static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
struct timer_of *to = to_timer_of(clkevt);
rttm_ack_irq(to->of_base.base);
RTTM_DEBUG(to->of_base.base);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static void rttm_stop_timer(void __iomem *base)
{
rttm_disable_timer(base);
rttm_ack_irq(base);
}
static void rttm_start_timer(struct timer_of *to, u32 mode)
{
rttm_set_counter(to->of_base.base, 0);
rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC);
}
static int rttm_next_event(unsigned long delta, struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, delta);
rttm_start_timer(to, RTTM_CTRL_COUNTER);
return 0;
}
static int rttm_state_oneshot(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
rttm_start_timer(to, RTTM_CTRL_COUNTER);
return 0;
}
static int rttm_state_periodic(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
rttm_start_timer(to, RTTM_CTRL_TIMER);
return 0;
}
static int rttm_state_shutdown(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
return 0;
}
static void rttm_setup_timer(void __iomem *base)
{
RTTM_DEBUG(base);
rttm_stop_timer(base);
rttm_set_period(base, 0);
}
static u64 rttm_read_clocksource(struct clocksource *cs)
{
struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs);
return (u64)rttm_get_counter(rcs->to.of_base.base);
}
/* Module initialization part. */
static DEFINE_PER_CPU(struct timer_of, rttm_to) = {
.flags = TIMER_OF_BASE | TIMER_OF_CLOCK | TIMER_OF_IRQ,
.of_irq = {
.flags = IRQF_PERCPU | IRQF_TIMER,
.handler = rttm_timer_interrupt,
},
.clkevt = {
.rating = 400,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_periodic = rttm_state_periodic,
.set_state_shutdown = rttm_state_shutdown,
.set_state_oneshot = rttm_state_oneshot,
.set_next_event = rttm_next_event
},
};
static int rttm_enable_clocksource(struct clocksource *cs)
{
struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs);
rttm_disable_irq(rcs->to.of_base.base);
rttm_setup_timer(rcs->to.of_base.base);
rttm_enable_timer(rcs->to.of_base.base, RTTM_CTRL_TIMER,
rcs->to.of_clk.rate / RTTM_TICKS_PER_SEC);
return 0;
}
struct rttm_cs rttm_cs = {
.to = {
.flags = TIMER_OF_BASE | TIMER_OF_CLOCK,
},
.cs = {
.name = "realtek_otto_timer",
.rating = 400,
.mask = CLOCKSOURCE_MASK(RTTM_BIT_COUNT),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = rttm_read_clocksource,
}
};
static u64 notrace rttm_read_clock(void)
{
return (u64)rttm_get_counter(rttm_cs.to.of_base.base);
}
static int rttm_cpu_starting(unsigned int cpu)
{
struct timer_of *to = per_cpu_ptr(&rttm_to, cpu);
RTTM_DEBUG(to->of_base.base);
to->clkevt.cpumask = cpumask_of(cpu);
irq_force_affinity(to->of_irq.irq, to->clkevt.cpumask);
clockevents_config_and_register(&to->clkevt, RTTM_TICKS_PER_SEC,
RTTM_MIN_DELTA, RTTM_MAX_DELTA);
rttm_enable_irq(to->of_base.base);
return 0;
}
static int __init rttm_probe(struct device_node *np)
{
int cpu, cpu_rollback;
struct timer_of *to;
int clkidx = num_possible_cpus();
/* Use the first n timers as per CPU clock event generators */
for_each_possible_cpu(cpu) {
to = per_cpu_ptr(&rttm_to, cpu);
to->of_irq.index = to->of_base.index = cpu;
if (timer_of_init(np, to)) {
pr_err("%s: setup of timer %d failed\n", __func__, cpu);
goto rollback;
}
rttm_setup_timer(to->of_base.base);
}
/* Activate the n'th + 1 timer as a stable CPU clocksource. */
to = &rttm_cs.to;
to->of_base.index = clkidx;
timer_of_init(np, to);
if (rttm_cs.to.of_base.base && rttm_cs.to.of_clk.rate) {
rttm_enable_clocksource(&rttm_cs.cs);
clocksource_register_hz(&rttm_cs.cs, RTTM_TICKS_PER_SEC);
sched_clock_register(rttm_read_clock, RTTM_BIT_COUNT, RTTM_TICKS_PER_SEC);
} else
pr_err("%s: setup of timer %d as clocksoure failed", __func__, clkidx);
return cpuhp_setup_state(CPUHP_AP_REALTEK_TIMER_STARTING,
"timer/realtek:online",
rttm_cpu_starting, NULL);
rollback:
pr_err("%s: timer registration failed\n", __func__);
for_each_possible_cpu(cpu_rollback) {
if (cpu_rollback == cpu)
break;
to = per_cpu_ptr(&rttm_to, cpu_rollback);
timer_of_cleanup(to);
}
return -EINVAL;
}
TIMER_OF_DECLARE(otto_timer, "realtek,otto-timer", rttm_probe);

View file

@ -1,483 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/of_platform.h>
#include "i2c-rtl9300.h"
#define REG(i, x) (i->base + x + (i->scl_num ? i->mst2_offset : 0))
#define REG_MASK(i, clear, set, reg) \
writel((readl(REG(i, reg)) & ~(clear)) | (set), REG(i, reg))
struct i2c_drv_data {
int scl0_pin;
int scl1_pin;
int sda0_pin;
struct i2c_algorithm *algo;
int (*read)(struct rtl9300_i2c *i2c, u8 *buf, int len);
int (*write)(struct rtl9300_i2c *i2c, u8 *buf, int len);
void (*reg_addr_set)(struct rtl9300_i2c *i2c, u32 reg, u16 len);
int (*config_xfer)(struct rtl9300_i2c *i2c, u16 addr, u16 len);
int (*execute_xfer)(struct rtl9300_i2c *i2c, char read_write, int size,
union i2c_smbus_data * data, int len);
void (*writel)(struct rtl9300_i2c *i2c, u32 data);
void (*config_io)(struct rtl9300_i2c *i2c, int scl_num, int sda_num);
u32 mst2_offset;
};
DEFINE_MUTEX(i2c_lock);
static void rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
/* Set register address width */
REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_MADDR_WIDTH, len << RTL9300_I2C_CTRL2_MADDR_WIDTH,
RTL9300_I2C_CTRL2);
/* Set register address */
REG_MASK(i2c, 0xffffff << RTL9300_I2C_CTRL1_MEM_ADDR, reg << RTL9300_I2C_CTRL1_MEM_ADDR,
RTL9300_I2C_CTRL1);
}
static void rtl9310_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
/* Set register address width */
REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_MADDR_WIDTH, len << RTL9310_I2C_CTRL_MADDR_WIDTH,
RTL9310_I2C_CTRL);
/* Set register address */
writel(reg, REG(i2c, RTL9310_I2C_MEMADDR));
}
static void rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
{
u32 v;
/* Set SCL pin */
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1);
/* Set SDA pin */
REG_MASK(i2c, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL,
i2c->sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1);
/* Set SDA pin to I2C functionality */
v = readl(i2c->base + RTL9300_I2C_MST_GLB_CTRL);
v |= BIT(i2c->sda_num);
writel(v, i2c->base + RTL9300_I2C_MST_GLB_CTRL);
}
static void rtl9310_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
{
u32 v;
/* Set SCL pin */
REG_MASK(i2c, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + scl_num), RTL9310_I2C_MST_IF_SEL);
/* Set SDA pin */
REG_MASK(i2c, 0x7 << RTL9310_I2C_CTRL_SDA_OUT_SEL,
i2c->sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL);
/* Set SDA pin to I2C functionality */
v = readl(i2c->base + RTL9310_I2C_MST_IF_SEL);
v |= BIT(i2c->sda_num);
writel(v, i2c->base + RTL9310_I2C_MST_IF_SEL);
}
static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
{
/* Set bus frequency */
REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_SCL_FREQ,
i2c->bus_freq << RTL9300_I2C_CTRL2_SCL_FREQ, RTL9300_I2C_CTRL2);
/* Set slave device address */
REG_MASK(i2c, 0x7f << RTL9300_I2C_CTRL2_DEV_ADDR,
addr << RTL9300_I2C_CTRL2_DEV_ADDR, RTL9300_I2C_CTRL2);
/* Set data length */
REG_MASK(i2c, 0xf << RTL9300_I2C_CTRL2_DATA_WIDTH,
((len - 1) & 0xf) << RTL9300_I2C_CTRL2_DATA_WIDTH, RTL9300_I2C_CTRL2);
/* Set read mode to random */
REG_MASK(i2c, 0x1 << RTL9300_I2C_CTRL2_READ_MODE, 0, RTL9300_I2C_CTRL2);
return 0;
}
static int rtl9310_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
{
/* Set bus frequency */
REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_SCL_FREQ,
i2c->bus_freq << RTL9310_I2C_CTRL_SCL_FREQ, RTL9310_I2C_CTRL);
/* Set slave device address */
REG_MASK(i2c, 0x7f << RTL9310_I2C_CTRL_DEV_ADDR,
addr << RTL9310_I2C_CTRL_DEV_ADDR, RTL9310_I2C_CTRL);
/* Set data length */
REG_MASK(i2c, 0xf << RTL9310_I2C_CTRL_DATA_WIDTH,
((len - 1) & 0xf) << RTL9310_I2C_CTRL_DATA_WIDTH, RTL9310_I2C_CTRL);
/* Set read mode to random */
REG_MASK(i2c, 0x1 << RTL9310_I2C_CTRL_READ_MODE, 0, RTL9310_I2C_CTRL);
return 0;
}
static int i2c_read(void __iomem *r0, u8 *buf, int len)
{
if (len > 16)
return -EIO;
for (int i = 0; i < len; i++) {
u32 v;
if (i % 4 == 0)
v = readl(r0 + i);
buf[i] = v;
v >>= 8;
}
return len;
}
static int i2c_write(void __iomem *r0, u8 *buf, int len)
{
if (len > 16)
return -EIO;
for (int i = 0; i < len; i++) {
u32 v;
if (! (i % 4))
v = 0;
v <<= 8;
v |= buf[i];
if (i % 4 == 3 || i == len - 1)
writel(v, r0 + (i / 4) * 4);
}
return len;
}
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_read(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len);
}
static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_write(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len);
}
static int rtl9310_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_read(REG(i2c, RTL9310_I2C_DATA), buf, len);
}
static int rtl9310_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_write(REG(i2c, RTL9310_I2C_DATA), buf, len);
}
static void rtl9300_writel(struct rtl9300_i2c *i2c, u32 data)
{
writel(data, REG(i2c, RTL9300_I2C_DATA_WORD0));
}
static void rtl9310_writel(struct rtl9300_i2c *i2c, u32 data)
{
writel(data, REG(i2c, RTL9310_I2C_DATA));
}
static int rtl9300_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
int size, union i2c_smbus_data * data, int len)
{
u32 v;
if (read_write == I2C_SMBUS_READ)
REG_MASK(i2c, BIT(RTL9300_I2C_CTRL1_RWOP), 0, RTL9300_I2C_CTRL1);
else
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_RWOP), RTL9300_I2C_CTRL1);
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_I2C_TRIG), RTL9300_I2C_CTRL1);
do {
v = readl(REG(i2c, RTL9300_I2C_CTRL1));
} while (v & BIT(RTL9300_I2C_CTRL1_I2C_TRIG));
if (v & BIT(RTL9300_I2C_CTRL1_I2C_FAIL))
return -EIO;
if (read_write == I2C_SMBUS_READ) {
if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
data->byte = readl(REG(i2c, RTL9300_I2C_DATA_WORD0));
} else if (size == I2C_SMBUS_WORD_DATA) {
data->word = readl(REG(i2c, RTL9300_I2C_DATA_WORD0));
} else if (len > 0) {
rtl9300_i2c_read(i2c, &data->block[0], len);
}
}
return 0;
}
static int rtl9310_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
int size, union i2c_smbus_data * data, int len)
{
u32 v;
if (read_write == I2C_SMBUS_READ)
REG_MASK(i2c, BIT(RTL9310_I2C_CTRL_RWOP), 0, RTL9310_I2C_CTRL);
else
REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_RWOP), RTL9310_I2C_CTRL);
REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_I2C_TRIG), RTL9310_I2C_CTRL);
do {
v = readl(REG(i2c, RTL9310_I2C_CTRL));
} while (v & BIT(RTL9310_I2C_CTRL_I2C_TRIG));
if (v & BIT(RTL9310_I2C_CTRL_I2C_FAIL))
return -EIO;
if (read_write == I2C_SMBUS_READ) {
if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
data->byte = readl(REG(i2c, RTL9310_I2C_DATA));
} else if (size == I2C_SMBUS_WORD_DATA) {
data->word = readl(REG(i2c, RTL9310_I2C_DATA));
} else if (len > 0) {
rtl9310_i2c_read(i2c, &data->block[0], len);
}
}
return 0;
}
static int rtl9300_i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct rtl9300_i2c *i2c = i2c_get_adapdata(adap);
struct i2c_drv_data *drv_data = (struct i2c_drv_data *)device_get_match_data(i2c->dev);
int len = 0, ret;
mutex_lock(&i2c_lock);
switch (size) {
case I2C_SMBUS_QUICK:
drv_data->config_xfer(i2c, addr, 0);
drv_data->reg_addr_set(i2c, 0, 0);
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE) {
drv_data->config_xfer(i2c, addr, 0);
drv_data->reg_addr_set(i2c, command, 1);
} else {
drv_data->config_xfer(i2c, addr, 1);
drv_data->reg_addr_set(i2c, 0, 0);
}
break;
case I2C_SMBUS_BYTE_DATA:
pr_debug("I2C_SMBUS_BYTE_DATA %02x, read %d cmd %02x\n", addr, read_write, command);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, 1);
if (read_write == I2C_SMBUS_WRITE) {
pr_debug("--> data %02x\n", data->byte);
drv_data->writel(i2c, data->byte);
}
break;
case I2C_SMBUS_WORD_DATA:
pr_debug("I2C_SMBUS_WORD %02x, read %d\n", addr, read_write);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, 2);
if (read_write == I2C_SMBUS_WRITE)
drv_data->writel(i2c, data->word);
break;
case I2C_SMBUS_BLOCK_DATA:
pr_debug("I2C_SMBUS_BLOCK_DATA %02x, read %d, len %d\n",
addr, read_write, data->block[0]);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, data->block[0]);
if (read_write == I2C_SMBUS_WRITE)
drv_data->write(i2c, &data->block[1], data->block[0]);
len = data->block[0];
break;
default:
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}
ret = drv_data->execute_xfer(i2c, read_write, size, data, len);
mutex_unlock(&i2c_lock);
return ret;
}
static u32 rtl9300_i2c_func(struct i2c_adapter *a)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static const struct i2c_algorithm rtl9300_i2c_algo = {
.smbus_xfer = rtl9300_i2c_smbus_xfer,
.functionality = rtl9300_i2c_func,
};
struct i2c_adapter_quirks rtl9300_i2c_quirks = {
.flags = I2C_AQ_NO_CLK_STRETCH,
.max_read_len = 16,
.max_write_len = 16,
};
static int rtl9300_i2c_probe(struct platform_device *pdev)
{
struct rtl9300_i2c *i2c;
struct i2c_adapter *adap;
struct i2c_drv_data *drv_data;
struct device_node *node = pdev->dev.of_node;
u32 clock_freq, pin;
int ret = 0;
pr_info("%s probing I2C adapter\n", __func__);
if (!node) {
dev_err(i2c->dev, "No DT found\n");
return -EINVAL;
}
drv_data = (struct i2c_drv_data *) device_get_match_data(&pdev->dev);
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rtl9300_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->base = devm_platform_ioremap_resource(pdev, 0);
i2c->mst2_offset = drv_data->mst2_offset;
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
pr_debug("%s base memory %08x\n", __func__, (u32)i2c->base);
i2c->dev = &pdev->dev;
if (of_property_read_u32(node, "clock-frequency", &clock_freq)) {
clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
}
switch(clock_freq) {
case I2C_MAX_STANDARD_MODE_FREQ:
i2c->bus_freq = RTL9300_I2C_STD_FREQ;
break;
case I2C_MAX_FAST_MODE_FREQ:
i2c->bus_freq = RTL9300_I2C_FAST_FREQ;
break;
default:
dev_warn(i2c->dev, "clock-frequency %d not supported\n", clock_freq);
return -EINVAL;
}
dev_info(&pdev->dev, "SCL speed %d, mode is %d\n", clock_freq, i2c->bus_freq);
if (of_property_read_u32(node, "scl-pin", &pin)) {
dev_warn(i2c->dev, "SCL pin not found in DT, using default\n");
pin = drv_data->scl0_pin;
}
if (!(pin == drv_data->scl0_pin || pin == drv_data->scl1_pin)) {
dev_warn(i2c->dev, "SCL pin %d not supported\n", pin);
return -EINVAL;
}
i2c->scl_num = pin == drv_data->scl0_pin ? 0 : 1;
pr_info("%s scl_num %d\n", __func__, i2c->scl_num);
if (of_property_read_u32(node, "sda-pin", &pin)) {
dev_warn(i2c->dev, "SDA pin not found in DT, using default \n");
pin = drv_data->sda0_pin;
}
i2c->sda_num = pin - drv_data->sda0_pin;
if (i2c->sda_num < 0 || i2c->sda_num > 7) {
dev_warn(i2c->dev, "SDA pin %d not supported\n", pin);
return -EINVAL;
}
pr_info("%s sda_num %d\n", __func__, i2c->sda_num);
adap = &i2c->adap;
adap->owner = THIS_MODULE;
adap->algo = &rtl9300_i2c_algo;
adap->retries = 3;
adap->dev.parent = &pdev->dev;
i2c_set_adapdata(adap, i2c);
adap->dev.of_node = node;
strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
platform_set_drvdata(pdev, i2c);
drv_data->config_io(i2c, i2c->scl_num, i2c->sda_num);
ret = i2c_add_adapter(adap);
return ret;
}
static int rtl9300_i2c_remove(struct platform_device *pdev)
{
struct rtl9300_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
return 0;
}
struct i2c_drv_data rtl9300_i2c_drv_data = {
.scl0_pin = 8,
.scl1_pin = 17,
.sda0_pin = 9,
.read = rtl9300_i2c_read,
.write = rtl9300_i2c_write,
.reg_addr_set = rtl9300_i2c_reg_addr_set,
.config_xfer = rtl9300_i2c_config_xfer,
.execute_xfer = rtl9300_execute_xfer,
.writel = rtl9300_writel,
.config_io = rtl9300_i2c_config_io,
.mst2_offset = 0x1c,
};
struct i2c_drv_data rtl9310_i2c_drv_data = {
.scl0_pin = 13,
.scl1_pin = 14,
.sda0_pin = 0,
.read = rtl9310_i2c_read,
.write = rtl9310_i2c_write,
.reg_addr_set = rtl9310_i2c_reg_addr_set,
.config_xfer = rtl9310_i2c_config_xfer,
.execute_xfer = rtl9310_execute_xfer,
.writel = rtl9310_writel,
.config_io = rtl9310_i2c_config_io,
.mst2_offset = 0x18,
};
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
{ .compatible = "realtek,rtl9300-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{ .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids);
static struct platform_driver rtl9300_i2c_driver = {
.probe = rtl9300_i2c_probe,
.remove = rtl9300_i2c_remove,
.driver = {
.name = "i2c-rtl9300",
.pm = NULL,
.of_match_table = i2c_rtl9300_dt_ids,
},
};
module_platform_driver(rtl9300_i2c_driver);
MODULE_AUTHOR("Birger Koblitz");
MODULE_DESCRIPTION("RTL9300 I2C host driver");
MODULE_LICENSE("GPL v2");

View file

@ -1,62 +0,0 @@
#ifndef I2C_RTL9300_H
#define I2C_RTL9300_H
#include <linux/i2c.h>
#define RTL9300_I2C_CTRL1 0x00
#define RTL9300_I2C_CTRL1_MEM_ADDR 8
#define RTL9300_I2C_CTRL1_SDA_OUT_SEL 4
#define RTL9300_I2C_CTRL1_GPIO8_SCL_SEL 3
#define RTL9300_I2C_CTRL1_RWOP 2
#define RTL9300_I2C_CTRL1_I2C_FAIL 1
#define RTL9300_I2C_CTRL1_I2C_TRIG 0
#define RTL9300_I2C_CTRL2 0x04
#define RTL9300_I2C_CTRL2_DRIVE_ACK_DELAY 20
#define RTL9300_I2C_CTRL2_CHECK_ACK_DELAY 16
#define RTL9300_I2C_CTRL2_READ_MODE 15
#define RTL9300_I2C_CTRL2_DEV_ADDR 8
#define RTL9300_I2C_CTRL2_DATA_WIDTH 4
#define RTL9300_I2C_CTRL2_MADDR_WIDTH 2
#define RTL9300_I2C_CTRL2_SCL_FREQ 0
#define RTL9300_I2C_DATA_WORD0 0x08
#define RTL9300_I2C_MST_GLB_CTRL 0x18
#define RTL9310_I2C_MST_IF_CTRL 0x00
#define RTL9310_I2C_MST_IF_SEL 0x04
#define RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL 12
#define RTL9310_I2C_CTRL 0x08
#define RTL9310_I2C_CTRL_SCL_FREQ 30
#define RTL9310_I2C_CTRL_CHECK_ACK_DELAY 26
#define RTL9310_I2C_CTRL_DRIVE_ACK_DELAY 22
#define RTL9310_I2C_CTRL_SDA_OUT_SEL 18
#define RTL9310_I2C_CTRL_DEV_ADDR 11
#define RTL9310_I2C_CTRL_MADDR_WIDTH 9
#define RTL9310_I2C_CTRL_DATA_WIDTH 5
#define RTL9310_I2C_CTRL_READ_MODE 4
#define RTL9310_I2C_CTRL_RWOP 2
#define RTL9310_I2C_CTRL_I2C_FAIL 1
#define RTL9310_I2C_CTRL_I2C_TRIG 0
#define RTL9310_I2C_MEMADDR 0x0c
#define RTL9310_I2C_DATA 0x10
#define RTL9300_I2C_STD_FREQ 0
#define RTL9300_I2C_FAST_FREQ 1
struct rtl9300_i2c {
void __iomem *base;
u32 mst2_offset;
struct device *dev;
struct i2c_adapter adap;
u8 bus_freq;
u8 sda_num; /* SDA channel number */
u8 scl_num; /* SCL channel, mapping to master 1 or 2 */
};
#endif

View file

@ -1,293 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C multiplexer for the 2 I2C Masters of the RTL9300
* with up to 8 channels each, but which are not entirely
* independent of each other
*/
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "../busses/i2c-rtl9300.h"
#define NUM_MASTERS 2
#define NUM_BUSSES 8
#define REG(mst, x) (mux->base + x + (mst ? mux->i2c->mst2_offset : 0))
#define REG_MASK(mst, clear, set, reg) \
writel((readl(REG((mst),(reg))) & ~(clear)) | (set), REG((mst),(reg)))
struct channel {
u8 sda_num;
u8 scl_num;
};
static struct channel channels[NUM_MASTERS * NUM_BUSSES];
struct rtl9300_mux {
void __iomem *base;
struct device *dev;
struct i2c_adapter *parent;
struct rtl9300_i2c * i2c;
};
struct i2c_mux_data {
int scl0_pin;
int scl1_pin;
int sda0_pin;
int sda_pins;
int (*i2c_mux_select)(struct i2c_mux_core *muxc, u32 chan);
int (*i2c_mux_deselect)(struct i2c_mux_core *muxc, u32 chan);
void (*sda_sel)(struct i2c_mux_core *muxc, int pin);
};
static int rtl9300_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
/* Set SCL pin */
REG_MASK(channels[chan].scl_num, 0,
BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1);
/* Set SDA pin */
REG_MASK(channels[chan].scl_num, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL,
channels[chan].sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1);
mux->i2c->sda_num = channels[chan].sda_num;
mux->i2c->scl_num = channels[chan].scl_num;
return 0;
}
static int rtl9310_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
/* Set SCL pin */
REG_MASK(0, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + channels[chan].scl_num),
RTL9310_I2C_MST_IF_SEL);
/* Set SDA pin */
REG_MASK(channels[chan].scl_num, 0xf << RTL9310_I2C_CTRL_SDA_OUT_SEL,
channels[chan].sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL);
mux->i2c->sda_num = channels[chan].sda_num;
mux->i2c->scl_num = channels[chan].scl_num;
return 0;
}
static int rtl9300_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
{
return 0;
}
static void rtl9300_sda_sel(struct i2c_mux_core *muxc, int pin)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
u32 v;
/* Set SDA pin to I2C functionality */
v = readl(REG(0, RTL9300_I2C_MST_GLB_CTRL));
v |= BIT(pin);
writel(v, REG(0, RTL9300_I2C_MST_GLB_CTRL));
}
static void rtl9310_sda_sel(struct i2c_mux_core *muxc, int pin)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
u32 v;
/* Set SDA pin to I2C functionality */
v = readl(REG(0, RTL9310_I2C_MST_IF_SEL));
v |= BIT(pin);
writel(v, REG(0, RTL9310_I2C_MST_IF_SEL));
}
static struct device_node *mux_parent_adapter(struct device *dev, struct rtl9300_mux *mux)
{
struct device_node *node = dev->of_node;
struct device_node *parent_np;
struct i2c_adapter *parent;
parent_np = of_parse_phandle(node, "i2c-parent", 0);
if (!parent_np) {
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
parent = of_find_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
if (!(of_device_is_compatible(parent_np, "realtek,rtl9300-i2c") ||
of_device_is_compatible(parent_np, "realtek,rtl9310-i2c"))){
dev_err(dev, "I2C parent not an RTL9300 I2C controller\n");
return ERR_PTR(-ENODEV);
}
mux->parent = parent;
mux->i2c = (struct rtl9300_i2c *)i2c_get_adapdata(parent);
mux->base = mux->i2c->base;
return parent_np;
}
struct i2c_mux_data rtl9300_i2c_mux_data = {
.scl0_pin = 8,
.scl1_pin = 17,
.sda0_pin = 9,
.sda_pins = 8,
.i2c_mux_select = rtl9300_i2c_mux_select,
.i2c_mux_deselect = rtl9300_i2c_mux_deselect,
.sda_sel = rtl9300_sda_sel,
};
struct i2c_mux_data rtl9310_i2c_mux_data = {
.scl0_pin = 13,
.scl1_pin = 14,
.sda0_pin = 0,
.sda_pins = 16,
.i2c_mux_select = rtl9310_i2c_mux_select,
.i2c_mux_deselect = rtl9300_i2c_mux_deselect,
.sda_sel = rtl9310_sda_sel,
};
static const struct of_device_id rtl9300_i2c_mux_of_match[] = {
{ .compatible = "realtek,i2c-mux-rtl9300", .data = (void *) &rtl9300_i2c_mux_data},
{ .compatible = "realtek,i2c-mux-rtl9310", .data = (void *) &rtl9310_i2c_mux_data},
{},
};
MODULE_DEVICE_TABLE(of, rtl9300_i2c_mux_of_match);
static int rtl9300_i2c_mux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct device_node *parent_np;
struct device_node *child;
struct i2c_mux_core *muxc;
struct rtl9300_mux *mux;
struct i2c_mux_data *mux_data;
int children;
int ret;
pr_info("%s probing I2C adapter\n", __func__);
if (!node) {
dev_err(dev, "No DT found\n");
return -EINVAL;
}
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
mux->dev = dev;
mux_data = (struct i2c_mux_data *) device_get_match_data(dev);
parent_np = mux_parent_adapter(dev, mux);
if (IS_ERR(parent_np))
return dev_err_probe(dev, PTR_ERR(parent_np), "i2c-parent adapter not found\n");
pr_info("%s base memory %08x\n", __func__, (u32)mux->base);
children = of_get_child_count(node);
muxc = i2c_mux_alloc(mux->parent, dev, children, 0, 0,
mux_data->i2c_mux_select, mux_data->i2c_mux_deselect);
if (!muxc) {
ret = -ENOMEM;
goto err_parent;
}
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
for_each_child_of_node(node, child) {
u32 chan;
u32 pin;
ret = of_property_read_u32(child, "reg", &chan);
if (ret < 0) {
dev_err(dev, "no reg property for node '%pOFn'\n",
child);
goto err_children;
}
if (chan >= NUM_MASTERS * NUM_BUSSES) {
dev_err(dev, "invalid reg %u\n", chan);
ret = -EINVAL;
goto err_children;
}
if (of_property_read_u32(child, "scl-pin", &pin)) {
dev_warn(dev, "SCL pin not found in DT, using default\n");
pin = mux_data->scl0_pin;
}
if (!(pin == mux_data->scl0_pin || pin == mux_data->scl1_pin)) {
dev_warn(dev, "SCL pin %d not supported\n", pin);
ret = -EINVAL;
goto err_children;
}
channels[chan].scl_num = pin == mux_data->scl0_pin ? 0 : 1;
pr_info("%s channel %d scl_num %d\n", __func__, chan, channels[chan].scl_num);
if (of_property_read_u32(child, "sda-pin", &pin)) {
dev_warn(dev, "SDA pin not found in DT, using default \n");
pin = mux_data->sda0_pin;
}
channels[chan].sda_num = pin - mux_data->sda0_pin;
if (channels[chan].sda_num < 0 || channels[chan].sda_num >= mux_data->sda_pins) {
dev_warn(dev, "SDA pin %d not supported\n", pin);
return -EINVAL;
}
pr_info("%s channel %d sda_num %d\n", __func__, chan, channels[chan].sda_num);
mux_data->sda_sel(muxc, channels[chan].sda_num);
ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
if (ret)
goto err_children;
}
dev_info(dev, "%d-port mux on %s adapter\n", children, mux->parent->name);
return 0;
err_children:
i2c_mux_del_adapters(muxc);
err_parent:
i2c_put_adapter(mux->parent);
return ret;
}
static int rtl9300_i2c_mux_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
return 0;
}
static struct platform_driver i2c_mux_driver = {
.probe = rtl9300_i2c_mux_probe,
.remove = rtl9300_i2c_mux_remove,
.driver = {
.name = "i2c-mux-rtl9300",
.of_match_table = rtl9300_i2c_mux_of_match,
},
};
module_platform_driver(i2c_mux_driver);
MODULE_DESCRIPTION("RTL9300 I2C multiplexer driver");
MODULE_AUTHOR("Birger Koblitz");
MODULE_LICENSE("GPL v2");

View file

@ -1,7 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config NET_DSA_RTL83XX
tristate "Realtek RTL838x/RTL839x switch support"
depends on MACH_REALTEK_RTL
select NET_DSA_TAG_TRAILER
help
This driver adds support for Realtek RTL83xx series switching.

View file

@ -1,3 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_RTL83XX) += common.o dsa.o \
rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o tc.o

File diff suppressed because it is too large Load diff

View file

@ -1,710 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
#define RTL838X_DRIVER_NAME "rtl838x"
#define RTL8390_LED_GLB_CTRL (0x00E4)
#define RTL8390_LED_SET_2_3_CTRL (0x00E8)
#define RTL8390_LED_SET_0_1_CTRL (0x00EC)
#define RTL8390_LED_COPR_SET_SEL_CTRL(p) (0x00F0 + (((p >> 4) << 2)))
#define RTL8390_LED_FIB_SET_SEL_CTRL(p) (0x0100 + (((p >> 4) << 2)))
#define RTL8390_LED_COPR_PMASK_CTRL(p) (0x0110 + (((p >> 5) << 2)))
#define RTL8390_LED_FIB_PMASK_CTRL(p) (0x00118 + (((p >> 5) << 2)))
#define RTL8390_LED_COMBO_CTRL(p) (0x0120 + (((p >> 5) << 2)))
#define RTL8390_LED_SW_CTRL (0x0128)
#define RTL8390_LED_SW_P_EN_CTRL(p) (0x012C + (((p / 10) << 2)))
#define RTL8390_LED_SW_P_CTRL(p) (0x0144 + (((p) << 2)))
#define RTL838X_MIR_QID_CTRL(grp) (0xAD44 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(grp) (0xAA70 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_TX_CTRL (0xA350)
#define RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL (0xAA80)
#define RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL (0xAA84)
#define RTL839X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2)))
#define RTL839X_MIR_RSPAN_TX_CTRL (0x69b0)
#define RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL (0x2550)
#define RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL (0x2554)
#define RTL839X_MIR_SAMPLE_RATE_CTRL (0x2558)
#define RTL838X_STAT_PRVTE_DROP_COUNTERS (0x6A00)
#define RTL839X_STAT_PRVTE_DROP_COUNTERS (0x3E00)
#define RTL930X_STAT_PRVTE_DROP_COUNTERS (0xB5B8)
#define RTL931X_STAT_PRVTE_DROP_COUNTERS (0xd800)
const char *rtl838x_drop_cntr[] = {
"ALE_TX_GOOD_PKTS", "MAC_RX_DROP", "ACL_FWD_DROP", "HW_ATTACK_PREVENTION_DROP",
"RMA_DROP", "VLAN_IGR_FLTR_DROP", "INNER_OUTER_CFI_EQUAL_1_DROP", "PORT_MOVE_DROP",
"NEW_SA_DROP", "MAC_LIMIT_SYS_DROP", "MAC_LIMIT_VLAN_DROP", "MAC_LIMIT_PORT_DROP",
"SWITCH_MAC_DROP", "ROUTING_EXCEPTION_DROP", "DA_LKMISS_DROP", "RSPAN_DROP",
"ACL_LKMISS_DROP", "ACL_DROP", "INBW_DROP", "IGR_METER_DROP",
"ACCEPT_FRAME_TYPE_DROP", "STP_IGR_DROP", "INVALID_SA_DROP", "SA_BLOCKING_DROP",
"DA_BLOCKING_DROP", "L2_INVALID_DPM_DROP", "MCST_INVALID_DPM_DROP", "RX_FLOW_CONTROL_DROP",
"STORM_SPPRS_DROP", "LALS_DROP", "VLAN_EGR_FILTER_DROP", "STP_EGR_DROP",
"SRC_PORT_FILTER_DROP", "PORT_ISOLATION_DROP", "ACL_FLTR_DROP", "MIRROR_FLTR_DROP",
"TX_MAX_DROP", "LINK_DOWN_DROP", "FLOW_CONTROL_DROP", "BRIDGE .1d discards"
};
const char *rtl839x_drop_cntr[] = {
"ALE_TX_GOOD_PKTS", "ERROR_PKTS", "EGR_ACL_DROP", "EGR_METER_DROP",
"OAM", "CFM" "VLAN_IGR_FLTR", "VLAN_ERR",
"INNER_OUTER_CFI_EQUAL_1", "VLAN_TAG_FORMAT", "SRC_PORT_SPENDING_TREE", "INBW",
"RMA", "HW_ATTACK_PREVENTION", "PROTO_STORM", "MCAST_SA",
"IGR_ACL_DROP", "IGR_METER_DROP", "DFLT_ACTION_FOR_MISS_ACL_AND_C2SC", "NEW_SA",
"PORT_MOVE", "SA_BLOCKING", "ROUTING_EXCEPTION", "SRC_PORT_SPENDING_TREE_NON_FWDING",
"MAC_LIMIT", "UNKNOW_STORM", "MISS_DROP", "CPU_MAC_DROP",
"DA_BLOCKING", "SRC_PORT_FILTER_BEFORE_EGR_ACL", "VLAN_EGR_FILTER", "SPANNING_TRE",
"PORT_ISOLATION", "OAM_EGRESS_DROP", "MIRROR_ISOLATION", "MAX_LEN_BEFORE_EGR_ACL",
"SRC_PORT_FILTER_BEFORE_MIRROR", "MAX_LEN_BEFORE_MIRROR", "SPECIAL_CONGEST_BEFORE_MIRROR",
"LINK_STATUS_BEFORE_MIRROR",
"WRED_BEFORE_MIRROR", "MAX_LEN_AFTER_MIRROR", "SPECIAL_CONGEST_AFTER_MIRROR",
"LINK_STATUS_AFTER_MIRROR",
"WRED_AFTER_MIRROR"
};
const char *rtl930x_drop_cntr[] = {
"OAM_PARSER", "UC_RPF", "DEI_CFI", "MAC_IP_SUBNET_BASED_VLAN", "VLAN_IGR_FILTER",
"L2_UC_MC", "IPV_IP6_MC_BRIDGE", "PTP", "USER_DEF_0_3", "RESERVED",
"RESERVED1", "RESERVED2", "BPDU_RMA", "LACP", "LLDP",
"EAPOL", "XX_RMA", "L3_IPUC_NON_IP", "IP4_IP6_HEADER_ERROR", "L3_BAD_IP",
"L3_DIP_DMAC_MISMATCH", "IP4_IP_OPTION", "IP_UC_MC_ROUTING_LOOK_UP_MISS", "L3_DST_NULL_INTF",
"L3_PBR_NULL_INTF",
"HOST_NULL_INTF", "ROUTE_NULL_INTF", "BRIDGING_ACTION", "ROUTING_ACTION", "IPMC_RPF",
"L2_NEXTHOP_AGE_OUT", "L3_UC_TTL_FAIL", "L3_MC_TTL_FAIL", "L3_UC_MTU_FAIL", "L3_MC_MTU_FAIL",
"L3_UC_ICMP_REDIR", "IP6_MLD_OTHER_ACT", "ND", "IP_MC_RESERVED", "IP6_HBH",
"INVALID_SA", "L2_HASH_FULL", "NEW_SA", "PORT_MOVE_FORBID", "STATIC_PORT_MOVING",
"DYNMIC_PORT_MOVING", "L3_CRC", "MAC_LIMIT", "ATTACK_PREVENT", "ACL_FWD_ACTION",
"OAMPDU", "OAM_MUX", "TRUNK_FILTER", "ACL_DROP", "IGR_BW",
"ACL_METER", "VLAN_ACCEPT_FRAME_TYPE", "MSTP_SRC_DROP_DISABLED_BLOCKING", "SA_BLOCK", "DA_BLOCK",
"STORM_CONTROL", "VLAN_EGR_FILTER", "MSTP_DESTINATION_DROP", "SRC_PORT_FILTER", "PORT_ISOLATION",
"TX_MAX_FRAME_SIZE", "EGR_LINK_STATUS", "MAC_TX_DISABLE", "MAC_PAUSE_FRAME", "MAC_RX_DROP",
"MIRROR_ISOLATE", "RX_FC", "EGR_QUEUE", "HSM_RUNOUT", "ROUTING_DISABLE", "INVALID_L2_NEXTHOP_ENTRY",
"L3_MC_SRC_FLT", "CPUTAG_FLT", "FWD_PMSK_NULL", "IPUC_ROUTING_LOOKUP_MISS", "MY_DEV_DROP",
"STACK_NONUC_BLOCKING_PMSK", "STACK_PORT_NOT_FOUND", "ACL_LOOPBACK_DROP", "IP6_ROUTING_EXT_HEADER"
};
const char *rtl931x_drop_cntr[] = {
"ALE_RX_GOOD_PKTS", "RX_MAX_FRAME_SIZE", "MAC_RX_DROP", "OPENFLOW_IP_MPLS_TTL", "OPENFLOW_TBL_MISS",
"IGR_BW", "SPECIAL_CONGEST", "EGR_QUEUE", "RESERVED", "EGR_LINK_STATUS", "STACK_UCAST_NONUCAST_TTL", /* 10 */
"STACK_NONUC_BLOCKING_PMSK", "L2_CRC", "SRC_PORT_FILTER", "PARSER_PACKET_TOO_LONG", "PARSER_MALFORM_PACKET",
"MPLS_OVER_2_LBL", "EACL_METER", "IACL_METER", "PROTO_STORM", "INVALID_CAPWAP_HEADER", /* 20 */
"MAC_IP_SUBNET_BASED_VLAN", "OAM_PARSER", "UC_MC_RPF", "IP_MAC_BINDING_MATCH_MISMATCH", "SA_BLOCK",
"TUNNEL_IP_ADDRESS_CHECK", "EACL_DROP", "IACL_DROP", "ATTACK_PREVENT", "SYSTEM_PORT_LIMIT_LEARN", /* 30 */
"OAMPDU", "CCM_RX", "CFM_UNKNOWN_TYPE", "LBM_LBR_LTM_LTR", "Y_1731", "VLAN_LIMIT_LEARN",
"VLAN_ACCEPT_FRAME_TYPE", "CFI_1", "STATIC_DYNAMIC_PORT_MOVING", "PORT_MOVE_FORBID", /* 40 */
"L3_CRC", "BPDU_PTP_LLDP_EAPOL_RMA", "MSTP_SRC_DROP_DISABLED_BLOCKING", "INVALID_SA", "NEW_SA",
"VLAN_IGR_FILTER", "IGR_VLAN_CONVERT", "GRATUITOUS_ARP", "MSTP_SRC_DROP", "L2_HASH_FULL", /* 50 */
"MPLS_UNKNOWN_LBL", "L3_IPUC_NON_IP", "TTL", "MTU", "ICMP_REDIRECT", "STORM_CONTROL", "L3_DIP_DMAC_MISMATCH",
"IP4_IP_OPTION", "IP6_HBH_EXT_HEADER", "IP4_IP6_HEADER_ERROR", /* 60 */
"ROUTING_IP_ADDR_CHECK", "ROUTING_EXCEPTION", "DA_BLOCK", "OAM_MUX", "PORT_ISOLATION", "VLAN_EGR_FILTER",
"MIRROR_ISOLATE", "MSTP_DESTINATION_DROP", "L2_MC_BRIDGE", "IP_UC_MC_ROUTING_LOOK_UP_MISS", /* 70 */
"L2_UC", "L2_MC", "IP4_MC", "IP6_MC", "L3_UC_MC_ROUTE", "UNKNOWN_L2_UC_FLPM", "BC_FLPM",
"VLAN_PRO_UNKNOWN_L2_MC_FLPM", "VLAN_PRO_UNKNOWN_IP4_MC_FLPM", "VLAN_PROFILE_UNKNOWN_IP6_MC_FLPM", /* 80 */
};
static ssize_t rtl838x_common_read(char __user *buffer, size_t count,
loff_t *ppos, unsigned int value)
{
char *buf;
ssize_t len;
if (*ppos != 0)
return 0;
buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static ssize_t rtl838x_common_write(const char __user *buffer, size_t count,
loff_t *ppos, unsigned int *value)
{
char b[32];
ssize_t len;
int ret;
if (*ppos != 0)
return -EINVAL;
if (count >= sizeof(b))
return -ENOSPC;
len = simple_write_to_buffer(b, sizeof(b) - 1, ppos,
buffer, count);
if (len < 0)
return len;
b[len] = '\0';
ret = kstrtouint(b, 16, value);
if (ret)
return -EIO;
return len;
}
static ssize_t stp_state_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
int value = rtl83xx_port_get_stp_state(ds->priv, p->dp->index);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t stp_state_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
rtl83xx_port_stp_state_set(p->dp->ds, p->dp->index, (u8)value);
return res;
}
static const struct file_operations stp_state_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = stp_state_read,
.write = stp_state_write,
};
static ssize_t drop_counter_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_switch_priv *priv = filp->private_data;
const char **d;
u32 v;
char *buf;
int n = 0, len, offset;
int num;
switch (priv->family_id) {
case RTL8380_FAMILY_ID:
d = rtl838x_drop_cntr;
offset = RTL838X_STAT_PRVTE_DROP_COUNTERS;
num = 40;
break;
case RTL8390_FAMILY_ID:
d = rtl839x_drop_cntr;
offset = RTL839X_STAT_PRVTE_DROP_COUNTERS;
num = 45;
break;
case RTL9300_FAMILY_ID:
d = rtl930x_drop_cntr;
offset = RTL930X_STAT_PRVTE_DROP_COUNTERS;
num = 85;
break;
case RTL9310_FAMILY_ID:
d = rtl931x_drop_cntr;
offset = RTL931X_STAT_PRVTE_DROP_COUNTERS;
num = 81;
break;
}
buf = kmalloc(30 * num, GFP_KERNEL);
if (!buf)
return -ENOMEM;
for (int i = 0; i < num; i++) {
v = sw_r32(offset + (i << 2)) & 0xffff;
n += sprintf(buf + n, "%s: %d\n", d[i], v);
}
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static const struct file_operations drop_counter_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = drop_counter_read,
};
static void l2_table_print_entry(struct seq_file *m, struct rtl838x_switch_priv *priv,
struct rtl838x_l2_entry *e)
{
u64 portmask;
if (e->type == L2_UNICAST) {
seq_puts(m, "L2_UNICAST\n");
seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n",
e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
e->vid, e->rvid);
seq_printf(m, " port %d age %d", e->port, e->age);
if (e->is_static)
seq_puts(m, " static");
if (e->block_da)
seq_puts(m, " block_da");
if (e->block_sa)
seq_puts(m, " block_sa");
if (e->suspended)
seq_puts(m, " suspended");
if (e->next_hop)
seq_printf(m, " next_hop route_id %u", e->nh_route_id);
seq_puts(m, "\n");
} else {
if (e->type == L2_MULTICAST) {
seq_puts(m, "L2_MULTICAST\n");
seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n",
e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
e->vid, e->rvid);
}
if (e->type == IP4_MULTICAST || e->type == IP6_MULTICAST) {
seq_puts(m, (e->type == IP4_MULTICAST) ?
"IP4_MULTICAST\n" : "IP6_MULTICAST\n");
seq_printf(m, " gip %08x sip %08x vid %u rvid %u\n",
e->mc_gip, e->mc_sip, e->vid, e->rvid);
}
portmask = priv->r->read_mcast_pmask(e->mc_portmask_index);
seq_printf(m, " index %u ports", e->mc_portmask_index);
for (int i = 0; i < 64; i++) {
if (portmask & BIT_ULL(i))
seq_printf(m, " %d", i);
}
seq_puts(m, "\n");
}
seq_puts(m, "\n");
}
static int l2_table_show(struct seq_file *m, void *v)
{
struct rtl838x_switch_priv *priv = m->private;
struct rtl838x_l2_entry e;
int bucket, index;
mutex_lock(&priv->reg_mutex);
for (int i = 0; i < priv->fib_entries; i++) {
bucket = i >> 2;
index = i & 0x3;
priv->r->read_l2_entry_using_hash(bucket, index, &e);
if (!e.valid)
continue;
seq_printf(m, "Hash table bucket %d index %d ", bucket, index);
l2_table_print_entry(m, priv, &e);
if (!((i + 1) % 64))
cond_resched();
}
for (int i = 0; i < 64; i++) {
priv->r->read_cam(i, &e);
if (!e.valid)
continue;
seq_printf(m, "CAM index %d ", i);
l2_table_print_entry(m, priv, &e);
}
mutex_unlock(&priv->reg_mutex);
return 0;
}
static int l2_table_open(struct inode *inode, struct file *filp)
{
return single_open(filp, l2_table_show, inode->i_private);
}
static const struct file_operations l2_table_fops = {
.owner = THIS_MODULE,
.open = l2_table_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t age_out_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
int value = sw_r32(priv->r->l2_port_aging_out);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t age_out_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
rtl83xx_fast_age(p->dp->ds, p->dp->index);
return res;
}
static const struct file_operations age_out_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = age_out_read,
.write = age_out_write,
};
static ssize_t port_egress_rate_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
int value;
if (priv->family_id == RTL8380_FAMILY_ID)
value = rtl838x_get_egress_rate(priv, p->dp->index);
else
value = rtl839x_get_egress_rate(priv, p->dp->index);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t port_egress_rate_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
if (priv->family_id == RTL8380_FAMILY_ID)
rtl838x_set_egress_rate(priv, p->dp->index, value);
else
rtl839x_set_egress_rate(priv, p->dp->index, value);
return res;
}
static const struct file_operations port_egress_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = port_egress_rate_read,
.write = port_egress_rate_write,
};
static const struct debugfs_reg32 port_ctrl_regs[] = {
{ .name = "port_isolation", .offset = RTL838X_PORT_ISO_CTRL(0), },
{ .name = "mac_force_mode", .offset = RTL838X_MAC_FORCE_MODE_CTRL, },
};
static void rtl838x_dbgfs_cleanup(struct rtl838x_switch_priv *priv)
{
debugfs_remove_recursive(priv->dbgfs_dir);
/* kfree(priv->dbgfs_entries); */
}
static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_priv *priv,
int port)
{
struct dentry *port_dir;
struct debugfs_regset32 *port_ctrl_regset;
port_dir = debugfs_create_dir(priv->ports[port].dp->name, parent);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("storm_rate_uc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port)));
debugfs_create_x32("storm_rate_mc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_MC(port)));
debugfs_create_x32("storm_rate_bc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port)));
} else {
debugfs_create_x32("storm_rate_uc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_UC_0(port)));
debugfs_create_x32("storm_rate_mc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_MC_0(port)));
debugfs_create_x32("storm_rate_bc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_BC_0(port)));
}
debugfs_create_u32("id", 0444, port_dir, (u32 *)&priv->ports[port].dp->index);
port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
if (!port_ctrl_regset)
return -ENOMEM;
port_ctrl_regset->regs = port_ctrl_regs;
port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (port << 2));
debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
debugfs_create_file("stp_state", 0600, port_dir, &priv->ports[port], &stp_state_fops);
debugfs_create_file("age_out", 0600, port_dir, &priv->ports[port], &age_out_fops);
debugfs_create_file("port_egress_rate", 0600, port_dir, &priv->ports[port],
&port_egress_fops);
return 0;
}
static int rtl838x_dbgfs_leds(struct dentry *parent, struct rtl838x_switch_priv *priv)
{
struct dentry *led_dir;
led_dir = debugfs_create_dir("led", parent);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_GLB_CTRL));
debugfs_create_x32("led_mode_sel", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_MODE_SEL));
debugfs_create_x32("led_mode_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_MODE_CTRL));
debugfs_create_x32("led_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_P_EN_CTRL));
debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_SW_CTRL));
debugfs_create_x32("led0_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED0_SW_P_EN_CTRL));
debugfs_create_x32("led1_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED1_SW_P_EN_CTRL));
debugfs_create_x32("led2_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED2_SW_P_EN_CTRL));
for (int p = 0; p < 28; p++) {
char led_sw_p_ctrl_name[20];
snprintf(led_sw_p_ctrl_name, sizeof(led_sw_p_ctrl_name),
"led_sw_p_ctrl.%02d", p);
debugfs_create_x32(led_sw_p_ctrl_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_LED_SW_P_CTRL_PORT(p)));
}
} else if (priv->family_id == RTL8390_FAMILY_ID) {
char port_led_name[20];
debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_GLB_CTRL));
debugfs_create_x32("led_set_2_3", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_2_3_CTRL));
debugfs_create_x32("led_set_0_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_0_1_CTRL));
for (int p = 0; p < 4; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_copr_set_sel.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_SET_SEL_CTRL(p << 4)));
snprintf(port_led_name, sizeof(port_led_name), "led_fib_set_sel.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_SET_SEL_CTRL(p << 4)));
}
debugfs_create_x32("led_copr_pmask_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(0)));
debugfs_create_x32("led_copr_pmask_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(32)));
debugfs_create_x32("led_fib_pmask_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(0)));
debugfs_create_x32("led_fib_pmask_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(32)));
debugfs_create_x32("led_combo_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(0)));
debugfs_create_x32("led_combo_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(32)));
debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_CTRL));
for (int p = 0; p < 5; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_en_ctrl.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_EN_CTRL(p * 10)));
}
for (int p = 0; p < 28; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_ctrl.%02d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_CTRL(p)));
}
}
return 0;
}
void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
{
struct dentry *rtl838x_dir;
struct dentry *port_dir;
struct dentry *mirror_dir;
struct debugfs_regset32 *port_ctrl_regset;
int ret;
char lag_name[10];
char mirror_name[10];
pr_info("%s called\n", __func__);
rtl838x_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL);
if (!rtl838x_dir)
rtl838x_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL);
priv->dbgfs_dir = rtl838x_dir;
debugfs_create_x32("soc", 0444, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MODEL_NAME_INFO));
/* Create one directory per port */
for (int i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
ret = rtl838x_dbgfs_port_init(rtl838x_dir, priv, i);
if (ret)
goto err;
}
}
/* Create directory for CPU-port */
port_dir = debugfs_create_dir("cpu_port", rtl838x_dir);
port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
if (!port_ctrl_regset) {
ret = -ENOMEM;
goto err;
}
port_ctrl_regset->regs = port_ctrl_regs;
port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (priv->cpu_port << 2));
debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
debugfs_create_u8("id", 0444, port_dir, &priv->cpu_port);
/* Create entries for LAGs */
for (int i = 0; i < priv->n_lags; i++) {
snprintf(lag_name, sizeof(lag_name), "lag.%02d", i);
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32(lag_name, 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
else
debugfs_create_x64(lag_name, 0644, rtl838x_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
}
/* Create directories for mirror groups */
for (int i = 0; i < 4; i++) {
snprintf(mirror_name, sizeof(mirror_name), "mirror.%1d", i);
mirror_dir = debugfs_create_dir(mirror_name, rtl838x_dir);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("ctrl", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_CTRL + i * 4));
debugfs_create_x32("ingress_pm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 4));
debugfs_create_x32("egress_pm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 4));
debugfs_create_x32("qid", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_QID_CTRL(i)));
debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL(i)));
debugfs_create_x32("rspan_vlan_mac", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(i)));
debugfs_create_x32("rspan_tx", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_CTRL));
debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL));
debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL));
} else {
debugfs_create_x32("ctrl", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_CTRL + i * 4));
debugfs_create_x64("ingress_pm", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 8));
debugfs_create_x64("egress_pm", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 8));
debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_VLAN_CTRL(i)));
debugfs_create_x32("rspan_tx", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_CTRL));
debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL));
debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL));
debugfs_create_x64("sample_rate", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + RTL839X_MIR_SAMPLE_RATE_CTRL));
}
}
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32("bpdu_flood_mask", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
else
debugfs_create_x64("bpdu_flood_mask", 0644, rtl838x_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_CTRL));
else
debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_CTRL));
ret = rtl838x_dbgfs_leds(rtl838x_dir, priv);
if (ret)
goto err;
debugfs_create_file("drop_counters", 0400, rtl838x_dir, priv, &drop_counter_fops);
debugfs_create_file("l2_table", 0400, rtl838x_dir, priv, &l2_table_fops);
return;
err:
rtl838x_dbgfs_cleanup(priv);
}
void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv)
{
struct dentry *dbg_dir;
pr_info("%s called\n", __func__);
dbg_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL);
if (!dbg_dir)
dbg_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL);
priv->dbgfs_dir = dbg_dir;
debugfs_create_file("drop_counters", 0400, dbg_dir, priv, &drop_counter_fops);
debugfs_create_file("l2_table", 0400, dbg_dir, priv, &l2_table_fops);
}

File diff suppressed because it is too large Load diff

View file

@ -1,565 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <net/dsa.h>
#include <linux/delay.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
static struct rtl838x_switch_priv *switch_priv;
extern struct rtl83xx_soc_info soc_info;
enum scheduler_type {
WEIGHTED_FAIR_QUEUE = 0,
WEIGHTED_ROUND_ROBIN,
};
int max_available_queue[] = {0, 1, 2, 3, 4, 5, 6, 7};
int default_queue_weights[] = {1, 1, 1, 1, 1, 1, 1, 1};
int dot1p_priority_remapping[] = {0, 1, 2, 3, 4, 5, 6, 7};
static void rtl839x_read_scheduling_table(int port)
{
u32 cmd = 1 << 9 | /* Execute cmd */
0 << 8 | /* Read */
0 << 6 | /* Table type 0b00 */
(port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl839x_write_scheduling_table(int port)
{
u32 cmd = 1 << 9 | /* Execute cmd */
1 << 8 | /* Write */
0 << 6 | /* Table type 0b00 */
(port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl839x_read_out_q_table(int port)
{
u32 cmd = 1 << 9 | /* Execute cmd */
0 << 8 | /* Read */
2 << 6 | /* Table type 0b10 */
(port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl838x_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable)
{
/* Enable Storm control for that port for UC, MC, and BC */
if (enable)
sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port));
else
sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port));
}
u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
{
if (port > priv->cpu_port)
return 0;
return sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)) & 0x3fff;
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
{
u32 old_rate;
if (port > priv->cpu_port)
return -1;
old_rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port));
sw_w32(rate, RTL838X_SCHED_P_EGR_RATE_CTRL(port));
return old_rate;
}
/* Set the rate limit for a particular queue in Bits/s
* units of the rate is 16Kbps
*/
void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
int queue, u32 rate)
{
if (port > priv->cpu_port)
return;
if (queue > 7)
return;
sw_w32(rate, RTL838X_SCHED_Q_EGR_RATE_CTRL(port, queue));
}
static void rtl838x_rate_control_init(struct rtl838x_switch_priv *priv)
{
pr_info("Enabling Storm control\n");
/* TICK_PERIOD_PPS */
if (priv->id == 0x8380)
sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0);
/* Set burst rate */
sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); /* UC */
sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); /* MC and BC */
/* Set burst Packets per Second to 32 */
sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); /* UC */
sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); /* MC and BC */
/* Include IFG in storm control, rate based on bytes/s (0 = packets) */
sw_w32_mask(0, 1 << 6 | 1 << 5, RTL838X_STORM_CTRL);
/* Bandwidth control includes preamble and IFG (10 Bytes) */
sw_w32_mask(0, 1, RTL838X_SCHED_CTRL);
/* On SoCs except RTL8382M, set burst size of port egress */
if (priv->id != 0x8382)
sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR);
/* Enable storm control on all ports with a PHY and limit rates,
* for UC and MC for both known and unknown addresses
*/
for (int i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i));
sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i));
sw_w32(0x8000, RTL838X_STORM_CTRL_PORT_BC(i));
rtl838x_storm_enable(priv, i, true);
}
}
/* Attack prevention, enable all attack prevention measures */
/* sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL); */
/* Attack prevention, drop (bit = 0) problematic packets on all ports.
* Setting bit = 1 means: trap to CPU
*/
/* sw_w32(0, RTL838X_ATK_PRVNT_ACT); */
/* Enable attack prevention on all ports */
/* sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN); */
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
{
u32 rate;
pr_debug("%s: Getting egress rate on port %d to %d\n", __func__, port, rate);
if (port >= priv->cpu_port)
return 0;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7));
rate <<= 12;
rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
mutex_unlock(&priv->reg_mutex);
return rate;
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625, returns previous rate */
int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
{
u32 old_rate;
pr_debug("%s: Setting egress rate on port %d to %d\n", __func__, port, rate);
if (port >= priv->cpu_port)
return -1;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
old_rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)) & 0xff;
old_rate <<= 12;
old_rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
sw_w32_mask(0xff, (rate >> 12) & 0xff, RTL839X_TBL_ACCESS_DATA_2(7));
sw_w32_mask(0xfff << 20, rate << 20, RTL839X_TBL_ACCESS_DATA_2(8));
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
return old_rate;
}
/* Set the rate limit for a particular queue in Bits/s
* units of the rate is 16Kbps
*/
static void rtl839x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
int queue, u32 rate)
{
int lsb = 128 + queue * 20;
int low_byte = 8 - (lsb >> 5);
int start_bit = lsb - (low_byte << 5);
u32 high_mask = 0xfffff >> (32 - start_bit);
pr_debug("%s: Setting egress rate on port %d, queue %d to %d\n",
__func__, port, queue, rate);
if (port >= priv->cpu_port)
return;
if (queue > 7)
return;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
sw_w32_mask(0xfffff << start_bit, (rate & 0xfffff) << start_bit,
RTL839X_TBL_ACCESS_DATA_2(low_byte));
if (high_mask)
sw_w32_mask(high_mask, (rate & 0xfffff) >> (32- start_bit),
RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
}
static void rtl839x_rate_control_init(struct rtl838x_switch_priv *priv)
{
pr_info("%s: enabling rate control\n", __func__);
/* Tick length and token size settings for SoC with 250MHz,
* RTL8350 family would use 50MHz
*/
/* Set the special tick period */
sw_w32(976563, RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL);
/* Ingress tick period and token length 10G */
sw_w32(18 << 11 | 151, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0);
/* Ingress tick period and token length 1G */
sw_w32(245 << 11 | 129, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1);
/* Egress tick period 10G, bytes/token 10G and tick period 1G, bytes/token 1G */
sw_w32(18 << 24 | 151 << 16 | 185 << 8 | 97, RTL839X_SCHED_LB_TICK_TKN_CTRL);
/* Set the tick period of the CPU and the Token Len */
sw_w32(3815 << 8 | 1, RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL);
/* Set the Weighted Fair Queueing burst size */
sw_w32_mask(0xffff, 4500, RTL839X_SCHED_LB_THR);
/* Storm-rate calculation is based on bytes/sec (bit 5), include IFG (bit 6) */
sw_w32_mask(0, 1 << 5 | 1 << 6, RTL839X_STORM_CTRL);
/* Based on the rate control mode being bytes/s
* set tick period and token length for 10G
*/
sw_w32(18 << 10 | 151, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0);
/* and for 1G ports */
sw_w32(246 << 10 | 129, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1);
/* Set default burst rates on all ports (the same for 1G / 10G) with a PHY
* for UC, MC and BC
* For 1G port, the minimum burst rate is 1700, maximum 65535,
* For 10G ports it is 2650 and 1048575 respectively */
for (int p = 0; p < priv->cpu_port; p++) {
if (priv->ports[p].phy && !priv->ports[p].is10G) {
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_UC_1(p));
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_MC_1(p));
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_BC_1(p));
}
}
/* Setup ingress/egress per-port rate control */
for (int p = 0; p < priv->cpu_port; p++) {
if (!priv->ports[p].phy)
continue;
if (priv->ports[p].is10G)
rtl839x_set_egress_rate(priv, p, 625000); /* 10GB/s */
else
rtl839x_set_egress_rate(priv, p, 62500); /* 1GB/s */
/* Setup queues: all RTL83XX SoCs have 8 queues, maximum rate */
for (int q = 0; q < 8; q++)
rtl839x_egress_rate_queue_limit(priv, p, q, 0xfffff);
if (priv->ports[p].is10G) {
/* Set high threshold to maximum */
sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p));
} else {
/* Set high threshold to maximum */
sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_1(p));
}
}
/* Set global ingress low watermark rate */
sw_w32(65532, RTL839X_IGR_BWCTRL_CTRL_LB_THR);
}
static void rtl838x_setup_prio2queue_matrix(int *min_queues)
{
u32 v = 0;
pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL838X_QM_INTPRI2QID_CTRL));
for (int i = 0; i < MAX_PRIOS; i++)
v |= i << (min_queues[i] * 3);
sw_w32(v, RTL838X_QM_INTPRI2QID_CTRL);
}
static void rtl839x_setup_prio2queue_matrix(int *min_queues)
{
pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL839X_QM_INTPRI2QID_CTRL(0)));
for (int i = 0; i < MAX_PRIOS; i++) {
int q = min_queues[i];
sw_w32(i << (q * 3), RTL839X_QM_INTPRI2QID_CTRL(q));
}
}
/* Sets the CPU queue depending on the internal priority of a packet */
static void rtl83xx_setup_prio2queue_cpu_matrix(int *max_queues)
{
int reg = soc_info.family == RTL8380_FAMILY_ID ? RTL838X_QM_PKT2CPU_INTPRI_MAP
: RTL839X_QM_PKT2CPU_INTPRI_MAP;
u32 v = 0;
pr_info("QM_PKT2CPU_INTPRI_MAP: %08x\n", sw_r32(reg));
for (int i = 0; i < MAX_PRIOS; i++)
v |= max_queues[i] << (i * 3);
sw_w32(v, reg);
}
static void rtl83xx_setup_default_prio2queue(void)
{
if (soc_info.family == RTL8380_FAMILY_ID) {
rtl838x_setup_prio2queue_matrix(max_available_queue);
} else {
rtl839x_setup_prio2queue_matrix(max_available_queue);
}
rtl83xx_setup_prio2queue_cpu_matrix(max_available_queue);
}
/* Sets the output queue assigned to a port, the port can be the CPU-port */
void rtl839x_set_egress_queue(int port, int queue)
{
sw_w32(queue << ((port % 10) *3), RTL839X_QM_PORT_QNUM(port));
}
/* Sets the priority assigned of an ingress port, the port can be the CPU-port */
static void rtl83xx_set_ingress_priority(int port, int priority)
{
if (soc_info.family == RTL8380_FAMILY_ID)
sw_w32(priority << ((port % 10) *3), RTL838X_PRI_SEL_PORT_PRI(port));
else
sw_w32(priority << ((port % 10) *3), RTL839X_PRI_SEL_PORT_PRI(port));
}
static int rtl839x_get_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port)
{
u32 v;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
v = sw_r32(RTL839X_TBL_ACCESS_DATA_2(8));
mutex_unlock(&priv->reg_mutex);
if (v & BIT(19))
return WEIGHTED_ROUND_ROBIN;
return WEIGHTED_FAIR_QUEUE;
}
static void rtl839x_set_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port,
enum scheduler_type sched)
{
enum scheduler_type t = rtl839x_get_scheduling_algorithm(priv, port);
u32 v, oam_state, oam_port_state;
u32 count;
int i, egress_rate;
mutex_lock(&priv->reg_mutex);
/* Check whether we need to empty the egress queue of that port due to Errata E0014503 */
if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
/* Read Operations, Adminstatrion and Management control register */
oam_state = sw_r32(RTL839X_OAM_CTRL);
/* Get current OAM state */
oam_port_state = sw_r32(RTL839X_OAM_PORT_ACT_CTRL(port));
/* Disable OAM to block traffice */
v = sw_r32(RTL839X_OAM_CTRL);
sw_w32_mask(0, 1, RTL839X_OAM_CTRL);
v = sw_r32(RTL839X_OAM_CTRL);
/* Set to trap action OAM forward (bits 1, 2) and OAM Mux Action Drop (bit 0) */
sw_w32(0x2, RTL839X_OAM_PORT_ACT_CTRL(port));
/* Set port egress rate to unlimited */
egress_rate = rtl839x_set_egress_rate(priv, port, 0xFFFFF);
/* Wait until the egress used page count of that port is 0 */
i = 0;
do {
usleep_range(100, 200);
rtl839x_read_out_q_table(port);
count = sw_r32(RTL839X_TBL_ACCESS_DATA_2(6));
count >>= 20;
i++;
} while (i < 3500 && count > 0);
}
/* Actually set the scheduling algorithm */
rtl839x_read_scheduling_table(port);
sw_w32_mask(BIT(19), sched ? BIT(19) : 0, RTL839X_TBL_ACCESS_DATA_2(8));
rtl839x_write_scheduling_table(port);
if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
/* Restore OAM state to control register */
sw_w32(oam_state, RTL839X_OAM_CTRL);
/* Restore trap action state */
sw_w32(oam_port_state, RTL839X_OAM_PORT_ACT_CTRL(port));
/* Restore port egress rate */
rtl839x_set_egress_rate(priv, port, egress_rate);
}
mutex_unlock(&priv->reg_mutex);
}
static void rtl839x_set_scheduling_queue_weights(struct rtl838x_switch_priv *priv, int port,
int *queue_weights)
{
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
for (int i = 0; i < 8; i++) {
int lsb = 48 + i * 8;
int low_byte = 8 - (lsb >> 5);
int start_bit = lsb - (low_byte << 5);
int high_mask = 0x3ff >> (32 - start_bit);
sw_w32_mask(0x3ff << start_bit, (queue_weights[i] & 0x3ff) << start_bit,
RTL839X_TBL_ACCESS_DATA_2(low_byte));
if (high_mask)
sw_w32_mask(high_mask, (queue_weights[i] & 0x3ff) >> (32- start_bit),
RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
}
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
}
static void rtl838x_config_qos(void)
{
u32 v;
pr_info("Setting up RTL838X QoS\n");
pr_info("RTL838X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL838X_PRI_SEL_TBL_CTRL(0)));
rtl83xx_setup_default_prio2queue();
/* Enable inner (bit 12) and outer (bit 13) priority remapping from DSCP */
sw_w32_mask(0, BIT(12) | BIT(13), RTL838X_PRI_DSCP_INVLD_CTRL0);
/* Set default weight for calculating internal priority, in prio selection group 0
* Port based (prio 3), Port outer-tag (4), DSCP (5), Inner Tag (6), Outer Tag (7)
*/
v = 3 | (4 << 3) | (5 << 6) | (6 << 9) | (7 << 12);
sw_w32(v, RTL838X_PRI_SEL_TBL_CTRL(0));
/* Set the inner and outer priority one-to-one to re-marked outer dot1p priority */
v = 0;
for (int p = 0; p < 8; p++)
v |= p << (3 * p);
sw_w32(v, RTL838X_RMK_OPRI_CTRL);
sw_w32(v, RTL838X_RMK_IPRI_CTRL);
v = 0;
for (int p = 0; p < 8; p++)
v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
sw_w32(v, RTL838X_PRI_SEL_IPRI_REMAP);
/* On all ports set scheduler type to WFQ */
for (int i = 0; i <= soc_info.cpu_port; i++)
sw_w32(0, RTL838X_SCHED_P_TYPE_CTRL(i));
/* Enable egress scheduler for CPU-Port */
sw_w32_mask(0, BIT(8), RTL838X_SCHED_LB_CTRL(soc_info.cpu_port));
/* Enable egress drop allways on */
sw_w32_mask(0, BIT(11), RTL838X_FC_P_EGR_DROP_CTRL(soc_info.cpu_port));
/* Give special trap frames priority 7 (BPDUs) and routing exceptions: */
sw_w32_mask(0, 7 << 3 | 7, RTL838X_QM_PKT2CPU_INTPRI_2);
/* Give RMA frames priority 7: */
sw_w32_mask(0, 7, RTL838X_QM_PKT2CPU_INTPRI_1);
}
static void rtl839x_config_qos(void)
{
u32 v;
struct rtl838x_switch_priv *priv = switch_priv;
pr_info("Setting up RTL839X QoS\n");
pr_info("RTL839X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL839X_PRI_SEL_TBL_CTRL(0)));
rtl83xx_setup_default_prio2queue();
for (int port = 0; port < soc_info.cpu_port; port++)
sw_w32(7, RTL839X_QM_PORT_QNUM(port));
/* CPU-port gets queue number 7 */
sw_w32(7, RTL839X_QM_PORT_QNUM(soc_info.cpu_port));
for (int port = 0; port <= soc_info.cpu_port; port++) {
rtl83xx_set_ingress_priority(port, 0);
rtl839x_set_scheduling_algorithm(priv, port, WEIGHTED_FAIR_QUEUE);
rtl839x_set_scheduling_queue_weights(priv, port, default_queue_weights);
/* Do re-marking based on outer tag */
sw_w32_mask(0, BIT(port % 32), RTL839X_RMK_PORT_DEI_TAG_CTRL(port));
}
/* Remap dot1p priorities to internal priority, for this the outer tag needs be re-marked */
v = 0;
for (int p = 0; p < 8; p++)
v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
sw_w32(v, RTL839X_PRI_SEL_IPRI_REMAP);
/* Configure Drop Precedence for Drop Eligible Indicator (DEI)
* Index 0: 0
* Index 1: 2
* Each indicator is 2 bits long
*/
sw_w32(2 << 2, RTL839X_PRI_SEL_DEI2DP_REMAP);
/* Re-mark DEI: 4 bit-fields of 2 bits each, field 0 is bits 0-1, ... */
sw_w32((0x1 << 2) | (0x1 << 4), RTL839X_RMK_DEI_CTRL);
/* Set Congestion avoidance drop probability to 0 for drop precedences 0-2 (bits 24-31)
* low threshold (bits 0-11) to 4095 and high threshold (bits 12-23) to 4095
* Weighted Random Early Detection (WRED) is used
*/
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(0));
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(1));
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(2));
/* Set queue-based congestion avoidance properties, register fields are as
* for forward RTL839X_WRED_PORT_THR_CTRL
*/
for (int q = 0; q < 8; q++) {
sw_w32(255 << 24 | 78 << 12 | 68, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
sw_w32(255 << 24 | 74 << 12 | 64, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
sw_w32(255 << 24 | 70 << 12 | 60, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
}
}
void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv)
{
switch_priv = priv;
pr_info("In %s\n", __func__);
if (priv->family_id == RTL8380_FAMILY_ID)
return rtl838x_config_qos();
else if (priv->family_id == RTL8390_FAMILY_ID)
return rtl839x_config_qos();
if (priv->family_id == RTL8380_FAMILY_ID)
rtl838x_rate_control_init(priv);
else if (priv->family_id == RTL8390_FAMILY_ID)
rtl839x_rate_control_init(priv);
}

View file

@ -1,174 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _NET_DSA_RTL83XX_H
#define _NET_DSA_RTL83XX_H
#include <net/dsa.h>
#include "rtl838x.h"
#define RTL8380_VERSION_A 'A'
#define RTL8390_VERSION_A 'A'
#define RTL8380_VERSION_B 'B'
struct fdb_update_work {
struct work_struct work;
struct net_device *ndev;
u64 macs[];
};
#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name}
struct rtl83xx_mib_desc {
unsigned int size;
unsigned int offset;
const char *name;
};
/* API for switch table access */
struct table_reg {
u16 addr;
u16 data;
u8 max_data;
u8 c_bit;
u8 t_bit;
u8 rmode;
u8 tbl;
struct mutex lock;
};
#define TBL_DESC(_addr, _data, _max_data, _c_bit, _t_bit, _rmode) \
{ .addr = _addr, .data = _data, .max_data = _max_data, .c_bit = _c_bit, \
.t_bit = _t_bit, .rmode = _rmode \
}
typedef enum {
RTL8380_TBL_L2 = 0,
RTL8380_TBL_0,
RTL8380_TBL_1,
RTL8390_TBL_L2,
RTL8390_TBL_0,
RTL8390_TBL_1,
RTL8390_TBL_2,
RTL9300_TBL_L2,
RTL9300_TBL_0,
RTL9300_TBL_1,
RTL9300_TBL_2,
RTL9300_TBL_HSB,
RTL9300_TBL_HSA,
RTL9310_TBL_0,
RTL9310_TBL_1,
RTL9310_TBL_2,
RTL9310_TBL_3,
RTL9310_TBL_4,
RTL9310_TBL_5,
RTL_TBL_END
} rtl838x_tbl_reg_t;
void rtl_table_init(void);
struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t);
void rtl_table_release(struct table_reg *r);
int rtl_table_read(struct table_reg *r, int idx);
int rtl_table_write(struct table_reg *r, int idx);
inline u16 rtl_table_data(struct table_reg *r, int i);
inline u32 rtl_table_data_r(struct table_reg *r, int i);
inline void rtl_table_data_w(struct table_reg *r, u32 v, int i);
void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv);
void rtl83xx_fast_age(struct dsa_switch *ds, int port);
int rtl83xx_packet_cntr_alloc(struct rtl838x_switch_priv *priv);
int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port);
int rtl83xx_port_is_under(const struct net_device * dev, struct rtl838x_switch_priv *priv);
void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data);
int read_phy(u32 port, u32 page, u32 reg, u32 *val);
int write_phy(u32 port, u32 page, u32 reg, u32 val);
/* Port register accessor functions for the RTL839x and RTL931X SoCs */
void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg);
u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
u64 rtl839x_get_port_reg_be(int reg);
void rtl839x_set_port_reg_be(u64 set, int reg);
void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg);
int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
void rtl839x_set_port_reg_le(u64 set, int reg);
u64 rtl839x_get_port_reg_le(int reg);
/* Port register accessor functions for the RTL838x and RTL930X SoCs */
void rtl838x_mask_port_reg(u64 clear, u64 set, int reg);
void rtl838x_set_port_reg(u64 set, int reg);
u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
u64 rtl838x_get_port_reg(int reg);
int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
/* RTL838x-specific */
u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl838x_switch_irq(int irq, void *dev_id);
void rtl8380_get_version(struct rtl838x_switch_priv *priv);
void rtl838x_vlan_profile_dump(int index);
void rtl8380_sds_rst(int mac);
int rtl8380_sds_power(int mac, int val);
void rtl838x_print_matrix(void);
/* RTL839x-specific */
u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
void rtl8390_get_version(struct rtl838x_switch_priv *priv);
void rtl839x_vlan_profile_dump(int index);
void rtl839x_exec_tbl2_cmd(u32 cmd);
void rtl839x_print_matrix(void);
/* RTL930x-specific */
u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl930x_switch_irq(int irq, void *dev_id);
irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
void rtl930x_vlan_profile_dump(int index);
int rtl9300_sds_power(int mac, int val);
extern int rtl9300_serdes_setup(int port, int sds_num, phy_interface_t phy_mode);
void rtl930x_print_matrix(void);
/* RTL931x-specific */
irqreturn_t rtl931x_switch_irq(int irq, void *dev_id);
int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode);
int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode);
extern void rtl931x_sds_init(u32 sds, phy_interface_t mode);
int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port, struct netdev_lag_upper_info *info);
int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port);
/* phy functions that will need to be moved to the future mdio driver */
int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val);
int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
/*
* TODO: The following functions are currently not in use. So compiler will complain if
* they are static and not made available externally. To preserve them for future use
* collect them in this section.
*/
void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
int queue, u32 rate);
int rtl8390_sds_power(int mac, int val);
void rtl839x_pie_rule_dump(struct pie_rule *pr);
void rtl839x_set_egress_queue(int port, int queue);
void rtl9300_dump_debug(void);
void rtl930x_pie_rule_dump_raw(u32 r[]);
void rtl931x_print_matrix(void);
void rtl931x_set_receive_management_action(int port, rma_ctrl_t type, action_type_t action);
void rtl931x_sw_init(struct rtl838x_switch_priv *priv);
#endif /* _NET_DSA_RTL83XX_H */

View file

@ -1,410 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <net/dsa.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <net/flow_offload.h>
#include <linux/rhashtable.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
#include "rtl838x.h"
/* Parse the flow rule for the matching conditions */
static int rtl83xx_parse_flow_rule(struct rtl838x_switch_priv *priv,
struct flow_rule *rule, struct rtl83xx_flow *flow)
{
struct flow_dissector *dissector = rule->match.dissector;
pr_debug("In %s\n", __func__);
/* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */
if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 ||
(dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) {
pr_err("Cannot form TC key: used_keys = 0x%llx\n", dissector->used_keys);
return -EOPNOTSUPP;
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
pr_debug("%s: BASIC\n", __func__);
flow_rule_match_basic(rule, &match);
if (match.key->n_proto == htons(ETH_P_ARP))
flow->rule.frame_type = 0;
if (match.key->n_proto == htons(ETH_P_IP))
flow->rule.frame_type = 2;
if (match.key->n_proto == htons(ETH_P_IPV6))
flow->rule.frame_type = 3;
if ((match.key->n_proto == htons(ETH_P_ARP)) || flow->rule.frame_type)
flow->rule.frame_type_m = 3;
if (flow->rule.frame_type >= 2) {
if (match.key->ip_proto == IPPROTO_UDP)
flow->rule.frame_type_l4 = 0;
if (match.key->ip_proto == IPPROTO_TCP)
flow->rule.frame_type_l4 = 1;
if (match.key->ip_proto == IPPROTO_ICMP || match.key->ip_proto == IPPROTO_ICMPV6)
flow->rule.frame_type_l4 = 2;
if (match.key->ip_proto == IPPROTO_TCP)
flow->rule.frame_type_l4 = 3;
if ((match.key->ip_proto == IPPROTO_UDP) || flow->rule.frame_type_l4)
flow->rule.frame_type_l4_m = 7;
}
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
struct flow_match_eth_addrs match;
pr_debug("%s: ETH_ADDR\n", __func__);
flow_rule_match_eth_addrs(rule, &match);
ether_addr_copy(flow->rule.dmac, match.key->dst);
ether_addr_copy(flow->rule.dmac_m, match.mask->dst);
ether_addr_copy(flow->rule.smac, match.key->src);
ether_addr_copy(flow->rule.smac_m, match.mask->src);
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
struct flow_match_vlan match;
pr_debug("%s: VLAN\n", __func__);
flow_rule_match_vlan(rule, &match);
flow->rule.itag = match.key->vlan_id;
flow->rule.itag_m = match.mask->vlan_id;
/* TODO: What about match.key->vlan_priority? */
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
struct flow_match_ipv4_addrs match;
pr_debug("%s: IPV4\n", __func__);
flow_rule_match_ipv4_addrs(rule, &match);
flow->rule.is_ipv6 = false;
flow->rule.dip = match.key->dst;
flow->rule.dip_m = match.mask->dst;
flow->rule.sip = match.key->src;
flow->rule.sip_m = match.mask->src;
} else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
struct flow_match_ipv6_addrs match;
pr_debug("%s: IPV6\n", __func__);
flow->rule.is_ipv6 = true;
flow_rule_match_ipv6_addrs(rule, &match);
flow->rule.dip6 = match.key->dst;
flow->rule.dip6_m = match.mask->dst;
flow->rule.sip6 = match.key->src;
flow->rule.sip6_m = match.mask->src;
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
struct flow_match_ports match;
pr_debug("%s: PORTS\n", __func__);
flow_rule_match_ports(rule, &match);
flow->rule.dport = match.key->dst;
flow->rule.dport_m = match.mask->dst;
flow->rule.sport = match.key->src;
flow->rule.sport_m = match.mask->src;
}
/* TODO: ICMP */
return 0;
}
static void rtl83xx_flow_bypass_all(struct rtl83xx_flow *flow)
{
flow->rule.bypass_sel = true;
flow->rule.bypass_all = true;
flow->rule.bypass_igr_stp = true;
flow->rule.bypass_ibc_sc = true;
}
static int rtl83xx_parse_fwd(struct rtl838x_switch_priv *priv,
const struct flow_action_entry *act, struct rtl83xx_flow *flow)
{
struct net_device *dev = act->dev;
int port;
port = rtl83xx_port_is_under(dev, priv);
if (port < 0) {
netdev_info(dev, "%s: not a DSA device.\n", __func__);
return -EINVAL;
}
flow->rule.fwd_sel = true;
flow->rule.fwd_data = port;
pr_debug("Using port index: %d\n", port);
rtl83xx_flow_bypass_all(flow);
return 0;
}
static int rtl83xx_add_flow(struct rtl838x_switch_priv *priv, struct flow_cls_offload *f,
struct rtl83xx_flow *flow)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
const struct flow_action_entry *act;
int i, err;
pr_debug("%s\n", __func__);
rtl83xx_parse_flow_rule(priv, rule, flow);
flow_action_for_each(i, act, &rule->action) {
switch (act->id) {
case FLOW_ACTION_DROP:
pr_debug("%s: DROP\n", __func__);
flow->rule.drop = true;
rtl83xx_flow_bypass_all(flow);
return 0;
case FLOW_ACTION_TRAP:
pr_debug("%s: TRAP\n", __func__);
flow->rule.fwd_data = priv->cpu_port;
flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT;
rtl83xx_flow_bypass_all(flow);
break;
case FLOW_ACTION_MANGLE:
pr_err("%s: FLOW_ACTION_MANGLE not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_ADD:
pr_err("%s: FLOW_ACTION_ADD not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_VLAN_PUSH:
pr_debug("%s: VLAN_PUSH\n", __func__);
/* TODO: act->vlan.proto */
flow->rule.ivid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ivid_sel = true;
flow->rule.ivid_data = htons(act->vlan.vid);
flow->rule.ovid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ovid_sel = true;
flow->rule.ovid_data = htons(act->vlan.vid);
flow->rule.fwd_mod_to_cpu = true;
break;
case FLOW_ACTION_VLAN_POP:
pr_debug("%s: VLAN_POP\n", __func__);
flow->rule.ivid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ivid_data = 0;
flow->rule.ivid_sel = true;
flow->rule.ovid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ovid_data = 0;
flow->rule.ovid_sel = true;
flow->rule.fwd_mod_to_cpu = true;
break;
case FLOW_ACTION_CSUM:
pr_err("%s: FLOW_ACTION_CSUM not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_REDIRECT:
pr_debug("%s: REDIRECT\n", __func__);
err = rtl83xx_parse_fwd(priv, act, flow);
if (err)
return err;
flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT;
break;
case FLOW_ACTION_MIRRED:
pr_debug("%s: MIRRED\n", __func__);
err = rtl83xx_parse_fwd(priv, act, flow);
if (err)
return err;
flow->rule.fwd_act = PIE_ACT_COPY_TO_PORT;
break;
default:
pr_err("%s: Flow action not supported: %d\n", __func__, act->id);
return -EOPNOTSUPP;
}
}
return 0;
}
static const struct rhashtable_params tc_ht_params = {
.head_offset = offsetof(struct rtl83xx_flow, node),
.key_offset = offsetof(struct rtl83xx_flow, cookie),
.key_len = sizeof(((struct rtl83xx_flow *)0)->cookie),
.automatic_shrinking = true,
};
static int rtl83xx_configure_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload *f)
{
struct rtl83xx_flow *flow;
int err = 0;
pr_debug("In %s\n", __func__);
rcu_read_lock();
pr_debug("Cookie %08lx\n", f->cookie);
flow = rhashtable_lookup(&priv->tc_ht, &f->cookie, tc_ht_params);
if (flow) {
pr_info("%s: Got flow\n", __func__);
err = -EEXIST;
goto rcu_unlock;
}
rcu_unlock:
rcu_read_unlock();
if (flow)
goto out;
pr_debug("%s: New flow\n", __func__);
flow = kzalloc(sizeof(*flow), GFP_KERNEL);
if (!flow) {
err = -ENOMEM;
goto out;
}
flow->cookie = f->cookie;
flow->priv = priv;
err = rhashtable_insert_fast(&priv->tc_ht, &flow->node, tc_ht_params);
if (err) {
pr_err("Could not insert add new rule\n");
goto out_free;
}
rtl83xx_add_flow(priv, f, flow); /* TODO: check error */
/* Add log action to flow */
flow->rule.packet_cntr = rtl83xx_packet_cntr_alloc(priv);
if (flow->rule.packet_cntr >= 0) {
pr_debug("Using packet counter %d\n", flow->rule.packet_cntr);
flow->rule.log_sel = true;
flow->rule.log_data = flow->rule.packet_cntr;
}
err = priv->r->pie_rule_add(priv, &flow->rule);
return err;
out_free:
kfree(flow);
out:
pr_err("%s: error %d\n", __func__, err);
return err;
}
static int rtl83xx_delete_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload * cls_flower)
{
struct rtl83xx_flow *flow;
pr_debug("In %s\n", __func__);
rcu_read_lock();
flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params);
if (!flow) {
rcu_read_unlock();
return -EINVAL;
}
priv->r->pie_rule_rm(priv, &flow->rule);
rhashtable_remove_fast(&priv->tc_ht, &flow->node, tc_ht_params);
kfree_rcu(flow, rcu_head);
rcu_read_unlock();
return 0;
}
static int rtl83xx_stats_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload * cls_flower)
{
struct rtl83xx_flow *flow;
unsigned long lastused = 0;
int total_packets, new_packets;
pr_debug("%s: \n", __func__);
flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params);
if (!flow)
return -1;
if (flow->rule.packet_cntr >= 0) {
total_packets = priv->r->packet_cntr_read(flow->rule.packet_cntr);
pr_debug("Total packets: %d\n", total_packets);
new_packets = total_packets - flow->rule.last_packet_cnt;
flow->rule.last_packet_cnt = total_packets;
}
/* TODO: We need a second PIE rule to count the bytes */
flow_stats_update(&cls_flower->stats, 100 * new_packets, new_packets, 0, lastused,
FLOW_ACTION_HW_STATS_IMMEDIATE);
return 0;
}
static int rtl83xx_setup_tc_cls_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload *cls_flower)
{
pr_debug("%s: %d\n", __func__, cls_flower->command);
switch (cls_flower->command) {
case FLOW_CLS_REPLACE:
return rtl83xx_configure_flower(priv, cls_flower);
case FLOW_CLS_DESTROY:
return rtl83xx_delete_flower(priv, cls_flower);
case FLOW_CLS_STATS:
return rtl83xx_stats_flower(priv, cls_flower);
default:
return -EOPNOTSUPP;
}
}
static int rtl83xx_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct rtl838x_switch_priv *priv = cb_priv;
switch (type) {
case TC_SETUP_CLSFLOWER:
pr_debug("%s: TC_SETUP_CLSFLOWER\n", __func__);
return rtl83xx_setup_tc_cls_flower(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static LIST_HEAD(rtl83xx_block_cb_list);
int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
{
struct rtl838x_switch_priv *priv;
struct flow_block_offload *f = type_data;
static bool first_time = true;
int err;
pr_debug("%s: %d\n", __func__, type);
if(!netdev_uses_dsa(dev)) {
pr_err("%s: no DSA\n", __func__);
return 0;
}
priv = dev->dsa_ptr->ds->priv;
switch (type) {
case TC_SETUP_BLOCK:
if (first_time) {
first_time = false;
err = rhashtable_init(&priv->tc_ht, &tc_ht_params);
if (err)
pr_err("%s: Could not initialize hash table\n", __func__);
}
f->unlocked_driver_cb = true;
return flow_block_cb_setup_simple(type_data,
&rtl83xx_block_cb_list,
rtl83xx_setup_tc_block_cb,
priv, priv, true);
default:
return -EOPNOTSUPP;
}
return 0;
}

View file

@ -1,455 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _RTL838X_ETH_H
#define _RTL838X_ETH_H
/* Register definition */
/* Per port MAC control */
#define RTL838X_MAC_PORT_CTRL (0xd560)
#define RTL839X_MAC_PORT_CTRL (0x8004)
#define RTL930X_MAC_L2_PORT_CTRL (0x3268)
#define RTL930X_MAC_PORT_CTRL (0x3260)
#define RTL931X_MAC_L2_PORT_CTRL (0x6000)
#define RTL931X_MAC_PORT_CTRL (0x6004)
/* DMA interrupt control and status registers */
#define RTL838X_DMA_IF_CTRL (0x9f58)
#define RTL838X_DMA_IF_INTR_STS (0x9f54)
#define RTL838X_DMA_IF_INTR_MSK (0x9f50)
#define RTL839X_DMA_IF_CTRL (0x786c)
#define RTL839X_DMA_IF_INTR_STS (0x7868)
#define RTL839X_DMA_IF_INTR_MSK (0x7864)
#define RTL930X_DMA_IF_CTRL (0xe028)
#define RTL930X_DMA_IF_INTR_RX_RUNOUT_STS (0xe01C)
#define RTL930X_DMA_IF_INTR_RX_DONE_STS (0xe020)
#define RTL930X_DMA_IF_INTR_TX_DONE_STS (0xe024)
#define RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK (0xe010)
#define RTL930X_DMA_IF_INTR_RX_DONE_MSK (0xe014)
#define RTL930X_DMA_IF_INTR_TX_DONE_MSK (0xe018)
#define RTL930X_L2_NTFY_IF_INTR_MSK (0xe04C)
#define RTL930X_L2_NTFY_IF_INTR_STS (0xe050)
/* TODO: RTL931X_DMA_IF_CTRL has different bits meanings */
#define RTL931X_DMA_IF_CTRL (0x0928)
#define RTL931X_DMA_IF_INTR_RX_RUNOUT_STS (0x091c)
#define RTL931X_DMA_IF_INTR_RX_DONE_STS (0x0920)
#define RTL931X_DMA_IF_INTR_TX_DONE_STS (0x0924)
#define RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK (0x0910)
#define RTL931X_DMA_IF_INTR_RX_DONE_MSK (0x0914)
#define RTL931X_DMA_IF_INTR_TX_DONE_MSK (0x0918)
#define RTL931X_L2_NTFY_IF_INTR_MSK (0x09E4)
#define RTL931X_L2_NTFY_IF_INTR_STS (0x09E8)
#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104)
#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc)
#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C)
#define RTL931X_MAC_FORCE_MODE_CTRL (0x0dcc)
#define RTL83XX_DMA_IF_INTR_STS_NOTIFY_MASK GENMASK(22, 20)
#define RTL83XX_DMA_IF_INTR_STS_RX_DONE_MASK GENMASK(15, 8)
#define RTL83XX_DMA_IF_INTR_STS_RX_RUN_OUT_MASK GENMASK(7, 0)
/* MAC address settings */
#define RTL838X_MAC (0xa9ec)
#define RTL839X_MAC (0x02b4)
#define RTL838X_MAC_ALE (0x6b04)
#define RTL838X_MAC2 (0xa320)
#define RTL930X_MAC_L2_ADDR_CTRL (0xC714)
#define RTL931X_MAC_L2_ADDR_CTRL (0x135c)
/* Ringbuffer setup */
#define RTL838X_DMA_RX_BASE (0x9f00)
#define RTL839X_DMA_RX_BASE (0x780c)
#define RTL930X_DMA_RX_BASE (0xdf00)
#define RTL931X_DMA_RX_BASE (0x0800)
#define RTL838X_DMA_TX_BASE (0x9f40)
#define RTL839X_DMA_TX_BASE (0x784c)
#define RTL930X_DMA_TX_BASE (0xe000)
#define RTL931X_DMA_TX_BASE (0x0900)
#define RTL838X_DMA_IF_RX_RING_SIZE (0xB7E4)
#define RTL839X_DMA_IF_RX_RING_SIZE (0x6038)
#define RTL930X_DMA_IF_RX_RING_SIZE (0x7C60)
#define RTL931X_DMA_IF_RX_RING_SIZE (0x2080)
#define RTL838X_DMA_IF_RX_RING_CNTR (0xB7E8)
#define RTL839X_DMA_IF_RX_RING_CNTR (0x603c)
#define RTL930X_DMA_IF_RX_RING_CNTR (0x7C8C)
#define RTL931X_DMA_IF_RX_RING_CNTR (0x20AC)
#define RTL838X_DMA_IF_RX_CUR (0x9F20)
#define RTL839X_DMA_IF_RX_CUR (0x782c)
#define RTL930X_DMA_IF_RX_CUR (0xdf80)
#define RTL931X_DMA_IF_RX_CUR (0x0880)
#define RTL838X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0x9F48)
#define RTL930X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0xE008)
#define RTL838X_DMY_REG31 (0x3b28)
#define RTL838X_SDS_MODE_SEL (0x0028)
#define RTL838X_SDS_CFG_REG (0x0034)
#define RTL838X_INT_MODE_CTRL (0x005c)
#define RTL838X_CHIP_INFO (0x00d8)
#define RTL838X_SDS4_REG28 (0xef80)
#define RTL838X_SDS4_DUMMY0 (0xef8c)
#define RTL838X_SDS5_EXT_REG6 (0xf18c)
/* L2 features */
#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180)
#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2))
#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914)
#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2))
/* MAC-side link state handling */
#define RTL838X_MAC_LINK_STS (0xa188)
#define RTL839X_MAC_LINK_STS (0x0390)
#define RTL930X_MAC_LINK_STS (0xCB10)
#define RTL931X_MAC_LINK_STS (0x0ec0)
#define RTL838X_MAC_LINK_SPD_STS (0xa190)
#define RTL839X_MAC_LINK_SPD_STS (0x03a0)
#define RTL930X_MAC_LINK_SPD_STS (0xCB18)
#define RTL931X_MAC_LINK_SPD_STS (0x0ed0)
#define RTL838X_MAC_LINK_DUP_STS (0xa19c)
#define RTL839X_MAC_LINK_DUP_STS (0x03b0)
#define RTL930X_MAC_LINK_DUP_STS (0xCB28)
#define RTL931X_MAC_LINK_DUP_STS (0x0ef0)
/* TODO: RTL8390_MAC_LINK_MEDIA_STS_ADDR??? */
#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0)
#define RTL839X_MAC_TX_PAUSE_STS (0x03b8)
#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C)
#define RTL931X_MAC_TX_PAUSE_STS (0x0ef8)
#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4)
#define RTL839X_MAC_RX_PAUSE_STS (0xCB30)
#define RTL930X_MAC_RX_PAUSE_STS (0xC2F8)
#define RTL931X_MAC_RX_PAUSE_STS (0x0f00)
#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04)
#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08)
#define RTL930X_L2_UNKN_UC_FLD_PMSK (0x9064)
#define RTL931X_L2_UNKN_UC_FLD_PMSK (0xC8F4)
#define RTL839X_MAC_GLB_CTRL (0x02a8)
#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60f8)
#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370)
#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0)
#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404)
#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C)
#define RTL930X_L2_PORT_SABLK_CTRL (0x905c)
#define RTL930X_L2_PORT_DABLK_CTRL (0x9060)
/* MAC link state bits */
#define FORCE_EN (1 << 0)
#define FORCE_LINK_EN (1 << 1)
#define NWAY_EN (1 << 2)
#define DUPLX_MODE (1 << 3)
#define TX_PAUSE_EN (1 << 6)
#define RX_PAUSE_EN (1 << 7)
/* L2 Notification DMA interface */
#define RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL (0x785C)
#define RTL839X_L2_NOTIFICATION_CTRL (0x7808)
#define RTL931X_L2_NTFY_RING_BASE_ADDR (0x09DC)
#define RTL931X_L2_NTFY_RING_CUR_ADDR (0x09E0)
#define RTL839X_L2_NOTIFICATION_CTRL (0x7808)
#define RTL931X_L2_NTFY_CTRL (0xCDC8)
#define RTL838X_L2_CTRL_0 (0x3200)
#define RTL839X_L2_CTRL_0 (0x3800)
#define RTL930X_L2_CTRL (0x8FD8)
#define RTL931X_L2_CTRL (0xC800)
/* TRAPPING to CPU-PORT */
#define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984)
#define RTL838X_RMA_CTRL_0 (0x4300)
#define RTL838X_RMA_CTRL_1 (0x4304)
#define RTL839X_RMA_CTRL_0 (0x1200)
#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058)
#define RTL839X_RMA_CTRL_1 (0x1204)
#define RTL839X_RMA_CTRL_2 (0x1208)
#define RTL839X_RMA_CTRL_3 (0x120C)
#define RTL930X_VLAN_APP_PKT_CTRL (0xA23C)
#define RTL930X_RMA_CTRL_0 (0x9E60)
#define RTL930X_RMA_CTRL_1 (0x9E64)
#define RTL930X_RMA_CTRL_2 (0x9E68)
#define RTL931X_VLAN_APP_PKT_CTRL (0x96b0)
#define RTL931X_RMA_CTRL_0 (0x8800)
#define RTL931X_RMA_CTRL_1 (0x8804)
#define RTL931X_RMA_CTRL_2 (0x8808)
/* Advanced SMI control for clause 45 PHYs */
#define RTL930X_SMI_MAC_TYPE_CTRL (0xCA04)
#define RTL930X_SMI_PORT24_27_ADDR_CTRL (0xCB90)
#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
#define RTL930X_SMI_10GPHY_POLLING_REG0_CFG (0xCBB4)
#define RTL930X_SMI_10GPHY_POLLING_REG9_CFG (0xCBB8)
#define RTL930X_SMI_10GPHY_POLLING_REG10_CFG (0xCBBC)
#define RTL930X_SMI_PRVTE_POLLING_CTRL (0xCA10)
/* Registers of the internal Serdes of the 8390 */
#define RTL839X_SDS12_13_XSG0 (0xB800)
/* Chip configuration registers of the RTL9310 */
#define RTL931X_MEM_ENCAP_INIT (0x4854)
#define RTL931X_MEM_MIB_INIT (0x7E18)
#define RTL931X_MEM_ACL_INIT (0x40BC)
#define RTL931X_MEM_ALE_INIT_0 (0x83F0)
#define RTL931X_MEM_ALE_INIT_1 (0x83F4)
#define RTL931X_MEM_ALE_INIT_2 (0x82E4)
#define RTL931X_MDX_CTRL_RSVD (0x0fcc)
#define RTL931X_PS_SOC_CTRL (0x13f8)
#define RTL931X_SMI_10GPHY_POLLING_SEL2 (0xCF8)
#define RTL931X_SMI_10GPHY_POLLING_SEL3 (0xCFC)
#define RTL931X_SMI_10GPHY_POLLING_SEL4 (0xD00)
/* Registers of the internal Serdes of the 8380 */
#define RTL838X_SDS4_FIB_REG0 (0xF800)
/* Default MTU with jumbo frames support */
#define DEFAULT_MTU 9000
inline int rtl838x_mac_port_ctrl(int p)
{
return RTL838X_MAC_PORT_CTRL + (p << 7);
}
inline int rtl839x_mac_port_ctrl(int p)
{
return RTL839X_MAC_PORT_CTRL + (p << 7);
}
/* On the RTL931XX, the functionality of the MAC port control register is split up
* into RTL931X_MAC_L2_PORT_CTRL and RTL931X_MAC_PORT_CTRL the functionality used
* by the Ethernet driver is in the same bits now in RTL931X_MAC_L2_PORT_CTRL
*/
inline int rtl930x_mac_port_ctrl(int p)
{
return RTL930X_MAC_L2_PORT_CTRL + (p << 6);
}
inline int rtl931x_mac_port_ctrl(int p)
{
return RTL931X_MAC_L2_PORT_CTRL + (p << 7);
}
inline int rtl838x_dma_if_rx_ring_size(int i)
{
return RTL838X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2);
}
inline int rtl839x_dma_if_rx_ring_size(int i)
{
return RTL839X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2);
}
inline int rtl930x_dma_if_rx_ring_size(int i)
{
return RTL930X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2);
}
inline int rtl931x_dma_if_rx_ring_size(int i)
{
return RTL931X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2);
}
inline int rtl838x_dma_if_rx_ring_cntr(int i)
{
return RTL838X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2);
}
inline int rtl839x_dma_if_rx_ring_cntr(int i)
{
return RTL839X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2);
}
inline int rtl930x_dma_if_rx_ring_cntr(int i)
{
return RTL930X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2);
}
inline int rtl931x_dma_if_rx_ring_cntr(int i)
{
return RTL931X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2);
}
inline u32 rtl838x_get_mac_link_sts(int port)
{
return (sw_r32(RTL838X_MAC_LINK_STS) & BIT(port));
}
inline u32 rtl839x_get_mac_link_sts(int p)
{
return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_link_sts(int port)
{
u32 link = sw_r32(RTL930X_MAC_LINK_STS);
link = sw_r32(RTL930X_MAC_LINK_STS);
pr_info("%s link state is %08x\n", __func__, link);
return link & BIT(port);
}
inline u32 rtl931x_get_mac_link_sts(int p)
{
return (sw_r32(RTL931X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_link_dup_sts(int port)
{
return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & BIT(port));
}
inline u32 rtl839x_get_mac_link_dup_sts(int p)
{
return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_link_dup_sts(int port)
{
return (sw_r32(RTL930X_MAC_LINK_DUP_STS) & BIT(port));
}
inline u32 rtl931x_get_mac_link_dup_sts(int p)
{
return (sw_r32(RTL931X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_link_spd_sts(int port)
{
int r = RTL838X_MAC_LINK_SPD_STS + ((port >> 4) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 16) << 1;
return (speed & 0x3);
}
inline u32 rtl839x_get_mac_link_spd_sts(int port)
{
int r = RTL839X_MAC_LINK_SPD_STS + ((port >> 4) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 16) << 1;
return (speed & 0x3);
}
inline u32 rtl930x_get_mac_link_spd_sts(int port)
{
int r = RTL930X_MAC_LINK_SPD_STS + ((port >> 3) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 8) << 2;
return (speed & 0xf);
}
inline u32 rtl931x_get_mac_link_spd_sts(int port)
{
int r = RTL931X_MAC_LINK_SPD_STS + ((port >> 3) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 8) << 2;
return (speed & 0xf);
}
inline u32 rtl838x_get_mac_rx_pause_sts(int port)
{
return (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port));
}
inline u32 rtl839x_get_mac_rx_pause_sts(int p)
{
return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_rx_pause_sts(int port)
{
return (sw_r32(RTL930X_MAC_RX_PAUSE_STS) & (1 << port));
}
inline u32 rtl931x_get_mac_rx_pause_sts(int p)
{
return (sw_r32(RTL931X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_tx_pause_sts(int port)
{
return (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port));
}
inline u32 rtl839x_get_mac_tx_pause_sts(int p)
{
return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_tx_pause_sts(int port)
{
return (sw_r32(RTL930X_MAC_TX_PAUSE_STS) & (1 << port));
}
inline u32 rtl931x_get_mac_tx_pause_sts(int p)
{
return (sw_r32(RTL931X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
struct p_hdr;
struct dsa_tag;
struct rtl838x_eth_reg {
irqreturn_t (*net_irq)(int irq, void *dev_id);
int (*mac_port_ctrl)(int port);
int dma_if_intr_sts;
int dma_if_intr_msk;
int dma_if_intr_rx_runout_sts;
int dma_if_intr_rx_done_sts;
int dma_if_intr_tx_done_sts;
int dma_if_intr_rx_runout_msk;
int dma_if_intr_rx_done_msk;
int dma_if_intr_tx_done_msk;
int l2_ntfy_if_intr_sts;
int l2_ntfy_if_intr_msk;
int dma_if_ctrl;
int mac_force_mode_ctrl;
int dma_rx_base;
int dma_tx_base;
int (*dma_if_rx_ring_size)(int ring);
int (*dma_if_rx_ring_cntr)(int ring);
int dma_if_rx_cur;
int rst_glb_ctrl;
u32 (*get_mac_link_sts)(int port);
u32 (*get_mac_link_dup_sts)(int port);
u32 (*get_mac_link_spd_sts)(int port);
u32 (*get_mac_rx_pause_sts)(int port);
u32 (*get_mac_tx_pause_sts)(int port);
int mac;
int l2_tbl_flush_ctrl;
void (*update_cntr)(int r, int work_done);
void (*create_tx_header)(struct p_hdr *h, unsigned int dest_port, int prio);
bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag);
};
int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum);
int phy_package_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val);
int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum);
int phy_package_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val);
int phy_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum);
int phy_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val);
#endif /* _RTL838X_ETH_H */

File diff suppressed because it is too large Load diff

View file

@ -1,98 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
struct rtl83xx_shared_private {
char *name;
};
struct __attribute__ ((__packed__)) part {
uint16_t start;
uint8_t wordsize;
uint8_t words;
};
struct __attribute__ ((__packed__)) fw_header {
uint32_t magic;
uint32_t phy;
uint32_t checksum;
uint32_t version;
struct part parts[10];
};
/* TODO: fixed path? */
#define FIRMWARE_838X_8380_1 "rtl838x_phy/rtl838x_8380.fw"
#define FIRMWARE_838X_8214FC_1 "rtl838x_phy/rtl838x_8214fc.fw"
#define FIRMWARE_838X_8218b_1 "rtl838x_phy/rtl838x_8218b.fw"
#define PHY_ID_RTL8214C 0x001cc942
#define PHY_ID_RTL8218B_E 0x001cc980
#define PHY_ID_RTL8214_OR_8218 0x001cc981
#define PHY_ID_RTL8218D 0x001cc983
#define PHY_ID_RTL8218B_I 0x001cca40
#define PHY_ID_RTL8221B 0x001cc849
#define PHY_ID_RTL8226 0x001cc838
#define PHY_ID_RTL8390_GENERIC 0x001ccab0
#define PHY_ID_RTL8393_I 0x001c8393
#define PHY_ID_RTL9300_I 0x70d03106
/* These PHYs share the same id (0x001cc981) */
#define PHY_IS_NOT_RTL821X 0
#define PHY_IS_RTL8214FC 1
#define PHY_IS_RTL8214FB 2
#define PHY_IS_RTL8218B_E 3
/* Registers of the internal Serdes of the 8380 */
#define RTL838X_SDS_MODE_SEL (0x0028)
#define RTL838X_SDS_CFG_REG (0x0034)
#define RTL838X_INT_MODE_CTRL (0x005c)
#define RTL838X_DMY_REG31 (0x3b28)
#define RTL8380_SDS4_FIB_REG0 (0xF800)
#define RTL838X_SDS4_REG28 (0xef80)
#define RTL838X_SDS4_DUMMY0 (0xef8c)
#define RTL838X_SDS5_EXT_REG6 (0xf18c)
#define RTL838X_SDS4_FIB_REG0 (RTL838X_SDS4_REG28 + 0x880)
#define RTL838X_SDS5_FIB_REG0 (RTL838X_SDS4_REG28 + 0x980)
/* Registers of the internal SerDes of the RTL8390 */
#define RTL839X_SDS12_13_XSG0 (0xB800)
/* Registers of the internal Serdes of the 9300 */
#define RTL930X_SDS_INDACS_CMD (0x03B0)
#define RTL930X_SDS_INDACS_DATA (0x03B4)
#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C)
/* Registers of the internal SerDes of the 9310 */
#define RTL931X_SERDES_INDRT_ACCESS_CTRL (0x5638)
#define RTL931X_SERDES_INDRT_DATA_CTRL (0x563C)
#define RTL931X_SERDES_MODE_CTRL (0x13cc)
#define RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR (0x13F4)
#define RTL931X_MAC_SERDES_MODE_CTRL(sds) (0x136C + (((sds) << 2)))
int rtl839x_read_sds_phy(int phy_addr, int phy_reg);
int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v);
int rtl9300_serdes_setup(int port, int sds_num, phy_interface_t phy_mode);
int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg);
int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v);
int rtl931x_read_sds_phy(int phy_addr, int page, int phy_reg);
int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v);
int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode);
void rtl931x_sds_init(u32 sds, phy_interface_t mode);
/*
* TODO: The following functions are currently not in use. So compiler will complain if
* they are static and not made available externally. Collect them in this section to
* preserve for future use.
*/
void rtl9300_do_rx_calibration_3(int sds_num, phy_interface_t phy_mode);
int rtl9300_sds_clock_wait(int timeout);
int rtl9300_sds_cmu_band_get(int sds);
void rtl9300_sds_rxcal_dcvs_get(u32 sds_num, u32 dcvs_id, u32 dcvs_list[]);
void rtl9300_sds_rxcal_dcvs_manual(u32 sds_num, u32 dcvs_id, bool manual, u32 dvcs_list[]);
void rtl9300_sds_set(int sds_num, u32 mode);
int rtl931x_link_sts_get(u32 sds);
void rtl931x_sds_fiber_disable(u32 sds);
int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode);

View file

@ -1,190 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Realtek thermal sensor driver
*
* Copyright (C) 2025 Bjørn Mork <bjorn@mork.no>>
*/
#include <linux/bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "thermal_hwmon.h"
#define RTL8380_THERMAL_METER_CTRL0 0x98
#define RTL8380_THERMAL_METER_CTRL1 0x9c
#define RTL8380_THERMAL_METER_CTRL2 0xa0
#define RTL8380_THERMAL_METER_RESULT 0xa4
#define RTL8380_TM_ENABLE BIT(0)
#define RTL8380_TEMP_VALID BIT(8)
#define RTL8380_TEMP_OUT_MASK GENMASK(6, 0)
#define RTL8390_THERMAL_METER0_CTRL0 0x274
#define RTL8390_THERMAL_METER0_CTRL1 0x278
#define RTL8390_THERMAL_METER0_CTRL2 0x27c
#define RTL8390_THERMAL_METER0_RESULT 0x280
#define RTL8390_THERMAL_METER1_CTRL0 0x284
#define RTL8390_THERMAL_METER1_CTRL1 0x288
#define RTL8390_THERMAL_METER1_CTRL2 0x28c
#define RTL8390_THERMAL_METER1_RESULT 0x290
#define RTL8390_TM_ENABLE BIT(0)
#define RTL8390_TEMP_VALID BIT(8)
#define RTL8390_TEMP_OUT_MASK GENMASK(6, 0)
#define RTL9300_THERMAL_METER_CTRL0 0x60
#define RTL9300_THERMAL_METER_CTRL1 0x64
#define RTL9300_THERMAL_METER_CTRL2 0x68
#define RTL9300_THERMAL_METER_RESULT0 0x6c
#define RTL9300_THERMAL_METER_RESULT1 0x70
#define RTL9300_TM_ENABLE BIT(16)
#define RTL9300_TEMP_VALID BIT(24)
#define RTL9300_TEMP_OUT_MASK GENMASK(23, 16)
#define RTL9300_SAMPLE_DLY_SHIFT (16)
#define RTL9300_SAMPLE_DLY_MASK GENMASK(RTL9300_SAMPLE_DLY_SHIFT + 15, RTL9300_SAMPLE_DLY_SHIFT)
#define RTL9300_COMPARE_DLY_SHIFT (0)
#define RTL9300_COMPARE_DLY_MASK GENMASK(RTL9300_COMPARE_DLY_SHIFT + 15, RTL9300_COMPARE_DLY_SHIFT)
struct realtek_thermal_priv {
struct regmap *regmap;
bool enabled;
};
static void rtl8380_thermal_init(struct realtek_thermal_priv *priv)
{
priv->enabled = !regmap_update_bits(priv->regmap, RTL8380_THERMAL_METER_CTRL0, RTL8380_TM_ENABLE, RTL8380_TM_ENABLE);
}
static int rtl8380_get_temp(struct thermal_zone_device *tz, int *res)
{
struct realtek_thermal_priv *priv = thermal_zone_device_priv(tz);
int offset = thermal_zone_get_offset(tz);
int slope = thermal_zone_get_slope(tz);
u32 val;
int ret;
if (!priv->enabled)
rtl8380_thermal_init(priv);
ret = regmap_read(priv->regmap, RTL8380_THERMAL_METER_RESULT, &val);
if (ret)
return ret;
if (!(val & RTL8380_TEMP_VALID))
return -EAGAIN;
*res = FIELD_GET(RTL8380_TEMP_OUT_MASK, val) * slope + offset;
return 0;
}
static const struct thermal_zone_device_ops rtl8380_ops = {
.get_temp = rtl8380_get_temp,
};
static void rtl8390_thermal_init(struct realtek_thermal_priv *priv)
{
priv->enabled = !regmap_update_bits(priv->regmap, RTL8390_THERMAL_METER0_CTRL0, RTL8390_TM_ENABLE, RTL8390_TM_ENABLE);
}
static int rtl8390_get_temp(struct thermal_zone_device *tz, int *res)
{
struct realtek_thermal_priv *priv = thermal_zone_device_priv(tz);
int offset = thermal_zone_get_offset(tz);
int slope = thermal_zone_get_slope(tz);
u32 val;
int ret;
if (!priv->enabled)
rtl8390_thermal_init(priv);
/* assume sensor0 is the CPU, both sensor0 & sensor1 report same values +/- 1 degree C */
ret = regmap_read(priv->regmap, RTL8390_THERMAL_METER0_RESULT, &val);
if (ret)
return ret;
if (!(val & RTL8390_TEMP_VALID))
return -EAGAIN;
*res = FIELD_GET(RTL8390_TEMP_OUT_MASK, val) * slope + offset;
return 0;
}
static const struct thermal_zone_device_ops rtl8390_ops = {
.get_temp = rtl8390_get_temp,
};
static void rtl9300_thermal_init(struct realtek_thermal_priv *priv)
{
/* increasing sample delay makes get_temp() succeed more often */
regmap_update_bits(priv->regmap, RTL9300_THERMAL_METER_CTRL1, RTL9300_SAMPLE_DLY_MASK, 0x0800 << RTL9300_SAMPLE_DLY_SHIFT);
priv->enabled = !regmap_update_bits(priv->regmap, RTL9300_THERMAL_METER_CTRL2, RTL9300_TM_ENABLE, RTL9300_TM_ENABLE);
}
static int rtl9300_get_temp(struct thermal_zone_device *tz, int *res)
{
struct realtek_thermal_priv *priv = thermal_zone_device_priv(tz);
int offset = thermal_zone_get_offset(tz);
int slope = thermal_zone_get_slope(tz);
u32 val;
int ret;
if (!priv->enabled)
rtl9300_thermal_init(priv);
ret = regmap_read(priv->regmap, RTL9300_THERMAL_METER_RESULT0, &val);
if (ret)
return ret;
if (!(val & RTL9300_TEMP_VALID))
return -EAGAIN;
*res = FIELD_GET(RTL9300_TEMP_OUT_MASK, val) * slope + offset;
return 0;
}
static const struct thermal_zone_device_ops rtl9300_ops = {
.get_temp = rtl9300_get_temp,
};
static int realtek_thermal_probe(struct platform_device *pdev)
{
struct realtek_thermal_priv *priv;
struct thermal_zone_device *tzdev;
struct device *dev = &pdev->dev;
struct regmap *regmap;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
regmap = syscon_node_to_regmap(dev->of_node->parent);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
priv->regmap = regmap;
tzdev = devm_thermal_of_zone_register(dev, 0, priv, device_get_match_data(dev));
if (IS_ERR(tzdev))
return PTR_ERR(tzdev);
return devm_thermal_add_hwmon_sysfs(dev, tzdev);
}
static const struct of_device_id realtek_sensor_ids[] = {
{ .compatible = "realtek,rtl8380-thermal", .data = &rtl8380_ops, },
{ .compatible = "realtek,rtl8390-thermal", .data = &rtl8390_ops, },
{ .compatible = "realtek,rtl9300-thermal", .data = &rtl9300_ops, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, realtek_sensor_ids);
static struct platform_driver realtek_thermal_driver = {
.probe = realtek_thermal_probe,
.driver = {
.name = "realtek-thermal",
.of_match_table = realtek_sensor_ids,
},
};
module_platform_driver(realtek_thermal_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
MODULE_DESCRIPTION("Realtek temperature sensor");

View file

@ -1,15 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2022 Markus Stockhausen
*
* RTL83XX clock indices
*/
#ifndef __DT_BINDINGS_CLOCK_RTL83XX_H
#define __DT_BINDINGS_CLOCK_RTL83XX_H
#define CLK_CPU 0
#define CLK_MEM 1
#define CLK_LXB 2
#define CLK_COUNT 3
#endif /* __DT_BINDINGS_CLOCK_RTL83XX_H */

View file

@ -1,89 +0,0 @@
From fce11f68491b46b93df69de0630cd9edb90bc772 Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Wed, 29 Dec 2021 21:54:21 +0100
Subject: [PATCH] realtek: Create 4 different Realtek Platforms
Creates RTL83XX as a basic kernel config parameter for the
RTL838X, RTL839x, RTL930X and RTL931X platforms with respective
configurations for the SoCs, which are introduced in addition.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
---
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -22,6 +22,7 @@ platform-$(CONFIG_MACH_NINTENDO64) += n6
platform-$(CONFIG_PIC32MZDA) += pic32/
platform-$(CONFIG_RALINK) += ralink/
platform-$(CONFIG_MIKROTIK_RB532) += rb532/
+platform-$(CONFIG_MACH_REALTEK_RTL) += rtl838x/
platform-$(CONFIG_SGI_IP22) += sgi-ip22/
platform-$(CONFIG_SGI_IP27) += sgi-ip27/
platform-$(CONFIG_SGI_IP28) += sgi-ip22/
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -620,21 +620,23 @@ config RALINK
config MACH_REALTEK_RTL
bool "Realtek RTL838x/RTL839x based machines"
- select MIPS_GENERIC
select DMA_NONCOHERENT
select IRQ_MIPS_CPU
- select CSRC_R4K
- select CEVT_R4K
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_MIPS16
- select SYS_SUPPORTS_MULTITHREADING
- select SYS_SUPPORTS_VPE_LOADER
select BOOT_RAW
select PINCTRL
select USE_OF
+ select NO_EXCEPT_FILL
+ select SYS_SUPPORTS_HIGHMEM
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_HAS_EARLY_PRINTK_8250
+ select USE_GENERIC_EARLY_PRINTK_8250
+ select ARCH_HAS_RESET_CONTROLLER
+ select RESET_CONTROLLER
config SGI_IP22
bool "SGI IP22 (Indy/Indigo2)"
@@ -970,6 +972,36 @@ config CAVIUM_OCTEON_SOC
endchoice
+config RTL838X
+ bool "Realtek RTL838X based platforms"
+ depends on MACH_REALTEK_RTL
+ select CPU_SUPPORTS_CPUFREQ
+ select MIPS_EXTERNAL_TIMER
+
+config RTL839X
+ bool "Realtek RTL839X based platforms"
+ depends on MACH_REALTEK_RTL
+ select CPU_SUPPORTS_CPUFREQ
+ select MIPS_EXTERNAL_TIMER
+ select SYS_SUPPORTS_MULTITHREADING
+
+config RTL930X
+ bool "Realtek RTL930X based platforms"
+ depends on MACH_REALTEK_RTL
+ select MIPS_CPU_SCACHE
+ select MIPS_EXTERNAL_TIMER
+ select SYS_SUPPORTS_MULTITHREADING
+
+config RTL931X
+ bool "Realtek RTL931X based platforms"
+ depends on RTL930X
+ select MIPS_GIC
+ select COMMON_CLK
+ select CLKSRC_MIPS_GIC
+ select SYS_SUPPORTS_VPE_LOADER
+ select SYS_SUPPORTS_SMP
+ select SYS_SUPPORTS_MIPS_CPS
+
source "arch/mips/alchemy/Kconfig"
source "arch/mips/ath25/Kconfig"
source "arch/mips/ath79/Kconfig"

View file

@ -1,93 +0,0 @@
From 3cc8011171186d906c547bc6f0c1f8e350edc7cf Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Mon, 3 Oct 2022 14:45:21 +0200
Subject: [PATCH] realtek: resurrect timer driver
Now that we provide a clock driver for the Reltek SOCs the CPU frequency might
change on demand. This has direct visible effects during operation
- the CEVT 4K timer is no longer a stable clocksource
- after CPU frequencies changes time calculation works wrong
- sched_clock falls back to kernel default interval (100 Hz)
- timestamps in dmesg have only 2 digits left
[ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps ...
[ 0.060000] pid_max: default: 32768 minimum: 301
[ 0.070000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.070000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.080000] dyndbg: Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build
[ 0.090000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, ...
Looking around where we can start the CEVT timer for RTL930X is a good basis.
Initially it was developed as a clocksource driver for the broken timer in that
specific SOC series. Afterwards it was shifted around to the CEVT location,
got SMP enablement and lost its clocksource feature. So we at least have
something to copy from. As the timers on these devices are well understood
the implementation follows this way:
- leave the RTL930X implementation as is
- provide a new driver for RTL83XX devices only
- swap RTL930X driver at a later time
Like the clock driver this patch contains a self contained module that is SOC
independet and already provides full support for the RTL838X, RTL839X and
RTL930X devices. Some of the new (or reestablished) features are:
- simplified initialization routines
- SMP setup with CPU hotplug framework
- derived from LXB clock speed
- supplied clocksource
- dedicated register functions for better readability
- documentation about some caveats
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
[remove unused header includes, remove old CONFIG_MIPS dependency, add
REALTEK_ prefix to driver symbol]
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/clocksource/Kconfig | 12 +++
drivers/clocksource/Makefile | 1 +
include/linux/cpuhotplug.h | 1 +
3 files changed, 14 insertions(+)
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -134,6 +134,17 @@ config RDA_TIMER
help
Enables the support for the RDA Micro timer driver.
+config REALTEK_OTTO_TIMER
+ bool "Clocksource/timer for the Realtek Otto platform"
+ select COMMON_CLK
+ select TIMER_OF
+ help
+ This driver adds support for the timers found in the Realtek RTL83xx
+ and RTL93xx SoCs series. This includes chips such as RTL8380, RTL8381
+ and RTL832, as well as chips from the RTL839x series, such as RTL8390
+ RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
+ such as RTL9301, RTL9302 or RTL9303.
+
config SUN4I_TIMER
bool "Sun4i timer driver" if COMPILE_TEST
depends on HAS_IOMEM
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi
obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o
obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o
obj-$(CONFIG_RDA_TIMER) += timer-rda.o
+obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o
obj-$(CONFIG_ARC_TIMERS) += arc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -181,6 +181,7 @@ enum cpuhp_state {
CPUHP_AP_MARCO_TIMER_STARTING,
CPUHP_AP_MIPS_GIC_TIMER_STARTING,
CPUHP_AP_ARC_TIMER_STARTING,
+ CPUHP_AP_REALTEK_TIMER_STARTING,
CPUHP_AP_RISCV_TIMER_STARTING,
CPUHP_AP_CLINT_TIMER_STARTING,
CPUHP_AP_CSKY_TIMER_STARTING,

View file

@ -1,46 +0,0 @@
From 63a0a4d85bc900464c5b046b13808a582345f8c8 Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Sat, 11 Dec 2021 20:14:47 +0100
Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C controller
This adds support for the RTL9300 and RTL9310 I2C controller.
The controller implements the SMBus protocol for SMBus transfers
over an I2C bus. The driver supports selecting one of the 2 possible
SCL pins and any of the 8 possible SDA pins. Bus speeds of
100kHz (standard speed) and 400kHz (high speed I2C) are supported.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
---
drivers/i2c/busses/Kconfig | 10 +++++++++
drivers/i2c/busses/Makefile | 1 +
2 files changed, 11 insertions(+)
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1023,6 +1023,16 @@ config I2C_RK3X
This driver can also be built as a module. If so, the module will
be called i2c-rk3x.
+config I2C_RTL9300
+ tristate "Realtek RTL9300 I2C adapter"
+ depends on OF
+ help
+ Say Y here to include support for the I2C adapter in Realtek RTL9300
+ and RTL9310 SoCs.
+
+ This driver can also be built as a module. If so, the module will
+ be called i2c-rtl9300.
+
config I2C_RZV2M
tristate "Renesas RZ/V2M adapter"
depends on ARCH_RENESAS || COMPILE_TEST
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
+obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o
obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o

View file

@ -1,46 +0,0 @@
From f4bdb7fdccdfe3fa382abe77f72a16c2f2e6add0 Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Sat, 11 Dec 2021 20:25:37 +0100
Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C multiplexing
The RTL9300/RTL9310 I2C controllers have support for 2 independent I2C
masters, each with a fixed SCL pin, that cannot be changed. Each of these
masters can use 8 (RTL9300) or 16 (RTL9310) different pins for SDA.
This multiplexer directly controls the two masters and their shared
IO configuration registers to allow multiplexing between any of these
busses. The two masters cannot be used in parallel as the multiplex
is protected by a standard multiplex lock.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
---
drivers/i2c/muxes/Kconfig | 9 +++++++
drivers/i2c/muxes/Makefile | 1 +
2 files changed, 10 insertions(+)
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -99,6 +99,15 @@ config I2C_MUX_REG
This driver can also be built as a module. If so, the module
will be called i2c-mux-reg.
+config I2C_MUX_RTL9300
+ tristate "RTL9300 based I2C multiplexer"
+ help
+ If you say yes to this option, support will be included for a
+ RTL9300 based I2C multiplexer.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-reg.
+
config I2C_DEMUX_PINCTRL
tristate "pinctrl-based I2C demultiplexer"
depends on PINCTRL && OF
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
+obj-$(CONFIG_I2C_MUX_RTL9300) += i2c-mux-rtl9300.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG

View file

@ -1,427 +0,0 @@
From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Fri, 31 Dec 2021 11:56:49 +0100
Subject: [PATCH] realtek: Add VPE support for the IRQ driver
In order to support VSMP, enable support for both VPEs of the RTL839X
and RTL930X SoCs in the irq-realtek-rtl driver. Add support for IRQ
affinity setting.
Up to kernel 5.15 this patch was divided into two parts
315-irqchip-irq-realtek-rtl-add-VPE-support.patch
319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch
As both parts will only work in combination they have been merged into
one patch.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 296 +++++++++++++++++++++++++-----
1 file changed, 249 insertions(+), 47 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -22,22 +22,58 @@
#define RTL_ICTL_IRR3 0x14
#define RTL_ICTL_NUM_INPUTS 32
-
-#define REG(x) (realtek_ictl_base + x)
+#define RTL_ICTL_NUM_OUTPUTS 15
static DEFINE_RAW_SPINLOCK(irq_lock);
-static void __iomem *realtek_ictl_base;
+
+#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset)
+
+static u32 realtek_ictl_unmask[NR_CPUS];
+static void __iomem *realtek_ictl_base[NR_CPUS];
+static cpumask_t realtek_ictl_cpu_configurable;
+
+struct realtek_ictl_output {
+ /* IRQ controller data */
+ struct fwnode_handle *fwnode;
+ /* Output specific data */
+ unsigned int output_index;
+ struct irq_domain *domain;
+ u32 child_mask;
+};
/*
- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
- * placing IRQ 31 in the first four bits. A routing value of '0' means the
- * interrupt is left disconnected. Routing values {1..15} connect to output
- * lines {0..14}.
+ * Per CPU we have a set of 5 registers that determine interrupt handling for
+ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that
+ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt
+ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering,
+ * placing IRQ 31 in the first four bits. The register combinations give the
+ * following results for a single interrupt in the wild:
+ *
+ * a) GIMR = 0 / IRRx > 0 -> no interrupts
+ * b) GIMR = 0 / IRRx = 0 -> no interrupts
+ * c) GIMR = 1 / IRRx > 0 -> interrupts
+ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment
+ *
+ * Combination d) seems to trigger interrupts only on a VPE if the other VPE
+ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide
+ * IRQ balancing features in SMP this driver will handle the registers as
+ * follows:
+ *
+ * 1) set IRRx > 0 for VPE where the interrupt is desired
+ * 2) set IRRx = 0 for VPE where the interrupt is not desired
+ * 3) set both GIMR = 0 to mask (disabled) interrupt
+ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0
*/
+
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
#define IRR_SHIFT(idx) ((idx * 4) % 32)
-static void write_irr(void __iomem *irr0, int idx, u32 value)
+static inline u32 read_irr(void __iomem *irr0, int idx)
+{
+ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf;
+}
+
+static inline void write_irr(void __iomem *irr0, int idx, u32 value)
{
unsigned int offset = IRR_OFFSET(idx);
unsigned int shift = IRR_SHIFT(idx);
@@ -48,16 +84,33 @@ static void write_irr(void __iomem *irr0
writel(irr, irr0 + offset);
}
+static inline void enable_gimr(int hwirq, int cpu)
+{
+ u32 value;
+
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
+ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]);
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
+}
+
+static inline void disable_gimr(int hwirq, int cpu)
+{
+ u32 value;
+
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
+ value &= ~BIT(hwirq);
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
+}
+
static void realtek_ictl_unmask_irq(struct irq_data *i)
{
unsigned long flags;
- u32 value;
+ int cpu;
raw_spin_lock_irqsave(&irq_lock, flags);
- value = readl(REG(RTL_ICTL_GIMR));
- value |= BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
+ enable_gimr(i->hwirq, cpu);
raw_spin_unlock_irqrestore(&irq_lock, flags);
}
@@ -65,110 +118,259 @@ static void realtek_ictl_unmask_irq(stru
static void realtek_ictl_mask_irq(struct irq_data *i)
{
unsigned long flags;
- u32 value;
+ int cpu;
raw_spin_lock_irqsave(&irq_lock, flags);
- value = readl(REG(RTL_ICTL_GIMR));
- value &= ~BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
+ disable_gimr(i->hwirq, cpu);
raw_spin_unlock_irqrestore(&irq_lock, flags);
}
+static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i,
+ const struct cpumask *dest, bool force)
+{
+ struct realtek_ictl_output *output = i->domain->host_data;
+ cpumask_t cpu_configure;
+ cpumask_t cpu_disable;
+ cpumask_t cpu_enable;
+ unsigned long flags;
+ int cpu;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable);
+
+ cpumask_and(&cpu_enable, &cpu_configure, dest);
+ cpumask_andnot(&cpu_disable, &cpu_configure, dest);
+
+ for_each_cpu(cpu, &cpu_disable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0);
+ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq);
+ disable_gimr(i->hwirq, cpu);
+ }
+
+ for_each_cpu(cpu, &cpu_enable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1);
+ realtek_ictl_unmask[cpu] |= BIT(i->hwirq);
+ enable_gimr(i->hwirq, cpu);
+ }
+
+ irq_data_update_effective_affinity(i, &cpu_enable);
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+
+ return IRQ_SET_MASK_OK;
+}
+
static struct irq_chip realtek_ictl_irq = {
.name = "realtek-rtl-intc",
.irq_mask = realtek_ictl_mask_irq,
.irq_unmask = realtek_ictl_unmask_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = realtek_ictl_irq_affinity,
+#endif
};
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
+ struct realtek_ictl_output *output = d->host_data;
unsigned long flags;
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
raw_spin_lock_irqsave(&irq_lock, flags);
- write_irr(REG(RTL_ICTL_IRR0), hw, 1);
+
+ output->child_mask |= BIT(hw);
+ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1);
+ realtek_ictl_unmask[0] |= BIT(hw);
+
raw_spin_unlock_irqrestore(&irq_lock, flags);
return 0;
}
+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ struct realtek_ictl_output *output = d->host_data;
+ bool routed_elsewhere;
+ unsigned long flags;
+ u32 routing_old;
+ int cpu;
+
+ if (fwspec->fwnode != output->fwnode)
+ return false;
+
+ /* Original specifiers had only one parameter */
+ if (fwspec->param_count < 2)
+ return true;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ /*
+ * Inputs can only be routed to one output, so they shouldn't be
+ * allowed to end up in multiple domains.
+ */
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
+ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]);
+ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1;
+ if (routed_elsewhere) {
+ pr_warn("soc int %d already routed to output %d\n",
+ fwspec->param[0], routing_old - 1);
+ break;
+ }
+ }
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+
+ return !routed_elsewhere && fwspec->param[1] == output->output_index;
+}
+
static const struct irq_domain_ops irq_domain_ops = {
.map = intc_map,
+ .select = intc_select,
.xlate = irq_domain_xlate_onecell,
};
static void realtek_irq_dispatch(struct irq_desc *desc)
{
+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
- struct irq_domain *domain;
+ int cpu = smp_processor_id();
unsigned long pending;
unsigned int soc_int;
chained_irq_enter(chip, desc);
- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
+ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu))
+ & output->child_mask;
if (unlikely(!pending)) {
spurious_interrupt();
goto out;
}
- domain = irq_desc_get_handler_data(desc);
- for_each_set_bit(soc_int, &pending, 32)
- generic_handle_domain_irq(domain, soc_int);
+ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
+ generic_handle_domain_irq(output->domain, soc_int);
out:
chained_irq_exit(chip, desc);
}
+/*
+ * SoC interrupts are cascaded to MIPS CPU interrupts according to the
+ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
+ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
+ * thus go into 4 IRRs. A routing value of '0' means the interrupt is left
+ * disconnected. Routing values {1..15} connect to output lines {0..14}.
+ */
+static int __init setup_parent_interrupts(struct device_node *node, int *parents,
+ unsigned int num_parents)
+{
+ struct realtek_ictl_output *outputs;
+ struct realtek_ictl_output *output;
+ struct irq_domain *domain;
+ unsigned int p;
+
+ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL);
+ if (!outputs)
+ return -ENOMEM;
+
+ for (p = 0; p < num_parents; p++) {
+ output = outputs + p;
+
+ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output);
+ if (!domain)
+ goto domain_err;
+
+ output->fwnode = of_node_to_fwnode(node);
+ output->output_index = p;
+ output->domain = domain;
+
+ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output);
+ }
+
+ return 0;
+
+domain_err:
+ while (p--) {
+ irq_set_chained_handler_and_data(parents[p], NULL, NULL);
+ irq_domain_remove(outputs[p].domain);
+ }
+
+ kfree(outputs);
+
+ return -ENOMEM;
+}
+
static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
{
+ int parent_irqs[RTL_ICTL_NUM_OUTPUTS];
struct of_phandle_args oirq;
- struct irq_domain *domain;
+ unsigned int num_parents;
unsigned int soc_irq;
- int parent_irq;
+ unsigned int p;
+ int cpu;
+
+ cpumask_clear(&realtek_ictl_cpu_configurable);
- realtek_ictl_base = of_iomap(node, 0);
- if (!realtek_ictl_base)
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ realtek_ictl_base[cpu] = of_iomap(node, cpu);
+ if (realtek_ictl_base[cpu]) {
+ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable);
+
+ /* Disable all cascaded interrupts and clear routing */
+ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0);
+ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq);
+ disable_gimr(soc_irq, cpu);
+ }
+ }
+ }
+
+ if (cpumask_empty(&realtek_ictl_cpu_configurable))
return -ENXIO;
- /* Disable all cascaded interrupts and clear routing */
- writel(0, REG(RTL_ICTL_GIMR));
- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
- write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0);
+ num_parents = of_irq_count(node);
+ if (num_parents > RTL_ICTL_NUM_OUTPUTS) {
+ pr_err("too many parent interrupts\n");
+ return -EINVAL;
+ }
- if (WARN_ON(!of_irq_count(node))) {
+ for (p = 0; p < num_parents; p++)
+ parent_irqs[p] = of_irq_get(node, p);
+
+ if (WARN_ON(!num_parents)) {
/*
* If DT contains no parent interrupts, assume MIPS CPU IRQ 2
* (HW0) is connected to the first output. This is the case for
* all known hardware anyway. "interrupt-map" is deprecated, so
* don't bother trying to parse that.
+ * Since this is to account for old devicetrees with one-cell
+ * interrupt specifiers, only one output domain is needed.
*/
oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
- oirq.args_count = 1;
- oirq.args[0] = 2;
-
- parent_irq = irq_create_of_mapping(&oirq);
+ if (oirq.np) {
+ oirq.args_count = 1;
+ oirq.args[0] = 2;
+
+ parent_irqs[0] = irq_create_of_mapping(&oirq);
+ num_parents = 1;
+ }
of_node_put(oirq.np);
- } else {
- parent_irq = of_irq_get(node, 0);
}
- if (parent_irq < 0)
- return parent_irq;
- else if (!parent_irq)
- return -ENODEV;
-
- domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
- if (!domain)
- return -ENOMEM;
-
- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
+ /* Ensure we haven't collected any errors before proceeding */
+ for (p = 0; p < num_parents; p++) {
+ if (parent_irqs[p] < 0)
+ return parent_irqs[p];
+ if (!parent_irqs[p])
+ return -ENODEV;
+ }
- return 0;
+ return setup_parent_interrupts(node, &parent_irqs[0], num_parents);
}
IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);

View file

@ -1,33 +0,0 @@
From 800d5fb3c6a16661932c932bacd660e38d06b727 Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Thu, 25 Aug 2022 08:22:36 +0200
Subject: [PATCH] realtek: add patch to enable new clock driver in kernel
Allow building the clock driver with kernel config options.
Submitted-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
2 files changed, 2 insertions(+)
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -484,6 +484,7 @@ source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/nuvoton/Kconfig"
source "drivers/clk/pistachio/Kconfig"
source "drivers/clk/qcom/Kconfig"
+source "drivers/clk/realtek/Kconfig"
source "drivers/clk/ralink/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/rockchip/Kconfig"
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pi
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-y += ralink/
+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/

View file

@ -1,21 +0,0 @@
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -522,4 +522,11 @@ config LOONGSON2_THERMAL
is higher than the high temperature threshold or lower than the low
temperature threshold, the interrupt will occur.
+config REALTEK_THERMAL
+ tristate "Realtek RTL838x and RTL930x thermal sensor support"
+ depends on RTL838X || RTL839X || RTL930X || COMPILE_TEST
+ depends on THERMAL_OF
+ help
+ Support thermal sensor in Realtek RTL838x, RTL839x and RTL930x SoCs
+
endif
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_AMLOGIC_THERMAL) += aml
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o
+obj-$(CONFIG_REALTEK_THERMAL) += realtek-thermal.o

View file

@ -1,132 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: realtek dsa/phy: Increase max ports for RTL839X/RTL931X
Linux standard can only support up to 32 devices per mdio bus and up to
12 ports per DSA switch. This is not enough for the large RTL839X and
RTL931X devices. Increase the max values accordingly. Additionally take
care about the functions that work on bit masks.
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/net/mdio/fwnode_mdio.c | 2 +-
include/linux/phy.h | 6 +++---
include/linux/platform_data/dsa.h | 2 +-
include/net/dsa.h | 14 +++++++-------
net/dsa/slave.c | 4 ++--
5 files changed, 14 insertions(+), 14 deletions(-)
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -87,7 +87,7 @@ int fwnode_mdiobus_phy_device_register(s
}
if (fwnode_property_read_bool(child, "broken-turn-around"))
- mdio->phy_ignore_ta_mask |= 1 << addr;
+ mdio->phy_ignore_ta_mask |= BIT_ULL(addr);
fwnode_property_read_u32(child, "reset-assert-us",
&phy->mdio.reset_assert_delay);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -294,7 +294,7 @@ static inline const char *phy_modes(phy_
#define PHY_INIT_TIMEOUT 100000
#define PHY_FORCE_TIMEOUT 10
-#define PHY_MAX_ADDR 32
+#define PHY_MAX_ADDR 64
/* Used when trying to connect to a specific phy (mii bus id:phy device id) */
#define PHY_ID_FMT "%s:%02x"
@@ -414,10 +414,10 @@ struct mii_bus {
struct mdio_device *mdio_map[PHY_MAX_ADDR];
/** @phy_mask: PHY addresses to be ignored when probing */
- u32 phy_mask;
+ u64 phy_mask;
/** @phy_ignore_ta_mask: PHY addresses to ignore the TA/read failure */
- u32 phy_ignore_ta_mask;
+ u64 phy_ignore_ta_mask;
/**
* @irq: An array of interrupts, each PHY's interrupt at the index
--- a/include/linux/platform_data/dsa.h
+++ b/include/linux/platform_data/dsa.h
@@ -6,7 +6,7 @@ struct device;
struct net_device;
#define DSA_MAX_SWITCHES 4
-#define DSA_MAX_PORTS 12
+#define DSA_MAX_PORTS 54
#define DSA_RTABLE_NONE -1
struct dsa_chip_data {
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -465,7 +465,7 @@ struct dsa_switch {
/*
* Slave mii_bus and devices for the individual ports.
*/
- u32 phys_mii_mask;
+ u64 phys_mii_mask;
struct mii_bus *slave_mii_bus;
/* Ageing Time limits in msecs */
@@ -597,24 +597,24 @@ static inline bool dsa_is_user_port(stru
dsa_switch_for_each_port_continue_reverse((_dp), (_ds)) \
if (dsa_port_is_cpu((_dp)))
-static inline u32 dsa_user_ports(struct dsa_switch *ds)
+static inline u64 dsa_user_ports(struct dsa_switch *ds)
{
struct dsa_port *dp;
- u32 mask = 0;
+ u64 mask = 0;
dsa_switch_for_each_user_port(dp, ds)
- mask |= BIT(dp->index);
+ mask |= BIT_ULL(dp->index);
return mask;
}
-static inline u32 dsa_cpu_ports(struct dsa_switch *ds)
+static inline u64 dsa_cpu_ports(struct dsa_switch *ds)
{
struct dsa_port *cpu_dp;
- u32 mask = 0;
+ u64 mask = 0;
dsa_switch_for_each_cpu_port(cpu_dp, ds)
- mask |= BIT(cpu_dp->index);
+ mask |= BIT_ULL(cpu_dp->index);
return mask;
}
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -320,7 +320,7 @@ static int dsa_slave_phy_read(struct mii
{
struct dsa_switch *ds = bus->priv;
- if (ds->phys_mii_mask & (1 << addr))
+ if (ds->phys_mii_mask & BIT_ULL(addr))
return ds->ops->phy_read(ds, addr, reg);
return 0xffff;
@@ -330,7 +330,7 @@ static int dsa_slave_phy_write(struct mi
{
struct dsa_switch *ds = bus->priv;
- if (ds->phys_mii_mask & (1 << addr))
+ if (ds->phys_mii_mask & BIT_ULL(addr))
return ds->ops->phy_write(ds, addr, reg, val);
return 0;

View file

@ -1,79 +0,0 @@
From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Wed, 8 Sep 2021 16:13:18 +0200
Subject: realtek phy: Add PHY hsgmii mode
This adds RTL93xx-specific MAC configuration routines that allow also configuration
of 10GBit links for phylink. There is support for the Realtek-specific HSGMII
protocol.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
---
drivers/net/phy/phy-core.c | 1 +
drivers/net/phy/phylink.c | 4 ++++
include/linux/phy.h | 3 +++
3 files changed, 8 insertions(+)
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -126,6 +126,7 @@ int phy_interface_num_ports(phy_interfac
case PHY_INTERFACE_MODE_MOCA:
case PHY_INTERFACE_MODE_TRGMII:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_HSGMII:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_SMII:
case PHY_INTERFACE_MODE_1000BASEX:
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -124,6 +124,7 @@ do { \
static const phy_interface_t phylink_sfp_interface_preference[] = {
PHY_INTERFACE_MODE_25GBASER,
PHY_INTERFACE_MODE_USXGMII,
+ PHY_INTERFACE_MODE_HSGMII,
PHY_INTERFACE_MODE_10GBASER,
PHY_INTERFACE_MODE_5GBASER,
PHY_INTERFACE_MODE_2500BASEX,
@@ -238,6 +239,7 @@ static int phylink_interface_max_speed(p
case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_RXAUI:
+ case PHY_INTERFACE_MODE_HSGMII:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_10GKR:
@@ -547,6 +549,7 @@ unsigned long phylink_get_capabilities(p
break;
case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_HSGMII:
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
@@ -957,6 +960,7 @@ static int phylink_parse_mode(struct phy
fallthrough;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GKR:
+ case PHY_INTERFACE_MODE_HSGMII:
case PHY_INTERFACE_MODE_10GBASER:
phylink_set(pl->supported, 10baseT_Half);
phylink_set(pl->supported, 10baseT_Full);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -148,6 +148,7 @@ typedef enum {
PHY_INTERFACE_MODE_XGMII,
PHY_INTERFACE_MODE_XLGMII,
PHY_INTERFACE_MODE_MOCA,
+ PHY_INTERFACE_MODE_HSGMII,
PHY_INTERFACE_MODE_PSGMII,
PHY_INTERFACE_MODE_QSGMII,
PHY_INTERFACE_MODE_TRGMII,
@@ -256,6 +257,8 @@ static inline const char *phy_modes(phy_
return "xlgmii";
case PHY_INTERFACE_MODE_MOCA:
return "moca";
+ case PHY_INTERFACE_MODE_HSGMII:
+ return "hsgmii";
case PHY_INTERFACE_MODE_PSGMII:
return "psgmii";
case PHY_INTERFACE_MODE_QSGMII:

View file

@ -1,32 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: phy: Add PHY ops for rtl838x EEE
* rename the target to realtek
* add refactored DSA driver
* add latest gpio driver
* lots of arch cleanups
* new irq driver
* additional boards
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
---
include/linux/phy.h | 4 ++++
1 file changed, 4 insertions(+)
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1178,6 +1178,8 @@ struct phy_driver {
*/
int (*led_polarity_set)(struct phy_device *dev, int index,
unsigned long modes);
+ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e);
+ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e);
};
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
struct phy_driver, mdiodrv)

View file

@ -1,49 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: net: phy: EEE support for rtl838x
* rename the target to realtek
* add refactored DSA driver
* add latest gpio driver
* lots of arch cleanups
* new irq driver
* additional boards
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
---
drivers/net/phy/phylink. | 14 +++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -2805,8 +2805,11 @@ int phylink_ethtool_get_eee(struct phyli
ASSERT_RTNL();
- if (pl->phydev)
+ if (pl->phydev) {
+ if (pl->phydev->drv->get_eee)
+ return pl->phydev->drv->get_eee(pl->phydev, eee);
ret = phy_ethtool_get_eee(pl->phydev, eee);
+ }
return ret;
}
@@ -2823,8 +2826,11 @@ int phylink_ethtool_set_eee(struct phyli
ASSERT_RTNL();
- if (pl->phydev)
+ if (pl->phydev) {
+ if (pl->phydev->drv->set_eee)
+ return pl->phydev->drv->set_eee(pl->phydev, eee);
ret = phy_ethtool_set_eee(pl->phydev, eee);
+ }
return ret;
}

View file

@ -1,428 +0,0 @@
From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001
From: Antoine Tenart <antoine.tenart@bootlin.com>
Date: Fri, 21 Feb 2020 11:51:27 +0100
Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library
Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
---
drivers/net/mdio/Kconfig | 11 +++++++
drivers/net/mdio/Makefile | 1 +
drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++
drivers/net/phy/Kconfig | 1 +
include/linux/mdio/mdio-i2c.h | 16 +++++++++
5 files changed, 91 insertions(+)
create mode 100644 drivers/net/mdio/mdio-smbus.c
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -54,6 +54,17 @@ config MDIO_SUN4I
interface units of the Allwinner SoC that have an EMAC (A10,
A12, A10s, etc.)
+config MDIO_SMBUS
+ tristate
+ depends on I2C_SMBUS
+ help
+ Support SMBus based PHYs. This provides a MDIO bus bridged
+ to SMBus to allow PHYs connected in SMBus mode to be accessed
+ using the existing infrastructure.
+
+ This is library mode.
+
+
config MDIO_XGENE
tristate "APM X-Gene SoC MDIO bus controller"
depends on ARCH_XGENE || COMPILE_TEST
--- a/drivers/net/mdio/Makefile
+++ b/drivers/net/mdio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms
obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o
+obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
--- /dev/null
+++ b/drivers/net/mdio/mdio-smbus.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MDIO SMBus bridge
+ *
+ * Copyright (C) 2020 Antoine Tenart
+ * Copyright (C) 2025 Bjørn Mork <bjorn@mork.no>
+ *
+ * Network PHYs can appear on SMBus when they are part of SFP modules.
+ */
+#include <linux/i2c.h>
+#include <linux/phy.h>
+#include <linux/mdio/mdio-i2c.h>
+#include <linux/sfp.h>
+
+static int smbus_mii_read_c45(struct mii_bus *mii, int phy_id, int devad, int reg)
+{
+ u16 bus_addr = i2c_mii_phy_addr(phy_id);
+ struct i2c_adapter *i2c = mii->priv;
+ union i2c_smbus_data data;
+ size_t addrlen;
+ u8 buf[5], *p;
+ int i, ret;
+
+ if (!i2c_mii_valid_phy_id(phy_id))
+ return 0xffff;
+
+ p = buf;
+ if (devad >= 0) {
+ *p++ = 0x20 | devad;
+ *p++ = reg >> 8;
+ }
+ *p++ = reg;
+ addrlen = p - buf;
+
+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
+ if (addrlen > 1) {
+ for (i = 1; i < addrlen; i++) {
+ data.byte = buf[i];
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ }
+ }
+
+ for (i = addrlen; i < addrlen + 2; i++) {
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, buf[0], I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ buf[i] = data.byte;
+ }
+
+unlock:
+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+ if (ret < 0)
+ return 0xffff;
+ return buf[addrlen] << 8 | buf[addrlen + 1];
+}
+
+static int smbus_mii_write_c45(struct mii_bus *mii, int phy_id, int devad, int reg, u16 val)
+{
+ u16 bus_addr = i2c_mii_phy_addr(phy_id);
+ struct i2c_adapter *i2c = mii->priv;
+ union i2c_smbus_data data;
+ size_t buflen;
+ u8 buf[5], *p;
+ int i, ret;
+
+ if (!i2c_mii_valid_phy_id(phy_id))
+ return 0;
+
+ p = buf;
+ if (devad >= 0) {
+ *p++ = devad;
+ *p++ = reg >> 8;
+ }
+ *p++ = reg;
+ *p++ = val >> 8;
+ *p++ = val;
+ buflen = p - buf;
+
+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
+ for (i = 1; i < buflen; i++) {
+ data.byte = buf[i];
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ }
+unlock:
+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+ return ret < 0 ? ret : 0;
+}
+
+static int smbus_mii_read_c22(struct mii_bus *bus, int phy_id, int reg)
+{
+ return smbus_mii_read_c45(bus, phy_id, -1, reg);
+}
+
+static int smbus_mii_write_c22(struct mii_bus *bus, int phy_id, int reg, u16 val)
+{
+ return smbus_mii_write_c45(bus, phy_id, -1, reg, val);
+}
+
+/* From mdio-i2c.c:
+ *
+ * RollBall SFPs do not access internal PHY via I2C address 0x56, but
+ * instead via address 0x51, when SFP page is set to 0x03 and password to
+ * 0xffffffff.
+ *
+ * address size contents description
+ * ------- ---- -------- -----------
+ * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done
+ * 0x81 1 DEV Clause 45 device
+ * 0x82 2 REG Clause 45 register
+ * 0x84 2 VAL Register value
+ */
+#define ROLLBALL_PHY_I2C_ADDR 0x51
+
+#define ROLLBALL_PASSWORD (SFP_VSL + 3)
+
+#define ROLLBALL_CMD_ADDR 0x80
+#define ROLLBALL_DATA_ADDR 0x81
+
+#define ROLLBALL_CMD_WRITE 0x01
+#define ROLLBALL_CMD_READ 0x02
+#define ROLLBALL_CMD_DONE 0x04
+
+#define SFP_PAGE_ROLLBALL_MDIO 3
+
+static int smbus_set_sfp_page_lock(struct i2c_adapter *i2c, int bus_addr, u8 page)
+{
+ union i2c_smbus_data data;
+ u8 oldpage;
+ int ret;
+
+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
+
+ /* read current page */
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+
+ oldpage = data.byte;
+ data.byte = page;
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret == 0)
+ return oldpage;
+
+unlock:
+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+
+ return ret;
+}
+
+static int __smbus_set_sfp_page_unlock(struct i2c_adapter *i2c, int bus_addr, u8 page)
+{
+ union i2c_smbus_data data;
+ int ret;
+
+ data.byte = page;
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data);
+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
+
+ return ret;
+}
+
+/* Wait for the ROLLBALL_CMD_ADDR register to read ROLLBALL_CMD_DONE,
+ * indicating that the previous command has completed.
+ *
+ * Quoting from the mdio-i2c.c implementation:
+ *
+ * By experiment it takes up to 70 ms to access a register for these
+ * SFPs. Sleep 20ms between iterations and try 10 times.
+ */
+static int __smbus_rollball_mii_poll(struct i2c_adapter *i2c , int bus_addr)
+{
+ union i2c_smbus_data data;
+ int i, ret;
+
+ i = 10;
+ do {
+ msleep(20);
+
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data.byte == ROLLBALL_CMD_DONE)
+ return 0;
+ } while (i-- > 0);
+ dev_dbg(&i2c->dev, "poll timed out\n");
+ return -ETIMEDOUT;
+}
+
+static int smbus_mii_read_rollball(struct mii_bus *bus, int phy_id, int devad, int reg)
+{
+ struct i2c_adapter *i2c = bus->priv;
+ union i2c_smbus_data data;
+ int i, bus_addr, old, ret;
+ u8 buf[6];
+
+ bus_addr = i2c_mii_phy_addr(phy_id);
+ if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
+ return 0xffff;
+
+ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO);
+ if (old < 0)
+ return 0xffff;
+
+ /* set address */
+ buf[0] = ROLLBALL_CMD_READ;
+ buf[1] = devad;
+ buf[2] = reg >> 8;
+ buf[3] = reg & 0xff;
+
+ /* send address */
+ for (i = 0; i < 4; i++) {
+ data.byte = buf[i];
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ }
+
+ /* wait for command to complete */
+ ret = __smbus_rollball_mii_poll(i2c, bus_addr);
+ if (ret)
+ goto unlock;
+
+ /* read result */
+ for (i = 4; i < 6; i++) {
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ buf[i] = data.byte;
+ }
+
+unlock:
+ __smbus_set_sfp_page_unlock(i2c, bus_addr, old);
+ if (ret < 0)
+ return 0xffff;
+ return buf[4] << 8 | buf[5];
+}
+
+static int smbus_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, int reg, u16 val)
+{
+ struct i2c_adapter *i2c = bus->priv;
+ union i2c_smbus_data data;
+ int i, bus_addr, old, ret;
+ u8 buf[6];
+
+ bus_addr = i2c_mii_phy_addr(phy_id);
+ if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
+ return 0;
+
+ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO);
+ if (old < 0)
+ return old;
+
+ /* set address */
+ buf[0] = ROLLBALL_CMD_WRITE;
+ buf[1] = devad;
+ buf[2] = reg >> 8;
+ buf[3] = reg & 0xff;
+ buf[4] = val >> 8;
+ buf[5] = val & 0xff;
+
+ /* send address and value */
+ for (i = 0; i < 6; i++) {
+ data.byte = buf[i];
+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ goto unlock;
+ }
+
+ /* wait for command to complete */
+ ret = __smbus_rollball_mii_poll(i2c, bus_addr);
+
+unlock:
+ __smbus_set_sfp_page_unlock(i2c, bus_addr, old);
+ return ret;
+}
+
+/* write "password" - four 0xff bytes - to the ROLLBALL_PASSWORD register */
+static int smbus_mii_init_rollball(struct i2c_adapter *i2c)
+{
+ union i2c_smbus_data data;
+ int i, ret;
+
+ data.byte = 0xff;
+ for (i = 0; i < 4; i++) {
+ ret = i2c_smbus_xfer(i2c, ROLLBALL_PHY_I2C_ADDR, 0, I2C_SMBUS_WRITE, ROLLBALL_PASSWORD + i, I2C_SMBUS_BYTE_DATA, &data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c,
+ enum mdio_i2c_proto protocol)
+{
+ struct mii_bus *mii;
+ int ret;
+
+ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA))
+ return ERR_PTR(-EINVAL);
+
+ mii = mdiobus_alloc();
+ if (!mii)
+ return ERR_PTR(-ENOMEM);
+
+ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent));
+ mii->parent = parent;
+ mii->priv = i2c;
+
+ switch (protocol) {
+ case MDIO_I2C_ROLLBALL:
+ ret = smbus_mii_init_rollball(i2c);
+ if (ret < 0) {
+ dev_err(parent,
+ "Cannot initialize RollBall MDIO protocol on SMBus: %d\n",
+ ret);
+ mdiobus_free(mii);
+ return ERR_PTR(ret);
+ }
+
+ mii->read_c45 = smbus_mii_read_rollball;
+ mii->write_c45 = smbus_mii_write_rollball;
+ break;
+ default:
+ mii->read = smbus_mii_read_c22;
+ mii->write = smbus_mii_write_c22;
+ mii->read_c45 = smbus_mii_read_c45;
+ mii->write_c45 = smbus_mii_write_c45;
+ break;
+ }
+
+ return mii;
+}
+
+MODULE_AUTHOR("Antoine Tenart");
+MODULE_DESCRIPTION("MDIO SMBus bridge library");
+MODULE_LICENSE("GPL");
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -65,6 +65,7 @@ config SFP
depends on I2C && PHYLINK
depends on HWMON || HWMON=n
select MDIO_I2C
+ select MDIO_SMBUS
comment "Switch configuration API + drivers"
--- a/include/linux/mdio/mdio-i2c.h
+++ b/include/linux/mdio/mdio-i2c.h
@@ -20,5 +20,9 @@ enum mdio_i2c_proto {
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
enum mdio_i2c_proto protocol);
+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c,
+ enum mdio_i2c_proto protocol);
+bool i2c_mii_valid_phy_id(int phy_id);
+unsigned int i2c_mii_phy_addr(int phy_id);
#endif
--- a/drivers/net/mdio/mdio-i2c.c
+++ b/drivers/net/mdio/mdio-i2c.c
@@ -20,12 +20,12 @@
* specified to be present in SFP modules. These correspond with PHY
* addresses 16 and 17. Disallow access to these "phy" addresses.
*/
-static bool i2c_mii_valid_phy_id(int phy_id)
+bool i2c_mii_valid_phy_id(int phy_id)
{
return phy_id != 0x10 && phy_id != 0x11;
}
-static unsigned int i2c_mii_phy_addr(int phy_id)
+unsigned int i2c_mii_phy_addr(int phy_id)
{
return phy_id + 0x40;
}

View file

@ -1,126 +0,0 @@
From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001
From: Antoine Tenart <antoine.tenart@bootlin.com>
Date: Fri, 21 Feb 2020 11:55:29 +0100
Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus
Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
---
drivers/net/phy/sfp.c | 92 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 88 insertions(+), 4 deletions(-)
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -675,10 +675,64 @@ static int sfp_i2c_write(struct sfp *sfp
return ret == ARRAY_SIZE(msgs) ? len : 0;
}
+static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
+ size_t len)
+{
+ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf;
+ union i2c_smbus_data data;
+ int ret;
+
+ bus_addr -= 0x40;
+
+ while (len > 0) {
+ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0,
+ I2C_SMBUS_READ, dev_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (ret)
+ return ret;
+ *val++ = data.byte;
+ dev_addr++;
+ len--;
+ }
+
+ return val - (u8 *)buf;
+}
+
+static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
+ size_t len)
+{
+ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf;
+ union i2c_smbus_data data;
+ int ret;
+
+ bus_addr -= 0x40;
+
+ while (len > 0) {
+ data.byte = *val++;
+ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0,
+ I2C_SMBUS_WRITE, dev_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (ret)
+ return ret;
+ dev_addr++;
+ len--;
+ }
+
+ return val - (u8 *)buf;
+}
+
static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
{
- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
- return -EINVAL;
+ if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) {
+ if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ sfp->i2c = i2c;
+ sfp->read = sfp_smbus_read;
+ sfp->write = sfp_smbus_write;
+
+ return 0;
+ } else
+ return -EINVAL;
+ }
sfp->i2c = i2c;
sfp->read = sfp_i2c_read;
@@ -710,6 +764,29 @@ static int sfp_i2c_mdiobus_create(struct
return 0;
}
+static int sfp_sm_mdiobus_create(struct sfp *sfp)
+{
+ struct mii_bus *sm_mii;
+ int ret;
+
+ sm_mii = mdio_smbus_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol);
+ if (IS_ERR(sm_mii))
+ return PTR_ERR(sm_mii);
+
+ sm_mii->name = "SFP SMBus";
+ sm_mii->phy_mask = ~0;
+
+ ret = mdiobus_register(sm_mii);
+ if (ret < 0) {
+ mdiobus_free(sm_mii);
+ return ret;
+ }
+
+ sfp->i2c_mii = sm_mii;
+
+ return 0;
+}
+
static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
{
mdiobus_unregister(sfp->i2c_mii);
@@ -1884,9 +1961,15 @@ static void sfp_sm_fault(struct sfp *sfp
static int sfp_sm_add_mdio_bus(struct sfp *sfp)
{
- if (sfp->mdio_protocol != MDIO_I2C_NONE)
+ if (sfp->mdio_protocol == MDIO_I2C_NONE)
+ return 0;
+
+ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_I2C))
return sfp_i2c_mdiobus_create(sfp);
+ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_SMBUS_BYTE_DATA))
+ return sfp_sm_mdiobus_create(sfp);
+
return 0;
}

View file

@ -1,48 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: net: ethernet: Add support for RTL838x ethernet
* rename the target to realtek
* add refactored DSA driver
* add latest gpio driver
* lots of arch cleanups
* new irq driver
* additional boards
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
---
drivers/net/ethernet/Kconfig | 7 +-
drivers/net/ethernet/Makefile | 1 +
2 files changed, 8 insertions(+)
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -170,6 +170,13 @@ source "drivers/net/ethernet/rdc/Kconfig
source "drivers/net/ethernet/realtek/Kconfig"
source "drivers/net/ethernet/renesas/Kconfig"
source "drivers/net/ethernet/rocker/Kconfig"
+
+config NET_RTL838X
+ tristate "Realtek rtl838x Ethernet MAC support"
+ depends on MACH_REALTEK_RTL
+ help
+ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC.
+
source "drivers/net/ethernet/samsung/Kconfig"
source "drivers/net/ethernet/seeq/Kconfig"
source "drivers/net/ethernet/sgi/Kconfig"
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real
obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/
+obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/

View file

@ -1,42 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: net: dsa: Add support for rtl838x switch
* rename the target to realtek
* add refactored DSA driver
* add latest gpio driver
* lots of arch cleanups
* new irq driver
* additional boards
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
---
drivers/net/dsa/rtl83xx/Kconfig | 2 ++
drivers/net/dsa/rtl83xx/Makefile | 1 +
2 files changed, 3 insertions(+)
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -89,6 +89,8 @@ source "drivers/net/dsa/xrs700x/Kconfig"
source "drivers/net/dsa/realtek/Kconfig"
+source "drivers/net/dsa/rtl83xx/Kconfig"
+
config NET_DSA_RZN1_A5PSW
tristate "Renesas RZ/N1 A5PSW Ethernet switch support"
depends on OF && ARCH_RZN1
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -24,5 +24,6 @@ obj-y += mv88e6xxx/
obj-y += ocelot/
obj-y += qca/
obj-y += realtek/
+obj-y += rtl83xx/
obj-y += sja1105/
obj-y += xrs700x/

View file

@ -1,39 +0,0 @@
From 89f71ebb355c624320c2b0ace8ae9488ff53cbeb Mon Sep 17 00:00:00 2001
From: Birger Koblitz <mail@birger-koblitz.de>
Date: Tue, 5 Jan 2021 20:40:52 +0100
Subject: PHY: Add realtek PHY
This fixes the build problems for the REALTEK target by adding a proper
configuration option for the phy module.
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/phy/Kconfig | 6 ++++++
drivers/net/phy/Makefile | 1 +
2 files changed, 7 insertions(+)
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -409,6 +409,12 @@ source "drivers/net/phy/realtek/Kconfig"
source "drivers/net/phy/rtl8261n/Kconfig"
+config REALTEK_SOC_PHY
+ tristate "Realtek SoC PHYs"
+ depends on MACH_REALTEK_RTL
+ help
+ Supports the PHYs found in combination with Realtek Switch SoCs
+
config RENESAS_PHY
tristate "Renesas PHYs"
help
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -101,6 +101,7 @@ obj-y += qcom/
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
obj-$(CONFIG_REALTEK_PHY) += realtek/
obj-y += rtl8261n/
+obj-$(CONFIG_REALTEK_SOC_PHY) += rtl83xx-phy.o
obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o
obj-$(CONFIG_SMSC_PHY) += smsc.o

View file

@ -1,61 +0,0 @@
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Thu, 26 Nov 2020 12:02:21 +0100
Subject: net: dsa: Add rtl838x support for tag trailer
* rename the target to realtek
* add refactored DSA driver
* add latest gpio driver
* lots of arch cleanups
* new irq driver
* additional boards
Submitted-by: Bert Vermeulen <bert@biot.com>
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
Submitted-by: Sander Vanheule <sander@svanheule.net>
Submitted-by: Bjørn Mork <bjorn@mork.no>
Submitted-by: John Crispin <john@phrozen.org>
---
net/dsa/tag_trailer.c | 16 +++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
--- a/net/dsa/tag_trailer.c
+++ b/net/dsa/tag_trailer.c
@@ -19,7 +19,12 @@ static struct sk_buff *trailer_xmit(stru
trailer = skb_put(skb, 4);
trailer[0] = 0x80;
+
+#ifdef CONFIG_NET_DSA_RTL83XX
+ trailer[1] = dp->index;
+#else
trailer[1] = 1 << dp->index;
+#endif /* CONFIG_NET_DSA_RTL838X */
trailer[2] = 0x10;
trailer[3] = 0x00;
@@ -35,12 +40,23 @@ static struct sk_buff *trailer_rcv(struc
return NULL;
trailer = skb_tail_pointer(skb) - 4;
+
+#ifdef CONFIG_NET_DSA_RTL83XX
+ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 ||
+ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)
+ return NULL;
+
+ if (trailer[1] & 0x40)
+ skb->offload_fwd_mark = 1;
+
+ source_port = trailer[1] & 0x3f;
+#else
if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 ||
(trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)
return NULL;
source_port = trailer[1] & 7;
-
+#endif
skb->dev = dsa_master_find_slave(dev, 0, source_port);
if (!skb->dev)
return NULL;

View file

@ -1,227 +0,0 @@
From ffb7da9aa25765b2115e7ff3ee4f6dafa60f5421 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Fri, 27 Dec 2024 14:55:31 +0100
Subject: [PATCH] net: mdio: Add Realtek Otto auxiliary controller
SoCs in Realtek's Otto platform such as the RTL8380, RTL8391, and
RTL9302 have a simple auxiliary MDIO controller that is commonly used to
manage RTL8231 GPIO expanders on switch devices.
Add a new MDIO controller driver supporting the RTL838x (maple), RTL839x
(cypress), and RTL930x (longan) SoCs.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/net/mdio/Kconfig | 10 ++
drivers/net/mdio/Makefile | 1 +
drivers/net/mdio/mdio-realtek-otto-aux.c | 175 +++++++++++++++++++++++
3 files changed, 186 insertions(+)
create mode 100644 drivers/net/mdio/mdio-realtek-otto-aux.c
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -207,6 +207,16 @@ config MDIO_REGMAP
regmap. Users willing to use this driver must explicitly select
REGMAP.
+config MDIO_REALTEK_OTTO_AUX
+ tristate "Realtek Otto auxiliary MDIO interface support"
+ default MACH_REALTEK_RTL
+ depends on MACH_REALTEK_RTL
+ depends on MFD_SYSCON
+ select MDIO_DEVRES
+ help
+ This driver supports the auxilairy MDIO bus on RTL838x SoCs. This bus
+ is typically used to attach RTL8231 GPIO extenders.
+
config MDIO_THUNDER
tristate "ThunderX SOCs MDIO buses"
depends on 64BIT
--- a/drivers/net/mdio/Makefile
+++ b/drivers/net/mdio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms
obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o
+obj-$(CONFIG_MDIO_REALTEK_OTTO_AUX) += mdio-realtek-otto-aux.o
obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
--- /dev/null
+++ b/drivers/net/mdio/mdio-realtek-otto-aux.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define RTL8380_EXT_GPIO_INDIRECT_ACCESS 0xA09C
+#define RTL8390_EXT_GPIO_INDIRECT_ACCESS 0x0224
+#define RTL9300_EXT_GPIO_INDIRECT_ACCESS 0xC620
+
+#define RTL83XX_AUX_MDIO_DATA_OFFSET 16
+#define RTL83XX_AUX_MDIO_RCMD_FAIL 0
+
+#define RTL93XX_AUX_MDIO_DATA_OFFSET 12
+#define RTL93XX_AUX_MDIO_RCMD_FAIL BIT(28)
+
+#define REALTEK_AUX_MDIO_REG GENMASK(11, 7)
+#define REALTEK_AUX_MDIO_PHY_ADDR GENMASK(6, 2)
+#define REALTEK_AUX_MDIO_WRITE BIT(1)
+#define REALTEK_AUX_MDIO_READ 0
+#define REALTEK_AUX_MDIO_EXEC BIT(0)
+
+struct realtek_aux_mdio_info {
+ unsigned int cmd_reg;
+ unsigned int data_offset;
+ unsigned int rcmd_fail_mask;
+ unsigned int timeout_us;
+};
+
+static const struct realtek_aux_mdio_info info_rtl838x = {
+ .cmd_reg = RTL8380_EXT_GPIO_INDIRECT_ACCESS,
+ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET,
+ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL,
+ .timeout_us = 1700,
+};
+
+static const struct realtek_aux_mdio_info info_rtl839x = {
+ .cmd_reg = RTL8390_EXT_GPIO_INDIRECT_ACCESS,
+ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET,
+ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL,
+ .timeout_us = 4120,
+};
+
+static const struct realtek_aux_mdio_info info_rtl930x = {
+ .cmd_reg = RTL9300_EXT_GPIO_INDIRECT_ACCESS,
+ .data_offset = RTL93XX_AUX_MDIO_DATA_OFFSET,
+ .rcmd_fail_mask = RTL93XX_AUX_MDIO_RCMD_FAIL,
+ .timeout_us = 19000,
+};
+
+struct realtek_aux_mdio_ctrl {
+ struct device *dev;
+ struct regmap *map;
+ const struct realtek_aux_mdio_info *info;
+};
+
+#define mii_bus_to_ctrl(bus) ((struct realtek_aux_mdio_ctrl *) bus->priv)
+
+static int realtek_aux_mdio_cmd(struct realtek_aux_mdio_ctrl *ctrl, int addr, int regnum,
+ u32 rw_bit, u16 *data)
+{
+ unsigned int cmd;
+ int err;
+
+ cmd = rw_bit | REALTEK_AUX_MDIO_EXEC;
+ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_PHY_ADDR, addr);
+ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_REG, regnum);
+
+ if (rw_bit == REALTEK_AUX_MDIO_WRITE)
+ cmd |= *data << ctrl->info->data_offset;
+
+ err = regmap_write(ctrl->map, ctrl->info->cmd_reg, cmd);
+ if (err)
+ return err;
+
+ err = regmap_read_poll_timeout_atomic(ctrl->map, ctrl->info->cmd_reg, cmd,
+ !(cmd & REALTEK_AUX_MDIO_EXEC), 3, ctrl->info->timeout_us);
+ if (err)
+ return err;
+
+ if (rw_bit == REALTEK_AUX_MDIO_READ) {
+ if (cmd & ctrl->info->rcmd_fail_mask)
+ return -EIO;
+
+ *data = (cmd >> ctrl->info->data_offset) & GENMASK(15, 0);
+ }
+
+ return 0;
+}
+
+static int realtek_aux_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus);
+ u16 data;
+ int err;
+
+ err = realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_READ, &data);
+
+ if (err)
+ return err;
+ else
+ return data;
+}
+
+static int realtek_aux_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus);
+
+ return realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_WRITE, &val);
+}
+
+static int realtek_aux_mdio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct realtek_aux_mdio_ctrl *ctrl;
+ struct mii_bus *bus;
+
+ bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctrl));
+ if (!bus)
+ return -ENOMEM;
+
+ ctrl = bus->priv;
+ ctrl->dev = &pdev->dev;
+ ctrl->info = (const struct realtek_aux_mdio_info *) device_get_match_data(ctrl->dev);
+ ctrl->map = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(ctrl->map))
+ return PTR_ERR(ctrl->map);
+
+ bus->name = "Realtek auxiliary MDIO bus";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "realtek-aux-mdio") ;
+ bus->parent = ctrl->dev;
+ bus->read = realtek_aux_mdio_read;
+ bus->write = realtek_aux_mdio_write;
+ /* Don't have interrupts */
+ for (unsigned int i = 0; i < PHY_MAX_ADDR; i++)
+ bus->irq[i] = PHY_POLL;
+
+ return devm_of_mdiobus_register(ctrl->dev, bus, np);
+}
+
+static const struct of_device_id realtek_aux_mdio_of_match[] = {
+ {
+ .compatible = "realtek,rtl8380-aux-mdio",
+ .data = &info_rtl838x,
+ },
+ {
+ .compatible = "realtek,rtl8390-aux-mdio",
+ .data = &info_rtl839x,
+ },
+ {
+ .compatible = "realtek,rtl9300-aux-mdio",
+ .data = &info_rtl930x,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, realtek_aux_mdio_of_match);
+
+static struct platform_driver realtek_aux_mdio_driver = {
+ .driver = {
+ .name = "realtek-otto-aux-mdio",
+ .of_match_table = realtek_aux_mdio_of_match
+ },
+ .probe = realtek_aux_mdio_probe,
+};
+module_platform_driver(realtek_aux_mdio_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek otto auxiliary MDIO bus");
+MODULE_LICENSE("GPL v2");

View file

@ -1,56 +0,0 @@
From b3f79468c90d8770f007d628a1e32b2d5d44a5c2 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Sat, 15 May 2021 11:57:32 +0200
Subject: [PATCH] gpio: regmap: Bypass cache for shadowed outputs
Some chips have the read-only input and write-only output data registers
aliased to the same offset, but do not perform direction multiplexing on
writes. Upon writing the register, this then always updates the output
value, even when the pin is configured as input. As a result it is not
safe to perform read-modify-writes on output pins, when other pins are
still configured as input.
For example, on a bit-banged I2C bus, where the lines are switched
between out-low and in (with external pull-up)
OUT(L) IN OUT(H)
SCK ....../''''''|''''''
SDA '''''''''\..........
^ ^- SCK switches to direction to OUT, but now has a high
| value, breaking the clock.
|
\- Perform RMW to update SDA. This reads the current input
value for SCK, updates the SDA value and writes back a 1
for SCK as well.
If a register is used for both the data input and data output (and is
not marked as volatile) the driver should ensure the cache is not
updated on register reads. This ensures proper functioning of writing
the output register with regmap_update_bits(), which will then use and
update the cache only on register writes.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/gpio/gpio-regmap.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -74,7 +74,15 @@ static int gpio_regmap_get(struct gpio_c
if (ret)
return ret;
- ret = regmap_read(gpio->regmap, reg, &val);
+ /*
+ * Ensure we don't spoil the register cache with pin input values and
+ * perform a bypassed read. This way the cache (if any) is only used and
+ * updated on register writes.
+ */
+ if (gpio->reg_dat_base == gpio->reg_set_base)
+ ret = regmap_read_bypassed(gpio->regmap, reg, &val);
+ else
+ ret = regmap_read(gpio->regmap, reg, &val);
if (ret)
return ret;

View file

@ -1,330 +0,0 @@
From 4e3455e058d40eb2a7326016494e3c81dc506c33 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Mon, 10 May 2021 18:33:01 +0200
Subject: [PATCH] mfd: Add RTL8231 core device
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
To provide different read and write semantics for the GPIO data
registers, a secondary virtual register range is used to enable separate
caching properties of pin input and output values.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/mfd/Kconfig | 9 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 193 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/rtl8231.h | 71 +++++++++++++
4 files changed, 274 insertions(+)
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1171,6 +1171,15 @@ config MFD_RDC321X
southbridge which provides access to GPIOs and Watchdog using the
southbridge PCI device configuration space.
+config MFD_RTL8231
+ tristate "Realtek RTL8231 GPIO and LED expander"
+ select MFD_CORE
+ select REGMAP_MDIO
+ help
+ Support for the Realtek RTL8231 GPIO and LED expander.
+ Provides up to 37 GPIOs, 88 LEDs, and one PWM output.
+ When built as a module, this module will be named rtl8231.
+
config MFD_RT4831
tristate "Richtek RT4831 four channel WLED and Display Bias Voltage"
depends on I2C
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -240,6 +240,7 @@ obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-
obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o
obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
obj-$(CONFIG_MFD_DLN2) += dln2.o
+obj-$(CONFIG_MFD_RTL8231) += rtl8231.o
obj-$(CONFIG_MFD_RT4831) += rt4831.o
obj-$(CONFIG_MFD_RT5033) += rt5033.o
obj-$(CONFIG_MFD_RT5120) += rt5120.o
--- /dev/null
+++ b/drivers/mfd/rtl8231.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/core.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/rtl8231.h>
+
+static bool rtl8231_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ /*
+ * Registers with self-clearing bits, strapping pin values.
+ * Don't mark the data registers as volatile, since we need
+ * caching for the output values.
+ */
+ case RTL8231_REG_FUNC0:
+ case RTL8231_REG_FUNC1:
+ case RTL8231_REG_PIN_HI_CFG:
+ case RTL8231_REG_LED_END:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_field RTL8231_FIELD_LED_START = REG_FIELD(RTL8231_REG_FUNC0, 1, 1);
+
+static const struct mfd_cell rtl8231_cells[] = {
+ {
+ .name = "rtl8231-pinctrl",
+ },
+ {
+ .name = "rtl8231-leds",
+ .of_compatible = "realtek,rtl8231-leds",
+ },
+};
+
+static int rtl8231_soft_reset(struct regmap *map)
+{
+ const unsigned int all_pins_mask = GENMASK(RTL8231_BITS_VAL - 1, 0);
+ unsigned int val;
+ int err;
+
+ /* SOFT_RESET bit self-clears when done */
+ regmap_write_bits(map, RTL8231_REG_PIN_HI_CFG,
+ RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET);
+ err = regmap_read_poll_timeout(map, RTL8231_REG_PIN_HI_CFG, val,
+ !(val & RTL8231_PIN_HI_CFG_SOFT_RESET), 50, 1000);
+ if (err)
+ return err;
+
+ regcache_mark_dirty(map);
+
+ /*
+ * Chip reset results in a pin configuration that is a mix of LED and GPIO outputs.
+ * Select GPI functionality for all pins before enabling pin outputs.
+ */
+ regmap_write(map, RTL8231_REG_PIN_MODE0, all_pins_mask);
+ regmap_write(map, RTL8231_REG_GPIO_DIR0, all_pins_mask);
+ regmap_write(map, RTL8231_REG_PIN_MODE1, all_pins_mask);
+ regmap_write(map, RTL8231_REG_GPIO_DIR1, all_pins_mask);
+ regmap_write(map, RTL8231_REG_PIN_HI_CFG,
+ RTL8231_PIN_HI_CFG_MODE_MASK | RTL8231_PIN_HI_CFG_DIR_MASK);
+
+ return 0;
+}
+
+static int rtl8231_init(struct device *dev, struct regmap *map)
+{
+ struct regmap_field *led_start;
+ unsigned int started;
+ unsigned int val;
+ int err;
+
+ err = regmap_read(map, RTL8231_REG_FUNC1, &val);
+ if (err) {
+ dev_err(dev, "failed to read READY_CODE\n");
+ return err;
+ }
+
+ val = FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK, val);
+ if (val != RTL8231_FUNC1_READY_CODE_VALUE) {
+ dev_err(dev, "RTL8231 not present or ready 0x%x != 0x%x\n",
+ val, RTL8231_FUNC1_READY_CODE_VALUE);
+ return -ENODEV;
+ }
+
+ led_start = dev_get_drvdata(dev);
+ err = regmap_field_read(led_start, &started);
+ if (err)
+ return err;
+
+ if (!started) {
+ err = rtl8231_soft_reset(map);
+ if (err)
+ return err;
+ /* LED_START enables power to output pins, and starts the LED engine */
+ err = regmap_field_force_write(led_start, 1);
+ }
+
+ return err;
+}
+
+static const struct regmap_config rtl8231_mdio_regmap_config = {
+ .val_bits = RTL8231_BITS_VAL,
+ .reg_bits = RTL8231_BITS_REG,
+ .volatile_reg = rtl8231_volatile_reg,
+ .max_register = RTL8231_REG_COUNT - 1,
+ .use_single_read = true,
+ .use_single_write = true,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ /* Cannot use REGCACHE_FLAT because it's not smart enough about cache invalidation */
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int rtl8231_mdio_probe(struct mdio_device *mdiodev)
+{
+ struct device *dev = &mdiodev->dev;
+ struct regmap_field *led_start;
+ struct regmap *map;
+ int err;
+
+ map = devm_regmap_init_mdio(mdiodev, &rtl8231_mdio_regmap_config);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap\n");
+ return PTR_ERR(map);
+ }
+
+ led_start = devm_regmap_field_alloc(dev, map, RTL8231_FIELD_LED_START);
+ if (IS_ERR(led_start))
+ return PTR_ERR(led_start);
+
+ dev_set_drvdata(dev, led_start);
+
+ mdiodev->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(mdiodev->reset_gpio))
+ return PTR_ERR(mdiodev->reset_gpio);
+
+ device_property_read_u32(dev, "reset-assert-delay", &mdiodev->reset_assert_delay);
+ device_property_read_u32(dev, "reset-deassert-delay", &mdiodev->reset_deassert_delay);
+
+ err = rtl8231_init(dev, map);
+ if (err)
+ return err;
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rtl8231_cells,
+ ARRAY_SIZE(rtl8231_cells), NULL, 0, NULL);
+}
+
+__maybe_unused static int rtl8231_suspend(struct device *dev)
+{
+ struct regmap_field *led_start = dev_get_drvdata(dev);
+
+ return regmap_field_force_write(led_start, 0);
+}
+
+__maybe_unused static int rtl8231_resume(struct device *dev)
+{
+ struct regmap_field *led_start = dev_get_drvdata(dev);
+
+ return regmap_field_force_write(led_start, 1);
+}
+
+static SIMPLE_DEV_PM_OPS(rtl8231_pm_ops, rtl8231_suspend, rtl8231_resume);
+
+static const struct of_device_id rtl8231_of_match[] = {
+ { .compatible = "realtek,rtl8231" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtl8231_of_match);
+
+static struct mdio_driver rtl8231_mdio_driver = {
+ .mdiodrv.driver = {
+ .name = "rtl8231-expander",
+ .of_match_table = rtl8231_of_match,
+ .pm = pm_ptr(&rtl8231_pm_ops),
+ },
+ .probe = rtl8231_mdio_probe,
+};
+mdio_module_driver(rtl8231_mdio_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/include/linux/mfd/rtl8231.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Register definitions the RTL8231 GPIO and LED expander chip
+ */
+
+#ifndef __LINUX_MFD_RTL8231_H
+#define __LINUX_MFD_RTL8231_H
+
+#include <linux/bits.h>
+
+/*
+ * Registers addresses are 5 bit, values are 16 bit
+ * Also define a duplicated range of virtual addresses, to enable
+ * different read/write behaviour on the GPIO data registers
+ */
+#define RTL8231_BITS_VAL 16
+#define RTL8231_BITS_REG 5
+
+/* Chip control */
+#define RTL8231_REG_FUNC0 0x00
+#define RTL8231_FUNC0_SCAN_MODE BIT(0)
+#define RTL8231_FUNC0_SCAN_SINGLE 0
+#define RTL8231_FUNC0_SCAN_BICOLOR BIT(0)
+
+#define RTL8231_REG_FUNC1 0x01
+#define RTL8231_FUNC1_READY_CODE_VALUE 0x37
+#define RTL8231_FUNC1_READY_CODE_MASK GENMASK(9, 4)
+#define RTL8231_FUNC1_DEBOUNCE_MASK GENMASK(15, 10)
+
+/* Pin control */
+#define RTL8231_REG_PIN_MODE0 0x02
+#define RTL8231_REG_PIN_MODE1 0x03
+
+#define RTL8231_PIN_MODE_LED 0
+#define RTL8231_PIN_MODE_GPIO 1
+
+/* Pin high config: pin and GPIO control for pins 32-26 */
+#define RTL8231_REG_PIN_HI_CFG 0x04
+#define RTL8231_PIN_HI_CFG_MODE_MASK GENMASK(4, 0)
+#define RTL8231_PIN_HI_CFG_DIR_MASK GENMASK(9, 5)
+#define RTL8231_PIN_HI_CFG_INV_MASK GENMASK(14, 10)
+#define RTL8231_PIN_HI_CFG_SOFT_RESET BIT(15)
+
+/* GPIO control registers */
+#define RTL8231_REG_GPIO_DIR0 0x05
+#define RTL8231_REG_GPIO_DIR1 0x06
+#define RTL8231_REG_GPIO_INVERT0 0x07
+#define RTL8231_REG_GPIO_INVERT1 0x08
+
+#define RTL8231_GPIO_DIR_IN 1
+#define RTL8231_GPIO_DIR_OUT 0
+
+/*
+ * GPIO data registers
+ * Only the output data can be written to these registers, and only the input
+ * data can be read.
+ */
+#define RTL8231_REG_GPIO_DATA0 0x1c
+#define RTL8231_REG_GPIO_DATA1 0x1d
+#define RTL8231_REG_GPIO_DATA2 0x1e
+#define RTL8231_PIN_HI_DATA_MASK GENMASK(4, 0)
+
+/* LED control base registers */
+#define RTL8231_REG_LED0_BASE 0x09
+#define RTL8231_REG_LED1_BASE 0x10
+#define RTL8231_REG_LED2_BASE 0x17
+#define RTL8231_REG_LED_END 0x1b
+
+#define RTL8231_REG_COUNT 0x1f
+
+#endif /* __LINUX_MFD_RTL8231_H */

View file

@ -1,581 +0,0 @@
From 098324288a63a6dcc44e96cc381aef3d5c48d89e Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Mon, 10 May 2021 22:15:31 +0200
Subject: [PATCH] pinctrl: Add RTL8231 pin control and GPIO support
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Debouncing is only available for the six highest GPIOs, and must be
emulated when other pins are used for (button) inputs. Although
described in the bindings, drive strength selection is currently not
implemented.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 521 ++++++++++++++++++++++++++++++
3 files changed, 533 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -417,6 +417,17 @@ config PINCTRL_ROCKCHIP
help
This support pinctrl and GPIO driver for Rockchip SoCs.
+config PINCTRL_RTL8231
+ tristate "Realtek RTL8231 GPIO expander's pin controller"
+ depends on MFD_RTL8231
+ default MFD_RTL8231
+ select GPIO_REGMAP
+ select GENERIC_PINCONF
+ select GENERIC_PINMUX_FUNCTIONS
+ help
+ Support for RTL8231 expander's GPIOs and pin controller.
+ When built as a module, the module will be called pinctrl-rtl8231.
+
config PINCTRL_SINGLE
tristate "One-register-per-pin type device tree based pinctrl driver"
depends on OF
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
+obj-$(CONFIG_PINCTRL_RTL8231) += pinctrl-rtl8231.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rtl8231.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/module.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "core.h"
+#include "pinmux.h"
+#include <linux/mfd/rtl8231.h>
+
+#define RTL8231_NUM_GPIOS 37
+#define RTL8231_DEBOUNCE_USEC 100000
+#define RTL8231_DEBOUNCE_MIN_OFFSET 31
+
+struct rtl8231_pin_ctrl {
+ struct pinctrl_desc pctl_desc;
+ struct regmap *map;
+};
+
+/*
+ * Pin controller functionality
+ */
+static const char * const rtl8231_pin_function_names[] = {
+ "gpio",
+ "led",
+ "pwm",
+};
+
+enum rtl8231_pin_function {
+ RTL8231_PIN_FUNCTION_GPIO = BIT(0),
+ RTL8231_PIN_FUNCTION_LED = BIT(1),
+ RTL8231_PIN_FUNCTION_PWM = BIT(2),
+};
+
+struct rtl8231_pin_desc {
+ const enum rtl8231_pin_function functions;
+ const u8 reg;
+ const u8 offset;
+ const u8 gpio_function_value;
+};
+
+#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \
+ [_num] = { \
+ .functions = RTL8231_PIN_FUNCTION_GPIO | _func, \
+ .reg = _reg, \
+ .offset = _fld, \
+ .gpio_function_value = _val, \
+ }
+#define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO)
+#define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO)
+#define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \
+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0)
+
+/*
+ * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit
+ * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic.
+ */
+static struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = {
+ RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0),
+ RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1),
+ RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2),
+ RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3),
+ RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4),
+ RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5),
+ RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6),
+ RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7),
+ RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8),
+ RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9),
+ RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10),
+ RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11),
+ RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12),
+ RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13),
+ RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14),
+ RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15),
+ RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0),
+ RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1),
+ RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2),
+ RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3),
+ RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4),
+ RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5),
+ RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6),
+ RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7),
+ RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8),
+ RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9),
+ RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10),
+ RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11),
+ RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12),
+ RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13),
+ RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14),
+ RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15),
+ RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0),
+ RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1),
+ RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2),
+ RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3),
+ RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4),
+};
+
+#define RTL8231_PIN(_num) \
+ { \
+ .number = _num, \
+ .name = "gpio" #_num, \
+ .drv_data = &rtl8231_pin_data[_num] \
+ }
+
+static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = {
+ RTL8231_PIN(0),
+ RTL8231_PIN(1),
+ RTL8231_PIN(2),
+ RTL8231_PIN(3),
+ RTL8231_PIN(4),
+ RTL8231_PIN(5),
+ RTL8231_PIN(6),
+ RTL8231_PIN(7),
+ RTL8231_PIN(8),
+ RTL8231_PIN(9),
+ RTL8231_PIN(10),
+ RTL8231_PIN(11),
+ RTL8231_PIN(12),
+ RTL8231_PIN(13),
+ RTL8231_PIN(14),
+ RTL8231_PIN(15),
+ RTL8231_PIN(16),
+ RTL8231_PIN(17),
+ RTL8231_PIN(18),
+ RTL8231_PIN(19),
+ RTL8231_PIN(20),
+ RTL8231_PIN(21),
+ RTL8231_PIN(22),
+ RTL8231_PIN(23),
+ RTL8231_PIN(24),
+ RTL8231_PIN(25),
+ RTL8231_PIN(26),
+ RTL8231_PIN(27),
+ RTL8231_PIN(28),
+ RTL8231_PIN(29),
+ RTL8231_PIN(30),
+ RTL8231_PIN(31),
+ RTL8231_PIN(32),
+ RTL8231_PIN(33),
+ RTL8231_PIN(34),
+ RTL8231_PIN(35),
+ RTL8231_PIN(36),
+};
+
+static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(rtl8231_pins);
+}
+
+static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector)
+{
+ return rtl8231_pins[selector].name;
+}
+
+static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
+ const unsigned int **pins, unsigned int *num_pins)
+{
+ if (selector >= ARRAY_SIZE(rtl8231_pins))
+ return -EINVAL;
+
+ *pins = &rtl8231_pins[selector].number;
+ *num_pins = 1;
+
+ return 0;
+}
+
+static const struct pinctrl_ops rtl8231_pinctrl_ops = {
+ .get_groups_count = rtl8231_get_groups_count,
+ .get_group_name = rtl8231_get_group_name,
+ .get_group_pins = rtl8231_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector,
+ unsigned int group_selector)
+{
+ const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector);
+ const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data;
+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int func_flag = (uintptr_t) func->data;
+ unsigned int function_mask;
+ unsigned int gpio_function;
+
+ if (!(desc->functions & func_flag))
+ return -EINVAL;
+
+ function_mask = BIT(desc->offset);
+ gpio_function = desc->gpio_function_value << desc->offset;
+
+ if (func_flag == RTL8231_PIN_FUNCTION_GPIO)
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
+ else
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function);
+}
+
+static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range, unsigned int offset)
+{
+ const struct rtl8231_pin_desc *desc = rtl8231_pins[offset].drv_data;
+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int function_mask;
+ unsigned int gpio_function;
+
+ function_mask = BIT(desc->offset);
+ gpio_function = desc->gpio_function_value << desc->offset;
+
+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function);
+}
+
+static const struct pinmux_ops rtl8231_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = rtl8231_set_mux,
+ .gpio_request_enable = rtl8231_gpio_request_enable,
+ .strict = true,
+};
+
+static int rtl8231_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset,
+ unsigned long *config)
+{
+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int param = pinconf_to_config_param(*config);
+ unsigned int arg;
+ int err;
+ int v;
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET)
+ return -EINVAL;
+
+ err = regmap_read(ctrl->map, RTL8231_REG_FUNC1, &v);
+ if (err)
+ return err;
+
+ v = FIELD_GET(RTL8231_FUNC1_DEBOUNCE_MASK, v);
+ if (v & BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET))
+ arg = RTL8231_DEBOUNCE_USEC;
+ else
+ arg = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return 0;
+}
+
+static int rtl8231_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset,
+ unsigned long *configs, unsigned int num_configs)
+{
+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int param, arg;
+ unsigned int pin_mask;
+ int err;
+ int i;
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET)
+ return -EINVAL;
+
+ pin_mask = FIELD_PREP(RTL8231_FUNC1_DEBOUNCE_MASK,
+ BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET));
+
+ switch (arg) {
+ case 0:
+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1,
+ pin_mask, 0);
+ break;
+ case RTL8231_DEBOUNCE_USEC:
+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1,
+ pin_mask, pin_mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ return err;
+}
+
+static const struct pinconf_ops rtl8231_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = rtl8231_pin_config_get,
+ .pin_config_set = rtl8231_pin_config_set,
+};
+
+static int rtl8231_pinctrl_init_functions(struct pinctrl_dev *pctl, struct rtl8231_pin_ctrl *ctrl)
+{
+ const char *function_name;
+ const char **groups;
+ unsigned int f_idx;
+ unsigned int pin;
+ int num_groups;
+ int err;
+
+ for (f_idx = 0; f_idx < ARRAY_SIZE(rtl8231_pin_function_names); f_idx++) {
+ function_name = rtl8231_pin_function_names[f_idx];
+
+ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++)
+ if (rtl8231_pin_data[pin].functions & BIT(f_idx))
+ num_groups++;
+
+ groups = devm_kcalloc(pctl->dev, num_groups, sizeof(*groups), GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+
+ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++)
+ if (rtl8231_pin_data[pin].functions & BIT(f_idx))
+ groups[num_groups++] = rtl8231_pins[pin].name;
+
+ err = pinmux_generic_add_function(pctl, function_name, groups, num_groups,
+ (void *) BIT(f_idx));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+struct pin_field_info {
+ const struct reg_field gpio_data;
+ const struct reg_field gpio_dir;
+ const struct reg_field mode;
+};
+
+static const struct pin_field_info pin_fields[] = {
+ {
+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA0, 0, 15),
+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR0, 0, 15),
+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE0, 0, 15),
+ },
+ {
+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA1, 0, 15),
+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR1, 0, 15),
+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE1, 0, 15),
+ },
+ {
+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA2, 0, 4),
+ .gpio_dir = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 5, 9),
+ .mode = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 0, 4),
+ },
+};
+
+static int rtl8231_configure_safe(struct device *dev, struct regmap *map)
+{
+ struct regmap_field *field_data;
+ struct regmap_field *field_mode;
+ struct regmap_field *field_dir;
+ unsigned int is_output;
+ unsigned int is_gpio;
+ unsigned int data;
+ unsigned int mode;
+ unsigned int dir;
+ int err;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(pin_fields); i++) {
+ field_data = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_data);
+ if (IS_ERR(field_data))
+ return PTR_ERR(field_data);
+
+ field_dir = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_dir);
+ if (IS_ERR(field_dir))
+ return PTR_ERR(field_dir);
+
+ field_mode = devm_regmap_field_alloc(dev, map, pin_fields[i].mode);
+ if (IS_ERR(field_mode))
+ return PTR_ERR(field_mode);
+
+ /* The register cache is invalid at start-up, so this should read from HW */
+ err = regmap_field_read(field_data, &data);
+ if (err)
+ return err;
+
+ err = regmap_field_read(field_dir, &dir);
+ if (err)
+ return err;
+
+ err = regmap_field_read(field_mode, &mode);
+ if (err)
+ return err;
+
+ /* Write back only the GPIO-out values to fix the cache */
+ data &= ~dir;
+ regmap_field_write(field_data, data);
+
+ /*
+ * Set every pin that is configured as gpio-output but muxed for the alternative
+ * (LED) function to gpio-in. That way the pin will be high impedance when it is
+ * muxed to GPIO, preventing unwanted glitches.
+ * The pin muxes are left as-is, so there are no signal changes.
+ */
+ is_gpio = mode;
+ is_output = ~dir;
+ regmap_field_write(field_dir, dir | (~is_gpio & is_output));
+
+ devm_regmap_field_free(dev, field_data);
+ devm_regmap_field_free(dev, field_dir);
+ devm_regmap_field_free(dev, field_mode);
+ }
+
+ return 0;
+}
+
+static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctrl *ctrl)
+{
+ struct pinctrl_dev *pctldev;
+ int err;
+
+ ctrl->pctl_desc.name = "rtl8231-pinctrl";
+ ctrl->pctl_desc.owner = THIS_MODULE;
+ ctrl->pctl_desc.confops = &rtl8231_pinconf_ops;
+ ctrl->pctl_desc.pctlops = &rtl8231_pinctrl_ops;
+ ctrl->pctl_desc.pmxops = &rtl8231_pinmux_ops;
+ ctrl->pctl_desc.npins = ARRAY_SIZE(rtl8231_pins);
+ ctrl->pctl_desc.pins = rtl8231_pins;
+
+ err = devm_pinctrl_register_and_init(dev->parent, &ctrl->pctl_desc, ctrl, &pctldev);
+ if (err) {
+ dev_err(dev, "failed to register pin controller\n");
+ return err;
+ }
+
+ err = rtl8231_pinctrl_init_functions(pctldev, ctrl);
+ if (err)
+ return err;
+
+ err = pinctrl_enable(pctldev);
+ if (err)
+ dev_err(dev, "failed to enable pin controller\n");
+
+ return err;
+}
+
+/*
+ * GPIO controller functionality
+ */
+static int rtl8231_gpio_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg, unsigned int *mask)
+{
+ unsigned int pin_mask = BIT(offset % RTL8231_BITS_VAL);
+
+ if (base == RTL8231_REG_GPIO_DATA0 || offset < 32) {
+ *reg = base + offset / RTL8231_BITS_VAL;
+ *mask = pin_mask;
+ } else if (base == RTL8231_REG_GPIO_DIR0) {
+ *reg = RTL8231_REG_PIN_HI_CFG;
+ *mask = FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK, pin_mask);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl8231_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rtl8231_pin_ctrl *ctrl;
+ struct gpio_regmap_config gpio_cfg = {};
+ int err;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->map = dev_get_regmap(dev->parent, NULL);
+ if (!ctrl->map)
+ return -ENODEV;
+
+ err = rtl8231_configure_safe(dev, ctrl->map);
+ if (err)
+ return err;
+
+ err = rtl8231_pinctrl_init(dev, ctrl);
+ if (err)
+ return err;
+
+ gpio_cfg.regmap = ctrl->map;
+ gpio_cfg.parent = dev->parent;
+ gpio_cfg.ngpio = RTL8231_NUM_GPIOS;
+ gpio_cfg.ngpio_per_reg = RTL8231_BITS_VAL;
+
+ gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);
+ gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);
+ gpio_cfg.reg_dir_in_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0);
+
+ gpio_cfg.reg_mask_xlate = rtl8231_gpio_reg_mask_xlate;
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_cfg));
+}
+
+static struct platform_driver rtl8231_pinctrl_driver = {
+ .driver = {
+ .name = "rtl8231-pinctrl",
+ },
+ .probe = rtl8231_pinctrl_probe,
+};
+module_platform_driver(rtl8231_pinctrl_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support");
+MODULE_LICENSE("GPL");

View file

@ -1,338 +0,0 @@
From 6b797a97c007e46d6081fc6f4b41ce8407078605 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Mon, 10 May 2021 22:16:11 +0200
Subject: [PATCH] leds: Add support for RTL8231 LED scan matrix
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as a child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/leds/Kconfig | 10 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++
3 files changed, 302 insertions(+)
create mode 100644 drivers/leds/leds-rtl8231.c
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -586,6 +586,16 @@ config LEDS_REGULATOR
help
This option enables support for regulator driven LEDs.
+config LEDS_RTL8231
+ tristate "RTL8231 LED matrix support"
+ depends on LEDS_CLASS
+ depends on MFD_RTL8231
+ default MFD_RTL8231
+ help
+ This option enables support for using the LED scanning matrix output
+ of the RTL8231 GPIO and LED expander chip.
+ When built as a module, this module will be named leds-rtl8231.
+
config LEDS_BD2606MVV
tristate "LED driver for BD2606MVV"
depends on LEDS_CLASS
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_PM8058) += leds-pm805
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
+obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
--- /dev/null
+++ b/drivers/leds/leds-rtl8231.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/rtl8231.h>
+
+/**
+ * struct led_toggle_rate - description of an LED blinking mode
+ * @interval_ms: LED toggle rate in milliseconds
+ * @mode: Register field value used to activate this mode
+ *
+ * For LED hardware accelerated blinking, with equal on and off delay.
+ * Both delays are given by @interval, so the interval at which the LED blinks
+ * (i.e. turn on and off once) is double this value.
+ */
+struct led_toggle_rate {
+ u16 interval_ms;
+ u8 mode;
+};
+
+/**
+ * struct led_modes - description of all LED modes
+ * @toggle_rates: Array of led_toggle_rate values, sorted by ascending interval
+ * @num_toggle_rates: Number of elements in @led_toggle_rate
+ * @off: Register field value to turn LED off
+ * @on: Register field value to turn LED on
+ */
+struct led_modes {
+ const struct led_toggle_rate *toggle_rates;
+ unsigned int num_toggle_rates;
+ u8 off;
+ u8 on;
+};
+
+struct rtl8231_led {
+ struct led_classdev led;
+ const struct led_modes *modes;
+ struct regmap_field *reg_field;
+};
+#define to_rtl8231_led(_cdev) container_of(_cdev, struct rtl8231_led, led)
+
+#define RTL8231_NUM_LEDS 3
+#define RTL8231_LED_PER_REG 5
+#define RTL8231_BITS_PER_LED 3
+
+static const unsigned int rtl8231_led_port_counts_single[RTL8231_NUM_LEDS] = {32, 32, 24};
+static const unsigned int rtl8231_led_port_counts_bicolor[RTL8231_NUM_LEDS] = {24, 24, 24};
+
+static const unsigned int rtl8231_led_base[RTL8231_NUM_LEDS] = {
+ RTL8231_REG_LED0_BASE,
+ RTL8231_REG_LED1_BASE,
+ RTL8231_REG_LED2_BASE,
+};
+
+#define RTL8231_DEFAULT_TOGGLE_INTERVAL_MS 500
+
+static const struct led_toggle_rate rtl8231_toggle_rates[] = {
+ { 40, 1},
+ { 80, 2},
+ { 160, 3},
+ { 320, 4},
+ { 640, 5},
+ {1280, 6},
+};
+
+static const struct led_modes rtl8231_led_modes = {
+ .off = 0,
+ .on = 7,
+ .num_toggle_rates = ARRAY_SIZE(rtl8231_toggle_rates),
+ .toggle_rates = rtl8231_toggle_rates,
+};
+
+static void rtl8231_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+
+ if (brightness)
+ regmap_field_write(pled->reg_field, pled->modes->on);
+ else
+ regmap_field_write(pled->reg_field, pled->modes->off);
+}
+
+static enum led_brightness rtl8231_led_brightness_get(struct led_classdev *led_cdev)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+ u32 current_mode = pled->modes->off;
+
+ regmap_field_read(pled->reg_field, &current_mode);
+
+ if (current_mode == pled->modes->off)
+ return LED_OFF;
+ else
+ return LED_ON;
+}
+
+static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled)
+{
+ unsigned int mode;
+ unsigned int i;
+
+ if (regmap_field_read(pled->reg_field, &mode))
+ return 0;
+
+ for (i = 0; i < pled->modes->num_toggle_rates; i++)
+ if (mode == pled->modes->toggle_rates[i].mode)
+ return pled->modes->toggle_rates[i].interval_ms;
+
+ return 0;
+}
+
+static int rtl8231_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev);
+ const struct led_toggle_rate *rates = pled->modes->toggle_rates;
+ unsigned int num_rates = pled->modes->num_toggle_rates;
+ unsigned int interval_ms;
+ unsigned int i;
+ int err;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ interval_ms = RTL8231_DEFAULT_TOGGLE_INTERVAL_MS;
+ } else {
+ /*
+ * If the current mode is blinking, choose the delay that (likely) changed.
+ * Otherwise, choose the interval that would have the same total delay.
+ */
+ interval_ms = rtl8231_led_current_interval(pled);
+ if (interval_ms > 0 && interval_ms == *delay_off)
+ interval_ms = *delay_on;
+ else if (interval_ms > 0 && interval_ms == *delay_on)
+ interval_ms = *delay_off;
+ else
+ interval_ms = (*delay_on + *delay_off) / 2;
+ }
+
+ /* Find clamped toggle interval */
+ for (i = 0; i < (num_rates - 1); i++)
+ if (interval_ms > rates[i].interval_ms)
+ break;
+
+ interval_ms = rates[i].interval_ms;
+
+ err = regmap_field_write(pled->reg_field, rates[i].mode);
+ if (err)
+ return err;
+
+ *delay_on = interval_ms;
+ *delay_off = interval_ms;
+
+ return 0;
+}
+
+static int rtl8231_led_read_address(struct fwnode_handle *fwnode, unsigned int *addr_port,
+ unsigned int *addr_led)
+{
+ u32 addr[2];
+ int err;
+
+ err = fwnode_property_count_u32(fwnode, "reg");
+ if (err < 0)
+ return err;
+ if (err != ARRAY_SIZE(addr))
+ return -EINVAL;
+
+ err = fwnode_property_read_u32_array(fwnode, "reg", addr, ARRAY_SIZE(addr));
+ if (err)
+ return err;
+
+ *addr_port = addr[0];
+ *addr_led = addr[1];
+
+ return 0;
+}
+
+static const struct regmap_field *rtl8231_led_get_field(struct device *dev, struct regmap *map,
+ unsigned int port_index, unsigned int led_index)
+{
+ unsigned int offset = port_index / RTL8231_LED_PER_REG;
+ unsigned int shift = (port_index % RTL8231_LED_PER_REG) * RTL8231_BITS_PER_LED;
+ const struct reg_field field = REG_FIELD(rtl8231_led_base[led_index] + offset, shift,
+ shift + RTL8231_BITS_PER_LED - 1);
+
+ return devm_regmap_field_alloc(dev, map, field);
+}
+
+static int rtl8231_led_probe_single(struct device *dev, struct regmap *map,
+ const unsigned int *port_counts, struct fwnode_handle *fwnode)
+{
+ struct led_init_data init_data = {};
+ struct rtl8231_led *pled;
+ unsigned int port_index;
+ unsigned int led_index;
+ int err;
+
+ pled = devm_kzalloc(dev, sizeof(*pled), GFP_KERNEL);
+ if (!pled)
+ return -ENOMEM;
+
+ err = rtl8231_led_read_address(fwnode, &port_index, &led_index);
+ if (err) {
+ dev_err(dev, "LED address invalid");
+ return err;
+ }
+
+ if (led_index >= RTL8231_NUM_LEDS || port_index >= port_counts[led_index]) {
+ dev_err(dev, "LED address (%d.%d) invalid", port_index, led_index);
+ return -EINVAL;
+ }
+
+ pled->reg_field = rtl8231_led_get_field(dev, map, port_index, led_index);
+ if (IS_ERR(pled->reg_field))
+ return PTR_ERR(pled->reg_field);
+
+ pled->modes = &rtl8231_led_modes;
+
+ pled->led.max_brightness = 1;
+ pled->led.brightness_get = rtl8231_led_brightness_get;
+ pled->led.brightness_set = rtl8231_led_brightness_set;
+ pled->led.blink_set = rtl8231_led_blink_set;
+
+ init_data.fwnode = fwnode;
+
+ return devm_led_classdev_register_ext(dev, &pled->led, &init_data);
+}
+
+static int rtl8231_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const unsigned int *port_counts;
+ struct fwnode_handle *child;
+ struct regmap *map;
+ int err;
+
+ map = dev_get_regmap(dev->parent, NULL);
+ if (!map)
+ return -ENODEV;
+
+ if (device_property_match_string(dev, "realtek,led-scan-mode", "single-color") >= 0) {
+ port_counts = rtl8231_led_port_counts_single;
+ regmap_update_bits(map, RTL8231_REG_FUNC0,
+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_SINGLE);
+ } else if (device_property_match_string(dev, "realtek,led-scan-mode", "bi-color") >= 0) {
+ port_counts = rtl8231_led_port_counts_bicolor;
+ regmap_update_bits(map, RTL8231_REG_FUNC0,
+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_BICOLOR);
+ } else {
+ dev_err(dev, "scan mode missing or invalid");
+ return -EINVAL;
+ }
+
+ fwnode_for_each_available_child_node(dev->fwnode, child) {
+ err = rtl8231_led_probe_single(dev, map, port_counts, child);
+ if (err)
+ dev_warn(dev, "failed to register LED %pfwP", child);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_rtl8231_led_match[] = {
+ { .compatible = "realtek,rtl8231-leds" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_rtl8231_led_match);
+
+static struct platform_driver rtl8231_led_driver = {
+ .driver = {
+ .name = "rtl8231-leds",
+ .of_match_table = of_rtl8231_led_match,
+ },
+ .probe = rtl8231_led_probe,
+};
+module_platform_driver(rtl8231_led_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek RTL8231 LED support");
+MODULE_LICENSE("GPL");

View file

@ -1,257 +0,0 @@
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=4096
# CONFIG_BMIPS_CPUFREQ is not set
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
CONFIG_COMMON_CLK_REALTEK=y
CONFIG_COMMON_CLK_RTL83XX=y
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
CONFIG_CPUFREQ_DT=y
CONFIG_CPUFREQ_DT_PLATDEV=y
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_FREQ=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
CONFIG_CPU_FREQ_GOV_COMMON=y
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_DIEI=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_RIXI=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MIPSR2=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_CPUFREQ=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CPU_SUPPORTS_MSA=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DTC=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_8250=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_EXTRA_FIRMWARE="rtl838x_phy/rtl838x_8214fc.fw rtl838x_phy/rtl838x_8218b.fw rtl838x_phy/rtl838x_8380.fw"
CONFIG_EXTRA_FIRMWARE_DIR="firmware"
CONFIG_FIXED_PHY=y
CONFIG_FORCE_NR_CPUS=y
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=0
CONFIG_FWNODE_MDIO=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_LIB_ASHLDI3=y
CONFIG_GENERIC_LIB_ASHRDI3=y
CONFIG_GENERIC_LIB_CMPDI2=y
CONFIG_GENERIC_LIB_LSHRDI3=y
CONFIG_GENERIC_LIB_UCMPDI2=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_GPIO_REALTEK_OTTO=y
CONFIG_GPIO_REGMAP=y
CONFIG_GPIO_WATCHDOG=y
# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set
CONFIG_GRO_CELLS=y
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HWMON=y
CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_MUX=y
# CONFIG_I2C_MUX_RTL9300 is not set
# CONFIG_I2C_RTL9300 is not set
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
# CONFIG_LEDS_RTL8231 is not set
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_MACH_REALTEK_RTL=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_GPIO=y
CONFIG_MDIO_I2C=y
CONFIG_MDIO_REALTEK_OTTO_AUX=y
CONFIG_MDIO_SMBUS=y
CONFIG_MFD_CORE=y
CONFIG_MFD_RTL8231=y
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
CONFIG_MIPS_ASID_BITS=8
CONFIG_MIPS_ASID_SHIFT=0
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
CONFIG_MIPS_CMDLINE_FROM_DTB=y
CONFIG_MIPS_EXTERNAL_TIMER=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
# CONFIG_MIPS_NO_APPENDED_DTB is not set
CONFIG_MIPS_RAW_APPENDED_DTB=y
CONFIG_MIPS_SPRAM=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
CONFIG_MTD_SPLIT_EVA_FW=y
CONFIG_MTD_SPLIT_FIRMWARE=y
CONFIG_MTD_SPLIT_H3C_VFS=y
CONFIG_MTD_SPLIT_TPLINK_FW=y
CONFIG_MTD_SPLIT_UIMAGE_FW=y
CONFIG_MTD_VIRT_CONCAT=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_RTL83XX=y
CONFIG_NET_DSA_TAG_TRAILER=y
CONFIG_NET_EGRESS=y
CONFIG_NET_INGRESS=y
CONFIG_NET_RTL838X=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_XGRESS=y
CONFIG_NO_EXCEPT_FILL=y
CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLIB_LEDS=y
CONFIG_PHYLINK=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_RTL8231=y
CONFIG_PM_OPP=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_GPIO_RESTART=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RATIONAL=y
CONFIG_REALTEK_OTTO_TIMER=y
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_REALTEK_PHY=y
CONFIG_REALTEK_PHY_HWMON=y
CONFIG_REALTEK_SOC_PHY=y
CONFIG_REALTEK_THERMAL=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_MDIO=y
CONFIG_REGMAP_MMIO=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RTL838X=y
# CONFIG_RTL839X is not set
# CONFIG_RTL930X is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SFP=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
CONFIG_SRAM=y
CONFIG_SWPHY=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
CONFIG_SYS_SUPPORTS_HIGHMEM=y
CONFIG_SYS_SUPPORTS_MIPS16=y
CONFIG_TARGET_ISA_REV=2
CONFIG_THERMAL=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
CONFIG_THERMAL_GOV_STEP_WISE=y
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_TINY_SRCU=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WATCHDOG_CORE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y

View file

@ -1,276 +0,0 @@
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=4096
# CONFIG_BMIPS_CPUFREQ is not set
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
CONFIG_COMMON_CLK_REALTEK=y
CONFIG_COMMON_CLK_RTL83XX=y
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
CONFIG_CONTEXT_TRACKING=y
CONFIG_CONTEXT_TRACKING_IDLE=y
CONFIG_CPUFREQ_DT=y
CONFIG_CPUFREQ_DT_PLATDEV=y
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_FREQ=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
CONFIG_CPU_FREQ_GOV_COMMON=y
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_DIEI=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_RIXI=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MIPSR2=y
CONFIG_CPU_MIPSR2_IRQ_EI=y
CONFIG_CPU_MIPSR2_IRQ_VI=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_CPUFREQ=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CPU_SUPPORTS_MSA=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DTC=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_8250=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_FIXED_PHY=y
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=0
CONFIG_FWNODE_MDIO=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_LIB_ASHLDI3=y
CONFIG_GENERIC_LIB_ASHRDI3=y
CONFIG_GENERIC_LIB_CMPDI2=y
CONFIG_GENERIC_LIB_LSHRDI3=y
CONFIG_GENERIC_LIB_UCMPDI2=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_GPIO_REALTEK_OTTO=y
CONFIG_GPIO_REGMAP=y
CONFIG_GPIO_WATCHDOG=y
# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set
CONFIG_GRO_CELLS=y
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HWMON=y
CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_GPIO=y
# CONFIG_I2C_MUX_RTL9300 is not set
# CONFIG_I2C_RTL9300 is not set
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
# CONFIG_LEDS_RTL8231 is not set
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_MACH_REALTEK_RTL=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_I2C=y
CONFIG_MDIO_REALTEK_OTTO_AUX=y
CONFIG_MDIO_SMBUS=y
CONFIG_MFD_CORE=y
CONFIG_MFD_RTL8231=y
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
CONFIG_MIPS_ASID_BITS=8
CONFIG_MIPS_ASID_SHIFT=0
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
CONFIG_MIPS_CMDLINE_FROM_DTB=y
CONFIG_MIPS_EXTERNAL_TIMER=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
CONFIG_MIPS_MT=y
# CONFIG_MIPS_MT_FPAFF is not set
CONFIG_MIPS_MT_SMP=y
# CONFIG_MIPS_NO_APPENDED_DTB is not set
CONFIG_MIPS_NR_CPU_NR_MAP=2
CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y
CONFIG_MIPS_RAW_APPENDED_DTB=y
CONFIG_MIPS_SPRAM=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
CONFIG_MTD_SPLIT_EVA_FW=y
CONFIG_MTD_SPLIT_FIRMWARE=y
CONFIG_MTD_SPLIT_H3C_VFS=y
CONFIG_MTD_SPLIT_TPLINK_FW=y
CONFIG_MTD_SPLIT_UIMAGE_FW=y
CONFIG_MTD_VIRT_CONCAT=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SRCU_NMI_SAFE=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_RTL83XX=y
CONFIG_NET_DSA_TAG_TRAILER=y
CONFIG_NET_EGRESS=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_INGRESS=y
CONFIG_NET_RTL838X=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_XGRESS=y
CONFIG_NO_EXCEPT_FILL=y
CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NR_CPUS=2
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_PADATA=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLIB_LEDS=y
CONFIG_PHYLINK=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_RTL8231=y
CONFIG_PM_OPP=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_GPIO_RESTART=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RATIONAL=y
CONFIG_REALTEK_OTTO_TIMER=y
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_REALTEK_PHY=y
CONFIG_REALTEK_PHY_HWMON=y
CONFIG_REALTEK_SOC_PHY=y
CONFIG_REALTEK_THERMAL=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_MDIO=y
CONFIG_REGMAP_MMIO=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
# CONFIG_RTL838X is not set
CONFIG_RTL839X=y
# CONFIG_RTL930X is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SFP=y
CONFIG_SMP=y
CONFIG_SMP_UP=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
CONFIG_SRAM=y
CONFIG_SWPHY=y
CONFIG_SYNC_R4K=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
CONFIG_SYS_SUPPORTS_HIGHMEM=y
CONFIG_SYS_SUPPORTS_MIPS16=y
CONFIG_SYS_SUPPORTS_MULTITHREADING=y
CONFIG_SYS_SUPPORTS_SCHED_SMT=y
CONFIG_SYS_SUPPORTS_SMP=y
CONFIG_TARGET_ISA_REV=2
CONFIG_THERMAL=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
CONFIG_THERMAL_GOV_STEP_WISE=y
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_TREE_RCU=y
CONFIG_TREE_SRCU=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WATCHDOG_CORE=y
CONFIG_XPS=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y

View file

@ -1,236 +0,0 @@
CONFIG_AQUANTIA_PHY=y
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BOARD_SCACHE=y
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
# CONFIG_COMMON_CLK_REALTEK is not set
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_DIEI=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_RIXI=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MIPSR2=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CPU_SUPPORTS_MSA=y
CONFIG_CRC_CCITT=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DTC=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_8250=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_FIXED_PHY=y
CONFIG_FORCE_NR_CPUS=y
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=0
CONFIG_FWNODE_MDIO=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_LIB_ASHLDI3=y
CONFIG_GENERIC_LIB_ASHRDI3=y
CONFIG_GENERIC_LIB_CMPDI2=y
CONFIG_GENERIC_LIB_LSHRDI3=y
CONFIG_GENERIC_LIB_UCMPDI2=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_REALTEK_OTTO=y
CONFIG_GRO_CELLS=y
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HIGHMEM=y
CONFIG_HWMON=y
CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_MUX=y
CONFIG_I2C_MUX_RTL9300=y
CONFIG_I2C_RTL9300=y
CONFIG_I2C_SMBUS=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
# CONFIG_LEDS_RTL8231 is not set
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_MACH_REALTEK_RTL=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_I2C=y
CONFIG_MDIO_REALTEK_OTTO_AUX=y
CONFIG_MDIO_SMBUS=y
CONFIG_MFD_RTL8231=y
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
CONFIG_MIPS_ASID_BITS=8
CONFIG_MIPS_ASID_SHIFT=0
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
CONFIG_MIPS_CMDLINE_FROM_DTB=y
CONFIG_MIPS_CPU_SCACHE=y
CONFIG_MIPS_EXTERNAL_TIMER=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
# CONFIG_MIPS_MT_SMP is not set
# CONFIG_MIPS_NO_APPENDED_DTB is not set
CONFIG_MIPS_RAW_APPENDED_DTB=y
CONFIG_MIPS_SPRAM=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
CONFIG_MTD_SPLIT_EVA_FW=y
CONFIG_MTD_SPLIT_FIRMWARE=y
CONFIG_MTD_SPLIT_TPLINK_FW=y
CONFIG_MTD_SPLIT_UIMAGE_FW=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_RTL83XX=y
CONFIG_NET_DSA_TAG_TRAILER=y
CONFIG_NET_EGRESS=y
CONFIG_NET_INGRESS=y
CONFIG_NET_RTL838X=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_XGRESS=y
CONFIG_NO_EXCEPT_FILL=y
CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLIB_LEDS=y
CONFIG_PHYLINK=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_RTL8231=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_GPIO_RESTART=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RATIONAL=y
CONFIG_REALTEK_OTTO_TIMER=y
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_REALTEK_PHY=y
CONFIG_REALTEK_PHY_HWMON=y
CONFIG_REALTEK_SOC_PHY=y
CONFIG_REALTEK_THERMAL=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_MDIO=y
CONFIG_REGMAP_MMIO=y
CONFIG_RESET_CONTROLLER=y
# CONFIG_RTL838X is not set
# CONFIG_RTL839X is not set
CONFIG_RTL930X=y
# CONFIG_RTL931X is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SFP=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
CONFIG_SWPHY=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
CONFIG_SYS_SUPPORTS_HIGHMEM=y
CONFIG_SYS_SUPPORTS_MIPS16=y
CONFIG_SYS_SUPPORTS_MULTITHREADING=y
CONFIG_TARGET_ISA_REV=2
CONFIG_THERMAL=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
CONFIG_THERMAL_GOV_STEP_WISE=y
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_TINY_SRCU=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WATCHDOG_CORE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y

View file

@ -1,259 +0,0 @@
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BOARD_SCACHE=y
CONFIG_CLKSRC_MIPS_GIC=y
CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=125
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
# CONFIG_COMMON_CLK_REALTEK is not set
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
CONFIG_CONTEXT_TRACKING=y
CONFIG_CONTEXT_TRACKING_IDLE=y
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_DIEI=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_RIXI=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MIPSR2=y
CONFIG_CPU_MIPSR2_IRQ_EI=y
CONFIG_CPU_MIPSR2_IRQ_VI=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CPU_SUPPORTS_MSA=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DTC=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_8250=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_FIXED_PHY=y
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=0
CONFIG_FWNODE_MDIO=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_LIB_ASHLDI3=y
CONFIG_GENERIC_LIB_ASHRDI3=y
CONFIG_GENERIC_LIB_CMPDI2=y
CONFIG_GENERIC_LIB_LSHRDI3=y
CONFIG_GENERIC_LIB_UCMPDI2=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_REALTEK_OTTO=y
CONFIG_GRO_CELLS=y
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HIGHMEM=y
CONFIG_HWMON=y
CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_MUX=y
CONFIG_I2C_MUX_RTL9300=y
CONFIG_I2C_RTL9300=y
CONFIG_I2C_SMBUS=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_KMAP_LOCAL=y
CONFIG_LEDS_GPIO=y
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_MACH_REALTEK_RTL=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_I2C=y
# CONFIG_MDIO_REALTEK_OTTO_AUX is not set
CONFIG_MDIO_SMBUS=y
# CONFIG_MFD_RTL8231 is not set
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
CONFIG_MIPS_ASID_BITS=8
CONFIG_MIPS_ASID_SHIFT=0
CONFIG_MIPS_CLOCK_VSYSCALL=y
CONFIG_MIPS_CM=y
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
CONFIG_MIPS_CMDLINE_FROM_DTB=y
CONFIG_MIPS_CPC=y
CONFIG_MIPS_CPS=y
# CONFIG_MIPS_CPS_NS16550_BOOL is not set
CONFIG_MIPS_CPU_SCACHE=y
CONFIG_MIPS_EXTERNAL_TIMER=y
CONFIG_MIPS_GIC=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
CONFIG_MIPS_MT=y
CONFIG_MIPS_MT_FPAFF=y
CONFIG_MIPS_MT_SMP=y
# CONFIG_MIPS_NO_APPENDED_DTB is not set
CONFIG_MIPS_NR_CPU_NR_MAP=2
CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y
CONFIG_MIPS_RAW_APPENDED_DTB=y
CONFIG_MIPS_SPRAM=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_BRNIMAGE_FW=y
CONFIG_MTD_SPLIT_EVA_FW=y
CONFIG_MTD_SPLIT_FIRMWARE=y
CONFIG_MTD_SPLIT_TPLINK_FW=y
CONFIG_MTD_SPLIT_UIMAGE_FW=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SRCU_NMI_SAFE=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_RTL83XX=y
CONFIG_NET_DSA_TAG_TRAILER=y
CONFIG_NET_EGRESS=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_INGRESS=y
CONFIG_NET_RTL838X=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_XGRESS=y
CONFIG_NO_EXCEPT_FILL=y
CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NR_CPUS=4
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_PADATA=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLIB_LEDS=y
CONFIG_PHYLINK=y
CONFIG_PINCTRL=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RATIONAL=y
# CONFIG_REALTEK_OTTO_TIMER is not set
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_REALTEK_SOC_PHY=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_MMIO=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
# CONFIG_RTL838X is not set
# CONFIG_RTL839X is not set
CONFIG_RTL930X=y
CONFIG_RTL931X=y
CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_LM75=y
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SFP=y
CONFIG_SMP=y
CONFIG_SMP_UP=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
CONFIG_SWPHY=y
CONFIG_SYNC_R4K=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
CONFIG_SYS_SUPPORTS_HIGHMEM=y
CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y
CONFIG_SYS_SUPPORTS_MIPS16=y
CONFIG_SYS_SUPPORTS_MIPS_CPS=y
CONFIG_SYS_SUPPORTS_MULTITHREADING=y
CONFIG_SYS_SUPPORTS_SCHED_SMT=y
CONFIG_SYS_SUPPORTS_SMP=y
CONFIG_SYS_SUPPORTS_VPE_LOADER=y
CONFIG_TARGET_ISA_REV=2
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_TREE_RCU=y
CONFIG_TREE_SRCU=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WATCHDOG_CORE=y
CONFIG_WEAK_ORDERING=y
CONFIG_XPS=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y