Merge branches 'clk-mvebu-spdx', 'clk-meson', 'clk-imx7d-mu', 'clk-imx-init-array-cleanup' and 'clk-rockchip' into clk-next

* clk-mvebu-spdx:
  clk: mvebu: armada-37xx-periph: switch to SPDX license identifier

* clk-meson:
  clk: meson: add gen_clk
  clk: meson: gxbb: remove HHI_GEN_CLK_CTNL duplicate definition
  clk: meson-axg: add clocks required by pcie driver
  clk: meson: remove unused clk-audio-divider driver
  clk: meson: stop rate propagation for audio clocks
  clk: meson: axg: add the audio clock controller driver
  clk: meson: add axg audio sclk divider driver
  clk: meson: add triple phase clock driver
  clk: meson: add clk-phase clock driver
  clk: meson: clean-up meson clock configuration
  clk: meson: remove obsolete register access
  clk: meson: expose GEN_CLK clkid
  clk: meson-axg: add pcie and mipi clock bindings
  dt-bindings: clock: add meson axg audio clock controller bindings
  clk: meson: audio-divider is one based
  clk: meson-gxbb: set fclk_div2 as CLK_IS_CRITICAL

* clk-imx7d-mu:
  :  - i.MX7D mailbox clk support
  clk: imx7d: add IMX7D_MU_ROOT_CLK

* clk-imx-init-array-cleanup:
  :  - i.MX clk init arrays removed in place of CLK_IS_CRITICAL
  clk: imx6sx: remove clks_init_on array
  clk: imx6sl: remove clks_init_on array
  clk: imx6q: remove clks_init_on array

* clk-rockchip:
  clk: rockchip: Add pclk_rkpwm_pmu to PMU critical clocks in rk3399
  clk: rockchip: fix clk_i2sout parent selection bits on rk3399
  clk: rockchip: add clock controller for px30
  clk: rockchip: add support for half divider
  dt-bindings: add bindings for px30 clock controller
  clk: rockchip: add dt-binding header for px30
This commit is contained in:
Stephen Boyd
2018-08-14 22:58:45 -07:00
共有 31 個文件被更改,包括 3729 次插入262 次删除

查看文件

@@ -96,12 +96,6 @@ static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
static struct clk *clk[IMX6QDL_CLK_END];
static struct clk_onecell_data clk_data;
static unsigned int const clks_init_on[] __initconst = {
IMX6QDL_CLK_MMDC_CH0_AXI,
IMX6QDL_CLK_ROM,
IMX6QDL_CLK_ARM,
};
static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
{ .val = 1, .div = 10, },
@@ -417,7 +411,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
void __iomem *anatop_base, *base;
int i;
int ret;
clk[IMX6QDL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
@@ -794,7 +787,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "mlb_podf", base + 0x74, 18);
else
clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18);
clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20);
clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2_flags("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clk[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22);
clk[IMX6QDL_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28);
clk[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30);
@@ -808,7 +801,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_GPMI_BCH] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26);
clk[IMX6QDL_CLK_GPMI_IO] = imx_clk_gate2("gpmi_io", "enfc", base + 0x78, 28);
clk[IMX6QDL_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30);
clk[IMX6QDL_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0);
clk[IMX6QDL_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL);
clk[IMX6QDL_CLK_SATA] = imx_clk_gate2("sata", "ahb", base + 0x7c, 4);
clk[IMX6QDL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6);
clk[IMX6QDL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
@@ -878,9 +871,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
*/
clk_set_parent(clk[IMX6QDL_CLK_ENFC_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]);
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clk[clks_init_on[i]]);
if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY1_GATE]);
clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY2_GATE]);

查看文件

@@ -104,10 +104,6 @@ static struct clk_onecell_data clk_data;
static void __iomem *ccm_base;
static void __iomem *anatop_base;
static const u32 clks_init_on[] __initconst = {
IMX6SL_CLK_IPG, IMX6SL_CLK_ARM, IMX6SL_CLK_MMDC_ROOT,
};
/*
* ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken
* during WAIT mode entry process could cause cache memory
@@ -195,7 +191,6 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
void __iomem *base;
int i;
int ret;
clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
@@ -426,13 +421,6 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
pr_warn("%s: failed to set AHB clock rate %d!\n",
__func__, ret);
/*
* Make sure those always on clocks are enabled to maintain the correct
* usecount and enabling/disabling of parent PLLs.
*/
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]);
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]);

查看文件

@@ -92,14 +92,6 @@ static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
static struct clk *clks[IMX6SX_CLK_CLK_END];
static struct clk_onecell_data clk_data;
static int const clks_init_on[] __initconst = {
IMX6SX_CLK_AIPS_TZ1, IMX6SX_CLK_AIPS_TZ2, IMX6SX_CLK_AIPS_TZ3,
IMX6SX_CLK_IPMUX1, IMX6SX_CLK_IPMUX2, IMX6SX_CLK_IPMUX3,
IMX6SX_CLK_WAKEUP, IMX6SX_CLK_MMDC_P0_FAST, IMX6SX_CLK_MMDC_P0_IPG,
IMX6SX_CLK_ROM, IMX6SX_CLK_ARM, IMX6SX_CLK_IPG, IMX6SX_CLK_OCRAM,
IMX6SX_CLK_PER2_MAIN, IMX6SX_CLK_PERCLK, IMX6SX_CLK_TZASC1,
};
static const struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
{ .val = 1, .div = 10, },
@@ -142,7 +134,6 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
void __iomem *base;
int i;
clks[IMX6SX_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
@@ -332,7 +323,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_QSPI1_PODF] = imx_clk_divider("qspi1_podf", "qspi1_sel", base + 0x1c, 26, 3);
clks[IMX6SX_CLK_EIM_SLOW_PODF] = imx_clk_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3);
clks[IMX6SX_CLK_LCDIF2_PODF] = imx_clk_divider("lcdif2_podf", "lcdif2_pred", base + 0x1c, 20, 3);
clks[IMX6SX_CLK_PERCLK] = imx_clk_divider("perclk", "perclk_sel", base + 0x1c, 0, 6);
clks[IMX6SX_CLK_PERCLK] = imx_clk_divider_flags("perclk", "perclk_sel", base + 0x1c, 0, 6, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_VID_PODF] = imx_clk_divider("vid_podf", "vid_sel", base + 0x20, 24, 2);
clks[IMX6SX_CLK_CAN_PODF] = imx_clk_divider("can_podf", "can_sel", base + 0x20, 2, 6);
clks[IMX6SX_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3);
@@ -380,8 +371,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
/* name parent_name reg shift */
/* CCGR0 */
clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2("aips_tz1", "ahb", base + 0x68, 0);
clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2("aips_tz2", "ahb", base + 0x68, 2);
clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_APBH_DMA] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4);
clks[IMX6SX_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6SX_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc);
@@ -394,7 +385,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_podf", base + 0x68, 20);
clks[IMX6SX_CLK_DCIC1] = imx_clk_gate2("dcic1", "display_podf", base + 0x68, 24);
clks[IMX6SX_CLK_DCIC2] = imx_clk_gate2("dcic2", "display_podf", base + 0x68, 26);
clks[IMX6SX_CLK_AIPS_TZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30);
clks[IMX6SX_CLK_AIPS_TZ3] = imx_clk_gate2_flags("aips_tz3", "ahb", base + 0x68, 30, CLK_IS_CRITICAL);
/* CCGR1 */
clks[IMX6SX_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0);
@@ -407,7 +398,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai);
clks[IMX6SX_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai);
clks[IMX6SX_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai);
clks[IMX6SX_CLK_WAKEUP] = imx_clk_gate2("wakeup", "ipg", base + 0x6c, 18);
clks[IMX6SX_CLK_WAKEUP] = imx_clk_gate2_flags("wakeup", "ipg", base + 0x6c, 18, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_GPT_BUS] = imx_clk_gate2("gpt_bus", "perclk", base + 0x6c, 20);
clks[IMX6SX_CLK_GPT_SERIAL] = imx_clk_gate2("gpt_serial", "perclk", base + 0x6c, 22);
clks[IMX6SX_CLK_GPU] = imx_clk_gate2("gpu", "gpu_core_podf", base + 0x6c, 26);
@@ -420,10 +411,10 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10);
clks[IMX6SX_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12);
clks[IMX6SX_CLK_IOMUXC] = imx_clk_gate2("iomuxc", "lcdif1_podf", base + 0x70, 14);
clks[IMX6SX_CLK_IPMUX1] = imx_clk_gate2("ipmux1", "ahb", base + 0x70, 16);
clks[IMX6SX_CLK_IPMUX2] = imx_clk_gate2("ipmux2", "ahb", base + 0x70, 18);
clks[IMX6SX_CLK_IPMUX3] = imx_clk_gate2("ipmux3", "ahb", base + 0x70, 20);
clks[IMX6SX_CLK_TZASC1] = imx_clk_gate2("tzasc1", "mmdc_podf", base + 0x70, 22);
clks[IMX6SX_CLK_IPMUX1] = imx_clk_gate2_flags("ipmux1", "ahb", base + 0x70, 16, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_IPMUX2] = imx_clk_gate2_flags("ipmux2", "ahb", base + 0x70, 18, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_IPMUX3] = imx_clk_gate2_flags("ipmux3", "ahb", base + 0x70, 20, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_TZASC1] = imx_clk_gate2_flags("tzasc1", "mmdc_podf", base + 0x70, 22, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "display_podf", base + 0x70, 28);
clks[IMX6SX_CLK_PXP_AXI] = imx_clk_gate2("pxp_axi", "display_podf", base + 0x70, 30);
@@ -437,15 +428,15 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_LDB_DI0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12);
clks[IMX6SX_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_podf", base + 0x74, 14);
clks[IMX6SX_CLK_MLB] = imx_clk_gate2("mlb", "ahb", base + 0x74, 18);
clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20);
clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2("mmdc_p0_ipg", "ipg", base + 0x74, 24);
clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28);
clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2_flags("ocram", "ocram_podf", base + 0x74, 28, CLK_IS_CRITICAL);
/* CCGR4 */
clks[IMX6SX_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "display_podf", base + 0x78, 0);
clks[IMX6SX_CLK_QSPI2] = imx_clk_gate2("qspi2", "qspi2_podf", base + 0x78, 10);
clks[IMX6SX_CLK_PER1_BCH] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12);
clks[IMX6SX_CLK_PER2_MAIN] = imx_clk_gate2("per2_main", "ahb", base + 0x78, 14);
clks[IMX6SX_CLK_PER2_MAIN] = imx_clk_gate2_flags("per2_main", "ahb", base + 0x78, 14, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16);
clks[IMX6SX_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18);
clks[IMX6SX_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20);
@@ -456,7 +447,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30);
/* CCGR5 */
clks[IMX6SX_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0);
clks[IMX6SX_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL);
clks[IMX6SX_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6);
clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio);
@@ -502,9 +493,6 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
clk_prepare_enable(clks[IMX6SX_CLK_USBPHY1_GATE]);
clk_prepare_enable(clks[IMX6SX_CLK_USBPHY2_GATE]);

