realtek: 6.12: restore files-6.6
Automatically generated commit. Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> Link: https://github.com/openwrt/openwrt/pull/18935 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
ac1be95438
commit
ade52b291e
36 changed files with 25990 additions and 0 deletions
|
@ -0,0 +1,85 @@
|
||||||
|
# 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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* 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
|
|
@ -0,0 +1,415 @@
|
||||||
|
/* 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_ */
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# Makefile for the rtl838x specific parts of the kernel
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-y := setup.o prom.o
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# Realtek RTL838x SoCs
|
||||||
|
#
|
||||||
|
cflags-$(CONFIG_MACH_REALTEK_RTL) += -I$(srctree)/arch/mips/include/asm/mach-rtl838x/
|
||||||
|
load-$(CONFIG_MACH_REALTEK_RTL) += 0xffffffff80100000
|
226
target/linux/realtek/files-6.6/arch/mips/rtl838x/prom.c
Normal file
226
target/linux/realtek/files-6.6/arch/mips/rtl838x/prom.c
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
// 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();
|
||||||
|
}
|
102
target/linux/realtek/files-6.6/arch/mips/rtl838x/setup.c
Normal file
102
target/linux/realtek/files-6.6/arch/mips/rtl838x/setup.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// 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();
|
||||||
|
}
|
19
target/linux/realtek/files-6.6/drivers/clk/realtek/Kconfig
Normal file
19
target/linux/realtek/files-6.6/drivers/clk/realtek/Kconfig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# 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
|
|
@ -0,0 +1,2 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
obj-$(CONFIG_COMMON_CLK_RTL83XX) += clk-rtl83xx.o clk-rtl838x-sram.o clk-rtl839x-sram.o
|
|
@ -0,0 +1,149 @@
|
||||||
|
/* 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
|
|
@ -0,0 +1,141 @@
|
||||||
|
/* 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
|
770
target/linux/realtek/files-6.6/drivers/clk/realtek/clk-rtl83xx.c
Normal file
770
target/linux/realtek/files-6.6/drivers/clk/realtek/clk-rtl83xx.c
Normal file
|
@ -0,0 +1,770 @@
|
||||||
|
// 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);
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* 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)
|
|
@ -0,0 +1,286 @@
|
||||||
|
/* 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);
|
483
target/linux/realtek/files-6.6/drivers/i2c/busses/i2c-rtl9300.c
Normal file
483
target/linux/realtek/files-6.6/drivers/i2c/busses/i2c-rtl9300.c
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
// 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");
|
|
@ -0,0 +1,62 @@
|
||||||
|
#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
|
|
@ -0,0 +1,293 @@
|
||||||
|
// 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");
|
|
@ -0,0 +1,7 @@
|
||||||
|
# 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.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# 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
|
1759
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/common.c
Normal file
1759
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/common.c
Normal file
File diff suppressed because it is too large
Load diff
710
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/debugfs.c
Normal file
710
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/debugfs.c
Normal file
|
@ -0,0 +1,710 @@
|
||||||
|
// 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);
|
||||||
|
}
|
2320
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
Normal file
2320
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
Normal file
File diff suppressed because it is too large
Load diff
565
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/qos.c
Normal file
565
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/qos.c
Normal file
|
@ -0,0 +1,565 @@
|
||||||
|
// 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);
|
||||||
|
}
|
2036
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c
Normal file
2036
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.c
Normal file
File diff suppressed because it is too large
Load diff
1122
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h
Normal file
1122
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl838x.h
Normal file
File diff suppressed because it is too large
Load diff
1911
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl839x.c
Normal file
1911
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl839x.c
Normal file
File diff suppressed because it is too large
Load diff
174
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl83xx.h
Normal file
174
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl83xx.h
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/* 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 */
|
2568
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl930x.c
Normal file
2568
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl930x.c
Normal file
File diff suppressed because it is too large
Load diff
1694
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl931x.c
Normal file
1694
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/rtl931x.c
Normal file
File diff suppressed because it is too large
Load diff
410
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/tc.c
Normal file
410
target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/tc.c
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
// 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;
|
||||||
|
}
|
2732
target/linux/realtek/files-6.6/drivers/net/ethernet/rtl838x_eth.c
Normal file
2732
target/linux/realtek/files-6.6/drivers/net/ethernet/rtl838x_eth.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,455 @@
|
||||||
|
/* 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 */
|
4074
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.c
Normal file
4074
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.c
Normal file
File diff suppressed because it is too large
Load diff
98
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.h
Normal file
98
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// 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);
|
190
target/linux/realtek/files-6.6/drivers/thermal/realtek-thermal.c
Normal file
190
target/linux/realtek/files-6.6/drivers/thermal/realtek-thermal.c
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
// 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");
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* 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 */
|
Loading…
Reference in a new issue