| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Support for asr spi controller |
| * |
| * Copyright (C) 2021 ASR Micro Limited |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/spinlock.h> |
| #include <linux/io.h> |
| #include <linux/clk.h> |
| #include <linux/clk-provider.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/of_address.h> |
| #include <linux/cputype.h> |
| #include <linux/clk/mmp.h> |
| #include <soc/asr/asrdcstat.h> |
| #include <soc/asr/addr-map.h> |
| #include <soc/asr/regs-addr.h> |
| #include <dt-bindings/clock/asr,asr1803.h> |
| |
| #ifdef CONFIG_ASR_DEBUG |
| #include <soc/asr/debugfs-asr.h> |
| #endif |
| |
| #include "clk.h" |
| #include "clk-asr1901.h" |
| #include "reset.h" |
| |
| #define APBC_RTC 0x28 |
| #define APBC_TWSI0 0x2c |
| #define APBC_KPC 0x30 |
| #define APBC_TWSI1 0x60 |
| #define APBC_TWSI2 0x70 |
| #define APBC_TWSI3 0x74 |
| #define APBC_TWSI4 0x7C |
| #define APBC_UART0 0x0 |
| #define APBC_UART1 0x4 |
| #define APBC_UART2 0x78 |
| #define APBC_GPIO 0x8 |
| #define APBC_PWM0 0xc |
| #define APBC_PWM1 0x10 |
| #define APBC_PWM2 0x14 |
| #define APBC_PWM3 0x18 |
| #define APBC_SSP0 0x1c |
| #define APBC_SSP1 0x20 |
| #define APBC_IPC 0x24 |
| #define APBC_TIMER0 0x34 |
| #define APBC_TIMER1 0x44 |
| #define APBC_TIMER2 0x68 |
| #define APBC_ONEWIRE 0x48 |
| #define APBC_SSP2 0x4c |
| |
| #define APBC_TSEN 0x6c |
| |
| #define APBC_AUXADC 0xA8 |
| |
| #define APMU_SDH0 0x54 |
| #define APMU_SDH1 0x58 |
| #define APMU_SDH2 0xe0 |
| #define APMU_USB0 0x3B8 |
| #define APMU_USB1 0x3C0 |
| #define APMU_PCIE 0x3CC |
| #define APMU_QSPI 0x60 |
| #define MPMU_PLL2CR 0x34 |
| #define MPMU_UART_PLL 0x14 |
| #define MPMU_UART_PLL2 0x10CC |
| #define MPMU_VRCR 0x18 |
| #define MPMU_FCCR 0x8 |
| #define MPMU_ISCCR0 0x40 |
| #define MPMU_ISCCR1 0x44 |
| #define APMU_TRACE 0x108 |
| #define APMU_GEU 0x68 |
| #define APMU_XGMAC 0x3D4 |
| #define CIU_MC_CONF 0x0040 |
| #define CIU_AP_DMA_AXI_XTC 0x00f8 |
| #define APMU_CORE_STATUS 0x090 |
| #define APMU_PRBC 0x200 |
| /* GBS: clock for GSSP */ |
| #define APBC_GBS 0xc |
| #define APBC_GCER 0x34 |
| #define APBC_DROTS 0x058 |
| |
| /* IPC clock */ |
| #define APBC_IPC_CLK_RST 0x24 |
| |
| /* pll1 d1p gating feature */ |
| #define APMU_CLK_GATE_CTRL 0x040 |
| #define APMU_DDR_FC_CTRL (0x15C) |
| #define APMU_EMAC_CLK_RST_CTRL 0x3D4 |
| |
| static int __init __init_dcstat_debugfs_node(void); |
| |
| static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { |
| {ASR1803_CLK_CLK32, "clk32", NULL, 0, 32768}, |
| {ASR1803_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000}, |
| {ASR1803_CLK_PLL1, "pll1_624", NULL, 0, 624000000}, |
| {ASR1803_CLK_PLL1_1248, "pll1_1248", NULL, 0, 1248000000}, |
| {ASR1803_CLK_PLL1_1475, "pll1_1475", NULL, 0, 1475000000}, |
| {ASR1803_CLK_PLL1_983, "pll1_983", NULL, 0, 983000000}, |
| {ASR1803_CLK_PLL1_832, "pll1_832", NULL, 0, 832000000}, |
| {ASR1803_CLK_PLL1_416, "pll1_416", NULL, 0, 416000000}, |
| {ASR1803_CLK_PLL1_312, "pll1_312", NULL, 0, 312000000}, |
| {ASR1803_CLK_PLL1_208, "pll1_208", NULL, 0, 208000000}, |
| {ASR1803_CLK_PLL1_156, "pll1_156", NULL, 0, 156000000}, |
| {ASR1803_CLK_DPLL_1600, "dpll_1600", NULL, 0, 1600000000}, |
| {ASR1803_CLK_DPLL_2000, "dpll_2000", NULL, 0, 2000000000}, |
| {ASR1803_CLK_DPLL_2133, "dpll_2133", NULL, 0, 2133000000}, |
| {ASR1803_CLK_PLL1_249, "pll1_249", NULL, 249000000}, |
| {ASR1803_CLK_PLL1_52, "pll1_52", NULL, 52000000}, |
| {ASR1803_CLK_UART_58P5M, "uart_58p5m", NULL, 0, 58500000}, |
| }; |
| |
| static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { |
| {ASR1803_CLK_CLK16, "clk16", "clk32", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_2, "pll1_2", "pll1_624", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_8, "pll1_8", "pll1_4", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_16, "pll1_16", "pll1_8", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_6, "pll1_6", "pll1_2", 1, 3, 0}, |
| {ASR1803_CLK_PLL1_12, "pll1_12", "pll1_6", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_384, "pll1_384", "pll1_192", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_768, "pll1_768", "pll1_384", 1, 2, 0}, |
| {ASR1803_CLK_PLL1_13, "pll1_13", "pll1_624", 1, 13, 0}, |
| {ASR1803_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, |
| {ASR1803_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, |
| }; |
| |
| static struct mmp_clk_factor_masks uart_factor_masks = { |
| .factor = 2, |
| .num_mask = 0x1fff, |
| .den_mask = 0x1fff, |
| .num_shift = 16, |
| .den_shift = 0, |
| }; |
| |
| static struct mmp_clk_factor_tbl uart_factor_tbl[] = { |
| {.num = 8125, .den = 1536}, /*14.745MHZ */ |
| {.num = 7998, .den = 1536}, /*14.97MHZ */ |
| {.num = 8156, .den = 1673}, /*15.99MHZ */ |
| {.num = 3250, .den = 2000}, /*48 MHZ */ |
| {.num = 2100, .den = 1600}, /*59.429MHZ */ |
| {.num = 2039, .den = 1673}, /*63.999MHZ */ |
| }; |
| |
| static struct mmp_clk_factor_tbl uart_factor_tbl_14m[] = { |
| {.num = 8125, .den = 1536}, /*14.745MHZ */ |
| {.num = 7998, .den = 1536}, /*14.97MHZ */ |
| {.num = 8156, .den = 418}, /*15.99MHZ */ |
| {.num = 2039, .den = 1673}, /*63.999MHZ */ |
| }; |
| |
| static struct mmp_clk_factor_tbl uart_factor_tbl_48m[] = { |
| {.num = 6144, .den = 960}, /*48MHZ */ |
| {.num = 2100, .den = 400}, /*59.429MHZ */ |
| }; |
| |
| static void asr1901_pll_init(struct asr1901_clk_unit *asr_unit) |
| { |
| struct clk *clk; |
| struct mmp_clk_unit *unit = &asr_unit->unit; |
| |
| mmp_register_fixed_rate_clks(unit, fixed_rate_clks, |
| ARRAY_SIZE(fixed_rate_clks)); |
| |
| mmp_register_fixed_factor_clks(unit, fixed_factor_clks, |
| ARRAY_SIZE(fixed_factor_clks)); |
| |
| if (cpu_is_asr1906 () ) { |
| clk = mmp_clk_register_factor("uart_pll", "pll1_4", |
| CLK_SET_RATE_PARENT, |
| asr_unit->mpmu_base + MPMU_UART_PLL, |
| &uart_factor_masks, uart_factor_tbl_14m, |
| ARRAY_SIZE(uart_factor_tbl_14m), NULL); |
| mmp_clk_add(unit, ASR1803_CLK_UART_PLL, clk); |
| |
| clk = mmp_clk_register_factor("uart_pll2", "pll1_624", |
| CLK_SET_RATE_PARENT, |
| asr_unit->mpmu_base + MPMU_UART_PLL2, |
| &uart_factor_masks, uart_factor_tbl_48m, |
| ARRAY_SIZE(uart_factor_tbl_48m), NULL); |
| mmp_clk_add(unit, ASR1803_CLK_UART_PLL2, clk); |
| } else { |
| clk = mmp_clk_register_factor("uart_pll", "pll1_4", |
| CLK_SET_RATE_PARENT, |
| asr_unit->mpmu_base + MPMU_UART_PLL, |
| &uart_factor_masks, uart_factor_tbl, |
| ARRAY_SIZE(uart_factor_tbl), NULL); |
| mmp_clk_add(unit, ASR1803_CLK_UART_PLL, clk); |
| } |
| } |
| |
| static DEFINE_SPINLOCK(uart0_lock); |
| static DEFINE_SPINLOCK(uart1_lock); |
| static DEFINE_SPINLOCK(uart2_lock); |
| static const char *uart_parent_names[] = {"uart_58p5m", "uart_pll"}; |
| static const char *uart_parent_names_48m[] = {"uart_58p5m", "uart_pll", "uart_pll2"}; |
| |
| static DEFINE_SPINLOCK(ssp0_lock); |
| static DEFINE_SPINLOCK(ssp1_lock); |
| static DEFINE_SPINLOCK(ssp2_lock); |
| static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; |
| |
| //static DEFINE_SPINLOCK(timer0_lock); |
| //static DEFINE_SPINLOCK(timer1_lock); |
| static DEFINE_SPINLOCK(kpc_lock); |
| //static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"}; |
| static const char *kpc_parent_names[] = {"clk32", "clk32", "pll1_24"}; |
| |
| static DEFINE_SPINLOCK(reset_lock); |
| |
| |
| static struct mmp_param_mux_clk asr1901_uart_mux_clks[] = { /* 1901 */ |
| {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock}, |
| {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, |
| {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART2, 4, 3, 0, &uart2_lock}, |
| }; |
| |
| static struct mmp_param_mux_clk asr1906_uart_mux_clks[] = { /* 1906 */ |
| {0, "uart0_mux", uart_parent_names_48m, ARRAY_SIZE(uart_parent_names_48m), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock}, |
| {0, "uart1_mux", uart_parent_names_48m, ARRAY_SIZE(uart_parent_names_48m), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, |
| {0, "uart2_mux", uart_parent_names_48m, ARRAY_SIZE(uart_parent_names_48m), CLK_SET_RATE_PARENT, APBC_UART2, 4, 3, 0, &uart2_lock}, |
| }; |
| |
| static struct mmp_param_mux_clk apbc_mux_clks[] = { |
| {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, |
| {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, |
| {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, |
| // {0, "timer0_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER0, 4, 3, 0, &timer0_lock}, |
| // {0, "timer1_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER1, 4, 3, 0, &timer1_lock}, |
| {0, "kpc_mux", kpc_parent_names, ARRAY_SIZE(kpc_parent_names), CLK_SET_RATE_PARENT, APBC_KPC, 4, 3, 0, &kpc_lock}, |
| }; |
| |
| static struct mmp_param_gate_clk apbc_gate_clks[] = { |
| {ASR1803_CLK_TWSI0, "twsi0_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_TWSI1, "twsi1_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_TWSI2, "twsi2_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI2, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_TWSI3, "twsi3_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_TWSI4, "twsi4_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI4, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_TSEN, "tsen_clk", NULL, CLK_SET_RATE_PARENT, APBC_TSEN, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| #ifdef CONFIG_PXA_DS1WM |
| {ASR1803_CLK_ONWIRE, "onewire", "vctcxo", CLK_SET_RATE_PARENT, APBC_ONEWIRE, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| #endif |
| {ASR1803_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x87, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, |
| {ASR1803_CLK_PWM0, "pwm0_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM0, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_PWM1, "pwm1_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM1, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_PWM2, "pwm2_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM2, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_PWM3, "pwm3_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM3, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| {ASR1803_CLK_IPC, "ipc_clk", NULL, CLK_SET_RATE_PARENT, APBC_IPC, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| |
| /* The gate clocks has mux parent. */ |
| {ASR1803_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x7, 0x3, 0x0, 0, &uart0_lock}, |
| {ASR1803_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x7, 0x3, 0x0, 0, &uart1_lock}, |
| {ASR1803_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBC_UART2, 0x7, 0x3, 0x0, 0, &uart2_lock}, |
| {ASR1803_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x7, 0x3, 0x0, 0, &ssp0_lock}, |
| {ASR1803_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock}, |
| {ASR1803_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock}, |
| // {ASR1803_CLK_TIMER0, "timer0_clk", "timer0_mux", CLK_SET_RATE_PARENT, APBC_TIMER0, 0x3, 0x3, 0x0, 0, &timer0_lock}, |
| // {ASR1803_CLK_TIMER1, "timer1_clk", "timer1_mux", CLK_SET_RATE_PARENT, APBC_TIMER1, 0x3, 0x3, 0x0, 0, &timer1_lock}, |
| {ASR1803_CLK_KPC, "kpc_clk", "kpc_mux", CLK_SET_RATE_PARENT, APBC_KPC, 0x7, 0x3, 0x0, 0, &kpc_lock}, |
| {ASR1803_CLK_AUXADC, "auxadc_clk", NULL, CLK_SET_RATE_PARENT, APBC_AUXADC, 0x7, 0x3, 0x0, 0, &reset_lock}, |
| }; |
| |
| static void asr1901_apb_periph_clk_init(struct asr1901_clk_unit *asr_unit) |
| { |
| struct mmp_clk_unit *unit = &asr_unit->unit; |
| |
| if (cpu_is_asr1901()) |
| mmp_register_mux_clks(unit, asr1901_uart_mux_clks, asr_unit->apbc_base, |
| ARRAY_SIZE(asr1901_uart_mux_clks)); |
| else |
| mmp_register_mux_clks(unit, asr1906_uart_mux_clks, asr_unit->apbc_base, |
| ARRAY_SIZE(asr1906_uart_mux_clks)); |
| |
| mmp_register_mux_clks(unit, apbc_mux_clks, asr_unit->apbc_base, |
| ARRAY_SIZE(apbc_mux_clks)); |
| |
| mmp_register_gate_clks(unit, apbc_gate_clks, asr_unit->apbc_base, |
| ARRAY_SIZE(apbc_gate_clks)); |
| |
| } |
| |
| static DEFINE_SPINLOCK(sdh0_lock); |
| static DEFINE_SPINLOCK(sdh1_lock); |
| static const char *qspi_clk_parents[] = { |
| "pll1_24", "pll1_12", "pll1_8", "pll1_6", |
| "pll1_4", "pll1_3", "pll1_2", "pll1_416" |
| }; |
| |
| /* qspi on 1906 and later platform use new clk/res control */ |
| static const char *qspi_clk_parents2[] = { |
| "pll1_416", "pll1_312", "pll1_249", "pll3_614", |
| "pll3_368", "pll2_737", "pll2_590", "pll1_52" |
| }; |
| |
| static struct mmp_clk_mix_config qspi_mix_config = { |
| .reg_info = DEFINE_MIX_REG_INFO(3, 9, 3, 6, 12), |
| }; |
| |
| static DEFINE_SPINLOCK(usb_lock); |
| static DEFINE_SPINLOCK(aes_lock); |
| static DEFINE_SPINLOCK(xgmac_lock); |
| static DEFINE_SPINLOCK(trace_lock); |
| static DEFINE_SPINLOCK(qspi_lock); |
| //static DEFINE_SPINLOCK(pcie0_lock); |
| //static DEFINE_SPINLOCK(pcie1_lock); |
| |
| static const char * const sdh_parent_names[] = {"pll1_416", "pll1_624", "pll1_13"}; |
| |
| static struct mmp_clk_mix_config sdh_mix_config = { |
| .reg_info = DEFINE_MIX_REG_INFO(3, 8, 2, 6, 11), |
| }; |
| |
| static struct mmp_param_mux_clk apmu_mux_clks[] = { |
| {0, "qspi_mux", qspi_clk_parents, ARRAY_SIZE(qspi_clk_parents), CLK_SET_RATE_PARENT, APMU_QSPI, 6, 3, 0, &qspi_lock}, |
| }; |
| |
| static struct mmp_param_gate_clk apmu_gate_clks[] = { |
| {ASR1803_CLK_USB, "usb_clk", NULL, 0, APMU_USB0, 0x1E000, 0x1E000, 0x0, 0, &usb_lock}, |
| {ASR1803_CLK_GEU, "aes_clk", NULL, 0, APMU_GEU, 0x30, 0x30, 0x10, 0, &aes_lock}, |
| {ASR1901_CLK_XGMAC, "xgmac-clk", NULL, 0, APMU_XGMAC, 0x3, 0x3, 0x0, 0, &xgmac_lock}, |
| {ASR1803_CLK_DBG, "dbg_clk", NULL, 0, APMU_TRACE, 0x10008, 0x10008, 0x10000, 0, &trace_lock}, |
| {ASR1803_CLK_TRACE, "trace_clk", NULL, 0, APMU_TRACE, 0x10010, 0x10010, 0x10000, 0, &trace_lock}, |
| {ASR1803_CLK_SDH_AXI, "sdh_axi", NULL, 0, APMU_SDH0, 0x9, 0x9, 0x1, 0, &sdh0_lock}, |
| // {asr1901_CLK_PCIE0, "pcie0_clk", NULL, 0, APMU_PCIE0, 0xff, 0xff, 0x0, 0, &pcie0_lock}, |
| |
| /* The gate clocks has mux parent. */ |
| {ASR1803_CLK_QSPI_FCLK, "qspi_fclk", "qspi_mux", 0, APMU_QSPI, 0x12, 0x12, 0x10, 0, &qspi_lock}, |
| |
| }; |
| |
| static void asr1901_axi_periph_clk_init(struct asr1901_clk_unit *asr_unit) |
| { |
| struct clk *clk; |
| struct mmp_clk_unit *unit = &asr_unit->unit; |
| |
| mmp_register_mux_clks(unit, apmu_mux_clks, asr_unit->apmu_base, |
| ARRAY_SIZE(apmu_mux_clks)); |
| |
| mmp_register_gate_clks(unit, apmu_gate_clks, asr_unit->apmu_base, |
| ARRAY_SIZE(apmu_gate_clks)); |
| #if 0 |
| if (cpu_is_asr1826s()) { |
| clk = mmp_clk_register_gate(NULL, "pcie1_clk", NULL, 0, |
| asr_unit->apmu_base + APMU_PCIE1, 0xff, 0xff, 0x0, 0, &pcie1_lock); |
| mmp_clk_add(unit, asr1901_CLK_PCIE1, clk); |
| } |
| #endif |
| sdh_mix_config.reg_info.reg_clk_ctrl = asr_unit->apmu_base + APMU_SDH0; |
| clk = mmp_clk_register_mix(NULL, "sdh0_mix_clk", |
| (const char **)sdh_parent_names, ARRAY_SIZE(sdh_parent_names), |
| CLK_SET_RATE_PARENT, |
| &sdh_mix_config, &sdh0_lock); |
| |
| clk = mmp_clk_register_gate(NULL, "sdh0_clk", "sdh0_mix_clk", |
| CLK_SET_RATE_PARENT | CLK_SET_RATE_ENABLED, |
| asr_unit->apmu_base + APMU_SDH0, |
| 0x12, 0x12, 0x0, 0, &sdh0_lock); |
| mmp_clk_add(unit, ASR1803_CLK_SDH0, clk); |
| clk = mmp_clk_register_dvfs_dummy("SDH0", NULL, |
| 0, DUMMY_VL_TO_KHZ(VL0)); |
| mmp_clk_add(unit, ASR1803_CLK_SDH0_TUNE, clk); |
| |
| sdh_mix_config.reg_info.reg_clk_ctrl = asr_unit->apmu_base + APMU_SDH1; |
| clk = mmp_clk_register_mix(NULL, "sdh1_mix_clk", |
| (const char **)sdh_parent_names, ARRAY_SIZE(sdh_parent_names), |
| CLK_SET_RATE_PARENT, |
| &sdh_mix_config, &sdh1_lock); |
| clk = mmp_clk_register_gate(NULL, "sdh1_clk", "sdh1_mix_clk", |
| CLK_SET_RATE_PARENT | CLK_SET_RATE_ENABLED, |
| asr_unit->apmu_base + APMU_SDH1, |
| 0x12, 0x12, 0x0, 0, &sdh1_lock); |
| mmp_clk_add(unit, ASR1803_CLK_SDH1, clk); |
| clk = mmp_clk_register_dvfs_dummy("SDH1", NULL, |
| 0, DUMMY_VL_TO_KHZ(VL0)); |
| mmp_clk_add(unit, ASR1803_CLK_SDH1_TUNE, clk); |
| |
| if (cpu_is_asr1906()) { |
| qspi_mix_config.reg_info.reg_clk_ctrl = asr_unit->apmu_base + APMU_QSPI; |
| clk = mmp_clk_register_mix(NULL, "qspi_mix_clk", |
| (const char **)qspi_clk_parents2, ARRAY_SIZE(qspi_clk_parents2), |
| CLK_SET_RATE_PARENT, |
| &qspi_mix_config, &qspi_lock); |
| clk = mmp_clk_register_gate(NULL, "qspi_bus", "qspi_mix_clk", |
| CLK_SET_RATE_PARENT | CLK_SET_RATE_ENABLED, |
| asr_unit->apmu_base + APMU_QSPI, |
| 0x12, 0x12, 0x0, 0, &qspi_lock); |
| mmp_clk_add(unit, ASR1803_CLK_QSPI, clk); |
| |
| clk = clk_register_fixed_factor(NULL, "qspi_bus_dtr", "qspi_bus", |
| CLK_SET_RATE_PARENT, 1, 4); |
| mmp_clk_add(unit, ASR1803_CLK_QSPI_DTR, clk); |
| } else { |
| /* QSPI Bus clock */ |
| clk = clk_register_fixed_factor(NULL, "qspi_bus", "qspi_fclk", |
| CLK_SET_RATE_PARENT, 1, 4); |
| mmp_clk_add(unit, ASR1803_CLK_QSPI, clk); |
| |
| /* dummy clk for xgmac to set dvc */ |
| clk = mmp_clk_register_dvfs_dummy("ETH", NULL, |
| 0, DUMMY_VL_TO_KHZ(VL0)); |
| mmp_clk_add(unit, ASR1803_CLK_XGMAC_DUMMY, clk); |
| } |
| |
| /* QSPI bus clock */ |
| clk = mmp_clk_register_gate(NULL, "qspi_bus_clk", NULL, 0, |
| asr_unit->apmu_base + APMU_QSPI, |
| 0x9, 0x9, 0x9, 0, &qspi_lock); |
| mmp_clk_add(unit, ASR1803_CLK_QSPI_BUS, clk); |
| |
| } |
| |
| static void asr1901_clk_reset_init(struct device_node *np, |
| struct asr1901_clk_unit *asr_unit) |
| { |
| struct mmp_clk_reset_cell *cells; |
| int i, base, nr_resets_apbc, nr_resets; |
| |
| nr_resets_apbc = ARRAY_SIZE(apbc_gate_clks); |
| nr_resets = nr_resets_apbc; |
| cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); |
| if (!cells) |
| return; |
| |
| base = 0; |
| for (i = 0; i < nr_resets_apbc; i++) { |
| cells[base + i].clk_id = apbc_gate_clks[i].id; |
| cells[base + i].reg = |
| asr_unit->apbc_base + apbc_gate_clks[i].offset; |
| cells[base + i].flags = 0; |
| cells[base + i].lock = apbc_gate_clks[i].lock; |
| cells[base + i].bits = 0x4; |
| } |
| |
| mmp_clk_reset_register(np, cells, nr_resets); |
| } |
| |
| static const char * const bootup_on_clock_tbl[] = { |
| "ipc_clk", "ripc_clk", "aes_clk", "trace_clk" |
| }; |
| |
| static const char * const bootup_off_clock_tbl[] = { |
| "aes_clk", "trace_clk" |
| }; |
| |
| void asr_clocks_enable(const char * const *clk_table, |
| int clk_table_size) |
| { |
| int i; |
| struct clk *clk; |
| |
| for (i = 0; i < clk_table_size; i++) { |
| clk = __clk_lookup(clk_table[i]); |
| if (!IS_ERR_OR_NULL(clk)) |
| clk_prepare_enable(clk); |
| else |
| pr_err("%s : can't find clk %s\n", __func__, |
| clk_table[i]); |
| } |
| } |
| |
| void asr_clocks_disable(const char * const *clk_table, |
| int clk_table_size) |
| { |
| int i; |
| struct clk *clk; |
| |
| for (i = 0; i < clk_table_size; i++) { |
| clk = __clk_lookup(clk_table[i]); |
| if (!IS_ERR_OR_NULL(clk)) |
| clk_disable_unprepare(clk); |
| else |
| pr_err("%s : can't find clk %s\n", __func__, |
| clk_table[i]); |
| } |
| } |
| |
| static void __init __maybe_unused asr1901_clk_misc_init(struct asr1901_clk_unit *asr_unit) |
| { |
| /* reset usb to prepare clean start point */ |
| writel(0x0, asr_unit->apmu_base + APMU_USB0); |
| } |
| |
| struct asr1901_clk_unit *global_clk_unit; |
| |
| void __init asr1901_clk_init(struct device_node *np) |
| { |
| struct asr1901_clk_unit *asr_unit; |
| |
| regs_addr_iomap(); |
| |
| asr_unit = kzalloc(sizeof(*asr_unit), GFP_KERNEL); |
| if (!asr_unit) |
| return; |
| |
| asr_unit->mpmu_base = of_iomap(np, 0); |
| if (!asr_unit->mpmu_base) { |
| pr_err("failed to map mpmu registers\n"); |
| goto free_memory; |
| } |
| |
| asr_unit->apmu_base = of_iomap(np, 1); |
| if (!asr_unit->apmu_base) { |
| pr_err("failed to map apmu registers\n"); |
| goto unmap_mpmu_region; |
| } |
| |
| asr_unit->apbc_base = of_iomap(np, 2); |
| if (!asr_unit->apbc_base) { |
| pr_err("failed to map apbc registers\n"); |
| goto unmap_apmu_region; |
| } |
| |
| asr_unit->apbs_base = of_iomap(np, 3); |
| if (!asr_unit->apbs_base) { |
| pr_err("failed to map apbs registers\n"); |
| goto unmap_apbs_region; |
| } |
| |
| asr_unit->ciu_base = of_iomap(np, 4); |
| if (!asr_unit->ciu_base) { |
| pr_err("failed to map ciu registers\n"); |
| goto unmap_ciu_region; |
| } |
| |
| asr_unit->ddrc_base = of_iomap(np, 5); |
| if (!asr_unit->ddrc_base) { |
| pr_err("failed to map ddrc registers\n"); |
| goto unmap_ddrc_region; |
| } |
| |
| mmp_clk_init(np, &asr_unit->unit, ASR1803_NR_CLKS); |
| |
| asr1901_clk_misc_init(asr_unit); |
| |
| asr1901_pll_init(asr_unit); |
| |
| asr1901_apb_periph_clk_init(asr_unit); |
| |
| asr1901_axi_periph_clk_init(asr_unit); |
| |
| asr1901_clk_reset_init(np, asr_unit); |
| |
| asr_clocks_enable(bootup_on_clock_tbl, ARRAY_SIZE(bootup_on_clock_tbl)); |
| |
| /* init clock and ddr will use the dvfs_platinfo. |
| * make sure initialize dvfs_platinfo before clock and ddr init. |
| */ |
| #if defined(CONFIG_ASR_DVFS) |
| setup_asr1901_dvfs_platinfo(); |
| #endif |
| |
| asr1901_acpu_init(asr_unit); |
| |
| asr1901_ddrc_init(asr_unit); |
| |
| asr_clocks_disable(bootup_off_clock_tbl, ARRAY_SIZE(bootup_off_clock_tbl)); |
| |
| global_clk_unit = asr_unit; |
| |
| return; |
| |
| unmap_ddrc_region: |
| iounmap(asr_unit->ciu_base); |
| unmap_ciu_region: |
| iounmap(asr_unit->apbs_base); |
| unmap_apbs_region: |
| iounmap(asr_unit->apbc_base); |
| unmap_apmu_region: |
| iounmap(asr_unit->apmu_base); |
| unmap_mpmu_region: |
| iounmap(asr_unit->mpmu_base); |
| free_memory: |
| kfree(asr_unit); |
| } |
| |
| CLK_OF_DECLARE(asr1901_clk, "asr,asr1803-clock", asr1901_clk_init); |
| |
| |
| #ifdef CONFIG_ASR_DEBUG |
| static struct dentry *stat; |
| CLK_DCSTAT_OPS(global_clk_unit->unit.clk_table[ASR1803_CLK_DDR], ddr) |
| CLK_DCSTAT_OPS(global_clk_unit->unit.clk_table[ASR1803_CLK_DDR], axi) |
| |
| static ssize_t asr1901_clk_stats_read(struct file *filp, |
| char __user *buffer, size_t count, loff_t *ppos) |
| { |
| char *buf; |
| int len = 0, ret = 0; |
| unsigned int reg, size = PAGE_SIZE - 1; |
| void __iomem *apbc_base, *mpmu_base, *apmu_base; |
| |
| apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K); |
| if (apbc_base == NULL) { |
| pr_err("error to ioremap APBC base\n"); |
| return ret; |
| } |
| |
| mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K); |
| if (mpmu_base == NULL) { |
| pr_err("error to ioremap MPMU base\n"); |
| return ret; |
| } |
| |
| apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K); |
| if (apmu_base == NULL) { |
| pr_err("error to ioremap APMU base\n"); |
| return ret; |
| } |
| |
| |
| buf = (char *)__get_free_pages(GFP_NOIO, 0); |
| if (!buf) |
| return -ENOMEM; |
| |
| len += snprintf(buf + len, size, |
| "|---------------|-------|\n|%14s\t|%s|\n" |
| "|---------------|-------|\n", "Clock Name", " Status"); |
| |
| /* PMU */ |
| reg = __raw_readl(mpmu_base + MPMU_PLL2CR); |
| if (((reg & (3 << 8)) >> 8) == 2) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PLL2", "off"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PLL2", "on"); |
| |
| reg = __raw_readl(apmu_base + APMU_SDH0); |
| if ((((reg & (1 << 3)) >> 3) == 1) && (((reg & (1 << 0)) >> 0) == 1)) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH ACLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH ACLK", "off"); |
| |
| if ((((reg & (1 << 4)) >> 4) == 1) && (((reg & (1 << 1)) >> 1) == 1)) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH0 FCLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH0 FCLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_SDH1); |
| if ((((reg & (1 << 4)) >> 4) == 1) && (((reg & (1 << 1)) >> 1) == 1)) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH1 FCLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH1 FCLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_SDH2); |
| if ((((reg & (1 << 4)) >> 4) == 1) && (((reg & (1 << 1)) >> 1) == 1)) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH2 FCLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SDH2 FCLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_USB0); |
| if ((reg & 0x1E000) == 0x1E000) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "USB ACLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "USB ACLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_TRACE); |
| if (((reg & (1 << 3)) >> 3) == 1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "DBG CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "DBG CLK", "off"); |
| if (((reg & (1 << 4)) >> 4) == 1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "TRACE CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "TRACE CLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_GEU); |
| if ((reg & 0x9) == 0x8) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "AES CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "AES CLK", "off"); |
| |
| reg = __raw_readl(apmu_base + APMU_QSPI); |
| if ((reg & 0x19b) == 0x19b) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "DFC CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "DFC CLK", "off"); |
| |
| /* APB */ |
| reg = __raw_readl(apbc_base + APBC_TWSI0); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "TWSI0 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "TWSI0 CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_GPIO); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "GPIO CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "GPIO CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_RTC); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "RTC CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "RTC CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_UART0); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "UART0 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "UART0 CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_UART1); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "UART1 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "UART1 CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_SSP0); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SSP0 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "SSP0 CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_DROTS); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "THERMAL CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "THERMAL CLK", "off"); |
| |
| reg = __raw_readl(apbc_base + APBC_PWM0); |
| if ((reg & 0x1) == 0x1) { |
| if ((reg & 0x6) == 0x2) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM0 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM0 CLK", "off"); |
| reg = __raw_readl(apbc_base + APBC_PWM1); |
| if ((reg & 0x6) == 0x2) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM1 CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM1 CLK", "off"); |
| } else { |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM0 CLK", "off"); |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "PWM1 CLK", "off"); |
| } |
| |
| reg = __raw_readl(apbc_base + APBC_IPC_CLK_RST); |
| if ((reg & 0x5) == 0x1) |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "IPC CLK", "on"); |
| else |
| len += snprintf(buf + len, size, |
| "|%14s\t|%5s\t|\n", "IPC CLK", "off"); |
| |
| len += snprintf(buf + len, size, "|---------------|-------|\n\n"); |
| |
| ret = simple_read_from_buffer(buffer, count, ppos, buf, len); |
| free_pages((unsigned long)buf, 0); |
| return ret; |
| } |
| |
| static const struct file_operations asr1901_clk_stats_ops = { |
| .owner = THIS_MODULE, |
| .read = asr1901_clk_stats_read, |
| }; |
| |
| static int __init __init_dcstat_debugfs_node(void) |
| { |
| struct dentry *cpu_dc_stat = NULL, *ddr_dc_stat = NULL; |
| struct dentry *axi_dc_stat = NULL; |
| struct dentry *clock_status = NULL; |
| |
| stat = debugfs_create_dir("stat", asr_debug_entry); |
| if (!stat) |
| return -ENOENT; |
| |
| cpu_dc_stat = cpu_dcstat_file_create("cpu_dc_stat", stat); |
| if (!cpu_dc_stat) |
| goto err_cpu_dc_stat; |
| |
| ddr_dc_stat = clk_dcstat_file_create("ddr_dc_stat", stat, |
| &ddr_dc_ops); |
| if (!ddr_dc_stat) |
| goto err_ddr_dc_stat; |
| |
| axi_dc_stat = clk_dcstat_file_create("axi_dc_stat", stat, |
| &axi_dc_ops); |
| if (!axi_dc_stat) |
| goto err_axi_dc_stat; |
| |
| clock_status = debugfs_create_file("clock_status", 0444, |
| asr_debug_entry, NULL, &asr1901_clk_stats_ops); |
| if (!clock_status) |
| goto err_clk_stats; |
| |
| return 0; |
| err_clk_stats: |
| debugfs_remove(axi_dc_stat); |
| err_axi_dc_stat: |
| debugfs_remove(ddr_dc_stat); |
| err_ddr_dc_stat: |
| debugfs_remove(cpu_dc_stat); |
| err_cpu_dc_stat: |
| debugfs_remove(stat); |
| return -ENOENT; |
| } |
| |
| arch_initcall(__init_dcstat_debugfs_node); |
| #endif |