查看文件

@@ -797,6 +797,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0);
clks[IMX7D_SNVS_CLK] = imx_clk_gate4("snvs_clk", "ipg_root_clk", base + 0x4250, 0);
clks[IMX7D_MU_ROOT_CLK] = imx_clk_gate4("mu_root_clk", "ipg_root_clk", base + 0x4270, 0);
clks[IMX7D_CAAM_CLK] = imx_clk_gate4("caam_clk", "ipg_root_clk", base + 0x4240, 0);
clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4690, 0);
clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);

查看文件

@@ -1,13 +1,19 @@
config COMMON_CLK_AMLOGIC
bool
depends on OF
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
config COMMON_CLK_AMLOGIC_AUDIO
bool
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_AMLOGIC
config COMMON_CLK_MESON_AO
bool
depends on OF
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON
select RESET_CONTROLLER
config COMMON_CLK_REGMAP_MESON
bool
@@ -15,9 +21,8 @@ config COMMON_CLK_REGMAP_MESON
config COMMON_CLK_MESON8B
bool
depends on COMMON_CLK_AMLOGIC
select COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
select COMMON_CLK_REGMAP_MESON
help
Support for the clock controller on AmLogic S802 (Meson8),
S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
@@ -25,10 +30,8 @@ config COMMON_CLK_MESON8B
config COMMON_CLK_GXBB
bool
depends on COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
select COMMON_CLK_AMLOGIC
select COMMON_CLK_MESON_AO
select COMMON_CLK_REGMAP_MESON
select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
@@ -36,11 +39,18 @@ config COMMON_CLK_GXBB
config COMMON_CLK_AXG
bool
depends on COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
select COMMON_CLK_AMLOGIC
select COMMON_CLK_MESON_AO
select COMMON_CLK_REGMAP_MESON
select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
Say Y if you want peripherals and CPU frequency scaling to work.
config COMMON_CLK_AXG_AUDIO
tristate "Meson AXG Audio Clock Controller Driver"
depends on COMMON_CLK_AXG
select COMMON_CLK_AMLOGIC_AUDIO
select MFD_SYSCON
help
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.

查看文件

@@ -2,9 +2,11 @@
# Makefile for Meson specific clk
#
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o

查看文件

