Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
config CLK_INTEL_SOCFPGA
bool "Intel SoCFPGA family clock support" if COMPILE_TEST && !ARCH_INTEL_SOCFPGA
default ARCH_INTEL_SOCFPGA
help
Support for the clock controllers present on Intel SoCFPGA and eASIC
devices like Aria, Cyclone, Stratix 10, Agilex and N5X eASIC.
if CLK_INTEL_SOCFPGA
config CLK_INTEL_SOCFPGA32
bool "Intel Aria / Cyclone clock controller support" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA)
default ARM && ARCH_INTEL_SOCFPGA
config CLK_INTEL_SOCFPGA64
bool "Intel Stratix / Agilex / N5X / Agilex5 clock controller support" if COMPILE_TEST && (!ARM64 || !ARCH_INTEL_SOCFPGA)
default ARM64 && ARCH_INTEL_SOCFPGA
endif # CLK_INTEL_SOCFPGA
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CLK_INTEL_SOCFPGA32) += clk.o clk-gate.o clk-pll.o clk-periph.o \
clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o
obj-$(CONFIG_CLK_INTEL_SOCFPGA64) += clk-s10.o \
clk-pll-s10.o clk-periph-s10.o clk-gate-s10.o \
clk-agilex.o clk-agilex5.o
@@ -0,0 +1,568 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019, Intel Corporation
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/agilex-clock.h>
#include "stratix10-clk.h"
static const struct clk_parent_data pll_mux[] = {
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data boot_mux[] = {
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
};
static const struct clk_parent_data mpu_free_mux[] = {
{ .fw_name = "main_pll_c0",
.name = "main_pll_c0", },
{ .fw_name = "peri_pll_c0",
.name = "peri_pll_c0", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data noc_free_mux[] = {
{ .fw_name = "main_pll_c1",
.name = "main_pll_c1", },
{ .fw_name = "peri_pll_c1",
.name = "peri_pll_c1", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data emaca_free_mux[] = {
{ .fw_name = "main_pll_c2",
.name = "main_pll_c2", },
{ .fw_name = "peri_pll_c2",
.name = "peri_pll_c2", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data emacb_free_mux[] = {
{ .fw_name = "main_pll_c3",
.name = "main_pll_c3", },
{ .fw_name = "peri_pll_c3",
.name = "peri_pll_c3", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data emac_ptp_free_mux[] = {
{ .fw_name = "main_pll_c3",
.name = "main_pll_c3", },
{ .fw_name = "peri_pll_c3",
.name = "peri_pll_c3", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data gpio_db_free_mux[] = {
{ .fw_name = "main_pll_c3",
.name = "main_pll_c3", },
{ .fw_name = "peri_pll_c3",
.name = "peri_pll_c3", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data psi_ref_free_mux[] = {
{ .fw_name = "main_pll_c2",
.name = "main_pll_c2", },
{ .fw_name = "peri_pll_c2",
.name = "peri_pll_c2", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data sdmmc_free_mux[] = {
{ .fw_name = "main_pll_c3",
.name = "main_pll_c3", },
{ .fw_name = "peri_pll_c3",
.name = "peri_pll_c3", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data s2f_usr0_free_mux[] = {
{ .fw_name = "main_pll_c2",
.name = "main_pll_c2", },
{ .fw_name = "peri_pll_c2",
.name = "peri_pll_c2", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data s2f_usr1_free_mux[] = {
{ .fw_name = "main_pll_c2",
.name = "main_pll_c2", },
{ .fw_name = "peri_pll_c2",
.name = "peri_pll_c2", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data mpu_mux[] = {
{ .fw_name = "mpu_free_clk",
.name = "mpu_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emac_mux[] = {
{ .fw_name = "emaca_free_clk",
.name = "emaca_free_clk", },
{ .fw_name = "emacb_free_clk",
.name = "emacb_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data noc_mux[] = {
{ .fw_name = "noc_free_clk",
.name = "noc_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data sdmmc_mux[] = {
{ .fw_name = "sdmmc_free_clk",
.name = "sdmmc_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data s2f_user0_mux[] = {
{ .fw_name = "s2f_user0_free_clk",
.name = "s2f_user0_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data s2f_user1_mux[] = {
{ .fw_name = "s2f_user1_free_clk",
.name = "s2f_user1_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data psi_mux[] = {
{ .fw_name = "psi_ref_free_clk",
.name = "psi_ref_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data gpio_db_mux[] = {
{ .fw_name = "gpio_db_free_clk",
.name = "gpio_db_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emac_ptp_mux[] = {
{ .fw_name = "emac_ptp_free_clk",
.name = "emac_ptp_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
/* clocks in AO (always on) controller */
static const struct stratix10_pll_clock agilex_pll_clks[] = {
{ AGILEX_BOOT_CLK, "boot_clk", boot_mux, ARRAY_SIZE(boot_mux), 0,
0x0},
{ AGILEX_MAIN_PLL_CLK, "main_pll", pll_mux, ARRAY_SIZE(pll_mux),
0, 0x48},
{ AGILEX_PERIPH_PLL_CLK, "periph_pll", pll_mux, ARRAY_SIZE(pll_mux),
0, 0x9c},
};
static const struct n5x_perip_c_clock n5x_main_perip_c_clks[] = {
{ AGILEX_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0, 0x54, 0},
{ AGILEX_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0, 0x54, 8},
{ AGILEX_MAIN_PLL_C2_CLK, "main_pll_c2", "main_pll", NULL, 1, 0, 0x54, 16},
{ AGILEX_MAIN_PLL_C3_CLK, "main_pll_c3", "main_pll", NULL, 1, 0, 0x54, 24},
{ AGILEX_PERIPH_PLL_C0_CLK, "peri_pll_c0", "periph_pll", NULL, 1, 0, 0xA8, 0},
{ AGILEX_PERIPH_PLL_C1_CLK, "peri_pll_c1", "periph_pll", NULL, 1, 0, 0xA8, 8},
{ AGILEX_PERIPH_PLL_C2_CLK, "peri_pll_c2", "periph_pll", NULL, 1, 0, 0xA8, 16},
{ AGILEX_PERIPH_PLL_C3_CLK, "peri_pll_c3", "periph_pll", NULL, 1, 0, 0xA8, 24},
};
static const struct stratix10_perip_c_clock agilex_main_perip_c_clks[] = {
{ AGILEX_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0, 0x58},
{ AGILEX_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0, 0x5C},
{ AGILEX_MAIN_PLL_C2_CLK, "main_pll_c2", "main_pll", NULL, 1, 0, 0x64},
{ AGILEX_MAIN_PLL_C3_CLK, "main_pll_c3", "main_pll", NULL, 1, 0, 0x68},
{ AGILEX_PERIPH_PLL_C0_CLK, "peri_pll_c0", "periph_pll", NULL, 1, 0, 0xAC},
{ AGILEX_PERIPH_PLL_C1_CLK, "peri_pll_c1", "periph_pll", NULL, 1, 0, 0xB0},
{ AGILEX_PERIPH_PLL_C2_CLK, "peri_pll_c2", "periph_pll", NULL, 1, 0, 0xB8},
{ AGILEX_PERIPH_PLL_C3_CLK, "peri_pll_c3", "periph_pll", NULL, 1, 0, 0xBC},
};
static const struct stratix10_perip_cnt_clock agilex_main_perip_cnt_clks[] = {
{ AGILEX_MPU_FREE_CLK, "mpu_free_clk", NULL, mpu_free_mux, ARRAY_SIZE(mpu_free_mux),
0, 0x3C, 0, 0, 0},
{ AGILEX_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
0, 0x40, 0, 0, 0},
{ AGILEX_L3_MAIN_FREE_CLK, "l3_main_free_clk", "noc_free_clk", NULL,
1, 0, 0, 1, 0, 0},
{ AGILEX_L4_SYS_FREE_CLK, "l4_sys_free_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0,
0, 4, 0x30, 1},
{ AGILEX_EMAC_A_FREE_CLK, "emaca_free_clk", NULL, emaca_free_mux, ARRAY_SIZE(emaca_free_mux),
0, 0xD4, 0, 0x88, 0},
{ AGILEX_EMAC_B_FREE_CLK, "emacb_free_clk", NULL, emacb_free_mux, ARRAY_SIZE(emacb_free_mux),
0, 0xD8, 0, 0x88, 1},
{ AGILEX_EMAC_PTP_FREE_CLK, "emac_ptp_free_clk", NULL, emac_ptp_free_mux,
ARRAY_SIZE(emac_ptp_free_mux), 0, 0xDC, 0, 0x88, 2},
{ AGILEX_GPIO_DB_FREE_CLK, "gpio_db_free_clk", NULL, gpio_db_free_mux,
ARRAY_SIZE(gpio_db_free_mux), 0, 0xE0, 0, 0x88, 3},
{ AGILEX_SDMMC_FREE_CLK, "sdmmc_free_clk", NULL, sdmmc_free_mux,
ARRAY_SIZE(sdmmc_free_mux), 0, 0xE4, 0, 0, 0},
{ AGILEX_S2F_USER0_FREE_CLK, "s2f_user0_free_clk", NULL, s2f_usr0_free_mux,
ARRAY_SIZE(s2f_usr0_free_mux), 0, 0xE8, 0, 0x30, 2},
{ AGILEX_S2F_USER1_FREE_CLK, "s2f_user1_free_clk", NULL, s2f_usr1_free_mux,
ARRAY_SIZE(s2f_usr1_free_mux), 0, 0xEC, 0, 0x88, 5},
{ AGILEX_PSI_REF_FREE_CLK, "psi_ref_free_clk", NULL, psi_ref_free_mux,
ARRAY_SIZE(psi_ref_free_mux), 0, 0xF0, 0, 0x88, 6},
};
static const struct stratix10_gate_clock agilex_gate_clks[] = {
{ AGILEX_MPU_CLK, "mpu_clk", NULL, mpu_mux, ARRAY_SIZE(mpu_mux), 0, 0x24,
0, 0, 0, 0, 0x30, 0, 0},
{ AGILEX_MPU_PERIPH_CLK, "mpu_periph_clk", "mpu_clk", NULL, 1, 0, 0x24,
0, 0, 0, 0, 0, 0, 4},
{ AGILEX_MPU_CCU_CLK, "mpu_ccu_clk", "mpu_clk", NULL, 1, 0, 0x24,
0, 0, 0, 0, 0, 0, 2},
{ AGILEX_L4_MAIN_CLK, "l4_main_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x24,
1, 0x44, 0, 2, 0x30, 1, 0},
{ AGILEX_L4_MP_CLK, "l4_mp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x24,
2, 0x44, 8, 2, 0x30, 1, 0},
/*
* The l4_sp_clk feeds a 100 MHz clock to various peripherals, one of them
* being the SP timers, thus cannot get gated.
*/
{ AGILEX_L4_SP_CLK, "l4_sp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), CLK_IS_CRITICAL, 0x24,
3, 0x44, 16, 2, 0x30, 1, 0},
{ AGILEX_CS_AT_CLK, "cs_at_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x24,
4, 0x44, 24, 2, 0x30, 1, 0},
{ AGILEX_CS_TRACE_CLK, "cs_trace_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x24,
4, 0x44, 26, 2, 0x30, 1, 0},
{ AGILEX_CS_PDBG_CLK, "cs_pdbg_clk", "cs_at_clk", NULL, 1, 0, 0x24,
4, 0x44, 28, 1, 0, 0, 0},
{ AGILEX_CS_TIMER_CLK, "cs_timer_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x24,
5, 0, 0, 0, 0x30, 1, 0},
{ AGILEX_EMAC0_CLK, "emac0_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0x7C,
0, 0, 0, 0, 0x94, 26, 0},
{ AGILEX_EMAC1_CLK, "emac1_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0x7C,
1, 0, 0, 0, 0x94, 27, 0},
{ AGILEX_EMAC2_CLK, "emac2_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0x7C,
2, 0, 0, 0, 0x94, 28, 0},
{ AGILEX_EMAC_PTP_CLK, "emac_ptp_clk", NULL, emac_ptp_mux, ARRAY_SIZE(emac_ptp_mux), 0, 0x7C,
3, 0, 0, 0, 0x88, 2, 0},
{ AGILEX_GPIO_DB_CLK, "gpio_db_clk", NULL, gpio_db_mux, ARRAY_SIZE(gpio_db_mux), 0, 0x7C,
4, 0x98, 0, 16, 0x88, 3, 0},
{ AGILEX_SDMMC_CLK, "sdmmc_clk", NULL, sdmmc_mux, ARRAY_SIZE(sdmmc_mux), 0, 0x7C,
5, 0, 0, 0, 0x88, 4, 4},
{ AGILEX_S2F_USER0_CLK, "s2f_user0_clk", NULL, s2f_user0_mux, ARRAY_SIZE(s2f_user0_mux), 0, 0x24,
6, 0, 0, 0, 0x30, 2, 0},
{ AGILEX_S2F_USER1_CLK, "s2f_user1_clk", NULL, s2f_user1_mux, ARRAY_SIZE(s2f_user1_mux), 0, 0x7C,
6, 0, 0, 0, 0x88, 5, 0},
{ AGILEX_PSI_REF_CLK, "psi_ref_clk", NULL, psi_mux, ARRAY_SIZE(psi_mux), 0, 0x7C,
7, 0, 0, 0, 0x88, 6, 0},
{ AGILEX_USB_CLK, "usb_clk", "l4_mp_clk", NULL, 1, 0, 0x7C,
8, 0, 0, 0, 0, 0, 0},
{ AGILEX_SPI_M_CLK, "spi_m_clk", "l4_mp_clk", NULL, 1, 0, 0x7C,
9, 0, 0, 0, 0, 0, 0},
{ AGILEX_NAND_X_CLK, "nand_x_clk", "l4_mp_clk", NULL, 1, 0, 0x7C,
10, 0, 0, 0, 0, 0, 0},
{ AGILEX_NAND_CLK, "nand_clk", "nand_x_clk", NULL, 1, 0, 0x7C,
10, 0, 0, 0, 0, 0, 4},
{ AGILEX_NAND_ECC_CLK, "nand_ecc_clk", "nand_x_clk", NULL, 1, 0, 0x7C,
10, 0, 0, 0, 0, 0, 4},
};
static int n5x_clk_register_c_perip(const struct n5x_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = n5x_register_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex_clk_register_c_perip(const struct stratix10_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex_clk_register_cnt_perip(const struct stratix10_perip_cnt_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_cnt_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex_clk_register_gate(const struct stratix10_gate_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = agilex_register_gate(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex_clk_register_pll(const struct stratix10_pll_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = agilex_register_pll(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int n5x_clk_register_pll(const struct stratix10_pll_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = n5x_register_pll(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex_clkmgr_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct stratix10_clock_data *clk_data;
void __iomem *base;
int i, num_clks;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
num_clks = AGILEX_NUM_CLKS;
clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws,
num_clks), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clk_data.num = num_clks;
clk_data->base = base;
for (i = 0; i < num_clks; i++)
clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
agilex_clk_register_pll(agilex_pll_clks, ARRAY_SIZE(agilex_pll_clks), clk_data);
agilex_clk_register_c_perip(agilex_main_perip_c_clks,
ARRAY_SIZE(agilex_main_perip_c_clks), clk_data);
agilex_clk_register_cnt_perip(agilex_main_perip_cnt_clks,
ARRAY_SIZE(agilex_main_perip_cnt_clks),
clk_data);
agilex_clk_register_gate(agilex_gate_clks, ARRAY_SIZE(agilex_gate_clks),
clk_data);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data);
return 0;
}
static int n5x_clkmgr_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct stratix10_clock_data *clk_data;
void __iomem *base;
int i, num_clks;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
num_clks = AGILEX_NUM_CLKS;
clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws,
num_clks), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->base = base;
clk_data->clk_data.num = num_clks;
for (i = 0; i < num_clks; i++)
clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
n5x_clk_register_pll(agilex_pll_clks, ARRAY_SIZE(agilex_pll_clks), clk_data);
n5x_clk_register_c_perip(n5x_main_perip_c_clks,
ARRAY_SIZE(n5x_main_perip_c_clks), clk_data);
agilex_clk_register_cnt_perip(agilex_main_perip_cnt_clks,
ARRAY_SIZE(agilex_main_perip_cnt_clks),
clk_data);
agilex_clk_register_gate(agilex_gate_clks, ARRAY_SIZE(agilex_gate_clks),
clk_data);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data);
return 0;
}
static int agilex_clkmgr_probe(struct platform_device *pdev)
{
int (*probe_func)(struct platform_device *init_func);
probe_func = of_device_get_match_data(&pdev->dev);
if (!probe_func)
return -ENODEV;
return probe_func(pdev);
}
static const struct of_device_id agilex_clkmgr_match_table[] = {
{ .compatible = "intel,agilex-clkmgr",
.data = agilex_clkmgr_init },
{ .compatible = "intel,easic-n5x-clkmgr",
.data = n5x_clkmgr_init },
{ }
};
static struct platform_driver agilex_clkmgr_driver = {
.probe = agilex_clkmgr_probe,
.driver = {
.name = "agilex-clkmgr",
.suppress_bind_attrs = true,
.of_match_table = agilex_clkmgr_match_table,
},
};
static int __init agilex_clk_init(void)
{
return platform_driver_register(&agilex_clkmgr_driver);
}
core_initcall(agilex_clk_init);
@@ -0,0 +1,561 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022-2024, Intel Corporation
* Copyright (C) 2025, Altera Corporation
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/intel,agilex5-clkmgr.h>
#include "stratix10-clk.h"
#include "clk.h"
/* External parent clocks come from DT via fw_name */
static const char * const boot_pll_parents[] = {
"osc1",
"cb-intosc-hs-div2-clk",
};
static const char * const main_pll_parents[] = {
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const periph_pll_parents[] = {
"osc1",
"cb-intosc-hs-div2-clk",
};
/* Core free muxes */
static const char * const core0_free_mux[] = {
"main_pll_c1",
"peri_pll_c0",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const core1_free_mux[] = {
"main_pll_c1",
"peri_pll_c0",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const core2_free_mux[] = {
"main_pll_c0",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const core3_free_mux[] = {
"main_pll_c0",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const dsu_free_mux[] = {
"main_pll_c2",
"peri_pll_c0",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const noc_free_mux[] = {
"main_pll_c3",
"peri_pll_c1",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const emac_ptp_free_mux[] = {
"main_pll_c3",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const emaca_free_mux[] = {
"main_pll_c2",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const emacb_free_mux[] = {
"main_pll_c3",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const gpio_db_free_mux[] = {
"main_pll_c3",
"peri_pll_c1",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const psi_ref_free_mux[] = {
"main_pll_c1",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const usb31_free_mux[] = {
"main_pll_c3",
"peri_pll_c2",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const s2f_user0_free_mux[] = {
"main_pll_c1",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
static const char * const s2f_user1_free_mux[] = {
"main_pll_c1",
"peri_pll_c3",
"osc1",
"cb-intosc-hs-div2-clk",
"f2s-free-clk",
};
/* Secondary muxes between free_clk and boot_clk */
static const char * const core0_mux[] = {
"core0_free_clk",
"boot_clk",
};
static const char * const core1_mux[] = {
"core1_free_clk",
"boot_clk",
};
static const char * const core2_mux[] = {
"core2_free_clk",
"boot_clk",
};
static const char * const core3_mux[] = {
"core3_free_clk",
"boot_clk",
};
static const char * const dsu_mux[] = {
"dsu_free_clk",
"boot_clk",
};
static const char * const noc_mux[] = {
"noc_free_clk",
"boot_clk",
};
static const char * const emac_mux[] = {
"emaca_free_clk",
"emacb_free_clk",
"boot_clk",
};
static const char * const s2f_user0_mux[] = {
"s2f_user0_free_clk",
"boot_clk",
};
static const char * const s2f_user1_mux[] = {
"s2f_user1_free_clk",
"boot_clk",
};
static const char * const psi_mux[] = {
"psi_ref_free_clk",
"boot_clk",
};
static const char * const gpio_db_mux[] = {
"gpio_db_free_clk",
"boot_clk",
};
static const char * const emac_ptp_mux[] = {
"emac_ptp_free_clk",
"boot_clk",
};
static const char * const usb31_mux[] = {
"usb31_free_clk",
"boot_clk",
};
static const struct agilex5_pll_clock agilex5_pll_clks[] = {
{
.id = AGILEX5_BOOT_CLK,
.name = "boot_clk",
.parent_names = boot_pll_parents,
.num_parents = ARRAY_SIZE(boot_pll_parents),
.flags = 0,
.offset = 0x0,
},
{
.id = AGILEX5_MAIN_PLL_CLK,
.name = "main_pll",
.parent_names = main_pll_parents,
.num_parents = ARRAY_SIZE(main_pll_parents),
.flags = 0,
.offset = 0x48,
},
{
.id = AGILEX5_PERIPH_PLL_CLK,
.name = "periph_pll",
.parent_names = periph_pll_parents,
.num_parents = ARRAY_SIZE(periph_pll_parents),
.flags = 0,
.offset = 0x9C,
},
};
/* Main PLL C0, C1, C2, C3 and Peri PLL C0, C1, C2, C3. With ping-pong counter. */
static const struct stratix10_perip_c_clock agilex5_main_perip_c_clks[] = {
{ AGILEX5_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0,
0x5C },
{ AGILEX5_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0,
0x60 },
{ AGILEX5_MAIN_PLL_C2_CLK, "main_pll_c2", "main_pll", NULL, 1, 0,
0x64 },
{ AGILEX5_MAIN_PLL_C3_CLK, "main_pll_c3", "main_pll", NULL, 1, 0,
0x68 },
{ AGILEX5_PERIPH_PLL_C0_CLK, "peri_pll_c0", "periph_pll", NULL, 1, 0,
0xB0 },
{ AGILEX5_PERIPH_PLL_C1_CLK, "peri_pll_c1", "periph_pll", NULL, 1, 0,
0xB4 },
{ AGILEX5_PERIPH_PLL_C2_CLK, "peri_pll_c2", "periph_pll", NULL, 1, 0,
0xB8 },
{ AGILEX5_PERIPH_PLL_C3_CLK, "peri_pll_c3", "periph_pll", NULL, 1, 0,
0xBC },
};
/* Non-SW clock-gated enabled clocks */
static const struct agilex5_perip_cnt_clock agilex5_main_perip_cnt_clks[] = {
{ AGILEX5_CORE0_FREE_CLK, "core0_free_clk", core0_free_mux,
ARRAY_SIZE(core0_free_mux), 0, 0x0100, 0, 0, 0},
{ AGILEX5_CORE1_FREE_CLK, "core1_free_clk", core1_free_mux,
ARRAY_SIZE(core1_free_mux), 0, 0x0104, 0, 0, 0},
{ AGILEX5_CORE2_FREE_CLK, "core2_free_clk", core2_free_mux,
ARRAY_SIZE(core2_free_mux), 0, 0x010C, 0, 0, 0},
{ AGILEX5_CORE3_FREE_CLK, "core3_free_clk", core3_free_mux,
ARRAY_SIZE(core3_free_mux), 0, 0x0110, 0, 0, 0},
{ AGILEX5_DSU_FREE_CLK, "dsu_free_clk", dsu_free_mux,
ARRAY_SIZE(dsu_free_mux), 0, 0xfc, 0, 0, 0},
{ AGILEX5_NOC_FREE_CLK, "noc_free_clk", noc_free_mux,
ARRAY_SIZE(noc_free_mux), 0, 0x40, 0, 0, 0 },
{ AGILEX5_EMAC_A_FREE_CLK, "emaca_free_clk", emaca_free_mux,
ARRAY_SIZE(emaca_free_mux), 0, 0xD4, 0, 0x88, 0 },
{ AGILEX5_EMAC_B_FREE_CLK, "emacb_free_clk", emacb_free_mux,
ARRAY_SIZE(emacb_free_mux), 0, 0xD8, 0, 0x88, 1 },
{ AGILEX5_EMAC_PTP_FREE_CLK, "emac_ptp_free_clk", emac_ptp_free_mux,
ARRAY_SIZE(emac_ptp_free_mux), 0, 0xDC, 0, 0x88, 2 },
{ AGILEX5_GPIO_DB_FREE_CLK, "gpio_db_free_clk", gpio_db_free_mux,
ARRAY_SIZE(gpio_db_free_mux), 0, 0xE0, 0, 0x88, 3 },
{ AGILEX5_S2F_USER0_FREE_CLK, "s2f_user0_free_clk", s2f_user0_free_mux,
ARRAY_SIZE(s2f_user0_free_mux), 0, 0xE8, 0, 0x30, 2 },
{ AGILEX5_S2F_USER1_FREE_CLK, "s2f_user1_free_clk", s2f_user1_free_mux,
ARRAY_SIZE(s2f_user1_free_mux), 0, 0xEC, 0, 0x88, 5 },
{ AGILEX5_PSI_REF_FREE_CLK, "psi_ref_free_clk", psi_ref_free_mux,
ARRAY_SIZE(psi_ref_free_mux), 0, 0xF0, 0, 0x88, 6 },
{ AGILEX5_USB31_FREE_CLK, "usb31_free_clk", usb31_free_mux,
ARRAY_SIZE(usb31_free_mux), 0, 0xF8, 0, 0x88, 7},
};
static const char * const cs_pdbg_parents[] = { "cs_at_clk" };
static const char * const usb31_bus_clk_early_parents[] = { "l4_main_clk" };
static const char * const l4_mp_clk_parent[] = { "l4_mp_clk" };
static const char * const l4_sp_clk_parent[] = { "l4_sp_clk" };
static const char * const dfi_clk_parent[] = { "dfi_clk" };
/* SW Clock gate enabled clocks */
static const struct agilex5_gate_clock agilex5_gate_clks[] = {
{ AGILEX5_CORE0_CLK, "core0_clk", core0_mux,
ARRAY_SIZE(core0_mux), 0, 0x24, 8, 0, 0, 0, 0x30, 5, 0 },
{ AGILEX5_CORE1_CLK, "core1_clk", core1_mux,
ARRAY_SIZE(core1_mux), 0, 0x24, 9, 0, 0, 0, 0x30, 5, 0 },
{ AGILEX5_CORE2_CLK, "core2_clk", core2_mux,
ARRAY_SIZE(core2_mux), 0, 0x24, 10, 0, 0, 0, 0x30, 6, 0 },
{ AGILEX5_CORE3_CLK, "core3_clk", core3_mux,
ARRAY_SIZE(core3_mux), 0, 0x24, 11, 0, 0, 0, 0x30, 7, 0 },
{ AGILEX5_MPU_CLK, "dsu_clk", dsu_mux, ARRAY_SIZE(dsu_mux), 0, 0, 0,
0, 0, 0, 0x34, 4, 0 },
{ AGILEX5_MPU_PERIPH_CLK, "mpu_periph_clk", dsu_mux,
ARRAY_SIZE(dsu_mux), 0, 0, 0, 0x44, 20, 2, 0x34, 4, 0 },
{ AGILEX5_MPU_CCU_CLK, "mpu_ccu_clk", dsu_mux,
ARRAY_SIZE(dsu_mux), 0, 0, 0, 0x44, 18, 2, 0x34, 4, 0 },
{ AGILEX5_L4_MAIN_CLK, "l4_main_clk", noc_mux, ARRAY_SIZE(noc_mux),
CLK_IS_CRITICAL, 0x24, 1, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_L4_MP_CLK, "l4_mp_clk", noc_mux, ARRAY_SIZE(noc_mux), 0,
0x24, 2, 0x44, 4, 2, 0x30, 1, 0 },
{ AGILEX5_L4_SYS_FREE_CLK, "l4_sys_free_clk", noc_mux,
ARRAY_SIZE(noc_mux), 0, 0, 0, 0x44, 2, 2, 0x30, 1, 0 },
{ AGILEX5_L4_SP_CLK, "l4_sp_clk", noc_mux, ARRAY_SIZE(noc_mux),
CLK_IS_CRITICAL, 0x24, 3, 0x44, 6, 2, 0x30, 1, 0 },
/* Core sight clocks*/
{ AGILEX5_CS_AT_CLK, "cs_at_clk", noc_mux, ARRAY_SIZE(noc_mux), 0,
0x24, 4, 0x44, 24, 2, 0x30, 1, 0 },
{ AGILEX5_CS_TRACE_CLK, "cs_trace_clk", noc_mux,
ARRAY_SIZE(noc_mux), 0, 0x24, 4, 0x44, 26, 2, 0x30, 1, 0 },
{ AGILEX5_CS_PDBG_CLK, "cs_pdbg_clk", cs_pdbg_parents, 1, 0, 0x24, 4,
0x44, 28, 1, 0, 0, 0 },
/* Main Peripheral PLL1 Begin */
{ AGILEX5_EMAC0_CLK, "emac0_clk", emac_mux, ARRAY_SIZE(emac_mux),
0, 0x7C, 0, 0, 0, 0, 0x94, 26, 0 },
{ AGILEX5_EMAC1_CLK, "emac1_clk", emac_mux, ARRAY_SIZE(emac_mux),
0, 0x7C, 1, 0, 0, 0, 0x94, 27, 0 },
{ AGILEX5_EMAC2_CLK, "emac2_clk", emac_mux, ARRAY_SIZE(emac_mux),
0, 0x7C, 2, 0, 0, 0, 0x94, 28, 0 },
{ AGILEX5_EMAC_PTP_CLK, "emac_ptp_clk", emac_ptp_mux,
ARRAY_SIZE(emac_ptp_mux), 0, 0x7C, 3, 0, 0, 0, 0x88, 2, 0 },
{ AGILEX5_GPIO_DB_CLK, "gpio_db_clk", gpio_db_mux,
ARRAY_SIZE(gpio_db_mux), 0, 0x7C, 4, 0x98, 0, 16, 0x88, 3, 1 },
/* Main Peripheral PLL1 End */
/* Peripheral clocks */
{ AGILEX5_S2F_USER0_CLK, "s2f_user0_clk", s2f_user0_mux,
ARRAY_SIZE(s2f_user0_mux), 0, 0x24, 6, 0, 0, 0, 0x30, 2, 0 },
{ AGILEX5_S2F_USER1_CLK, "s2f_user1_clk", s2f_user1_mux,
ARRAY_SIZE(s2f_user1_mux), 0, 0x7C, 6, 0, 0, 0, 0x88, 5, 0 },
{ AGILEX5_PSI_REF_CLK, "psi_ref_clk", psi_mux,
ARRAY_SIZE(psi_mux), 0, 0x7C, 7, 0, 0, 0, 0x88, 6, 0 },
{ AGILEX5_USB31_SUSPEND_CLK, "usb31_suspend_clk", usb31_mux,
ARRAY_SIZE(usb31_mux), 0, 0x7C, 25, 0, 0, 0, 0x88, 7, 0 },
{ AGILEX5_USB31_BUS_CLK_EARLY, "usb31_bus_clk_early", usb31_bus_clk_early_parents,
1, 0, 0x7C, 25, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_USB2OTG_HCLK, "usb2otg_hclk", l4_mp_clk_parent, 1, 0, 0x7C,
8, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPIM_0_CLK, "spim_0_clk", l4_mp_clk_parent, 1, 0, 0x7C, 9,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPIM_1_CLK, "spim_1_clk", l4_mp_clk_parent, 1, 0, 0x7C, 11,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPIS_0_CLK, "spis_0_clk", l4_sp_clk_parent, 1, 0, 0x7C, 12,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPIS_1_CLK, "spis_1_clk", l4_sp_clk_parent, 1, 0, 0x7C, 13,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_DMA_CORE_CLK, "dma_core_clk", l4_mp_clk_parent, 1, 0, 0x7C,
14, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_DMA_HS_CLK, "dma_hs_clk", l4_mp_clk_parent, 1, 0, 0x7C, 14,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_I3C_0_CORE_CLK, "i3c_0_core_clk", l4_mp_clk_parent, 1, 0,
0x7C, 18, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_I3C_1_CORE_CLK, "i3c_1_core_clk", l4_mp_clk_parent, 1, 0,
0x7C, 19, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_I2C_0_PCLK, "i2c_0_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 15,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_I2C_1_PCLK, "i2c_1_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 16,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_I2C_EMAC0_PCLK, "i2c_emac0_pclk", l4_sp_clk_parent, 1, 0,
0x7C, 17, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_I2C_EMAC1_PCLK, "i2c_emac1_pclk", l4_sp_clk_parent, 1, 0,
0x7C, 22, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_I2C_EMAC2_PCLK, "i2c_emac2_pclk", l4_sp_clk_parent, 1, 0,
0x7C, 27, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_UART_0_PCLK, "uart_0_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 20,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_UART_1_PCLK, "uart_1_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 21,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPTIMER_0_PCLK, "sptimer_0_pclk", l4_sp_clk_parent, 1, 0,
0x7C, 23, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_SPTIMER_1_PCLK, "sptimer_1_pclk", l4_sp_clk_parent, 1, 0,
0x7C, 24, 0, 0, 0, 0, 0, 0 },
/*NAND, SD/MMC and SoftPHY overall clocking*/
{ AGILEX5_DFI_CLK, "dfi_clk", l4_mp_clk_parent, 1, 0, 0, 0, 0x44, 16,
2, 0, 0, 0 },
{ AGILEX5_NAND_NF_CLK, "nand_nf_clk", dfi_clk_parent, 1, 0, 0x7C, 10,
0, 0, 0, 0, 0, 0 },
{ AGILEX5_NAND_BCH_CLK, "nand_bch_clk", l4_mp_clk_parent, 1, 0, 0x7C,
10, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_SDMMC_SDPHY_REG_CLK, "sdmmc_sdphy_reg_clk", l4_mp_clk_parent,
1, 0, 0x7C, 5, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_SDMCLK, "sdmclk", dfi_clk_parent, 1, 0, 0x7C, 5, 0, 0, 0,
0, 0, 0 },
{ AGILEX5_SOFTPHY_REG_PCLK, "softphy_reg_pclk", l4_mp_clk_parent, 1, 0,
0x7C, 26, 0, 0, 0, 0, 0, 0 },
{ AGILEX5_SOFTPHY_PHY_CLK, "softphy_phy_clk", l4_mp_clk_parent, 1, 0,
0x7C, 26, 0x44, 16, 2, 0, 0, 0 },
{ AGILEX5_SOFTPHY_CTRL_CLK, "softphy_ctrl_clk", dfi_clk_parent, 1, 0,
0x7C, 26, 0, 0, 0, 0, 0, 0 },
};
static int
agilex5_clk_register_c_perip(const struct stratix10_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int
agilex5_clk_register_cnt_perip(const struct agilex5_perip_cnt_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = agilex5_register_cnt_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex5_clk_register_gate(const struct agilex5_gate_clock *clks,
int nums,
struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = agilex5_register_gate(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex5_clk_register_pll(const struct agilex5_pll_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = agilex5_register_pll(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int agilex5_clkmgr_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct stratix10_clock_data *clk_data;
void __iomem *base;
int i, num_clks;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
num_clks = AGILEX5_NUM_CLKS;
clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws,
num_clks), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->base = base;
clk_data->clk_data.num = num_clks;
for (i = 0; i < num_clks; i++)
clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
agilex5_clk_register_pll(agilex5_pll_clks, ARRAY_SIZE(agilex5_pll_clks),
clk_data);
/* mainPLL C0, C1, C2, C3 and periph PLL C0, C1, C2, C3*/
agilex5_clk_register_c_perip(agilex5_main_perip_c_clks,
ARRAY_SIZE(agilex5_main_perip_c_clks),
clk_data);
agilex5_clk_register_cnt_perip(agilex5_main_perip_cnt_clks,
ARRAY_SIZE(agilex5_main_perip_cnt_clks),
clk_data);
agilex5_clk_register_gate(agilex5_gate_clks,
ARRAY_SIZE(agilex5_gate_clks), clk_data);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data);
return 0;
}
static int agilex5_clkmgr_probe(struct platform_device *pdev)
{
int (*probe_func)(struct platform_device *init_func);
probe_func = of_device_get_match_data(&pdev->dev);
if (!probe_func)
return -ENODEV;
return probe_func(pdev);
}
static const struct of_device_id agilex5_clkmgr_match_table[] = {
{ .compatible = "intel,agilex5-clkmgr", .data = agilex5_clkmgr_init },
{}
};
static struct platform_driver agilex5_clkmgr_driver = {
.probe = agilex5_clkmgr_probe,
.driver = {
.name = "agilex5-clkmgr",
.suppress_bind_attrs = true,
.of_match_table = agilex5_clkmgr_match_table,
},
};
static int __init agilex5_clk_init(void)
{
return platform_driver_register(&agilex5_clkmgr_driver);
}
core_initcall(agilex5_clk_init);
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Altera Corporation. All rights reserved
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include "clk.h"
#define streq(a, b) (strcmp((a), (b)) == 0)
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
/* SDMMC Group for System Manager defines */
#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x28
static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 div = 1, val;
if (socfpgaclk->fixed_div)
div = socfpgaclk->fixed_div;
else if (socfpgaclk->div_reg) {
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
val &= GENMASK(socfpgaclk->width - 1, 0);
div = (1 << val);
}
return parent_rate / div;
}
static struct clk_ops gateclk_ops = {
.recalc_rate = socfpga_gate_clk_recalc_rate,
};
static void __init __socfpga_gate_init(struct device_node *node,
const struct clk_ops *ops)
{
u32 clk_gate[2];
u32 div_reg[3];
u32 fixed_div;
struct clk_hw *hw_clk;
struct socfpga_gate_clk *socfpga_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
int rc;
socfpga_clk = kzalloc_obj(*socfpga_clk);
if (WARN_ON(!socfpga_clk))
return;
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
if (rc)
clk_gate[0] = 0;
if (clk_gate[0]) {
socfpga_clk->hw.reg = clk_mgr_a10_base_addr + clk_gate[0];
socfpga_clk->hw.bit_idx = clk_gate[1];
gateclk_ops.enable = clk_gate_ops.enable;
gateclk_ops.disable = clk_gate_ops.disable;
}
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
if (rc)
socfpga_clk->fixed_div = 0;
else
socfpga_clk->fixed_div = fixed_div;
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
if (!rc) {
socfpga_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0];
socfpga_clk->shift = div_reg[1];
socfpga_clk->width = div_reg[2];
} else {
socfpga_clk->div_reg = NULL;
}
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
init.parent_names = parent_name;
socfpga_clk->hw.hw.init = &init;
hw_clk = &socfpga_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(socfpga_clk);
}
void __init socfpga_a10_gate_init(struct device_node *node)
{
__socfpga_gate_init(node, &gateclk_ops);
}
@@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017, Intel Corporation
*/
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/slab.h>
#include "stratix10-clk.h"
#include "clk.h"
#define SOCFPGA_CS_PDBG_CLK "cs_pdbg_clk"
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
#define SOCFPGA_EMAC0_CLK "emac0_clk"
#define SOCFPGA_EMAC1_CLK "emac1_clk"
#define SOCFPGA_EMAC2_CLK "emac2_clk"
#define AGILEX_BYPASS_OFFSET 0xC
#define STRATIX10_BYPASS_OFFSET 0x2C
#define BOOTCLK_BYPASS 2
static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 div = 1, val;
if (socfpgaclk->fixed_div) {
div = socfpgaclk->fixed_div;
} else if (socfpgaclk->div_reg) {
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
val &= GENMASK(socfpgaclk->width - 1, 0);
div = (1 << val);
}
return parent_rate / div;
}
static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 div, val;
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
val &= GENMASK(socfpgaclk->width - 1, 0);
div = (1 << val);
div = div ? 4 : 1;
return parent_rate / div;
}
static u8 socfpga_gate_get_parent(struct clk_hw *hwclk)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 mask, second_bypass;
u8 parent = 0;
const char *name = clk_hw_get_name(hwclk);
if (socfpgaclk->bypass_reg) {
mask = (0x1 << socfpgaclk->bypass_shift);
parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
socfpgaclk->bypass_shift);
}
if (streq(name, SOCFPGA_EMAC0_CLK) ||
streq(name, SOCFPGA_EMAC1_CLK) ||
streq(name, SOCFPGA_EMAC2_CLK)) {
second_bypass = readl(socfpgaclk->bypass_reg -
STRATIX10_BYPASS_OFFSET);
/* EMACA bypass to bootclk @0xB0 offset */
if (second_bypass & 0x1)
if (parent == 0) /* only applicable if parent is maca */
parent = BOOTCLK_BYPASS;
if (second_bypass & 0x2)
if (parent == 1) /* only applicable if parent is macb */
parent = BOOTCLK_BYPASS;
}
return parent;
}
static u8 socfpga_agilex_gate_get_parent(struct clk_hw *hwclk)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 mask, second_bypass;
u8 parent = 0;
const char *name = clk_hw_get_name(hwclk);
if (socfpgaclk->bypass_reg) {
mask = (0x1 << socfpgaclk->bypass_shift);
parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
socfpgaclk->bypass_shift);
}
if (streq(name, SOCFPGA_EMAC0_CLK) ||
streq(name, SOCFPGA_EMAC1_CLK) ||
streq(name, SOCFPGA_EMAC2_CLK)) {
second_bypass = readl(socfpgaclk->bypass_reg -
AGILEX_BYPASS_OFFSET);
/* EMACA bypass to bootclk @0x88 offset */
if (second_bypass & 0x1)
if (parent == 0) /* only applicable if parent is maca */
parent = BOOTCLK_BYPASS;
if (second_bypass & 0x2)
if (parent == 1) /* only applicable if parent is macb */
parent = BOOTCLK_BYPASS;
}
return parent;
}
static struct clk_ops gateclk_ops = {
.recalc_rate = socfpga_gate_clk_recalc_rate,
.get_parent = socfpga_gate_get_parent,
};
static const struct clk_ops agilex_gateclk_ops = {
.recalc_rate = socfpga_gate_clk_recalc_rate,
.get_parent = socfpga_agilex_gate_get_parent,
};
static const struct clk_ops dbgclk_ops = {
.recalc_rate = socfpga_dbg_clk_recalc_rate,
.get_parent = socfpga_gate_get_parent,
};
struct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_gate_clk *socfpga_clk;
struct clk_init_data init;
const char *parent_name = clks->parent_name;
int ret;
socfpga_clk = kzalloc_obj(*socfpga_clk);
if (!socfpga_clk)
return NULL;
socfpga_clk->hw.reg = regbase + clks->gate_reg;
socfpga_clk->hw.bit_idx = clks->gate_idx;
gateclk_ops.enable = clk_gate_ops.enable;
gateclk_ops.disable = clk_gate_ops.disable;
socfpga_clk->fixed_div = clks->fixed_div;
if (clks->div_reg)
socfpga_clk->div_reg = regbase + clks->div_reg;
else
socfpga_clk->div_reg = NULL;
socfpga_clk->width = clks->div_width;
socfpga_clk->shift = clks->div_offset;
if (clks->bypass_reg)
socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
else
socfpga_clk->bypass_reg = NULL;
socfpga_clk->bypass_shift = clks->bypass_shift;
if (streq(clks->name, "cs_pdbg_clk"))
init.ops = &dbgclk_ops;
else
init.ops = &gateclk_ops;
init.name = clks->name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
if (init.parent_names == NULL)
init.parent_data = clks->parent_data;
socfpga_clk->hw.hw.init = &init;
hw_clk = &socfpga_clk->hw.hw;
ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
if (ret) {
kfree(socfpga_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_gate_clk *socfpga_clk;
struct clk_init_data init;
const char *parent_name = clks->parent_name;
int ret;
socfpga_clk = kzalloc_obj(*socfpga_clk);
if (!socfpga_clk)
return NULL;
socfpga_clk->hw.reg = regbase + clks->gate_reg;
socfpga_clk->hw.bit_idx = clks->gate_idx;
gateclk_ops.enable = clk_gate_ops.enable;
gateclk_ops.disable = clk_gate_ops.disable;
socfpga_clk->fixed_div = clks->fixed_div;
if (clks->div_reg)
socfpga_clk->div_reg = regbase + clks->div_reg;
else
socfpga_clk->div_reg = NULL;
socfpga_clk->width = clks->div_width;
socfpga_clk->shift = clks->div_offset;
if (clks->bypass_reg)
socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
else
socfpga_clk->bypass_reg = NULL;
socfpga_clk->bypass_shift = clks->bypass_shift;
if (streq(clks->name, "cs_pdbg_clk"))
init.ops = &dbgclk_ops;
else
init.ops = &agilex_gateclk_ops;
init.name = clks->name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
if (init.parent_names == NULL)
init.parent_data = clks->parent_data;
socfpga_clk->hw.hw.init = &init;
hw_clk = &socfpga_clk->hw.hw;
ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
if (ret) {
kfree(socfpga_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *agilex5_register_gate(const struct agilex5_gate_clock *clks, void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_gate_clk *socfpga_clk;
struct clk_init_data init;
int ret;
socfpga_clk = kzalloc_obj(*socfpga_clk);
if (!socfpga_clk)
return NULL;
socfpga_clk->hw.reg = regbase + clks->gate_reg;
socfpga_clk->hw.bit_idx = clks->gate_idx;
gateclk_ops.enable = clk_gate_ops.enable;
gateclk_ops.disable = clk_gate_ops.disable;
socfpga_clk->fixed_div = clks->fixed_div;
if (clks->div_reg)
socfpga_clk->div_reg = regbase + clks->div_reg;
else
socfpga_clk->div_reg = NULL;
socfpga_clk->width = clks->div_width;
socfpga_clk->shift = clks->div_offset;
if (clks->bypass_reg)
socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
else
socfpga_clk->bypass_reg = NULL;
socfpga_clk->bypass_shift = clks->bypass_shift;
if (streq(clks->name, "cs_pdbg_clk"))
init.ops = &dbgclk_ops;
else
init.ops = &agilex_gateclk_ops;
init.name = clks->name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = clks->parent_names;
socfpga_clk->hw.hw.init = &init;
hw_clk = &socfpga_clk->hw.hw;
ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
if (ret) {
kfree(socfpga_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
+223
View File
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011-2012 Calxeda, Inc.
* Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
*
* Based from clk-highbank.c
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include "clk.h"
#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
#define SOCFPGA_NAND_CLK "nand_clk"
#define SOCFPGA_NAND_X_CLK "nand_x_clk"
#define SOCFPGA_MMC_CLK "sdmmc_clk"
#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
/* SDMMC Group for System Manager defines */
#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
{
u32 l4_src;
u32 perpll_src;
const char *name = clk_hw_get_name(hwclk);
if (streq(name, SOCFPGA_L4_MP_CLK)) {
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
return l4_src & 0x1;
}
if (streq(name, SOCFPGA_L4_SP_CLK)) {
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
return !!(l4_src & 2);
}
perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
if (streq(name, SOCFPGA_MMC_CLK))
return perpll_src & 0x3;
if (streq(name, SOCFPGA_NAND_CLK) ||
streq(name, SOCFPGA_NAND_X_CLK))
return (perpll_src >> 2) & 3;
/* QSPI clock */
return (perpll_src >> 4) & 3;
}
static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
{
u32 src_reg;
const char *name = clk_hw_get_name(hwclk);
if (streq(name, SOCFPGA_L4_MP_CLK)) {
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
src_reg &= ~0x1;
src_reg |= parent;
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
} else if (streq(name, SOCFPGA_L4_SP_CLK)) {
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
src_reg &= ~0x2;
src_reg |= (parent << 1);
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
} else {
src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
if (streq(name, SOCFPGA_MMC_CLK)) {
src_reg &= ~0x3;
src_reg |= parent;
} else if (streq(name, SOCFPGA_NAND_CLK) ||
streq(name, SOCFPGA_NAND_X_CLK)) {
src_reg &= ~0xC;
src_reg |= (parent << 2);
} else {/* QSPI clock */
src_reg &= ~0x30;
src_reg |= (parent << 4);
}
writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
}
return 0;
}
static u32 socfpga_clk_get_div(struct socfpga_gate_clk *socfpgaclk)
{
u32 div = 1, val;
if (socfpgaclk->fixed_div)
div = socfpgaclk->fixed_div;
else if (socfpgaclk->div_reg) {
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
val &= GENMASK(socfpgaclk->width - 1, 0);
/* Check for GPIO_DB_CLK by its offset */
if ((uintptr_t) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET)
div = val + 1;
else
div = (1 << val);
}
return div;
}
static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 div = socfpga_clk_get_div(socfpgaclk);
return parent_rate / div;
}
static int socfpga_clk_determine_rate(struct clk_hw *hwclk,
struct clk_rate_request *req)
{
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
u32 div = socfpga_clk_get_div(socfpgaclk);
req->rate = req->best_parent_rate / div;
return 0;
}
static struct clk_ops gateclk_ops = {
.recalc_rate = socfpga_clk_recalc_rate,
.determine_rate = socfpga_clk_determine_rate,
.get_parent = socfpga_clk_get_parent,
.set_parent = socfpga_clk_set_parent,
};
void __init socfpga_gate_init(struct device_node *node)
{
u32 clk_gate[2];
u32 div_reg[3];
u32 fixed_div;
struct clk_hw *hw_clk;
struct socfpga_gate_clk *socfpga_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
struct clk_ops *ops;
int rc;
socfpga_clk = kzalloc_obj(*socfpga_clk);
if (WARN_ON(!socfpga_clk))
return;
ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL);
if (WARN_ON(!ops))
goto err_kmemdup;
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
if (rc)
clk_gate[0] = 0;
if (clk_gate[0]) {
socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
socfpga_clk->hw.bit_idx = clk_gate[1];
ops->enable = clk_gate_ops.enable;
ops->disable = clk_gate_ops.disable;
}
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
if (rc)
socfpga_clk->fixed_div = 0;
else
socfpga_clk->fixed_div = fixed_div;
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
if (!rc) {
socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
socfpga_clk->shift = div_reg[1];
socfpga_clk->width = div_reg[2];
} else {
socfpga_clk->div_reg = NULL;
}
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
if (init.num_parents < 2) {
ops->get_parent = NULL;
ops->set_parent = NULL;
}
init.parent_names = parent_name;
socfpga_clk->hw.hw.init = &init;
hw_clk = &socfpga_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(ops);
err_kmemdup:
kfree(socfpga_clk);
}
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Altera Corporation. All rights reserved
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include "clk.h"
#define CLK_MGR_FREE_SHIFT 16
#define CLK_MGR_FREE_MASK 0x7
#define SOCFPGA_MPU_FREE_CLK "mpu_free_clk"
#define SOCFPGA_NOC_FREE_CLK "noc_free_clk"
#define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk"
#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
u32 div;
if (socfpgaclk->fixed_div) {
div = socfpgaclk->fixed_div;
} else if (socfpgaclk->div_reg) {
div = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
div &= GENMASK(socfpgaclk->width - 1, 0);
div += 1;
} else {
div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
}
return parent_rate / div;
}
static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
{
struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
u32 clk_src;
const char *name = clk_hw_get_name(hwclk);
clk_src = readl(socfpgaclk->hw.reg);
if (streq(name, SOCFPGA_MPU_FREE_CLK) ||
streq(name, SOCFPGA_NOC_FREE_CLK) ||
streq(name, SOCFPGA_SDMMC_FREE_CLK))
return (clk_src >> CLK_MGR_FREE_SHIFT) &
CLK_MGR_FREE_MASK;
else
return 0;
}
static const struct clk_ops periclk_ops = {
.recalc_rate = clk_periclk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
static void __init __socfpga_periph_init(struct device_node *node,
const struct clk_ops *ops)
{
u32 reg;
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
int rc;
u32 fixed_div;
u32 div_reg[3];
of_property_read_u32(node, "reg", &reg);
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return;
periph_clk->hw.reg = clk_mgr_a10_base_addr + reg;
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
if (!rc) {
periph_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0];
periph_clk->shift = div_reg[1];
periph_clk->width = div_reg[2];
} else {
periph_clk->div_reg = NULL;
}
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
if (rc)
periph_clk->fixed_div = 0;
else
periph_clk->fixed_div = fixed_div;
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
init.parent_names = parent_name;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(periph_clk);
}
void __init socfpga_a10_periph_init(struct device_node *node)
{
__socfpga_periph_init(node, &periclk_ops);
}
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017, Intel Corporation
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include "stratix10-clk.h"
#include "clk.h"
#define CLK_MGR_FREE_SHIFT 16
#define CLK_MGR_FREE_MASK 0x7
#define SWCTRLBTCLKSEN_SHIFT 8
#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
static unsigned long n5x_clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
unsigned long div;
unsigned long shift = socfpgaclk->shift;
u32 val;
val = readl(socfpgaclk->hw.reg);
val &= (0x1f << shift);
div = (val >> shift) + 1;
return parent_rate / div;
}
static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
unsigned long div = 1;
u32 val;
val = readl(socfpgaclk->hw.reg);
val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
parent_rate /= val;
return parent_rate / div;
}
static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
unsigned long div = 1;
if (socfpgaclk->fixed_div) {
div = socfpgaclk->fixed_div;
} else {
if (socfpgaclk->hw.reg)
div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
}
return parent_rate / div;
}
static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
u32 clk_src, mask;
u8 parent = 0;
/* handle the bypass first */
if (socfpgaclk->bypass_reg) {
mask = (0x1 << socfpgaclk->bypass_shift);
parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
socfpgaclk->bypass_shift);
if (parent)
return parent;
}
if (socfpgaclk->hw.reg) {
clk_src = readl(socfpgaclk->hw.reg);
parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
CLK_MGR_FREE_MASK;
}
return parent;
}
static const struct clk_ops n5x_peri_c_clk_ops = {
.recalc_rate = n5x_clk_peri_c_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
static const struct clk_ops peri_c_clk_ops = {
.recalc_rate = clk_peri_c_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
static const struct clk_ops peri_cnt_clk_ops = {
.recalc_rate = clk_peri_cnt_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,
void __iomem *reg)
{
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
struct clk_init_data init;
const char *name = clks->name;
const char *parent_name = clks->parent_name;
int ret;
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return NULL;
periph_clk->hw.reg = reg + clks->offset;
init.name = name;
init.ops = &peri_c_clk_ops;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
if (init.parent_names == NULL)
init.parent_data = clks->parent_data;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(periph_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,
void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
struct clk_init_data init;
const char *name = clks->name;
const char *parent_name = clks->parent_name;
int ret;
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return NULL;
periph_clk->hw.reg = regbase + clks->offset;
periph_clk->shift = clks->shift;
init.name = name;
init.ops = &n5x_peri_c_clk_ops;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(periph_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
struct clk_init_data init;
const char *name = clks->name;
const char *parent_name = clks->parent_name;
int ret;
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return NULL;
if (clks->offset)
periph_clk->hw.reg = regbase + clks->offset;
else
periph_clk->hw.reg = NULL;
if (clks->bypass_reg)
periph_clk->bypass_reg = regbase + clks->bypass_reg;
else
periph_clk->bypass_reg = NULL;
periph_clk->bypass_shift = clks->bypass_shift;
periph_clk->fixed_div = clks->fixed_divider;
init.name = name;
init.ops = &peri_cnt_clk_ops;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = parent_name ? &parent_name : NULL;
if (init.parent_names == NULL)
init.parent_data = clks->parent_data;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(periph_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *agilex5_register_cnt_periph(const struct agilex5_perip_cnt_clock *clks,
void __iomem *regbase)
{
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
struct clk_init_data init;
const char *name = clks->name;
int ret;
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return NULL;
if (clks->offset)
periph_clk->hw.reg = regbase + clks->offset;
else
periph_clk->hw.reg = NULL;
if (clks->bypass_reg)
periph_clk->bypass_reg = regbase + clks->bypass_reg;
else
periph_clk->bypass_reg = NULL;
periph_clk->bypass_shift = clks->bypass_shift;
periph_clk->fixed_div = clks->fixed_divider;
init.name = name;
init.ops = &peri_cnt_clk_ops;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = clks->parent_names;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(periph_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011-2012 Calxeda, Inc.
* Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
*
* Based from clk-highbank.c
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include "clk.h"
#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
u32 div, val;
if (socfpgaclk->fixed_div) {
div = socfpgaclk->fixed_div;
} else {
if (socfpgaclk->div_reg) {
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
val &= GENMASK(socfpgaclk->width - 1, 0);
parent_rate /= (val + 1);
}
div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
}
return parent_rate / div;
}
static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
{
u32 clk_src;
clk_src = readl(clk_mgr_base_addr + CLKMGR_DBCTRL);
return clk_src & 0x1;
}
static const struct clk_ops periclk_ops = {
.recalc_rate = clk_periclk_recalc_rate,
.get_parent = clk_periclk_get_parent,
};
static void __init __socfpga_periph_init(struct device_node *node,
const struct clk_ops *ops)
{
u32 reg;
struct clk_hw *hw_clk;
struct socfpga_periph_clk *periph_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
int rc;
u32 fixed_div;
u32 div_reg[3];
of_property_read_u32(node, "reg", &reg);
periph_clk = kzalloc_obj(*periph_clk);
if (WARN_ON(!periph_clk))
return;
periph_clk->hw.reg = clk_mgr_base_addr + reg;
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
if (!rc) {
periph_clk->div_reg = clk_mgr_base_addr + div_reg[0];
periph_clk->shift = div_reg[1];
periph_clk->width = div_reg[2];
} else {
periph_clk->div_reg = NULL;
}
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
if (rc)
periph_clk->fixed_div = 0;
else
periph_clk->fixed_div = fixed_div;
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name,
SOCFPGA_MAX_PARENTS);
init.parent_names = parent_name;
periph_clk->hw.hw.init = &init;
hw_clk = &periph_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(periph_clk);
}
void __init socfpga_periph_init(struct device_node *node)
{
__socfpga_periph_init(node, &periclk_ops);
}
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Altera Corporation. All rights reserved
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk.h"
/* Clock Manager offsets */
#define CLK_MGR_PLL_CLK_SRC_SHIFT 8
#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
/* Clock bypass bits */
#define SOCFPGA_PLL_BG_PWRDWN 0
#define SOCFPGA_PLL_PWR_DOWN 1
#define SOCFPGA_PLL_EXT_ENA 2
#define SOCFPGA_PLL_DIVF_MASK 0x00001FFF
#define SOCFPGA_PLL_DIVF_SHIFT 0
#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
#define SOCFPGA_PLL_DIVQ_SHIFT 16
#define SOCFGPA_MAX_PARENTS 5
#define SOCFPGA_MAIN_PLL_CLK "main_pll"
#define SOCFPGA_PERIP_PLL_CLK "periph_pll"
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
void __iomem *clk_mgr_a10_base_addr;
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 divf, divq, reg;
unsigned long long vco_freq;
/* read VCO1 reg for numerator and denominator */
reg = readl(socfpgaclk->hw.reg + 0x4);
divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
vco_freq = (unsigned long long)parent_rate * (divf + 1);
do_div(vco_freq, (1 + divq));
return (unsigned long)vco_freq;
}
static u8 clk_pll_get_parent(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 pll_src;
pll_src = readl(socfpgaclk->hw.reg);
return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
CLK_MGR_PLL_CLK_SRC_MASK;
}
static const struct clk_ops clk_pll_ops = {
.recalc_rate = clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
};
static void __init __socfpga_pll_init(struct device_node *node,
const struct clk_ops *ops)
{
u32 reg;
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFGPA_MAX_PARENTS];
struct clk_init_data init;
struct device_node *clkmgr_np;
int rc;
int i = 0;
of_property_read_u32(node, "reg", &reg);
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return;
clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0);
of_node_put(clkmgr_np);
BUG_ON(!clk_mgr_a10_base_addr);
pll_clk->hw.reg = clk_mgr_a10_base_addr + reg;
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
of_clk_get_parent_name(node, i)) != NULL)
i++;
init.num_parents = i;
init.parent_names = parent_name;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
hw_clk = &pll_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(pll_clk);
}
void __init socfpga_a10_pll_init(struct device_node *node)
{
__socfpga_pll_init(node, &clk_pll_ops);
}
@@ -0,0 +1,342 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017, Intel Corporation
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include "stratix10-clk.h"
#include "clk.h"
/* Clock Manager offsets */
#define CLK_MGR_PLL_CLK_SRC_SHIFT 16
#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
/* PLL Clock enable bits */
#define SOCFPGA_PLL_POWER 0
#define SOCFPGA_PLL_RESET_MASK 0x2
#define SOCFPGA_PLL_REFDIV_MASK 0x00003F00
#define SOCFPGA_PLL_REFDIV_SHIFT 8
#define SOCFPGA_PLL_AREFDIV_MASK 0x00000F00
#define SOCFPGA_PLL_DREFDIV_MASK 0x00003000
#define SOCFPGA_PLL_DREFDIV_SHIFT 12
#define SOCFPGA_PLL_MDIV_MASK 0xFF000000
#define SOCFPGA_PLL_MDIV_SHIFT 24
#define SOCFPGA_AGILEX_PLL_MDIV_MASK 0x000003FF
#define SWCTRLBTCLKSEL_MASK 0x200
#define SWCTRLBTCLKSEL_SHIFT 9
#define SOCFPGA_N5X_PLLDIV_FDIV_MASK GENMASK(16, 8)
#define SOCFPGA_N5X_PLLDIV_FDIV_SHIFT 8
#define SOCFPGA_N5X_PLLDIV_RDIV_MASK GENMASK(5, 0)
#define SOCFPGA_N5X_PLLDIV_QDIV_MASK GENMASK(26, 24)
#define SOCFPGA_N5X_PLLDIV_QDIV_SHIFT 24
#define SOCFPGA_BOOT_CLK "boot_clk"
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
static unsigned long n5x_clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
unsigned long fdiv, reg, rdiv, qdiv;
u32 power = 1;
/* read VCO1 reg for numerator and denominator */
reg = readl(socfpgaclk->hw.reg + 0x8);
fdiv = (reg & SOCFPGA_N5X_PLLDIV_FDIV_MASK) >> SOCFPGA_N5X_PLLDIV_FDIV_SHIFT;
rdiv = (reg & SOCFPGA_N5X_PLLDIV_RDIV_MASK);
qdiv = (reg & SOCFPGA_N5X_PLLDIV_QDIV_MASK) >> SOCFPGA_N5X_PLLDIV_QDIV_SHIFT;
while (qdiv) {
power *= 2;
qdiv--;
}
return ((parent_rate * 2 * (fdiv + 1)) / ((rdiv + 1) * power));
}
static unsigned long agilex_clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
unsigned long arefdiv, reg, mdiv;
unsigned long long vco_freq;
/* read VCO1 reg for numerator and denominator */
reg = readl(socfpgaclk->hw.reg);
arefdiv = (reg & SOCFPGA_PLL_AREFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;
vco_freq = (unsigned long long)parent_rate / arefdiv;
/* Read mdiv and fdiv from the fdbck register */
reg = readl(socfpgaclk->hw.reg + 0x24);
mdiv = reg & SOCFPGA_AGILEX_PLL_MDIV_MASK;
vco_freq = (unsigned long long)vco_freq * mdiv;
return (unsigned long)vco_freq;
}
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 mdiv;
u32 refdiv;
u32 reg;
unsigned long long vco_freq;
/* read VCO1 reg for numerator and denominator */
reg = readl(socfpgaclk->hw.reg);
refdiv = (reg & SOCFPGA_PLL_REFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;
vco_freq = parent_rate;
do_div(vco_freq, refdiv);
/* Read mdiv and fdiv from the fdbck register */
reg = readl(socfpgaclk->hw.reg + 0x4);
mdiv = (reg & SOCFPGA_PLL_MDIV_MASK) >> SOCFPGA_PLL_MDIV_SHIFT;
vco_freq = (unsigned long long)vco_freq * (mdiv + 6);
return (unsigned long)vco_freq;
}
static unsigned long clk_boot_clk_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 div;
div = ((readl(socfpgaclk->hw.reg) &
SWCTRLBTCLKSEL_MASK) >>
SWCTRLBTCLKSEL_SHIFT);
div += 1;
return parent_rate / div;
}
static u8 clk_pll_get_parent(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 pll_src;
pll_src = readl(socfpgaclk->hw.reg);
return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
CLK_MGR_PLL_CLK_SRC_MASK;
}
static u8 clk_boot_get_parent(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 pll_src;
pll_src = readl(socfpgaclk->hw.reg);
return (pll_src >> SWCTRLBTCLKSEL_SHIFT) &
SWCTRLBTCLKSEL_MASK;
}
static int clk_pll_prepare(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 reg;
/* Bring PLL out of reset */
reg = readl(socfpgaclk->hw.reg);
reg |= SOCFPGA_PLL_RESET_MASK;
writel(reg, socfpgaclk->hw.reg);
return 0;
}
static int n5x_clk_pll_prepare(struct clk_hw *hwclk)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 reg;
/* Bring PLL out of reset */
reg = readl(socfpgaclk->hw.reg + 0x4);
reg |= SOCFPGA_PLL_RESET_MASK;
writel(reg, socfpgaclk->hw.reg + 0x4);
return 0;
}
static const struct clk_ops n5x_clk_pll_ops = {
.recalc_rate = n5x_clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
.prepare = n5x_clk_pll_prepare,
};
static const struct clk_ops agilex_clk_pll_ops = {
.recalc_rate = agilex_clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
.prepare = clk_pll_prepare,
};
static const struct clk_ops clk_pll_ops = {
.recalc_rate = clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
.prepare = clk_pll_prepare,
};
static const struct clk_ops clk_boot_ops = {
.recalc_rate = clk_boot_clk_recalc_rate,
.get_parent = clk_boot_get_parent,
.prepare = clk_pll_prepare,
};
struct clk_hw *s10_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg)
{
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
struct clk_init_data init;
const char *name = clks->name;
int ret;
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return NULL;
pll_clk->hw.reg = reg + clks->offset;
if (streq(name, SOCFPGA_BOOT_CLK))
init.ops = &clk_boot_ops;
else
init.ops = &clk_pll_ops;
init.name = name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = NULL;
init.parent_data = clks->parent_data;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
hw_clk = &pll_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(pll_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *agilex_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg)
{
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
struct clk_init_data init;
const char *name = clks->name;
int ret;
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return NULL;
pll_clk->hw.reg = reg + clks->offset;
if (streq(name, SOCFPGA_BOOT_CLK))
init.ops = &clk_boot_ops;
else
init.ops = &agilex_clk_pll_ops;
init.name = name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = NULL;
init.parent_data = clks->parent_data;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
hw_clk = &pll_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(pll_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg)
{
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
struct clk_init_data init;
const char *name = clks->name;
int ret;
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return NULL;
pll_clk->hw.reg = reg + clks->offset;
if (streq(name, SOCFPGA_BOOT_CLK))
init.ops = &clk_boot_ops;
else
init.ops = &n5x_clk_pll_ops;
init.name = name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = NULL;
init.parent_data = clks->parent_data;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
hw_clk = &pll_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(pll_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
struct clk_hw *agilex5_register_pll(const struct agilex5_pll_clock *clks,
void __iomem *reg)
{
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
struct clk_init_data init;
const char *name = clks->name;
int ret;
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return NULL;
pll_clk->hw.reg = reg + clks->offset;
if (streq(name, SOCFPGA_BOOT_CLK))
init.ops = &clk_boot_ops;
else
init.ops = &agilex_clk_pll_ops;
init.name = name;
init.flags = clks->flags;
init.num_parents = clks->num_parents;
init.parent_names = clks->parent_names;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
hw_clk = &pll_clk->hw.hw;
ret = clk_hw_register(NULL, hw_clk);
if (ret) {
kfree(pll_clk);
return ERR_PTR(ret);
}
return hw_clk;
}
+135
View File
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011-2012 Calxeda, Inc.
* Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
*
* Based from clk-highbank.c
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk.h"
/* Clock bypass bits */
#define MAINPLL_BYPASS (1<<0)
#define SDRAMPLL_BYPASS (1<<1)
#define SDRAMPLL_SRC_BYPASS (1<<2)
#define PERPLL_BYPASS (1<<3)
#define PERPLL_SRC_BYPASS (1<<4)
#define SOCFPGA_PLL_BG_PWRDWN 0
#define SOCFPGA_PLL_EXT_ENA 1
#define SOCFPGA_PLL_PWR_DOWN 2
#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
#define SOCFPGA_PLL_DIVF_SHIFT 3
#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
#define SOCFPGA_PLL_DIVQ_SHIFT 16
#define CLK_MGR_PLL_CLK_SRC_SHIFT 22
#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
void __iomem *clk_mgr_base_addr;
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
u32 divf, divq, reg;
unsigned long long vco_freq;
u32 bypass;
reg = readl(socfpgaclk->hw.reg);
bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
if (bypass & MAINPLL_BYPASS)
return parent_rate;
divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
vco_freq = (unsigned long long)parent_rate * (divf + 1);
do_div(vco_freq, (1 + divq));
return (unsigned long)vco_freq;
}
static u8 clk_pll_get_parent(struct clk_hw *hwclk)
{
u32 pll_src;
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
pll_src = readl(socfpgaclk->hw.reg);
return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
CLK_MGR_PLL_CLK_SRC_MASK;
}
static const struct clk_ops clk_pll_ops = {
.recalc_rate = clk_pll_recalc_rate,
.get_parent = clk_pll_get_parent,
};
static void __init __socfpga_pll_init(struct device_node *node,
const struct clk_ops *ops)
{
u32 reg;
struct clk_hw *hw_clk;
struct socfpga_pll *pll_clk;
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
struct device_node *clkmgr_np;
int rc;
of_property_read_u32(node, "reg", &reg);
pll_clk = kzalloc_obj(*pll_clk);
if (WARN_ON(!pll_clk))
return;
clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
clk_mgr_base_addr = of_iomap(clkmgr_np, 0);
of_node_put(clkmgr_np);
BUG_ON(!clk_mgr_base_addr);
pll_clk->hw.reg = clk_mgr_base_addr + reg;
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
init.ops = ops;
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
init.parent_names = parent_name;
pll_clk->hw.hw.init = &init;
pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
hw_clk = &pll_clk->hw.hw;
rc = clk_hw_register(NULL, hw_clk);
if (rc) {
pr_err("Could not register clock:%s\n", clk_name);
goto err_clk_hw_register;
}
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
clk_name);
goto err_of_clk_add_hw_provider;
}
return;
err_of_clk_add_hw_provider:
clk_hw_unregister(hw_clk);
err_clk_hw_register:
kfree(pll_clk);
}
void __init socfpga_pll_init(struct device_node *node)
{
__socfpga_pll_init(node, &clk_pll_ops);
}
+451
View File
@@ -0,0 +1,451 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017, Intel Corporation
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/stratix10-clock.h>
#include "stratix10-clk.h"
static const struct clk_parent_data pll_mux[] = {
{ .fw_name = "osc1",
.name = "osc1" },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk" },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk" },
};
static const struct clk_parent_data cntr_mux[] = {
{ .fw_name = "main_pll",
.name = "main_pll", },
{ .fw_name = "periph_pll",
.name = "periph_pll", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data boot_mux[] = {
{ .fw_name = "osc1",
.name = "osc1" },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk" },
};
static const struct clk_parent_data noc_free_mux[] = {
{ .fw_name = "main_noc_base_clk",
.name = "main_noc_base_clk", },
{ .fw_name = "peri_noc_base_clk",
.name = "peri_noc_base_clk", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data emaca_free_mux[] = {
{ .fw_name = "peri_emaca_clk",
.name = "peri_emaca_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emacb_free_mux[] = {
{ .fw_name = "peri_emacb_clk",
.name = "peri_emacb_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emac_ptp_free_mux[] = {
{ .fw_name = "peri_emac_ptp_clk",
.name = "peri_emac_ptp_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data gpio_db_free_mux[] = {
{ .fw_name = "peri_gpio_db_clk",
.name = "peri_gpio_db_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data sdmmc_free_mux[] = {
{ .fw_name = "main_sdmmc_clk",
.name = "main_sdmmc_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data s2f_usr1_free_mux[] = {
{ .fw_name = "peri_s2f_usr1_clk",
.name = "peri_s2f_usr1_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data psi_ref_free_mux[] = {
{ .fw_name = "peri_psi_ref_clk",
.name = "peri_psi_ref_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data mpu_mux[] = {
{ .fw_name = "mpu_free_clk",
.name = "mpu_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data s2f_usr0_mux[] = {
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emac_mux[] = {
{ .fw_name = "emaca_free_clk",
.name = "emaca_free_clk", },
{ .fw_name = "emacb_free_clk",
.name = "emacb_free_clk", },
};
static const struct clk_parent_data noc_mux[] = {
{ .fw_name = "noc_free_clk",
.name = "noc_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data mpu_free_mux[] = {
{ .fw_name = "main_mpu_base_clk",
.name = "main_mpu_base_clk", },
{ .fw_name = "peri_mpu_base_clk",
.name = "peri_mpu_base_clk", },
{ .fw_name = "osc1",
.name = "osc1", },
{ .fw_name = "cb-intosc-hs-div2-clk",
.name = "cb-intosc-hs-div2-clk", },
{ .fw_name = "f2s-free-clk",
.name = "f2s-free-clk", },
};
static const struct clk_parent_data sdmmc_mux[] = {
{ .fw_name = "sdmmc_free_clk",
.name = "sdmmc_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data s2f_user1_mux[] = {
{ .fw_name = "s2f_user1_free_clk",
.name = "s2f_user1_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data psi_mux[] = {
{ .fw_name = "psi_ref_free_clk",
.name = "psi_ref_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data gpio_db_mux[] = {
{ .fw_name = "gpio_db_free_clk",
.name = "gpio_db_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
static const struct clk_parent_data emac_ptp_mux[] = {
{ .fw_name = "emac_ptp_free_clk",
.name = "emac_ptp_free_clk", },
{ .fw_name = "boot_clk",
.name = "boot_clk", },
};
/* clocks in AO (always on) controller */
static const struct stratix10_pll_clock s10_pll_clks[] = {
{ STRATIX10_BOOT_CLK, "boot_clk", boot_mux, ARRAY_SIZE(boot_mux), 0,
0x0},
{ STRATIX10_MAIN_PLL_CLK, "main_pll", pll_mux, ARRAY_SIZE(pll_mux),
0, 0x74},
{ STRATIX10_PERIPH_PLL_CLK, "periph_pll", pll_mux, ARRAY_SIZE(pll_mux),
0, 0xe4},
};
static const struct stratix10_perip_c_clock s10_main_perip_c_clks[] = {
{ STRATIX10_MAIN_MPU_BASE_CLK, "main_mpu_base_clk", "main_pll", NULL, 1, 0, 0x84},
{ STRATIX10_MAIN_NOC_BASE_CLK, "main_noc_base_clk", "main_pll", NULL, 1, 0, 0x88},
{ STRATIX10_PERI_MPU_BASE_CLK, "peri_mpu_base_clk", "periph_pll", NULL, 1, 0,
0xF4},
{ STRATIX10_PERI_NOC_BASE_CLK, "peri_noc_base_clk", "periph_pll", NULL, 1, 0,
0xF8},
};
static const struct stratix10_perip_cnt_clock s10_main_perip_cnt_clks[] = {
{ STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, mpu_free_mux, ARRAY_SIZE(mpu_free_mux),
0, 0x48, 0, 0, 0},
{ STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
0, 0x4C, 0, 0x3C, 1},
{ STRATIX10_MAIN_EMACA_CLK, "main_emaca_clk", "main_noc_base_clk", NULL, 1, 0,
0x50, 0, 0, 0},
{ STRATIX10_MAIN_EMACB_CLK, "main_emacb_clk", "main_noc_base_clk", NULL, 1, 0,
0x54, 0, 0, 0},
{ STRATIX10_MAIN_EMAC_PTP_CLK, "main_emac_ptp_clk", "main_noc_base_clk", NULL, 1, 0,
0x58, 0, 0, 0},
{ STRATIX10_MAIN_GPIO_DB_CLK, "main_gpio_db_clk", "main_noc_base_clk", NULL, 1, 0,
0x5C, 0, 0, 0},
{ STRATIX10_MAIN_SDMMC_CLK, "main_sdmmc_clk", "main_noc_base_clk", NULL, 1, 0,
0x60, 0, 0, 0},
{ STRATIX10_MAIN_S2F_USR0_CLK, "main_s2f_usr0_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0x64, 0, 0, 0},
{ STRATIX10_MAIN_S2F_USR1_CLK, "main_s2f_usr1_clk", "main_noc_base_clk", NULL, 1, 0,
0x68, 0, 0, 0},
{ STRATIX10_MAIN_PSI_REF_CLK, "main_psi_ref_clk", "main_noc_base_clk", NULL, 1, 0,
0x6C, 0, 0, 0},
{ STRATIX10_PERI_EMACA_CLK, "peri_emaca_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xBC, 0, 0, 0},
{ STRATIX10_PERI_EMACB_CLK, "peri_emacb_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xC0, 0, 0, 0},
{ STRATIX10_PERI_EMAC_PTP_CLK, "peri_emac_ptp_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xC4, 0, 0, 0},
{ STRATIX10_PERI_GPIO_DB_CLK, "peri_gpio_db_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xC8, 0, 0, 0},
{ STRATIX10_PERI_SDMMC_CLK, "peri_sdmmc_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xCC, 0, 0, 0},
{ STRATIX10_PERI_S2F_USR0_CLK, "peri_s2f_usr0_clk", "peri_noc_base_clk", NULL, 1, 0,
0xD0, 0, 0, 0},
{ STRATIX10_PERI_S2F_USR1_CLK, "peri_s2f_usr1_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
0, 0xD4, 0, 0, 0},
{ STRATIX10_PERI_PSI_REF_CLK, "peri_psi_ref_clk", "peri_noc_base_clk", NULL, 1, 0,
0xD8, 0, 0, 0},
{ STRATIX10_L4_SYS_FREE_CLK, "l4_sys_free_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0,
0, 4, 0x3C, 1},
{ STRATIX10_EMAC_A_FREE_CLK, "emaca_free_clk", NULL, emaca_free_mux, ARRAY_SIZE(emaca_free_mux),
0, 0, 2, 0xB0, 0},
{ STRATIX10_EMAC_B_FREE_CLK, "emacb_free_clk", NULL, emacb_free_mux, ARRAY_SIZE(emacb_free_mux),
0, 0, 2, 0xB0, 1},
{ STRATIX10_EMAC_PTP_FREE_CLK, "emac_ptp_free_clk", NULL, emac_ptp_free_mux,
ARRAY_SIZE(emac_ptp_free_mux), 0, 0, 2, 0xB0, 2},
{ STRATIX10_GPIO_DB_FREE_CLK, "gpio_db_free_clk", NULL, gpio_db_free_mux,
ARRAY_SIZE(gpio_db_free_mux), 0, 0, 0, 0xB0, 3},
{ STRATIX10_SDMMC_FREE_CLK, "sdmmc_free_clk", NULL, sdmmc_free_mux,
ARRAY_SIZE(sdmmc_free_mux), 0, 0, 0, 0xB0, 4},
{ STRATIX10_S2F_USER1_FREE_CLK, "s2f_user1_free_clk", NULL, s2f_usr1_free_mux,
ARRAY_SIZE(s2f_usr1_free_mux), 0, 0, 0, 0xB0, 5},
{ STRATIX10_PSI_REF_FREE_CLK, "psi_ref_free_clk", NULL, psi_ref_free_mux,
ARRAY_SIZE(psi_ref_free_mux), 0, 0, 0, 0xB0, 6},
};
static const struct stratix10_gate_clock s10_gate_clks[] = {
{ STRATIX10_MPU_CLK, "mpu_clk", NULL, mpu_mux, ARRAY_SIZE(mpu_mux), 0, 0x30,
0, 0, 0, 0, 0x3C, 0, 0},
{ STRATIX10_MPU_PERIPH_CLK, "mpu_periph_clk", "mpu_clk", NULL, 1, 0, 0x30,
0, 0, 0, 0, 0, 0, 4},
{ STRATIX10_MPU_L2RAM_CLK, "mpu_l2ram_clk", "mpu_clk", NULL, 1, 0, 0x30,
0, 0, 0, 0, 0, 0, 2},
{ STRATIX10_L4_MAIN_CLK, "l4_main_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
1, 0x70, 0, 2, 0x3C, 1, 0},
{ STRATIX10_L4_MP_CLK, "l4_mp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
2, 0x70, 8, 2, 0x3C, 1, 0},
{ STRATIX10_L4_SP_CLK, "l4_sp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), CLK_IS_CRITICAL, 0x30,
3, 0x70, 16, 2, 0x3C, 1, 0},
{ STRATIX10_CS_AT_CLK, "cs_at_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
4, 0x70, 24, 2, 0x3C, 1, 0},
{ STRATIX10_CS_TRACE_CLK, "cs_trace_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
4, 0x70, 26, 2, 0x3C, 1, 0},
{ STRATIX10_CS_PDBG_CLK, "cs_pdbg_clk", "cs_at_clk", NULL, 1, 0, 0x30,
4, 0x70, 28, 1, 0, 0, 0},
{ STRATIX10_CS_TIMER_CLK, "cs_timer_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
5, 0, 0, 0, 0x3C, 1, 0},
{ STRATIX10_S2F_USER0_CLK, "s2f_user0_clk", NULL, s2f_usr0_mux, ARRAY_SIZE(s2f_usr0_mux), 0, 0x30,
6, 0, 0, 0, 0, 0, 0},
{ STRATIX10_EMAC0_CLK, "emac0_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
0, 0, 0, 0, 0xDC, 26, 0},
{ STRATIX10_EMAC1_CLK, "emac1_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
1, 0, 0, 0, 0xDC, 27, 0},
{ STRATIX10_EMAC2_CLK, "emac2_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
2, 0, 0, 0, 0xDC, 28, 0},
{ STRATIX10_EMAC_PTP_CLK, "emac_ptp_clk", NULL, emac_ptp_mux, ARRAY_SIZE(emac_ptp_mux), 0, 0xA4,
3, 0, 0, 0, 0xB0, 2, 0},
{ STRATIX10_GPIO_DB_CLK, "gpio_db_clk", NULL, gpio_db_mux, ARRAY_SIZE(gpio_db_mux), 0, 0xA4,
4, 0xE0, 0, 16, 0xB0, 3, 0},
{ STRATIX10_SDMMC_CLK, "sdmmc_clk", NULL, sdmmc_mux, ARRAY_SIZE(sdmmc_mux), 0, 0xA4,
5, 0, 0, 0, 0xB0, 4, 4},
{ STRATIX10_S2F_USER1_CLK, "s2f_user1_clk", NULL, s2f_user1_mux, ARRAY_SIZE(s2f_user1_mux), 0, 0xA4,
6, 0, 0, 0, 0xB0, 5, 0},
{ STRATIX10_PSI_REF_CLK, "psi_ref_clk", NULL, psi_mux, ARRAY_SIZE(psi_mux), 0, 0xA4,
7, 0, 0, 0, 0xB0, 6, 0},
{ STRATIX10_USB_CLK, "usb_clk", "l4_mp_clk", NULL, 1, 0, 0xA4,
8, 0, 0, 0, 0, 0, 0},
{ STRATIX10_SPI_M_CLK, "spi_m_clk", "l4_mp_clk", NULL, 1, 0, 0xA4,
9, 0, 0, 0, 0, 0, 0},
{ STRATIX10_NAND_X_CLK, "nand_x_clk", "l4_mp_clk", NULL, 1, 0, 0xA4,
10, 0, 0, 0, 0, 0, 0},
{ STRATIX10_NAND_CLK, "nand_clk", "nand_x_clk", NULL, 1, 0, 0xA4,
10, 0, 0, 0, 0, 0, 4},
{ STRATIX10_NAND_ECC_CLK, "nand_ecc_clk", "nand_x_clk", NULL, 1, 0, 0xA4,
10, 0, 0, 0, 0, 0, 4},
};
static int s10_clk_register_c_perip(const struct stratix10_perip_c_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int s10_clk_register_cnt_perip(const struct stratix10_perip_cnt_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_cnt_periph(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int s10_clk_register_gate(const struct stratix10_gate_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_gate(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int s10_clk_register_pll(const struct stratix10_pll_clock *clks,
int nums, struct stratix10_clock_data *data)
{
struct clk_hw *hw_clk;
void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
hw_clk = s10_register_pll(&clks[i], base);
if (IS_ERR(hw_clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
data->clk_data.hws[clks[i].id] = hw_clk;
}
return 0;
}
static int s10_clkmgr_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct stratix10_clock_data *clk_data;
void __iomem *base;
int i, num_clks;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
pr_err("%s: failed to map clock registers\n", __func__);
return PTR_ERR(base);
}
num_clks = STRATIX10_NUM_CLKS;
clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws,
num_clks), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->base = base;
clk_data->clk_data.num = num_clks;
for (i = 0; i < num_clks; i++)
clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
s10_clk_register_pll(s10_pll_clks, ARRAY_SIZE(s10_pll_clks), clk_data);
s10_clk_register_c_perip(s10_main_perip_c_clks,
ARRAY_SIZE(s10_main_perip_c_clks), clk_data);
s10_clk_register_cnt_perip(s10_main_perip_cnt_clks,
ARRAY_SIZE(s10_main_perip_cnt_clks),
clk_data);
s10_clk_register_gate(s10_gate_clks, ARRAY_SIZE(s10_gate_clks),
clk_data);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data);
return 0;
}
static int s10_clkmgr_probe(struct platform_device *pdev)
{
return s10_clkmgr_init(pdev);
}
static const struct of_device_id stratix10_clkmgr_match_table[] = {
{ .compatible = "intel,stratix10-clkmgr",
.data = s10_clkmgr_init },
{ }
};
static struct platform_driver stratix10_clkmgr_driver = {
.probe = s10_clkmgr_probe,
.driver = {
.name = "stratix10-clkmgr",
.suppress_bind_attrs = true,
.of_match_table = stratix10_clkmgr_match_table,
},
};
static int __init s10_clk_init(void)
{
return platform_driver_register(&stratix10_clkmgr_driver);
}
core_initcall(s10_clk_init);
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011-2012 Calxeda, Inc.
* Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
*
* Based from clk-highbank.c
*/
#include <linux/of.h>
#include "clk.h"
CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init);
CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init);
CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init);
CLK_OF_DECLARE(socfpga_a10_pll_clk, "altr,socfpga-a10-pll-clock",
socfpga_a10_pll_init);
CLK_OF_DECLARE(socfpga_a10_perip_clk, "altr,socfpga-a10-perip-clk",
socfpga_a10_periph_init);
CLK_OF_DECLARE(socfpga_a10_gate_clk, "altr,socfpga-a10-gate-clk",
socfpga_a10_gate_init);
+66
View File
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de>
*
* based on drivers/clk/tegra/clk.h
*/
#ifndef __SOCFPGA_CLK_H
#define __SOCFPGA_CLK_H
#include <linux/clk-provider.h>
/* Clock Manager offsets */
#define CLKMGR_CTRL 0x0
#define CLKMGR_BYPASS 0x4
#define CLKMGR_DBCTRL 0x10
#define CLKMGR_L4SRC 0x70
#define CLKMGR_PERPLL_SRC 0xAC
#define SOCFPGA_MAX_PARENTS 5
#define streq(a, b) (strcmp((a), (b)) == 0)
#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
#define SYSMGR_SDMMC_CTRL_SET_AS10(smplsel, drvsel) \
((((smplsel) & 0x7) << 4) | (((drvsel) & 0x7) << 0))
extern void __iomem *clk_mgr_base_addr;
extern void __iomem *clk_mgr_a10_base_addr;
void __init socfpga_pll_init(struct device_node *node);
void __init socfpga_periph_init(struct device_node *node);
void __init socfpga_gate_init(struct device_node *node);
void socfpga_a10_pll_init(struct device_node *node);
void socfpga_a10_periph_init(struct device_node *node);
void socfpga_a10_gate_init(struct device_node *node);
struct socfpga_pll {
struct clk_gate hw;
};
struct socfpga_gate_clk {
struct clk_gate hw;
char *parent_name;
u32 fixed_div;
void __iomem *div_reg;
void __iomem *bypass_reg;
struct regmap *sys_mgr_base_addr;
u32 width; /* only valid if div_reg != 0 */
u32 shift; /* only valid if div_reg != 0 */
u32 bypass_shift; /* only valid if bypass_reg != 0 */
};
struct socfpga_periph_clk {
struct clk_gate hw;
char *parent_name;
u32 fixed_div;
void __iomem *div_reg;
void __iomem *bypass_reg;
u32 width; /* only valid if div_reg != 0 */
u32 shift; /* only valid if div_reg != 0 */
u32 bypass_shift; /* only valid if bypass_reg != 0 */
};
#endif /* SOCFPGA_CLK_H */
@@ -0,0 +1,135 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2017, Intel Corporation
*/
#ifndef __STRATIX10_CLK_H
#define __STRATIX10_CLK_H
struct stratix10_clock_data {
void __iomem *base;
/* Must be last */
struct clk_hw_onecell_data clk_data;
};
struct stratix10_pll_clock {
unsigned int id;
const char *name;
const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long flags;
unsigned long offset;
};
struct stratix10_perip_c_clock {
unsigned int id;
const char *name;
const char *parent_name;
const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long flags;
unsigned long offset;
};
struct n5x_perip_c_clock {
unsigned int id;
const char *name;
const char *parent_name;
const char *const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
unsigned long shift;
};
struct stratix10_perip_cnt_clock {
unsigned int id;
const char *name;
const char *parent_name;
const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 fixed_divider;
unsigned long bypass_reg;
unsigned long bypass_shift;
};
struct stratix10_gate_clock {
unsigned int id;
const char *name;
const char *parent_name;
const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long flags;
unsigned long gate_reg;
u8 gate_idx;
unsigned long div_reg;
u8 div_offset;
u8 div_width;
unsigned long bypass_reg;
u8 bypass_shift;
u8 fixed_div;
};
struct agilex5_pll_clock {
unsigned int id;
const char *name;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
};
struct agilex5_perip_cnt_clock {
unsigned int id;
const char *name;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 fixed_divider;
unsigned long bypass_reg;
unsigned long bypass_shift;
};
struct agilex5_gate_clock {
unsigned int id;
const char *name;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long gate_reg;
u8 gate_idx;
unsigned long div_reg;
u8 div_offset;
u8 div_width;
unsigned long bypass_reg;
u8 bypass_shift;
u8 fixed_div;
};
struct clk_hw *s10_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg);
struct clk_hw *agilex_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg);
struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks,
void __iomem *reg);
struct clk_hw *agilex5_register_pll(const struct agilex5_pll_clock *clks,
void __iomem *reg);
struct clk_hw *agilex5_register_cnt_periph(const struct agilex5_perip_cnt_clock *clks,
void __iomem *regbase);
struct clk_hw *agilex5_register_gate(const struct agilex5_gate_clock *clks,
void __iomem *regbase);
struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,
void __iomem *reg);
struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,
void __iomem *reg);
struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
void __iomem *reg);
struct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks,
void __iomem *reg);
struct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks,
void __iomem *reg);
#endif /* __STRATIX10_CLK_H */