diff options
Diffstat (limited to 'drivers/clk/imx/clk-imx6sx.c')
-rw-r--r-- | drivers/clk/imx/clk-imx6sx.c | 174 |
1 files changed, 160 insertions, 14 deletions
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index c4685c01929a..274c31cf61a2 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -10,13 +10,18 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/imx_sema4.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/types.h> +#include <soc/imx/gpc.h> +#include <soc/imx/src.h> #include "clk.h" +#define CCM_CCGR_OFFSET(index) (index * 2) + static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; @@ -83,6 +88,12 @@ static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; static struct clk_hw **hws; static struct clk_hw_onecell_data *clk_hw_data; +struct imx_sema4_mutex *amp_power_mutex; + +static int clks_shared[MAX_SHARED_CLK_NUMBER]; + +struct imx_shared_mem *shared_mem; +static unsigned int shared_mem_paddr, shared_mem_size; static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, @@ -116,18 +127,43 @@ static u32 share_count_ssi3; static u32 share_count_sai1; static u32 share_count_sai2; -static const int uart_clk_ids[] __initconst = { - IMX6SX_CLK_UART_IPG, - IMX6SX_CLK_UART_SERIAL, -}; +/* + * As IMX6SX_CLK_M4_PRE_SEL is NOT a glitchless MUX, so when + * M4 is trying to change its clk parent, need to ask A9 to + * help do it, and M4 must be hold in wfi. To avoid glitch + * occur, need to gate M4 clk first before switching its parent. + */ +void imx6sx_set_m4_highfreq(bool high_freq) +{ + static struct clk *m4_high_freq_sel; + + imx_gpc_hold_m4_in_sleep(); + + clk_disable_unprepare(hws[IMX6SX_CLK_M4]->clk); + clk_set_parent(hws[IMX6SX_CLK_M4_SEL]->clk, + hws[IMX6SX_CLK_LDB_DI0]->clk); + + if (high_freq) { + /* FIXME: m4_high_freq_sel possible used without intialization? */ + clk_set_parent(hws[IMX6SX_CLK_M4_PRE_SEL]->clk, + m4_high_freq_sel); + } else { + m4_high_freq_sel = clk_get_parent(hws[IMX6SX_CLK_M4_PRE_SEL]->clk); + clk_set_parent(hws[IMX6SX_CLK_M4_PRE_SEL]->clk, + hws[IMX6SX_CLK_OSC]->clk); + } + + clk_set_parent(hws[IMX6SX_CLK_M4_SEL]->clk, + hws[IMX6SX_CLK_M4_PRE_SEL]->clk); + clk_prepare_enable(hws[IMX6SX_CLK_M4]->clk); -static struct clk **uart_clks[ARRAY_SIZE(uart_clk_ids) + 1] __initdata; + imx_gpc_release_m4_in_sleep(); +} static void __init imx6sx_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *base; - int i; clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, IMX6SX_CLK_CLK_END), GFP_KERNEL); @@ -189,7 +225,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clk_set_parent(hws[IMX6SX_PLL6_BYPASS]->clk, hws[IMX6SX_CLK_PLL6]->clk); clk_set_parent(hws[IMX6SX_PLL7_BYPASS]->clk, hws[IMX6SX_CLK_PLL7]->clk); - hws[IMX6SX_CLK_PLL1_SYS] = imx_clk_hw_gate("pll1_sys", "pll1_bypass", base + 0x00, 13); + hws[IMX6SX_CLK_PLL1_SYS] = imx_clk_hw_fixed_factor("pll1_sys", "pll1_bypass", 1, 1); hws[IMX6SX_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); hws[IMX6SX_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); hws[IMX6SX_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); @@ -405,7 +441,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) hws[IMX6SX_CLK_GPT_BUS] = imx_clk_hw_gate2("gpt_bus", "perclk", base + 0x6c, 20); hws[IMX6SX_CLK_GPT_SERIAL] = imx_clk_hw_gate2("gpt_serial", "perclk", base + 0x6c, 22); hws[IMX6SX_CLK_GPU] = imx_clk_hw_gate2("gpu", "gpu_core_podf", base + 0x6c, 26); - hws[IMX6SX_CLK_OCRAM_S] = imx_clk_hw_gate2("ocram_s", "ahb", base + 0x6c, 28); + hws[IMX6SX_CLK_OCRAM_S] = imx_clk_hw_gate2_flags("ocram_s", "ahb", base + 0x6c, 28, CLK_IS_CRITICAL); hws[IMX6SX_CLK_CANFD] = imx_clk_hw_gate2("canfd", "can_podf", base + 0x6c, 30); /* CCGR2 */ @@ -489,13 +525,59 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) hws[IMX6SX_CLK_CKO1] = imx_clk_hw_gate("cko1", "cko1_podf", base + 0x60, 7); hws[IMX6SX_CLK_CKO2] = imx_clk_hw_gate("cko2", "cko2_podf", base + 0x60, 24); + /* get those shared clk nodes if M4 is active */ + if (imx_src_is_m4_enabled()) { + u32 num; + + of_property_read_u32(np, "fsl,shared-clks-number", &num); + if (num > MAX_SHARED_CLK_NUMBER) + pr_err("clk: shared clk nodes exceed the max number!\n"); + of_property_read_u32_array(np, "fsl,shared-clks-index", + clks_shared, num); + if (of_property_read_u32(np, "fsl,shared-mem-addr", + &shared_mem_paddr)) + pr_err("clk: fsl,shared-mem-addr NOT found!\n"); + if (of_property_read_u32(np, "fsl,shared-mem-size", + &shared_mem_size)) + pr_err("clk: fsl,shared-mem-size NOT found!\n"); + } + /* mask handshake of mmdc */ imx_mmdc_mask_handshake(base, 0); imx_check_clk_hws(hws, IMX6SX_CLK_CLK_END); + /* + * QSPI2/GPMI_IO share the same clock source but with the + * different gate, need explicitely gate the QSPI2 & GPMI_IO + * during the clock init phase according to the SOC design. + */ + if (!imx_src_is_m4_enabled()) { + writel_relaxed(readl_relaxed(base + 0x78) & + ~(3 << CCM_CCGR_OFFSET(5)), base + 0x78); + writel_relaxed(readl_relaxed(base + 0x78) & + ~(3 << CCM_CCGR_OFFSET(14)), base + 0x78); + } + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + /* + * As some of the modules need to access ocotp in MSL, + * need to make sure ocotp clk(CCM_CCGR2_CG6) is enabled + * during MSL, as on i.MX6SX, accessing OCOTP registers + * needs its clk on, it will be disabled by clk late + * init and managed by ocotp driver. + */ + writel_relaxed(readl_relaxed(base + 0x70) | 1 << 12, base + 0x70); + + /* maintain M4 usecount */ + if (imx_src_is_m4_enabled()) + clk_prepare_enable(hws[IMX6SX_CLK_M4]->clk); + + /* set perclk to from OSC */ + clk_set_parent(hws[IMX6SX_CLK_PERCLK_SEL]->clk, hws[IMX6SX_CLK_OSC]->clk); + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { clk_prepare_enable(hws[IMX6SX_CLK_USBPHY1_GATE]->clk); clk_prepare_enable(hws[IMX6SX_CLK_USBPHY2_GATE]->clk); @@ -527,7 +609,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clk_set_rate(hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk, 393216000); clk_set_parent(hws[IMX6SX_CLK_SPDIF_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); - clk_set_rate(hws[IMX6SX_CLK_SPDIF_PODF]->clk, 98304000); + clk_set_rate(hws[IMX6SX_CLK_SPDIF_PODF]->clk, 24576000); clk_set_parent(hws[IMX6SX_CLK_AUDIO_SEL]->clk, hws[IMX6SX_CLK_PLL3_USB_OTG]->clk); clk_set_rate(hws[IMX6SX_CLK_AUDIO_PODF]->clk, 24000000); @@ -542,6 +624,12 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clk_set_parent(hws[IMX6SX_CLK_ESAI_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); clk_set_rate(hws[IMX6SX_CLK_ESAI_PODF]->clk, 24576000); + /* Set the UART parent if needed. */ + if (uart_from_osc) + clk_set_parent(hws[IMX6SX_CLK_UART_SEL]->clk, hws[IMX6SX_CLK_OSC]->clk); + else + clk_set_parent(hws[IMX6SX_CLK_UART_SEL]->clk, hws[IMX6SX_CLK_PLL3_80M]->clk); + /* Set parent clock for vadc */ clk_set_parent(hws[IMX6SX_CLK_VID_SEL]->clk, hws[IMX6SX_CLK_PLL3_USB_OTG]->clk); @@ -551,16 +639,74 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) /* Update gpu clock from default 528M to 720M */ clk_set_parent(hws[IMX6SX_CLK_GPU_CORE_SEL]->clk, hws[IMX6SX_CLK_PLL3_PFD0]->clk); clk_set_parent(hws[IMX6SX_CLK_GPU_AXI_SEL]->clk, hws[IMX6SX_CLK_PLL3_PFD0]->clk); + if (!imx_src_is_m4_enabled()) + /* default parent of can_sel clock is invalid, manually set it here */ + clk_set_parent(hws[IMX6SX_CLK_CAN_SEL]->clk, hws[IMX6SX_CLK_PLL3_60M]->clk); clk_set_parent(hws[IMX6SX_CLK_QSPI1_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk); clk_set_parent(hws[IMX6SX_CLK_QSPI2_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk); - for (i = 0; i < ARRAY_SIZE(uart_clk_ids); i++) { - int index = uart_clk_ids[i]; + imx_register_uart_clocks(); +} +CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init); + +int imx_update_shared_mem(struct clk_hw *hw, bool enable) +{ + int i; - uart_clks[i] = &hws[index]->clk; + for (i = 0; i < ARRAY_SIZE(clks_shared); i++) { + if (shared_mem->imx_clk[i].self == hw->clk) + break; } - imx_register_uart_clocks(uart_clks); + if (i >= ARRAY_SIZE(clks_shared)) + return 1; + + /* update ca9 clk status in shared memory */ + if (enable) + shared_mem->imx_clk[i].ca9_enabled = 1; + else + shared_mem->imx_clk[i].ca9_enabled = 0; + + if (shared_mem->imx_clk[i].cm4_enabled == 0) + return 1; + + return 0; } -CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init); + +static int __init imx_amp_power_init(void) +{ + int i; + void __iomem *shared_mem_base; + + if (!(imx_src_is_m4_enabled() && clk_on_imx6sx())) + return 0; + + amp_power_mutex = imx_sema4_mutex_create(0, MCC_POWER_SHMEM_NUMBER); + + shared_mem_base = ioremap_nocache(shared_mem_paddr, shared_mem_size); + + if (!amp_power_mutex) { + pr_err("Failed to create sema4 mutex!\n"); + return 0; + } + + shared_mem = (struct imx_shared_mem *)shared_mem_base; + + for (i = 0; i < ARRAY_SIZE(clks_shared); i++) { + shared_mem->imx_clk[i].self = hws[clks_shared[i]]->clk; + shared_mem->imx_clk[i].ca9_enabled = 1; + pr_debug("%d: name %s, addr 0x%x\n", i, + __clk_get_name(shared_mem->imx_clk[i].self), + (u32)&(shared_mem->imx_clk[i])); + } + /* enable amp power management */ + shared_mem->ca9_valid = SHARED_MEM_MAGIC_NUMBER; + + pr_info("A9-M4 sema4 num %d, A9-M4 magic number 0x%x - 0x%x.\n", + amp_power_mutex->gate_num, shared_mem->ca9_valid, + shared_mem->cm4_valid); + + return 0; +} +late_initcall(imx_amp_power_init); |