@@ -0,0 +1,845 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include "clkc-audio.h"
#include "axg-audio.h"
#define AXG_MST_IN_COUNT 8
#define AXG_SLV_SCLK_COUNT 10
#define AXG_SLV_LRCLK_COUNT 10
#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags) \
struct clk_regmap axg_##_name = { \
.data = &(struct clk_regmap_gate_data){ \
.offset = (_reg), \
.bit_idx = (_bit), \
}, \
.hw.init = &(struct clk_init_data) { \
.name = "axg_"#_name, \
.ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ _pname }, \
.num_parents = 1, \
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
}, \
}
#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \
struct clk_regmap axg_##_name = { \
.data = &(struct clk_regmap_mux_data){ \
.offset = (_reg), \
.mask = (_mask), \
.shift = (_shift), \
.flags = (_dflags), \
}, \
.hw.init = &(struct clk_init_data){ \
.name = "axg_"#_name, \
.ops = &clk_regmap_mux_ops, \
.parent_names = (_pnames), \
.num_parents = ARRAY_SIZE(_pnames), \
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
}, \
}
#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \
struct clk_regmap axg_##_name = { \
.data = &(struct clk_regmap_div_data){ \
.offset = (_reg), \
.shift = (_shift), \
.width = (_width), \
.flags = (_dflags), \
}, \
.hw.init = &(struct clk_init_data){ \
.name = "axg_"#_name, \
.ops = &clk_regmap_divider_ops, \
.parent_names = (const char *[]) { _pname }, \
.num_parents = 1, \
.flags = (_iflags), \
}, \
}
#define AXG_PCLK_GATE(_name, _bit) \
AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
/* Audio peripheral clocks */
static AXG_PCLK_GATE(ddr_arb, 0);
static AXG_PCLK_GATE(pdm, 1);
static AXG_PCLK_GATE(tdmin_a, 2);
static AXG_PCLK_GATE(tdmin_b, 3);
static AXG_PCLK_GATE(tdmin_c, 4);
static AXG_PCLK_GATE(tdmin_lb, 5);
static AXG_PCLK_GATE(tdmout_a, 6);
static AXG_PCLK_GATE(tdmout_b, 7);
static AXG_PCLK_GATE(tdmout_c, 8);
static AXG_PCLK_GATE(frddr_a, 9);
static AXG_PCLK_GATE(frddr_b, 10);
static AXG_PCLK_GATE(frddr_c, 11);
static AXG_PCLK_GATE(toddr_a, 12);
static AXG_PCLK_GATE(toddr_b, 13);
static AXG_PCLK_GATE(toddr_c, 14);
static AXG_PCLK_GATE(loopback, 15);
static AXG_PCLK_GATE(spdifin, 16);
static AXG_PCLK_GATE(spdifout, 17);
static AXG_PCLK_GATE(resample, 18);
static AXG_PCLK_GATE(power_detect, 19);
/* Audio Master Clocks */
static const char * const mst_mux_parent_names[] = {
"axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3",
"axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7",
};
#define AXG_MST_MCLK_MUX(_name, _reg) \
AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \
mst_mux_parent_names, CLK_SET_RATE_PARENT)
static AXG_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL);
static AXG_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL);
static AXG_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL);
static AXG_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL);
static AXG_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL);
static AXG_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL);
static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
static AXG_MST_MCLK_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
static AXG_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
static AXG_MST_MCLK_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
#define AXG_MST_MCLK_DIV(_name, _reg) \
AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \
"axg_"#_name"_sel", CLK_SET_RATE_PARENT) \
static AXG_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL);
static AXG_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL);
static AXG_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL);
static AXG_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL);
static AXG_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL);
static AXG_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL);
static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
static AXG_MST_MCLK_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
static AXG_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
static AXG_MST_MCLK_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
#define AXG_MST_MCLK_GATE(_name, _reg) \
AXG_AUD_GATE(_name, _reg, 31, "axg_"#_name"_div", \
CLK_SET_RATE_PARENT)
static AXG_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL);
static AXG_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL);
static AXG_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL);
static AXG_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL);
static AXG_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL);
static AXG_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL);
static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
static AXG_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
static AXG_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
static AXG_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
/* Sample Clocks */
#define AXG_MST_SCLK_PRE_EN(_name, _reg) \
AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \
"axg_mst_"#_name"_mclk", 0)
static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0);
static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0);
static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0);
static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0);
static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0);
static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0);
#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
_hi_shift, _hi_width, _pname, _iflags) \
struct clk_regmap axg_##_name = { \
.data = &(struct meson_sclk_div_data) { \
.div = { \
.reg_off = (_reg), \
.shift = (_div_shift), \
.width = (_div_width), \
}, \
.hi = { \
.reg_off = (_reg), \
.shift = (_hi_shift), \
.width = (_hi_width), \
}, \
}, \
.hw.init = &(struct clk_init_data) { \
.name = "axg_"#_name, \
.ops = &meson_sclk_div_ops, \
.parent_names = (const char *[]) { _pname }, \
.num_parents = 1, \
.flags = (_iflags), \
}, \
}
#define AXG_MST_SCLK_DIV(_name, _reg) \
AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \
"axg_mst_"#_name"_sclk_pre_en", \
CLK_SET_RATE_PARENT)
static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
#define AXG_MST_SCLK_POST_EN(_name, _reg) \
AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \
"axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT)
static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0);
static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0);
static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0);
static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0);
static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0);
static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0);
#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
_pname, _iflags) \
struct clk_regmap axg_##_name = { \
.data = &(struct meson_clk_triphase_data) { \
.ph0 = { \
.reg_off = (_reg), \
.shift = (_shift0), \
.width = (_width), \
}, \
.ph1 = { \
.reg_off = (_reg), \
.shift = (_shift1), \
.width = (_width), \
}, \
.ph2 = { \
.reg_off = (_reg), \
.shift = (_shift2), \
.width = (_width), \
}, \
}, \
.hw.init = &(struct clk_init_data) { \
.name = "axg_"#_name, \
.ops = &meson_clk_triphase_ops, \
.parent_names = (const char *[]) { _pname }, \
.num_parents = 1, \
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
}, \
}
#define AXG_MST_SCLK(_name, _reg) \
AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \
"axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT)
static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1);
static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1);
static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1);
static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1);
static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1);
static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1);
#define AXG_MST_LRCLK_DIV(_name, _reg) \
AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \
"axg_mst_"#_name"_sclk_post_en", 0) \
static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
#define AXG_MST_LRCLK(_name, _reg) \
AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \
"axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT)
static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1);
static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1);
static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1);
static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1);
static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1);
static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1);
static const char * const tdm_sclk_parent_names[] = {
"axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk",
"axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk",
"axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2",
"axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5",
"axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8",
"axg_slv_sclk9"
};
#define AXG_TDM_SCLK_MUX(_name, _reg) \
AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \
CLK_MUX_ROUND_CLOSEST, \
tdm_sclk_parent_names, 0)
static AXG_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL);
static AXG_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL);
static AXG_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL);
static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
#define AXG_TDM_SCLK_PRE_EN(_name, _reg) \
AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \
"axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT)
static AXG_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
static AXG_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
static AXG_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
#define AXG_TDM_SCLK_POST_EN(_name, _reg) \
AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \
"axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT)
static AXG_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
static AXG_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
static AXG_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
#define AXG_TDM_SCLK(_name, _reg) \
struct clk_regmap axg_tdm##_name##_sclk = { \
.data = &(struct meson_clk_phase_data) { \
.ph = { \
.reg_off = (_reg), \
.shift = 29, \
.width = 1, \
}, \
}, \
.hw.init = &(struct clk_init_data) { \
.name = "axg_tdm"#_name"_sclk", \
.ops = &meson_clk_phase_ops, \
.parent_names = (const char *[]) \
{ "axg_tdm"#_name"_sclk_post_en" }, \
.num_parents = 1, \
.flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \
}, \
}
static AXG_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
static AXG_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
static AXG_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
static const char * const tdm_lrclk_parent_names[] = {
"axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk",
"axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk",
"axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2",
"axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5",
"axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8",
"axg_slv_lrclk9"
};
#define AXG_TDM_LRLCK(_name, _reg) \
AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \
CLK_MUX_ROUND_CLOSEST, \
tdm_lrclk_parent_names, 0)
static AXG_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
static AXG_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
static AXG_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
/*
* Array of all clocks provided by this provider
* The input clocks of the controller will be populated at runtime
*/
static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
.hws = {
[AUD_CLKID_DDR_ARB] = &axg_ddr_arb.hw,
[AUD_CLKID_PDM] = &axg_pdm.hw,
[AUD_CLKID_TDMIN_A] = &axg_tdmin_a.hw,
[AUD_CLKID_TDMIN_B] = &axg_tdmin_b.hw,
[AUD_CLKID_TDMIN_C] = &axg_tdmin_c.hw,
[AUD_CLKID_TDMIN_LB] = &axg_tdmin_lb.hw,
[AUD_CLKID_TDMOUT_A] = &axg_tdmout_a.hw,
[AUD_CLKID_TDMOUT_B] = &axg_tdmout_b.hw,
[AUD_CLKID_TDMOUT_C] = &axg_tdmout_c.hw,
[AUD_CLKID_FRDDR_A] = &axg_frddr_a.hw,
[AUD_CLKID_FRDDR_B] = &axg_frddr_b.hw,
[AUD_CLKID_FRDDR_C] = &axg_frddr_c.hw,
[AUD_CLKID_TODDR_A] = &axg_toddr_a.hw,
[AUD_CLKID_TODDR_B] = &axg_toddr_b.hw,
[AUD_CLKID_TODDR_C] = &axg_toddr_c.hw,
[AUD_CLKID_LOOPBACK] = &axg_loopback.hw,
[AUD_CLKID_SPDIFIN] = &axg_spdifin.hw,
[AUD_CLKID_SPDIFOUT] = &axg_spdifout.hw,
[AUD_CLKID_RESAMPLE] = &axg_resample.hw,
[AUD_CLKID_POWER_DETECT] = &axg_power_detect.hw,
[AUD_CLKID_MST_A_MCLK_SEL] = &axg_mst_a_mclk_sel.hw,
[AUD_CLKID_MST_B_MCLK_SEL] = &axg_mst_b_mclk_sel.hw,
[AUD_CLKID_MST_C_MCLK_SEL] = &axg_mst_c_mclk_sel.hw,
[AUD_CLKID_MST_D_MCLK_SEL] = &axg_mst_d_mclk_sel.hw,
[AUD_CLKID_MST_E_MCLK_SEL] = &axg_mst_e_mclk_sel.hw,
[AUD_CLKID_MST_F_MCLK_SEL] = &axg_mst_f_mclk_sel.hw,
[AUD_CLKID_MST_A_MCLK_DIV] = &axg_mst_a_mclk_div.hw,
[AUD_CLKID_MST_B_MCLK_DIV] = &axg_mst_b_mclk_div.hw,
[AUD_CLKID_MST_C_MCLK_DIV] = &axg_mst_c_mclk_div.hw,
[AUD_CLKID_MST_D_MCLK_DIV] = &axg_mst_d_mclk_div.hw,
[AUD_CLKID_MST_E_MCLK_DIV] = &axg_mst_e_mclk_div.hw,
[AUD_CLKID_MST_F_MCLK_DIV] = &axg_mst_f_mclk_div.hw,
[AUD_CLKID_MST_A_MCLK] = &axg_mst_a_mclk.hw,
[AUD_CLKID_MST_B_MCLK] = &axg_mst_b_mclk.hw,
[AUD_CLKID_MST_C_MCLK] = &axg_mst_c_mclk.hw,
[AUD_CLKID_MST_D_MCLK] = &axg_mst_d_mclk.hw,
[AUD_CLKID_MST_E_MCLK] = &axg_mst_e_mclk.hw,
[AUD_CLKID_MST_F_MCLK] = &axg_mst_f_mclk.hw,
[AUD_CLKID_SPDIFOUT_CLK_SEL] = &axg_spdifout_clk_sel.hw,
[AUD_CLKID_SPDIFOUT_CLK_DIV] = &axg_spdifout_clk_div.hw,
[AUD_CLKID_SPDIFOUT_CLK] = &axg_spdifout_clk.hw,
[AUD_CLKID_SPDIFIN_CLK_SEL] = &axg_spdifin_clk_sel.hw,
[AUD_CLKID_SPDIFIN_CLK_DIV] = &axg_spdifin_clk_div.hw,
[AUD_CLKID_SPDIFIN_CLK] = &axg_spdifin_clk.hw,
[AUD_CLKID_PDM_DCLK_SEL] = &axg_pdm_dclk_sel.hw,
[AUD_CLKID_PDM_DCLK_DIV] = &axg_pdm_dclk_div.hw,
[AUD_CLKID_PDM_DCLK] = &axg_pdm_dclk.hw,
[AUD_CLKID_PDM_SYSCLK_SEL] = &axg_pdm_sysclk_sel.hw,
[AUD_CLKID_PDM_SYSCLK_DIV] = &axg_pdm_sysclk_div.hw,
[AUD_CLKID_PDM_SYSCLK] = &axg_pdm_sysclk.hw,
[AUD_CLKID_MST_A_SCLK_PRE_EN] = &axg_mst_a_sclk_pre_en.hw,
[AUD_CLKID_MST_B_SCLK_PRE_EN] = &axg_mst_b_sclk_pre_en.hw,
[AUD_CLKID_MST_C_SCLK_PRE_EN] = &axg_mst_c_sclk_pre_en.hw,
[AUD_CLKID_MST_D_SCLK_PRE_EN] = &axg_mst_d_sclk_pre_en.hw,
[AUD_CLKID_MST_E_SCLK_PRE_EN] = &axg_mst_e_sclk_pre_en.hw,
[AUD_CLKID_MST_F_SCLK_PRE_EN] = &axg_mst_f_sclk_pre_en.hw,
[AUD_CLKID_MST_A_SCLK_DIV] = &axg_mst_a_sclk_div.hw,
[AUD_CLKID_MST_B_SCLK_DIV] = &axg_mst_b_sclk_div.hw,
[AUD_CLKID_MST_C_SCLK_DIV] = &axg_mst_c_sclk_div.hw,
[AUD_CLKID_MST_D_SCLK_DIV] = &axg_mst_d_sclk_div.hw,
[AUD_CLKID_MST_E_SCLK_DIV] = &axg_mst_e_sclk_div.hw,
[AUD_CLKID_MST_F_SCLK_DIV] = &axg_mst_f_sclk_div.hw,
[AUD_CLKID_MST_A_SCLK_POST_EN] = &axg_mst_a_sclk_post_en.hw,
[AUD_CLKID_MST_B_SCLK_POST_EN] = &axg_mst_b_sclk_post_en.hw,
[AUD_CLKID_MST_C_SCLK_POST_EN] = &axg_mst_c_sclk_post_en.hw,
[AUD_CLKID_MST_D_SCLK_POST_EN] = &axg_mst_d_sclk_post_en.hw,
[AUD_CLKID_MST_E_SCLK_POST_EN] = &axg_mst_e_sclk_post_en.hw,
[AUD_CLKID_MST_F_SCLK_POST_EN] = &axg_mst_f_sclk_post_en.hw,
[AUD_CLKID_MST_A_SCLK] = &axg_mst_a_sclk.hw,
[AUD_CLKID_MST_B_SCLK] = &axg_mst_b_sclk.hw,
[AUD_CLKID_MST_C_SCLK] = &axg_mst_c_sclk.hw,
[AUD_CLKID_MST_D_SCLK] = &axg_mst_d_sclk.hw,
[AUD_CLKID_MST_E_SCLK] = &axg_mst_e_sclk.hw,
[AUD_CLKID_MST_F_SCLK] = &axg_mst_f_sclk.hw,
[AUD_CLKID_MST_A_LRCLK_DIV] = &axg_mst_a_lrclk_div.hw,
[AUD_CLKID_MST_B_LRCLK_DIV] = &axg_mst_b_lrclk_div.hw,
[AUD_CLKID_MST_C_LRCLK_DIV] = &axg_mst_c_lrclk_div.hw,
[AUD_CLKID_MST_D_LRCLK_DIV] = &axg_mst_d_lrclk_div.hw,
[AUD_CLKID_MST_E_LRCLK_DIV] = &axg_mst_e_lrclk_div.hw,
[AUD_CLKID_MST_F_LRCLK_DIV] = &axg_mst_f_lrclk_div.hw,
[AUD_CLKID_MST_A_LRCLK] = &axg_mst_a_lrclk.hw,
[AUD_CLKID_MST_B_LRCLK] = &axg_mst_b_lrclk.hw,
[AUD_CLKID_MST_C_LRCLK] = &axg_mst_c_lrclk.hw,
[AUD_CLKID_MST_D_LRCLK] = &axg_mst_d_lrclk.hw,
[AUD_CLKID_MST_E_LRCLK] = &axg_mst_e_lrclk.hw,
[AUD_CLKID_MST_F_LRCLK] = &axg_mst_f_lrclk.hw,
[AUD_CLKID_TDMIN_A_SCLK_SEL] = &axg_tdmin_a_sclk_sel.hw,
[AUD_CLKID_TDMIN_B_SCLK_SEL] = &axg_tdmin_b_sclk_sel.hw,
[AUD_CLKID_TDMIN_C_SCLK_SEL] = &axg_tdmin_c_sclk_sel.hw,
[AUD_CLKID_TDMIN_LB_SCLK_SEL] = &axg_tdmin_lb_sclk_sel.hw,
[AUD_CLKID_TDMOUT_A_SCLK_SEL] = &axg_tdmout_a_sclk_sel.hw,
[AUD_CLKID_TDMOUT_B_SCLK_SEL] = &axg_tdmout_b_sclk_sel.hw,
[AUD_CLKID_TDMOUT_C_SCLK_SEL] = &axg_tdmout_c_sclk_sel.hw,
[AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &axg_tdmin_a_sclk_pre_en.hw,
[AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &axg_tdmin_b_sclk_pre_en.hw,
[AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &axg_tdmin_c_sclk_pre_en.hw,
[AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw,
[AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw,
[AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw,
[AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw,
[AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw,
[AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw,
[AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw,
[AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw,
[AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw,
[AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw,
[AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw,
[AUD_CLKID_TDMIN_A_SCLK] = &axg_tdmin_a_sclk.hw,
[AUD_CLKID_TDMIN_B_SCLK] = &axg_tdmin_b_sclk.hw,
[AUD_CLKID_TDMIN_C_SCLK] = &axg_tdmin_c_sclk.hw,
[AUD_CLKID_TDMIN_LB_SCLK] = &axg_tdmin_lb_sclk.hw,
[AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
[AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
[AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
[AUD_CLKID_TDMIN_A_LRCLK] = &axg_tdmin_a_lrclk.hw,
[AUD_CLKID_TDMIN_B_LRCLK] = &axg_tdmin_b_lrclk.hw,
[AUD_CLKID_TDMIN_C_LRCLK] = &axg_tdmin_c_lrclk.hw,
[AUD_CLKID_TDMIN_LB_LRCLK] = &axg_tdmin_lb_lrclk.hw,
[AUD_CLKID_TDMOUT_A_LRCLK] = &axg_tdmout_a_lrclk.hw,
[AUD_CLKID_TDMOUT_B_LRCLK] = &axg_tdmout_b_lrclk.hw,
[AUD_CLKID_TDMOUT_C_LRCLK] = &axg_tdmout_c_lrclk.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
};
/* Convenience table to populate regmap in .probe() */
static struct clk_regmap *const axg_audio_clk_regmaps[] = {
&axg_ddr_arb,
&axg_pdm,
&axg_tdmin_a,
&axg_tdmin_b,
&axg_tdmin_c,
&axg_tdmin_lb,
&axg_tdmout_a,
&axg_tdmout_b,
&axg_tdmout_c,
&axg_frddr_a,
&axg_frddr_b,
&axg_frddr_c,
&axg_toddr_a,
&axg_toddr_b,
&axg_toddr_c,
&axg_loopback,
&axg_spdifin,
&axg_spdifout,
&axg_resample,
&axg_power_detect,
&axg_mst_a_mclk_sel,
&axg_mst_b_mclk_sel,
&axg_mst_c_mclk_sel,
&axg_mst_d_mclk_sel,
&axg_mst_e_mclk_sel,
&axg_mst_f_mclk_sel,
&axg_mst_a_mclk_div,
&axg_mst_b_mclk_div,
&axg_mst_c_mclk_div,
&axg_mst_d_mclk_div,
&axg_mst_e_mclk_div,
&axg_mst_f_mclk_div,
&axg_mst_a_mclk,
&axg_mst_b_mclk,
&axg_mst_c_mclk,
&axg_mst_d_mclk,
&axg_mst_e_mclk,
&axg_mst_f_mclk,
&axg_spdifout_clk_sel,
&axg_spdifout_clk_div,
&axg_spdifout_clk,
&axg_spdifin_clk_sel,
&axg_spdifin_clk_div,
&axg_spdifin_clk,
&axg_pdm_dclk_sel,
&axg_pdm_dclk_div,
&axg_pdm_dclk,
&axg_pdm_sysclk_sel,
&axg_pdm_sysclk_div,
&axg_pdm_sysclk,
&axg_mst_a_sclk_pre_en,
&axg_mst_b_sclk_pre_en,
&axg_mst_c_sclk_pre_en,
&axg_mst_d_sclk_pre_en,
&axg_mst_e_sclk_pre_en,
&axg_mst_f_sclk_pre_en,
&axg_mst_a_sclk_div,
&axg_mst_b_sclk_div,
&axg_mst_c_sclk_div,
&axg_mst_d_sclk_div,
&axg_mst_e_sclk_div,
&axg_mst_f_sclk_div,
&axg_mst_a_sclk_post_en,
&axg_mst_b_sclk_post_en,
&axg_mst_c_sclk_post_en,
&axg_mst_d_sclk_post_en,
&axg_mst_e_sclk_post_en,
&axg_mst_f_sclk_post_en,
&axg_mst_a_sclk,
&axg_mst_b_sclk,
&axg_mst_c_sclk,
&axg_mst_d_sclk,
&axg_mst_e_sclk,
&axg_mst_f_sclk,
&axg_mst_a_lrclk_div,
&axg_mst_b_lrclk_div,
&axg_mst_c_lrclk_div,
&axg_mst_d_lrclk_div,
&axg_mst_e_lrclk_div,
&axg_mst_f_lrclk_div,
&axg_mst_a_lrclk,
&axg_mst_b_lrclk,
&axg_mst_c_lrclk,
&axg_mst_d_lrclk,
&axg_mst_e_lrclk,
&axg_mst_f_lrclk,
&axg_tdmin_a_sclk_sel,
&axg_tdmin_b_sclk_sel,
&axg_tdmin_c_sclk_sel,
&axg_tdmin_lb_sclk_sel,
&axg_tdmout_a_sclk_sel,
&axg_tdmout_b_sclk_sel,
&axg_tdmout_c_sclk_sel,
&axg_tdmin_a_sclk_pre_en,
&axg_tdmin_b_sclk_pre_en,
&axg_tdmin_c_sclk_pre_en,
&axg_tdmin_lb_sclk_pre_en,
&axg_tdmout_a_sclk_pre_en,
&axg_tdmout_b_sclk_pre_en,
&axg_tdmout_c_sclk_pre_en,
&axg_tdmin_a_sclk_post_en,
&axg_tdmin_b_sclk_post_en,
&axg_tdmin_c_sclk_post_en,
&axg_tdmin_lb_sclk_post_en,
&axg_tdmout_a_sclk_post_en,
&axg_tdmout_b_sclk_post_en,
&axg_tdmout_c_sclk_post_en,
&axg_tdmin_a_sclk,
&axg_tdmin_b_sclk,
&axg_tdmin_c_sclk,
&axg_tdmin_lb_sclk,
&axg_tdmout_a_sclk,
&axg_tdmout_b_sclk,
&axg_tdmout_c_sclk,
&axg_tdmin_a_lrclk,
&axg_tdmin_b_lrclk,
&axg_tdmin_c_lrclk,
&axg_tdmin_lb_lrclk,
&axg_tdmout_a_lrclk,
&axg_tdmout_b_lrclk,
&axg_tdmout_c_lrclk,
};
static struct clk *devm_clk_get_enable(struct device *dev, char *id)
{
struct clk *clk;
int ret;
clk = devm_clk_get(dev, id);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
dev_err(dev, "failed to get %s", id);
return clk;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(dev, "failed to enable %s", id);
return ERR_PTR(ret);
}
ret = devm_add_action_or_reset(dev,
(void(*)(void *))clk_disable_unprepare,
clk);
if (ret) {
dev_err(dev, "failed to add reset action on %s", id);
return ERR_PTR(ret);
}
return clk;
}
static const struct clk_ops axg_clk_no_ops = {};
static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev,
const char *name,
const char *parent_name)
{
struct clk_hw *hw;
struct clk_init_data init;
char *clk_name;
int ret;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return ERR_PTR(-ENOMEM);
clk_name = kasprintf(GFP_KERNEL, "axg_%s", name);
if (!clk_name)
return ERR_PTR(-ENOMEM);
init.name = clk_name;
init.ops = &axg_clk_no_ops;
init.flags = 0;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
hw->init = &init;
ret = devm_clk_hw_register(dev, hw);
kfree(clk_name);
return ret ? ERR_PTR(ret) : hw;
}
static int axg_register_clk_hw_input(struct device *dev,
const char *name,
unsigned int clkid)
{
struct clk *parent_clk = devm_clk_get(dev, name);
struct clk_hw *hw = NULL;
if (IS_ERR(parent_clk)) {
int err = PTR_ERR(parent_clk);
/* It is ok if an input clock is missing */
if (err == -ENOENT) {
dev_dbg(dev, "%s not provided", name);
} else {
if (err != -EPROBE_DEFER)
dev_err(dev, "failed to get %s clock", name);
return err;
}
} else {
hw = axg_clk_hw_register_bypass(dev, name,
__clk_get_name(parent_clk));
}
if (IS_ERR(hw)) {
dev_err(dev, "failed to register %s clock", name);
return PTR_ERR(hw);
}
axg_audio_hw_onecell_data.hws[clkid] = hw;
return 0;
}
static int axg_register_clk_hw_inputs(struct device *dev,
const char *basename,
unsigned int count,
unsigned int clkid)
{
char *name;
int i, ret;
for (i = 0; i < count; i++) {
name = kasprintf(GFP_KERNEL, "%s%d", basename, i);
if (!name)
return -ENOMEM;
ret = axg_register_clk_hw_input(dev, name, clkid + i);
kfree(name);
if (ret)
return ret;
}
return 0;
}
static const struct regmap_config axg_audio_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = AUDIO_CLK_PDMIN_CTRL1,
};
static int axg_audio_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap *map;
struct resource *res;
void __iomem *regs;
struct clk *clk;
struct clk_hw *hw;
int ret, i;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
if (IS_ERR(map)) {
dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
return PTR_ERR(map);
}
/* Get the mandatory peripheral clock */
clk = devm_clk_get_enable(dev, "pclk");
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = device_reset(dev);
if (ret) {
dev_err(dev, "failed to reset device\n");
return ret;
}
/* Register the peripheral input clock */
hw = axg_clk_hw_register_bypass(dev, "audio_pclk",
__clk_get_name(clk));
if (IS_ERR(hw))
return PTR_ERR(hw);
axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw;
/* Register optional input master clocks */
ret = axg_register_clk_hw_inputs(dev, "mst_in",
AXG_MST_IN_COUNT,
AUD_CLKID_MST0);
if (ret)
return ret;
/* Register optional input slave sclks */
ret = axg_register_clk_hw_inputs(dev, "slv_sclk",
AXG_SLV_SCLK_COUNT,
AUD_CLKID_SLV_SCLK0);
if (ret)
return ret;
/* Register optional input slave lrclks */
ret = axg_register_clk_hw_inputs(dev, "slv_lrclk",
AXG_SLV_LRCLK_COUNT,
AUD_CLKID_SLV_LRCLK0);
if (ret)
return ret;
/* Populate regmap for the regmap backed clocks */
for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++)
axg_audio_clk_regmaps[i]->map = map;
/* Take care to skip the registered input clocks */
for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) {
hw = axg_audio_hw_onecell_data.hws[i];
/* array might be sparse */
if (!hw)
continue;
ret = devm_clk_hw_register(dev, hw);
if (ret) {
dev_err(dev, "failed to register clock %s\n",
hw->init->name);
return ret;
}
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
&axg_audio_hw_onecell_data);
}
static const struct of_device_id clkc_match_table[] = {
{ .compatible = "amlogic,axg-audio-clkc" },
{}
};
MODULE_DEVICE_TABLE(of, clkc_match_table);
static struct platform_driver axg_audio_driver = {
.probe = axg_audio_clkc_probe,
.driver = {
.name = "axg-audio-clkc",
.of_match_table = clkc_match_table,
},
};
module_platform_driver(axg_audio_driver);
MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");

查看文件

@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __AXG_AUDIO_CLKC_H
#define __AXG_AUDIO_CLKC_H
/*
* Audio Clock register offsets
*
* Register offsets from the datasheet must be multiplied by 4 before
* to get the right offset
*/
#define AUDIO_CLK_GATE_EN 0x000
#define AUDIO_MCLK_A_CTRL 0x004
#define AUDIO_MCLK_B_CTRL 0x008
#define AUDIO_MCLK_C_CTRL 0x00C
#define AUDIO_MCLK_D_CTRL 0x010
#define AUDIO_MCLK_E_CTRL 0x014
#define AUDIO_MCLK_F_CTRL 0x018
#define AUDIO_MST_A_SCLK_CTRL0 0x040
#define AUDIO_MST_A_SCLK_CTRL1 0x044
#define AUDIO_MST_B_SCLK_CTRL0 0x048
#define AUDIO_MST_B_SCLK_CTRL1 0x04C
#define AUDIO_MST_C_SCLK_CTRL0 0x050
#define AUDIO_MST_C_SCLK_CTRL1 0x054
#define AUDIO_MST_D_SCLK_CTRL0 0x058
#define AUDIO_MST_D_SCLK_CTRL1 0x05C
#define AUDIO_MST_E_SCLK_CTRL0 0x060
#define AUDIO_MST_E_SCLK_CTRL1 0x064
#define AUDIO_MST_F_SCLK_CTRL0 0x068
#define AUDIO_MST_F_SCLK_CTRL1 0x06C
#define AUDIO_CLK_TDMIN_A_CTRL 0x080
#define AUDIO_CLK_TDMIN_B_CTRL 0x084
#define AUDIO_CLK_TDMIN_C_CTRL 0x088
#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
#define AUDIO_CLK_LOCKER_CTRL 0x0A8
#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
/*
* CLKID index values
* These indices are entirely contrived and do not map onto the hardware.
*/
#define AUD_CLKID_PCLK 0
#define AUD_CLKID_MST0 1
#define AUD_CLKID_MST1 2
#define AUD_CLKID_MST2 3
#define AUD_CLKID_MST3 4
#define AUD_CLKID_MST4 5
#define AUD_CLKID_MST5 6
#define AUD_CLKID_MST6 7
#define AUD_CLKID_MST7 8
#define AUD_CLKID_MST_A_MCLK_SEL 59
#define AUD_CLKID_MST_B_MCLK_SEL 60
#define AUD_CLKID_MST_C_MCLK_SEL 61
#define AUD_CLKID_MST_D_MCLK_SEL 62
#define AUD_CLKID_MST_E_MCLK_SEL 63
#define AUD_CLKID_MST_F_MCLK_SEL 64
#define AUD_CLKID_MST_A_MCLK_DIV 65
#define AUD_CLKID_MST_B_MCLK_DIV 66
#define AUD_CLKID_MST_C_MCLK_DIV 67
#define AUD_CLKID_MST_D_MCLK_DIV 68
#define AUD_CLKID_MST_E_MCLK_DIV 69
#define AUD_CLKID_MST_F_MCLK_DIV 70
#define AUD_CLKID_SPDIFOUT_CLK_SEL 71
#define AUD_CLKID_SPDIFOUT_CLK_DIV 72
#define AUD_CLKID_SPDIFIN_CLK_SEL 73
#define AUD_CLKID_SPDIFIN_CLK_DIV 74
#define AUD_CLKID_PDM_DCLK_SEL 75
#define AUD_CLKID_PDM_DCLK_DIV 76
#define AUD_CLKID_PDM_SYSCLK_SEL 77
#define AUD_CLKID_PDM_SYSCLK_DIV 78
#define AUD_CLKID_MST_A_SCLK_PRE_EN 92
#define AUD_CLKID_MST_B_SCLK_PRE_EN 93
#define AUD_CLKID_MST_C_SCLK_PRE_EN 94
#define AUD_CLKID_MST_D_SCLK_PRE_EN 95
#define AUD_CLKID_MST_E_SCLK_PRE_EN 96
#define AUD_CLKID_MST_F_SCLK_PRE_EN 97
#define AUD_CLKID_MST_A_SCLK_DIV 98
#define AUD_CLKID_MST_B_SCLK_DIV 99
#define AUD_CLKID_MST_C_SCLK_DIV 100
#define AUD_CLKID_MST_D_SCLK_DIV 101
#define AUD_CLKID_MST_E_SCLK_DIV 102
#define AUD_CLKID_MST_F_SCLK_DIV 103
#define AUD_CLKID_MST_A_SCLK_POST_EN 104
#define AUD_CLKID_MST_B_SCLK_POST_EN 105
#define AUD_CLKID_MST_C_SCLK_POST_EN 106
#define AUD_CLKID_MST_D_SCLK_POST_EN 107
#define AUD_CLKID_MST_E_SCLK_POST_EN 108
#define AUD_CLKID_MST_F_SCLK_POST_EN 109
#define AUD_CLKID_MST_A_LRCLK_DIV 110
#define AUD_CLKID_MST_B_LRCLK_DIV 111
#define AUD_CLKID_MST_C_LRCLK_DIV 112
#define AUD_CLKID_MST_D_LRCLK_DIV 113
#define AUD_CLKID_MST_E_LRCLK_DIV 114
#define AUD_CLKID_MST_F_LRCLK_DIV 115
#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137
#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138
#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139
#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140
#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141
#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142
#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143
#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144
#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145
#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146
#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147
#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148
#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149
#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150
/* include the CLKIDs which are part of the DT bindings */
#include <dt-bindings/clock/axg-audio-clkc.h>
#define NR_CLKS 151
#endif /*__AXG_AUDIO_CLKC_H */

查看文件

@@ -12,7 +12,6 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
@@ -626,6 +625,137 @@ static struct clk_regmap axg_mpll3 = {
},
};
static const struct pll_rate_table axg_pcie_pll_rate_table[] = {
{
.rate = 100000000,
.m = 200,
.n = 3,
.od = 1,
.od2 = 3,
},
{ /* sentinel */ },
};
static const struct reg_sequence axg_pcie_init_regs[] = {
{ .reg = HHI_PCIE_PLL_CNTL, .def = 0x400106c8 },
{ .reg = HHI_PCIE_PLL_CNTL1, .def = 0x0084a2aa },
{ .reg = HHI_PCIE_PLL_CNTL2, .def = 0xb75020be },
{ .reg = HHI_PCIE_PLL_CNTL3, .def = 0x0a47488e },
{ .reg = HHI_PCIE_PLL_CNTL4, .def = 0xc000004d },
{ .reg = HHI_PCIE_PLL_CNTL5, .def = 0x00078000 },
{ .reg = HHI_PCIE_PLL_CNTL6, .def = 0x002323c6 },
};
static struct clk_regmap axg_pcie_pll = {
.data = &(struct meson_clk_pll_data){
.m = {
.reg_off = HHI_PCIE_PLL_CNTL,
.shift = 0,
.width = 9,
},
.n = {
.reg_off = HHI_PCIE_PLL_CNTL,
.shift = 9,
.width = 5,
},
.od = {
.reg_off = HHI_PCIE_PLL_CNTL,
.shift = 16,
.width = 2,
},
.od2 = {
.reg_off = HHI_PCIE_PLL_CNTL6,
.shift = 6,
.width = 2,
},
.frac = {
.reg_off = HHI_PCIE_PLL_CNTL1,
.shift = 0,
.width = 12,
},
.l = {
.reg_off = HHI_PCIE_PLL_CNTL,
.shift = 31,
.width = 1,
},
.rst = {
.reg_off = HHI_PCIE_PLL_CNTL,
.shift = 29,
.width = 1,
},
.table = axg_pcie_pll_rate_table,
.init_regs = axg_pcie_init_regs,
.init_count = ARRAY_SIZE(axg_pcie_init_regs),
},
.hw.init = &(struct clk_init_data){
.name = "pcie_pll",
.ops = &meson_clk_pll_ops,
.parent_names = (const char *[]){ "xtal" },
.num_parents = 1,
},
};
static struct clk_regmap axg_pcie_mux = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_PCIE_PLL_CNTL6,
.mask = 0x1,
.shift = 2,
},
.hw.init = &(struct clk_init_data){
.name = "pcie_mux",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "mpll3", "pcie_pll" },
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_pcie_ref = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_PCIE_PLL_CNTL6,
.mask = 0x1,
.shift = 1,
/* skip the parent 0, reserved for debug */
.table = (u32[]){ 1 },
},
.hw.init = &(struct clk_init_data){
.name = "pcie_ref",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "pcie_mux" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_pcie_cml_en0 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_PCIE_PLL_CNTL6,
.bit_idx = 4,
},
.hw.init = &(struct clk_init_data) {
.name = "pcie_cml_en0",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "pcie_ref" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_pcie_cml_en1 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_PCIE_PLL_CNTL6,
.bit_idx = 3,
},
.hw.init = &(struct clk_init_data) {
.name = "pcie_cml_en1",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "pcie_ref" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
"xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
@@ -779,6 +909,63 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = {
},
};
static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
9, 10, 11, 13, 14, };
static const char * const gen_clk_parent_names[] = {
"xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3",
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
};
static struct clk_regmap axg_gen_clk_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_GEN_CLK_CNTL,
.mask = 0xf,
.shift = 12,
.table = mux_table_gen_clk,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_sel",
.ops = &clk_regmap_mux_ops,
/*
* bits 15:12 selects from 14 possible parents:
* xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt],
* hifi_pll, mpll0, mpll1, mpll2, mpll3, fdiv4,
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
*/
.parent_names = gen_clk_parent_names,
.num_parents = ARRAY_SIZE(gen_clk_parent_names),
},
};
static struct clk_regmap axg_gen_clk_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_GEN_CLK_CNTL,
.shift = 0,
.width = 11,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_div",
.ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "gen_clk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap axg_gen_clk = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_GEN_CLK_CNTL,
.bit_idx = 7,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "gen_clk_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
/* Everything Else (EE) domain gates */
static MESON_GATE(axg_ddr, HHI_GCLK_MPEG0, 0);
static MESON_GATE(axg_audio_locker, HHI_GCLK_MPEG0, 2);
@@ -821,6 +1008,7 @@ static MESON_GATE(axg_mmc_pclk, HHI_GCLK_MPEG2, 11);
static MESON_GATE(axg_vpu_intr, HHI_GCLK_MPEG2, 25);
static MESON_GATE(axg_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
static MESON_GATE(axg_gic, HHI_GCLK_MPEG2, 30);
static MESON_GATE(axg_mipi_enable, HHI_MIPI_CNTL0, 29);
/* Always On (AO) domain gates */
@@ -910,6 +1098,15 @@ static struct clk_hw_onecell_data axg_hw_onecell_data = {
[CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw,
[CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw,
[CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw,
[CLKID_PCIE_PLL] = &axg_pcie_pll.hw,
[CLKID_PCIE_MUX] = &axg_pcie_mux.hw,
[CLKID_PCIE_REF] = &axg_pcie_ref.hw,
[CLKID_PCIE_CML_EN0] = &axg_pcie_cml_en0.hw,
[CLKID_PCIE_CML_EN1] = &axg_pcie_cml_en1.hw,
[CLKID_MIPI_ENABLE] = &axg_mipi_enable.hw,
[CLKID_GEN_CLK_SEL] = &axg_gen_clk_sel.hw,
[CLKID_GEN_CLK_DIV] = &axg_gen_clk_div.hw,
[CLKID_GEN_CLK] = &axg_gen_clk.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
@@ -988,6 +1185,15 @@ static struct clk_regmap *const axg_clk_regmaps[] = {
&axg_fclk_div4,
&axg_fclk_div5,
&axg_fclk_div7,
&axg_pcie_pll,
&axg_pcie_mux,
&axg_pcie_ref,
&axg_pcie_cml_en0,
&axg_pcie_cml_en1,
&axg_mipi_enable,
&axg_gen_clk_sel,
&axg_gen_clk_div,
&axg_gen_clk,
};
static const struct of_device_id clkc_match_table[] = {
@@ -995,49 +1201,17 @@ static const struct of_device_id clkc_match_table[] = {
{}
};
static const struct regmap_config clkc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int axg_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *clk_base = NULL;
struct regmap *map;
int ret, i;
/* Get the hhi system controller node if available */
map = syscon_node_to_regmap(of_get_parent(dev->of_node));
if (IS_ERR(map)) {
dev_err(dev,
"failed to get HHI regmap - Trying obsolete regs\n");
/*
* FIXME: HHI registers should be accessed through
* the appropriate system controller. This is required because
* there is more than just clocks in this register space
*
* This fallback method is only provided temporarily until
* all the platform DTs are properly using the syscon node
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
clk_base = devm_ioremap(dev, res->start, resource_size(res));
if (!clk_base) {
dev_err(dev, "Unable to map clk base\n");
return -ENXIO;
}
map = devm_regmap_init_mmio(dev, clk_base,
&clkc_regmap_config);
if (IS_ERR(map))
return PTR_ERR(map);
dev_err(dev, "failed to get HHI regmap\n");
return PTR_ERR(map);
}
/* Populate regmap for the regmap backed clocks */

查看文件

@@ -16,6 +16,7 @@
* Register offsets from the data sheet must be multiplied by 4 before
* adding them to the base address to get the right value.
*/
#define HHI_MIPI_CNTL0 0x00
#define HHI_GP0_PLL_CNTL 0x40
#define HHI_GP0_PLL_CNTL2 0x44
#define HHI_GP0_PLL_CNTL3 0x48
@@ -127,8 +128,13 @@
#define CLKID_FCLK_DIV4_DIV 73
#define CLKID_FCLK_DIV5_DIV 74
#define CLKID_FCLK_DIV7_DIV 75
#define CLKID_PCIE_PLL 76
#define CLKID_PCIE_MUX 77
#define CLKID_PCIE_REF 78
#define CLKID_GEN_CLK_SEL 82
#define CLKID_GEN_CLK_DIV 83
#define NR_CLKS 76
#define NR_CLKS 85
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/axg-clkc.h>

查看文件

@@ -1,110 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 AmLogic, Inc.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
/*
* i2s master clock divider: The algorithm of the generic clk-divider used with
* a very precise clock parent such as the mpll tends to select a low divider
* factor. This gives poor results with this particular divider, especially with
* high frequencies (> 100 MHz)
*
* This driver try to select the maximum possible divider with the rate the
* upstream clock can provide.
*/
#include <linux/clk-provider.h>
#include "clkc.h"
static inline struct meson_clk_audio_div_data *
meson_clk_audio_div_data(struct clk_regmap *clk)
{
return (struct meson_clk_audio_div_data *)clk->data;
}
static int _div_round(unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
if (flags & CLK_DIVIDER_ROUND_CLOSEST)
return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, rate);
return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
}
static int _get_val(unsigned long parent_rate, unsigned long rate)
{
return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
}
static int _valid_divider(unsigned int width, int divider)
{
int max_divider = 1 << width;
return clamp(divider, 1, max_divider);
}
static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
unsigned long divider;
divider = meson_parm_read(clk->map, &adiv->div);
return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
}
static long audio_divider_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
unsigned long max_prate;
int divider;
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
divider = _div_round(*parent_rate, rate, adiv->flags);
divider = _valid_divider(adiv->div.width, divider);
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
}
/* Get the maximum parent rate */
max_prate = clk_hw_round_rate(clk_hw_get_parent(hw), ULONG_MAX);
/* Get the corresponding rounded down divider */
divider = max_prate / rate;
divider = _valid_divider(adiv->div.width, divider);
/* Get actual rate of the parent */
*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
divider * rate);
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
}
static int audio_divider_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
int val = _get_val(parent_rate, rate);
meson_parm_write(clk->map, &adiv->div, val);
return 0;
}
const struct clk_ops meson_clk_audio_divider_ro_ops = {
.recalc_rate = audio_divider_recalc_rate,
.round_rate = audio_divider_round_rate,
};
const struct clk_ops meson_clk_audio_divider_ops = {
.recalc_rate = audio_divider_recalc_rate,
.round_rate = audio_divider_round_rate,
.set_rate = audio_divider_set_rate,
};

查看文件

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#include <linux/clk-provider.h>
#include "clkc.h"
#define phase_step(_width) (360 / (1 << (_width)))
static inline struct meson_clk_phase_data *
meson_clk_phase_data(struct clk_regmap *clk)
{
return (struct meson_clk_phase_data *)clk->data;
}
int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
{
return phase_step(width) * val;
}
EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
{
unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
/*
* This last calculation is here for cases when degrees is rounded
* to 360, in which case val == (1 << width).
*/
return val % (1 << width);
}
EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
static int meson_clk_phase_get_phase(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
unsigned int val;
val = meson_parm_read(clk->map, &phase->ph);
return meson_clk_degrees_from_val(val, phase->ph.width);
}
static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
unsigned int val;
val = meson_clk_degrees_to_val(degrees, phase->ph.width);
meson_parm_write(clk->map, &phase->ph, val);
return 0;
}
const struct clk_ops meson_clk_phase_ops = {
.get_phase = meson_clk_phase_get_phase,
.set_phase = meson_clk_phase_set_phase,
};
EXPORT_SYMBOL_GPL(meson_clk_phase_ops);

查看文件

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#include <linux/clk-provider.h>
#include "clkc-audio.h"
/*
* This is a special clock for the audio controller.
* The phase of mst_sclk clock output can be controlled independently
* for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
* Controlling these 3 phases as just one makes things simpler and
* give the same clock view to all the element on the i2s bus.
* If necessary, we can still control the phase in the tdm block
* which makes these independent control redundant.
*/
static inline struct meson_clk_triphase_data *
meson_clk_triphase_data(struct clk_regmap *clk)
{
return (struct meson_clk_triphase_data *)clk->data;
}
static void meson_clk_triphase_sync(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Get phase 0 and sync it to phase 1 and 2 */
val = meson_parm_read(clk->map, &tph->ph0);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
}
static int meson_clk_triphase_get_phase(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
/* Phase are in sync, reading phase 0 is enough */
val = meson_parm_read(clk->map, &tph->ph0);
return meson_clk_degrees_from_val(val, tph->ph0.width);
}
static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
meson_parm_write(clk->map, &tph->ph0, val);
meson_parm_write(clk->map, &tph->ph1, val);
meson_parm_write(clk->map, &tph->ph2, val);
return 0;
}
const struct clk_ops meson_clk_triphase_ops = {
.init = meson_clk_triphase_sync,
.get_phase = meson_clk_triphase_get_phase,
.set_phase = meson_clk_triphase_set_phase,
};
EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);

查看文件

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
#ifndef __MESON_CLKC_AUDIO_H
#define __MESON_CLKC_AUDIO_H
#include "clkc.h"
struct meson_clk_triphase_data {
struct parm ph0;
struct parm ph1;
struct parm ph2;
};
struct meson_sclk_div_data {
struct parm div;
struct parm hi;
unsigned int cached_div;
struct clk_duty cached_duty;
};
extern const struct clk_ops meson_clk_triphase_ops;
extern const struct clk_ops meson_sclk_div_ops;
#endif /* __MESON_CLKC_AUDIO_H */

查看文件

@@ -91,11 +91,13 @@ struct meson_clk_mpll_data {
#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
struct meson_clk_audio_div_data {
struct parm div;
u8 flags;
struct meson_clk_phase_data {
struct parm ph;
};
int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
#define MESON_GATE(_name, _reg, _bit) \
struct clk_regmap _name = { \
.data = &(struct clk_regmap_gate_data){ \
@@ -117,7 +119,6 @@ extern const struct clk_ops meson_clk_pll_ops;
extern const struct clk_ops meson_clk_cpu_ops;
extern const struct clk_ops meson_clk_mpll_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;
extern const struct clk_ops meson_clk_audio_divider_ro_ops;
extern const struct clk_ops meson_clk_audio_divider_ops;
extern const struct clk_ops meson_clk_phase_ops;
#endif /* __CLKC_H */

查看文件

@@ -7,7 +7,6 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
@@ -498,6 +497,7 @@ static struct clk_regmap gxbb_fclk_div2 = {
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "fclk_div2_div" },
.num_parents = 1,
.flags = CLK_IS_CRITICAL,
},
};
@@ -970,28 +970,26 @@ static struct clk_regmap gxbb_cts_amclk_sel = {
.mask = 0x3,
.shift = 9,
.table = (u32[]){ 1, 2, 3 },
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "cts_amclk_sel",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
.num_parents = 3,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap gxbb_cts_amclk_div = {
.data = &(struct meson_clk_audio_div_data){
.div = {
.reg_off = HHI_AUD_CLK_CNTL,
.shift = 0,
.width = 8,
},
.data = &(struct clk_regmap_div_data) {
.offset = HHI_AUD_CLK_CNTL,
.shift = 0,
.width = 8,
.flags = CLK_DIVIDER_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "cts_amclk_div",
.ops = &meson_clk_audio_divider_ops,
.ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "cts_amclk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1018,13 +1016,13 @@ static struct clk_regmap gxbb_cts_mclk_i958_sel = {
.mask = 0x3,
.shift = 25,
.table = (u32[]){ 1, 2, 3 },
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data) {
.name = "cts_mclk_i958_sel",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
.num_parents = 3,
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1626,6 +1624,63 @@ static struct clk_regmap gxbb_vdec_hevc = {
},
};
static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
9, 10, 11, 13, 14, };
static const char * const gen_clk_parent_names[] = {
"xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2",
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
};
static struct clk_regmap gxbb_gen_clk_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_GEN_CLK_CNTL,
.mask = 0xf,
.shift = 12,
.table = mux_table_gen_clk,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_sel",
.ops = &clk_regmap_mux_ops,
/*
* bits 15:12 selects from 14 possible parents:
* xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt],
* vid_pll, vid2_pll (hevc), mpll0, mpll1, mpll2, fdiv4,
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
*/
.parent_names = gen_clk_parent_names,
.num_parents = ARRAY_SIZE(gen_clk_parent_names),
},
};
static struct clk_regmap gxbb_gen_clk_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_GEN_CLK_CNTL,
.shift = 0,
.width = 11,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_div",
.ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "gen_clk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
static struct clk_regmap gxbb_gen_clk = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_GEN_CLK_CNTL,
.bit_idx = 7,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk",
.ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "gen_clk_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
/* Everything Else (EE) domain gates */
static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0);
static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1);
@@ -1875,6 +1930,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
[CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
[CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
[CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
[CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
[CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
[CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
@@ -2037,6 +2095,9 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = {
[CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
[CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
[CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
[CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
[CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
[CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
@@ -2201,6 +2262,9 @@ static struct clk_regmap *const gx_clk_regmaps[] = {
&gxbb_vdec_hevc_sel,
&gxbb_vdec_hevc_div,
&gxbb_vdec_hevc,
&gxbb_gen_clk_sel,
&gxbb_gen_clk_div,
&gxbb_gen_clk,
};
struct clkc_data {
@@ -2227,17 +2291,9 @@ static const struct of_device_id clkc_match_table[] = {
{},
};
static const struct regmap_config clkc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int gxbb_clkc_probe(struct platform_device *pdev)
{
const struct clkc_data *clkc_data;
struct resource *res;
void __iomem *clk_base;
struct regmap *map;
int ret, i;
struct device *dev = &pdev->dev;
@@ -2249,31 +2305,8 @@ static int gxbb_clkc_probe(struct platform_device *pdev)
/* Get the hhi system controller node if available */
map = syscon_node_to_regmap(of_get_parent(dev->of_node));
if (IS_ERR(map)) {
dev_err(dev,
"failed to get HHI regmap - Trying obsolete regs\n");
/*
* FIXME: HHI registers should be accessed through
* the appropriate system controller. This is required because
* there is more than just clocks in this register space
*
* This fallback method is only provided temporarily until
* all the platform DTs are properly using the syscon node
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
clk_base = devm_ioremap(dev, res->start, resource_size(res));
if (!clk_base) {
dev_err(dev, "Unable to map clk base\n");
return -ENXIO;
}
map = devm_regmap_init_mmio(dev, clk_base,
&clkc_regmap_config);
if (IS_ERR(map))
return PTR_ERR(map);
dev_err(dev, "failed to get HHI regmap\n");
return PTR_ERR(map);
}
/* Populate regmap for the common regmap backed clocks */

查看文件

@@ -66,7 +66,6 @@
#define HHI_USB_CLK_CNTL 0x220 /* 0x88 offset in data sheet */
#define HHI_32K_CLK_CNTL 0x224 /* 0x89 offset in data sheet */
#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */
#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */
#define HHI_PCM_CLK_CNTL 0x258 /* 0x96 offset in data sheet */
#define HHI_NAND_CLK_CNTL 0x25C /* 0x97 offset in data sheet */
@@ -158,8 +157,10 @@
#define CLKID_VDEC_1_DIV 152
#define CLKID_VDEC_HEVC_SEL 154
#define CLKID_VDEC_HEVC_DIV 155
#define CLKID_GEN_CLK_SEL 157
#define CLKID_GEN_CLK_DIV 158
#define NR_CLKS 157
#define NR_CLKS 160
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/gxbb-clkc.h>

查看文件

@@ -0,0 +1,243 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*
* Sample clock generator divider:
* This HW divider gates with value 0 but is otherwise a zero based divider:
*
* val >= 1
* divider = val + 1
*
* The duty cycle may also be set for the LR clock variant. The duty cycle
* ratio is:
*
* hi = [0 - val]
* duty_cycle = (1 + hi) / (1 + val)
*/
#include "clkc-audio.h"
static inline struct meson_sclk_div_data *
meson_sclk_div_data(struct clk_regmap *clk)
{
return (struct meson_sclk_div_data *)clk->data;
}
static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
{
return (1 << sclk->div.width) - 1;
}
static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
{
return sclk_div_maxval(sclk) + 1;
}
static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
unsigned long prate, int maxdiv)
{
int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
return clamp(div, 2, maxdiv);
}
static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *prate,
struct meson_sclk_div_data *sclk)
{
struct clk_hw *parent = clk_hw_get_parent(hw);
int bestdiv = 0, i;
unsigned long maxdiv, now, parent_now;
unsigned long best = 0, best_parent = 0;
if (!rate)
rate = 1;
maxdiv = sclk_div_maxdiv(sclk);
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
return sclk_div_getdiv(hw, rate, *prate, maxdiv);
/*
* The maximum divider we can use without overflowing
* unsigned long in rate * i below
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 2; i <= maxdiv; i++) {
/*
* It's the most ideal case if the requested rate can be
* divided from parent clock without needing to change
* parent rate, so return the divider immediately.
*/
if (rate * i == *prate)
return i;
parent_now = clk_hw_round_rate(parent, rate * i);
now = DIV_ROUND_UP_ULL((u64)parent_now, i);
if (abs(rate - now) < abs(rate - best)) {
bestdiv = i;
best = now;
best_parent = parent_now;
}
}
if (!bestdiv)
bestdiv = sclk_div_maxdiv(sclk);
else
*prate = best_parent;
return bestdiv;
}
static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
int div;
div = sclk_div_bestdiv(hw, rate, prate, sclk);
return DIV_ROUND_UP_ULL((u64)*prate, div);
}
static void sclk_apply_ratio(struct clk_regmap *clk,
struct meson_sclk_div_data *sclk)
{
unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
sclk->cached_duty.num,
sclk->cached_duty.den);
if (hi)
hi -= 1;
meson_parm_write(clk->map, &sclk->hi, hi);
}
static int sclk_div_set_duty_cycle(struct clk_hw *hw,
struct clk_duty *duty)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
if (MESON_PARM_APPLICABLE(&sclk->hi)) {
memcpy(&sclk->cached_duty, duty, sizeof(*duty));
sclk_apply_ratio(clk, sclk);
}
return 0;
}
static int sclk_div_get_duty_cycle(struct clk_hw *hw,
struct clk_duty *duty)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
int hi;
if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
duty->num = 1;
duty->den = 2;
return 0;
}
hi = meson_parm_read(clk->map, &sclk->hi);
duty->num = hi + 1;
duty->den = sclk->cached_div;
return 0;
}
static void sclk_apply_divider(struct clk_regmap *clk,
struct meson_sclk_div_data *sclk)
{
if (MESON_PARM_APPLICABLE(&sclk->hi))
sclk_apply_ratio(clk, sclk);
meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
}
static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
unsigned long maxdiv = sclk_div_maxdiv(sclk);
sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
if (clk_hw_is_enabled(hw))
sclk_apply_divider(clk, sclk);
return 0;
}
static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
unsigned long prate)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
}
static int sclk_div_enable(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
sclk_apply_divider(clk, sclk);
return 0;
}
static void sclk_div_disable(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
meson_parm_write(clk->map, &sclk->div, 0);
}
static int sclk_div_is_enabled(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
if (meson_parm_read(clk->map, &sclk->div))
return 1;
return 0;
}
static void sclk_div_init(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
unsigned int val;
val = meson_parm_read(clk->map, &sclk->div);
/* if the divider is initially disabled, assume max */
if (!val)
sclk->cached_div = sclk_div_maxdiv(sclk);
else
sclk->cached_div = val + 1;
sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
}
const struct clk_ops meson_sclk_div_ops = {
.recalc_rate = sclk_div_recalc_rate,
.round_rate = sclk_div_round_rate,
.set_rate = sclk_div_set_rate,
.enable = sclk_div_enable,
.disable = sclk_div_disable,
.is_enabled = sclk_div_is_enabled,
.get_duty_cycle = sclk_div_get_duty_cycle,
.set_duty_cycle = sclk_div_set_duty_cycle,
.init = sclk_div_init,
};
EXPORT_SYMBOL_GPL(meson_sclk_div_ops);

查看文件

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Marvell Armada 37xx SoC Peripheral clocks
*
@@ -5,10 +6,6 @@
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is"
* without any warranty of any kind, whether express or implied.
*
* Most of the peripheral clocks can be modelled like this:
* _____ _______ _______
* TBG-A-P --| | | | | | ______

查看文件

@@ -6,12 +6,14 @@
obj-y += clk.o
obj-y += clk-pll.o
obj-y += clk-cpu.o
obj-y += clk-half-divider.o
obj-y += clk-inverter.o
obj-y += clk-mmc-phase.o
obj-y += clk-muxgrf.o
obj-y += clk-ddr.o
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
obj-y += clk-px30.o
obj-y += clk-rv1108.o
obj-y += clk-rk3036.o
obj-y += clk-rk3128.o

查看文件

@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include "clk.h"
#define div_mask(width) ((1 << (width)) - 1)
static bool _is_best_half_div(unsigned long rate, unsigned long now,
unsigned long best, unsigned long flags)
{
if (flags & CLK_DIVIDER_ROUND_CLOSEST)
return abs(rate - now) < abs(rate - best);
return now <= rate && now > best;
}
static unsigned long clk_half_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int val;
val = clk_readl(divider->reg) >> divider->shift;
val &= div_mask(divider->width);
val = val * 2 + 3;
return DIV_ROUND_UP_ULL(((u64)parent_rate * 2), val);
}
static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate, u8 width,
unsigned long flags)
{
unsigned int i, bestdiv = 0;
unsigned long parent_rate, best = 0, now, maxdiv;
unsigned long parent_rate_saved = *best_parent_rate;
if (!rate)
rate = 1;
maxdiv = div_mask(width);
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
bestdiv = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
if (bestdiv < 3)
bestdiv = 0;
else
bestdiv = (bestdiv - 3) / 2;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
}
/*
* The maximum divider we can use without overflowing
* unsigned long in rate * i below
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 0; i <= maxdiv; i++) {
if (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) {
/*
* It's the most ideal case if the requested rate can be
* divided from parent clock without needing to change
* parent rate, so return the divider immediately.
*/
*best_parent_rate = parent_rate_saved;
return i;
}
parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
((u64)rate * (i * 2 + 3)) / 2);
now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2),
(i * 2 + 3));
if (_is_best_half_div(rate, now, best, flags)) {
bestdiv = i;
best = now;
*best_parent_rate = parent_rate;
}
}
if (!bestdiv) {
bestdiv = div_mask(width);
*best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1);
}
return bestdiv;
}
static long clk_half_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_divider *divider = to_clk_divider(hw);
int div;
div = clk_half_divider_bestdiv(hw, rate, prate,
divider->width,
divider->flags);
return DIV_ROUND_UP_ULL(((u64)*prate * 2), div * 2 + 3);
}
static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int value;
unsigned long flags = 0;
u32 val;
value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
value = (value - 3) / 2;
value = min_t(unsigned int, value, div_mask(divider->width));
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
else
__acquire(divider->lock);
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = div_mask(divider->width) << (divider->shift + 16);
} else {
val = clk_readl(divider->reg);
val &= ~(div_mask(divider->width) << divider->shift);
}
val |= value << divider->shift;
clk_writel(val, divider->reg);
if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags);
else
__release(divider->lock);
return 0;
}
const struct clk_ops clk_half_divider_ops = {
.recalc_rate = clk_half_divider_recalc_rate,
.round_rate = clk_half_divider_round_rate,
.set_rate = clk_half_divider_set_rate,
};
EXPORT_SYMBOL_GPL(clk_half_divider_ops);
/**
* Register a clock branch.
* Most clock branches have a form like
*
* src1 --|--\
* |M |--[GATE]-[DIV]-
* src2 --|--/
*
* sometimes without one of those components.
*/
struct clk *rockchip_clk_register_halfdiv(const char *name,
const char *const *parent_names,
u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift,
u8 mux_width, u8 mux_flags,
u8 div_shift, u8 div_width,
u8 div_flags, int gate_offset,
u8 gate_shift, u8 gate_flags,
unsigned long flags,
spinlock_t *lock)
{
struct clk *clk;
struct clk_mux *mux = NULL;
struct clk_gate *gate = NULL;
struct clk_divider *div = NULL;
const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
*gate_ops = NULL;
if (num_parents > 1) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->reg = base + muxdiv_offset;
mux->shift = mux_shift;
mux->mask = BIT(mux_width) - 1;
mux->flags = mux_flags;
mux->lock = lock;
mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
: &clk_mux_ops;
}
if (gate_offset >= 0) {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
goto err_gate;
gate->flags = gate_flags;
gate->reg = base + gate_offset;
gate->bit_idx = gate_shift;
gate->lock = lock;
gate_ops = &clk_gate_ops;
}
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
goto err_div;
div->flags = div_flags;
div->reg = base + muxdiv_offset;
div->shift = div_shift;
div->width = div_width;
div->lock = lock;
div_ops = &clk_half_divider_ops;
}
clk = clk_register_composite(NULL, name, parent_names, num_parents,
mux ? &mux->hw : NULL, mux_ops,
div ? &div->hw : NULL, div_ops,
gate ? &gate->hw : NULL, gate_ops,
flags);
return clk;
err_div:
kfree(gate);
err_gate:
kfree(mux);
return ERR_PTR(-ENOMEM);
}

