/* * Copyright (C) 2013 Allwinnertech * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "clk-sun55iw3.h" #include "clk-sun55iw3_tbl.c" #include "clk_factor.h" #include "clk_periph.h" #include #include #include #define FACTOR_SIZEOF(name) (sizeof(factor_pll##name##_tbl)/ \ sizeof(struct sunxi_clk_factor_freq)) #define FACTOR_SEARCH(name) (sunxi_clk_com_ftr_sr( \ &sunxi_clk_factor_pll_##name, factor, \ factor_pll##name##_tbl, index, \ FACTOR_SIZEOF(name))) #ifndef CONFIG_EVB_PLATFORM #define LOCKBIT(x) 31 #else #define LOCKBIT(x) x #endif DEFINE_SPINLOCK(clk_lock); void __iomem *sunxi_clk_base; void __iomem *sunxi_clk_cpus_base; int sunxi_clk_maxreg = SUNXI_CLK_MAX_REG; int cpus_clk_maxreg = CPUS_CLK_MAX_REG; #define REG_ADDR(x) (sunxi_clk_base + x) /* ns nw ks kw ms mw ps pw d1s d1w d2s d2w {frac out mode} en-s sdmss sdmsw sdmpat sdmval*/ SUNXI_CLK_FACTORS(pll_periph0_2x, 8, 8, 0, 0, 0, 0, 0, 0, 1, 1, 16, 3, 0, 0, 0, 31, 0, 0, 0, 0); SUNXI_CLK_FACTORS(pll_periph1_2x, 8, 8, 0, 0, 0, 0, 0, 0, 1, 1, 16, 3, 0, 0, 0, 31, 0, 0, 0, 0); SUNXI_CLK_FACTORS(pll_periph1_800m, 8, 8, 0, 0, 0, 0, 0, 0, 1, 1, 20, 3, 0, 0, 0, 31, 0, 0, 0, 0); SUNXI_CLK_FACTORS(pll_video0x4, 8, 8, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 31, 24, 1, PLL_VIDEO0PAT0, 0xd1303333); SUNXI_CLK_FACTORS(pll_video3x4, 8, 8, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 31, 24, 1, PLL_VIDEO3PAT0, 0xd1303333); static int get_factors_pll_periph0_2x(u32 rate, u32 parent_rate, struct clk_factors_value *factor) { int index; u64 tmp_rate; if (!factor) return -1; tmp_rate = rate > pllperiph0_2x_max ? pllperiph0_2x_max : rate; do_div(tmp_rate, 1000000); index = tmp_rate; if (FACTOR_SEARCH(periph0_2x)) return -1; return 0; } static int get_factors_pll_periph1_2x(u32 rate, u32 parent_rate, struct clk_factors_value *factor) { int index; u64 tmp_rate; if (!factor) return -1; tmp_rate = rate > pllperiph1_2x_max ? pllperiph1_2x_max : rate; do_div(tmp_rate, 1000000); index = tmp_rate; if (FACTOR_SEARCH(periph1_2x)) return -1; return 0; } static int get_factors_pll_periph1_800m(u32 rate, u32 parent_rate, struct clk_factors_value *factor) { int index; u64 tmp_rate; if (!factor) return -1; tmp_rate = rate > pllperiph1_800m_max ? pllperiph1_800m_max : rate; do_div(tmp_rate, 1000000); index = tmp_rate; if (FACTOR_SEARCH(periph1_800m)) return -1; return 0; } static int get_factors_pll_video0x4(u32 rate, u32 parent_rate, struct clk_factors_value *factor) { u64 tmp_rate; int index; if (!factor) return -1; tmp_rate = rate > pllvideo0x4_max ? pllvideo0x4_max : rate; do_div(tmp_rate, 1000000); index = tmp_rate; if (FACTOR_SEARCH(video0x4)) return -1; return 0; } static int get_factors_pll_video3x4(u32 rate, u32 parent_rate, struct clk_factors_value *factor) { u64 tmp_rate; int index; if (!factor) return -1; tmp_rate = rate > pllvideo3x4_max ? pllvideo3x4_max : rate; do_div(tmp_rate, 1000000); index = tmp_rate; if (FACTOR_SEARCH(video3x4)) return -1; return 0; } static unsigned long calc_rate_pll_periph(u32 parent_rate, struct clk_factors_value *factor) { u64 tmp_rate = (parent_rate ? parent_rate : 24000000); factor->factorn = factor->factorn >= 0xff ? 0 : factor->factorn; factor->factorm = factor->factorm >= 0xff ? 0 : factor->factorm; factor->factork = factor->factork >= 0xff ? 0 : factor->factork; factor->factorp = factor->factorp >= 0xff ? 0 : factor->factorp; factor->factord1 = factor->factord1 >= 0xff ? 0 : factor->factord1; factor->factord2 = factor->factord2 >= 0xff ? 0 : factor->factord2; tmp_rate = tmp_rate * (factor->factorn + 1); do_div(tmp_rate, (factor->factord1 + 1) * (factor->factord2 + 1)); return (unsigned long)tmp_rate; } /* pll_video0x4/pll_video1x4: 24*N/D1 */ static unsigned long calc_rate_video0(u32 parent_rate, struct clk_factors_value *factor) { u64 tmp_rate = (parent_rate ? parent_rate : 24000000); tmp_rate = tmp_rate * (factor->factorn + 1); do_div(tmp_rate, (factor->factord1 + 1) * (factor->factord2 + 1)); return (unsigned long)tmp_rate; } static const char *hosc_parents[] = {"hosc"}; struct factor_init_data sunxi_factos[] = { /* name parent parent_num, flags reg lock_reg lock_bit pll_lock_ctrl_reg lock_en_bit lock_mode config get_factors calc_rate priv_ops*/ {"pll_periph0_2x", hosc_parents, 1, 0, PLL_PERIPH0, PLL_PERIPH0, LOCKBIT(28), PLL_PERIPH0, 29, PLL_LOCK_NEW_MODE, &sunxi_clk_factor_pll_periph0_2x, &get_factors_pll_periph0_2x, &calc_rate_pll_periph, (struct clk_ops *)NULL}, {"pll_periph1_2x", hosc_parents, 1, 0, PLL_PERIPH1, PLL_PERIPH1, LOCKBIT(28), PLL_PERIPH1, 29, PLL_LOCK_NEW_MODE, &sunxi_clk_factor_pll_periph1_2x, &get_factors_pll_periph1_2x, &calc_rate_pll_periph, (struct clk_ops *)NULL}, {"pll_periph1_800m", hosc_parents, 1, 0, PLL_PERIPH1, PLL_PERIPH1, LOCKBIT(28), PLL_PERIPH1, 29, PLL_LOCK_NEW_MODE, &sunxi_clk_factor_pll_periph1_800m, &get_factors_pll_periph1_800m, &calc_rate_pll_periph, (struct clk_ops *)NULL}, {"pll_video0x4", hosc_parents, 1, CLK_NO_DISABLE, PLL_VIDEO0, PLL_VIDEO0, LOCKBIT(28), PLL_VIDEO0, 29, PLL_LOCK_NEW_MODE, &sunxi_clk_factor_pll_video0x4, &get_factors_pll_video0x4, &calc_rate_video0, (struct clk_ops *)NULL}, {"pll_video3x4", hosc_parents, 1, CLK_NO_DISABLE, PLL_VIDEO3, PLL_VIDEO3, LOCKBIT(28), PLL_VIDEO3, 29, PLL_LOCK_NEW_MODE, &sunxi_clk_factor_pll_video3x4, &get_factors_pll_video3x4, &calc_rate_video0, (struct clk_ops *)NULL}, }; static const char *de_parents[] = {"pll_periph0_300m", "pll_periph1_300m", "pll_video3x4", "pll_video3x3"}; static const char *smhc0_parents[] = {"hosc", "pll_periph0_400m", "pll_periph0_300m", "pll_periph1_400m", "pll_periph1_300m"}; static const char *smhc2_parents[] = {"hosc", "pll_periph0_800m", "pll_periph0_600m", "pll_periph1_800m", "pll_periph1_600m"}; static const char *dsi0_parents[] = {"hosc", "pll-periph0_200m", "pll_periph0_150m"}; static const char *dsi1_parents[] = {"hosc", "pll-periph0_200m", "pll_periph0_150m"}; static const char *tcon_lcd0_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph_2x", "", ""}; static const char *tcon_lcd1_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph0_2x", "pll_video0x3", "pll_video1x3"}; static const char *tcon_lcd2_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph_2x", "", ""}; static const char *combphy0_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph0_2x", "pll_periph0x3", "pll_periph1x3"}; static const char *combphy1_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph0_2x", "pll_periph0x3", "pll_periph1x3"}; static const char *tcontv_parents[] = {"pll_video0x4", "pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_periph_2x", "", ""}; static const char *tcontv1_parents[] = {"pll_video1x4", "pll_video2x4", "pll_video3x4", "pll_video0x4", "pll_periph_2x", "", ""}; /* SUNXI_CLK_PERIPH(name, mux_reg, mux_sft, mux_wid, div_reg, div_msft, div_mwid, div_nsft, div_nwid, gate_flag, en_reg, rst_reg, bus_gate_reg, drm_gate_reg, en_sft, rst_sft, bus_gate_sft, dram_gate_sft, lock, com_gate, com_gate_off) */ SUNXI_CLK_PERIPH(de0, DE0_CFG, 24, 3, DE0_CFG, 0, 4, 0, 0, 0, DE0_CFG, DE_GATE, DE_GATE, 0, 31, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(sdmmc0_mod, SMHC0_CFG, 24, 3, SMHC0_CFG, 0, 4, 8, 4, 0, SMHC0_CFG, SMHC_GATE, SMHC_GATE, 0, 31, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(sdmmc2_mod, SMHC2_CFG, 24, 3, SMHC2_CFG, 0, 4, 8, 4, 0, SMHC2_CFG, SMHC_GATE, SMHC_GATE, 0, 31, 18, 2, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(dpss_top0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, DPSS_TOP0_GATE, DPSS_TOP0_GATE, 0, 0, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(dpss_top1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, DPSS_TOP1_GATE, DPSS_TOP1_GATE, 0, 0, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(mipi_dsi0, DSI_CFG0, 24, 3, DSI_CFG0, 0, 4, 0, 0, 0, DSI_CFG0, DSI_GATE, DSI_GATE, 0, 31, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(mipi_dsi1, DSI_CFG1, 24, 3, DSI_CFG1, 0, 4, 0, 0, 0, DSI_CFG1, DSI_GATE, DSI_GATE, 0, 31, 17, 1, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(tcon_lcd0, TCON_LCD_CFG0, 24, 3, TCON_LCD_CFG0, 0, 4, 8, 2, 0, TCON_LCD_CFG0, TCON_LCD_GATE, TCON_LCD_GATE, 0, 31, 16, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(tcon_lcd1, TCON_LCD_CFG1, 24, 3, TCON_LCD_CFG1, 0, 4, 8, 2, 0, TCON_LCD_CFG1, TCON_LCD_GATE, TCON_LCD_GATE, 0, 31, 17, 1, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(tcon_lcd2, TCON_LCD_CFG2, 24, 3, TCON_LCD_CFG2, 0, 4, 8, 2, 0, TCON_LCD_CFG2, TCON_LCD_GATE, TCON_LCD_GATE, 0, 31, 17, 1, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(mipi_dsi_combphy0, COMBPHY1_CFG, 24, 3, COMBPHY0_CFG, 0, 4, 8, 2, 0, COMBPHY0_CFG, 0, 0, 0, 31, 0, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(mipi_dsi_combphy1, COMBPHY1_CFG, 24, 3, COMBPHY1_CFG, 0, 4, 8, 2, 0, COMBPHY1_CFG, 0, 0, 0, 31, 0, 0, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(tcontv, TCONTV_CFG, 24, 3, TCONTV_CFG, 0, 4, 8, 2, 0, TCONTV_CFG, TCONTV_GATE, TCONTV_GATE, 0, 31, 17, 1, 0, &clk_lock, NULL, 0); SUNXI_CLK_PERIPH(tcontv1, TCONTV_CFG1, 24, 3, TCONTV_CFG1, 0, 4, 8, 2, 0, TCONTV_CFG1, TCONTV_GATE, TCONTV_GATE, 0, 31, 17, 1, 0, &clk_lock, NULL, 0); struct periph_init_data sunxi_periphs_init[] = { {"de0", 0, de_parents, ARRAY_SIZE(de_parents), &sunxi_clk_periph_de0 }, {"sdmmc0_mod", 0, smhc0_parents, ARRAY_SIZE(smhc0_parents), &sunxi_clk_periph_sdmmc0_mod }, {"sdmmc2_mod", 0, smhc2_parents, ARRAY_SIZE(smhc2_parents), &sunxi_clk_periph_sdmmc2_mod }, {"dpss_top0", 0, hosc_parents, ARRAY_SIZE(hosc_parents), &sunxi_clk_periph_dpss_top0 }, {"dpss_top1", 0, hosc_parents, ARRAY_SIZE(hosc_parents), &sunxi_clk_periph_dpss_top1 }, {"mipi_dsi0", 0, dsi0_parents, ARRAY_SIZE(dsi0_parents), &sunxi_clk_periph_mipi_dsi0 }, {"mipi_dsi1", 0, dsi1_parents, ARRAY_SIZE(dsi1_parents), &sunxi_clk_periph_mipi_dsi1 }, {"tcon_lcd0", 0, tcon_lcd0_parents, ARRAY_SIZE(tcon_lcd0_parents), &sunxi_clk_periph_tcon_lcd0 }, {"tcon_lcd1", 0, tcon_lcd1_parents, ARRAY_SIZE(tcon_lcd1_parents), &sunxi_clk_periph_tcon_lcd1 }, {"tcon_lcd2", 0, tcon_lcd2_parents, ARRAY_SIZE(tcon_lcd2_parents), &sunxi_clk_periph_tcon_lcd2 }, {"mipi_dsi_combphy0", 0, combphy0_parents, ARRAY_SIZE(combphy0_parents), &sunxi_clk_periph_mipi_dsi_combphy0 }, {"mipi_dsi_combphy1", 0, combphy1_parents, ARRAY_SIZE(combphy1_parents), &sunxi_clk_periph_mipi_dsi_combphy1 }, {"tcontv", 0, tcontv_parents, ARRAY_SIZE(tcontv_parents), &sunxi_clk_periph_tcontv }, {"tcontv1", 0, tcontv1_parents, ARRAY_SIZE(tcontv1_parents), &sunxi_clk_periph_tcontv1 }, }; /* * sunxi_clk_get_factor_by_name() - Get factor clk init config */ struct factor_init_data *sunxi_clk_get_factor_by_name(const char *name) { struct factor_init_data *factor; int i; /* get pll clk init config */ for (i = 0; i < ARRAY_SIZE(sunxi_factos); i++) { factor = &sunxi_factos[i]; if (strcmp(name, factor->name)) continue; return factor; } return NULL; } /* * sunxi_clk_get_periph_by_name() - Get periph clk init config */ struct periph_init_data *sunxi_clk_get_periph_by_name(const char *name) { struct periph_init_data *perpih; int i; for (i = 0; i < ARRAY_SIZE(sunxi_periphs_init); i++) { perpih = &sunxi_periphs_init[i]; if (strcmp(name, perpih->name)) continue; return perpih; } return NULL; } /* * sunxi_clk_get_periph_cpus_by_name() - Get periph clk init config */ struct periph_init_data *sunxi_clk_get_periph_cpus_by_name(const char *name) { return NULL; } void init_clocks(void) { int i; struct factor_init_data *factor; struct periph_init_data *periph; /* get clk register base address */ sunxi_clk_base = (void *)0x02001000; // fixed base address. sunxi_clk_factor_initlimits(); clk_register_fixed_rate(NULL, "hosc", NULL, CLK_IS_ROOT, 24000000); /* register normal factors, based on sunxi factor framework */ for (i = 0; i < ARRAY_SIZE(sunxi_factos); i++) { factor = &sunxi_factos[i]; factor->priv_regops = NULL; sunxi_clk_register_factors(NULL, (void *)sunxi_clk_base, (struct factor_init_data *)factor); } clk_register_fixed_factor(NULL, "pll_periph1_600m", "pll_periph1_2x", 0, 1, 2); clk_register_fixed_factor(NULL, "pll_periph1_300m", "pll_periph1_2x", 0, 1, 4); clk_register_fixed_factor(NULL, "pll_periph1_400m", "pll_periph1_2x", 0, 1, 3); clk_register_fixed_factor(NULL, "pll_periph0_300m", "pll_periph0_2x", 0, 1, 4); clk_register_fixed_factor(NULL, "pll_periph0_150m", "pll_periph0_300m", 0, 1, 2); /* register periph clock */ for (i = 0; i < ARRAY_SIZE(sunxi_periphs_init); i++) { periph = &sunxi_periphs_init[i]; periph->periph->priv_regops = NULL; sunxi_clk_register_periph(periph, sunxi_clk_base); } }