文件差異過大導致無法顯示 Load Diff

查看文件

@@ -631,7 +631,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
MUX(0, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT,
RK3399_CLKSEL_CON(31), 0, 2, MFLAGS),
COMPOSITE_NODIV(SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_p, CLK_SET_RATE_PARENT,
RK3399_CLKSEL_CON(30), 8, 2, MFLAGS,
RK3399_CLKSEL_CON(31), 2, 1, MFLAGS,
RK3399_CLKGATE_CON(8), 12, GFLAGS),
/* uart */
@@ -1523,6 +1523,7 @@ static const char *const rk3399_pmucru_critical_clocks[] __initconst = {
"pclk_pmu_src",
"fclk_cm0s_src_pmu",
"clk_timer_src_pmu",
"pclk_rkpwm_pmu",
};
static void __init rk3399_clk_init(struct device_node *np)

查看文件

@@ -492,6 +492,16 @@ void __init rockchip_clk_register_branches(
list->gate_flags, flags, list->child,
&ctx->lock);
break;
case branch_half_divider:
clk = rockchip_clk_register_halfdiv(list->name,
list->parent_names, list->num_parents,
ctx->reg_base, list->muxdiv_offset,
list->mux_shift, list->mux_width,
list->mux_flags, list->div_shift,
list->div_width, list->div_flags,
list->gate_offset, list->gate_shift,
list->gate_flags, flags, &ctx->lock);
break;
case branch_gate:
flags |= CLK_SET_RATE_PARENT;

查看文件

@@ -34,7 +34,46 @@ struct clk;
#define HIWORD_UPDATE(val, mask, shift) \
((val) << (shift) | (mask) << ((shift) + 16))
/* register positions shared by RV1108, RK2928, RK3036, RK3066, RK3188 and RK3228 */
/* register positions shared by PX30, RV1108, RK2928, RK3036, RK3066, RK3188 and RK3228 */
#define BOOST_PLL_H_CON(x) ((x) * 0x4)
#define BOOST_CLK_CON 0x0008
#define BOOST_BOOST_CON 0x000c
#define BOOST_SWITCH_CNT 0x0010
#define BOOST_HIGH_PERF_CNT0 0x0014
#define BOOST_HIGH_PERF_CNT1 0x0018
#define BOOST_STATIS_THRESHOLD 0x001c
#define BOOST_SHORT_SWITCH_CNT 0x0020
#define BOOST_SWITCH_THRESHOLD 0x0024
#define BOOST_FSM_STATUS 0x0028
#define BOOST_PLL_L_CON(x) ((x) * 0x4 + 0x2c)
#define BOOST_RECOVERY_MASK 0x1
#define BOOST_RECOVERY_SHIFT 1
#define BOOST_SW_CTRL_MASK 0x1
#define BOOST_SW_CTRL_SHIFT 2
#define BOOST_LOW_FREQ_EN_MASK 0x1
#define BOOST_LOW_FREQ_EN_SHIFT 3
#define BOOST_BUSY_STATE BIT(8)
#define PX30_PLL_CON(x) ((x) * 0x4)
#define PX30_CLKSEL_CON(x) ((x) * 0x4 + 0x100)
#define PX30_CLKGATE_CON(x) ((x) * 0x4 + 0x200)
#define PX30_GLB_SRST_FST 0xb8
#define PX30_GLB_SRST_SND 0xbc
#define PX30_SOFTRST_CON(x) ((x) * 0x4 + 0x300)
#define PX30_MODE_CON 0xa0
#define PX30_MISC_CON 0xa4
#define PX30_SDMMC_CON0 0x380
#define PX30_SDMMC_CON1 0x384
#define PX30_SDIO_CON0 0x388
#define PX30_SDIO_CON1 0x38c
#define PX30_EMMC_CON0 0x390
#define PX30_EMMC_CON1 0x394
#define PX30_PMU_PLL_CON(x) ((x) * 0x4)
#define PX30_PMU_CLKSEL_CON(x) ((x) * 0x4 + 0x40)
#define PX30_PMU_CLKGATE_CON(x) ((x) * 0x4 + 0x80)
#define PX30_PMU_MODE 0x0020
#define RV1108_PLL_CON(x) ((x) * 0x4)
#define RV1108_CLKSEL_CON(x) ((x) * 0x4 + 0x60)
#define RV1108_CLKGATE_CON(x) ((x) * 0x4 + 0x120)
@@ -354,6 +393,7 @@ enum rockchip_clk_branch_type {
branch_inverter,
branch_factor,
branch_ddrclk,
branch_half_divider,
};
struct rockchip_clk_branch {
@@ -684,6 +724,79 @@ struct rockchip_clk_branch {
.gate_flags = gf, \
}
#define COMPOSITE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
df, go, gs, gf) \
{ \
.id = _id, \
.branch_type = branch_half_divider, \
.name = cname, \
.parent_names = pnames, \
.num_parents = ARRAY_SIZE(pnames), \
.flags = f, \
.muxdiv_offset = mo, \
.mux_shift = ms, \
.mux_width = mw, \
.mux_flags = mf, \
.div_shift = ds, \
.div_width = dw, \
.div_flags = df, \
.gate_offset = go, \
.gate_shift = gs, \
.gate_flags = gf, \
}
#define COMPOSITE_NOGATE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf, \
ds, dw, df) \
{ \
.id = _id, \
.branch_type = branch_half_divider, \
.name = cname, \
.parent_names = pnames, \
.num_parents = ARRAY_SIZE(pnames), \
.flags = f, \
.muxdiv_offset = mo, \
.mux_shift = ms, \
.mux_width = mw, \
.mux_flags = mf, \
.div_shift = ds, \
.div_width = dw, \
.div_flags = df, \
.gate_offset = -1, \
}
#define COMPOSITE_NOMUX_HALFDIV(_id, cname, pname, f, mo, ds, dw, df, \
go, gs, gf) \
{ \
.id = _id, \
.branch_type = branch_half_divider, \
.name = cname, \
.parent_names = (const char *[]){ pname }, \
.num_parents = 1, \
.flags = f, \
.muxdiv_offset = mo, \
.div_shift = ds, \
.div_width = dw, \
.div_flags = df, \
.gate_offset = go, \
.gate_shift = gs, \
.gate_flags = gf, \
}
#define DIV_HALF(_id, cname, pname, f, o, s, w, df) \
{ \
.id = _id, \
.branch_type = branch_half_divider, \
.name = cname, \
.parent_names = (const char *[]){ pname }, \
.num_parents = 1, \
.flags = f, \
.muxdiv_offset = o, \
.div_shift = s, \
.div_width = w, \
.div_flags = df, \
.gate_offset = -1, \
}
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
void __iomem *base, unsigned long nr_clks);
void rockchip_clk_of_add_provider(struct device_node *np,
@@ -708,6 +821,17 @@ void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
struct clk *rockchip_clk_register_halfdiv(const char *name,
const char *const *parent_names,
u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift,
u8 mux_width, u8 mux_flags,
u8 div_shift, u8 div_width,
u8 div_flags, int gate_offset,
u8 gate_shift, u8 gate_flags,
unsigned long flags,
spinlock_t *lock);
#ifdef CONFIG_RESET_CONTROLLER
void rockchip_register_softrst(struct device_node *np,
unsigned int num_regs,