i.MX clock updates for 4.10:

- A patch series to fix the long standing issue with glitchy parent
    mux of ldb_di_clk, which can hang up LVDS display when ipu_di_clk
    is sourced from ldb_di_clk.
  - A patch to add imx6ull clock support on top of imx6ul clock driver.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJYKni2AAoJEFBXWFqHsHzOu+UIAKpiVKqacJYxD1wEJDh9Mtq2
 o9U10aGY6yl3ZEa5Ik8OTqg2Aa6ZT5kaV8h9i5uv7XIGqCSM6SUcWweF0KlRyJP4
 7bjz9rcir2/zXys+dVuRodUiF8uowoFxgw7wAHwHfzs1oA7ihQaUB4v6vBiNbADq
 zheCn2AqDWhIKAOFkLcyEid2IyIz0S/tlyzfElBmukSons+0zTrJ+e9QePzcuRZO
 TnhZAxc/FQwcPZ/a2kiwiOOfQXWQld5pIeIp1YHWD4+L4m0Yxb/WK1yaocTxox2O
 Ek8BPlkpNYdic8g3DRlwZGCHe5UaESSEQ3pyXlgYAdIOA2i+0tlnnCVCW0gNvQc=
 =DJo7
 -----END PGP SIGNATURE-----

Merge tag 'imx-clk-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into clk-next

Pull i.MX clock updates from Shawn Guo:

 - A patch series to fix the long standing issue with glitchy parent
   mux of ldb_di_clk, which can hang up LVDS display when ipu_di_clk
   is sourced from ldb_di_clk.
 - A patch to add imx6ull clock support on top of imx6ul clock driver.

* tag 'imx-clk-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  clk: imx: clk-imx6ul: add clk support for imx6ull
  clk: imx6: Fix procedure to switch the parent of LDB_DI_CLK
  clk: imx6: Make the LDB_DI0 and LDB_DI1 clocks read-only
  clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf
This commit is contained in:
Stephen Boyd 2016-11-16 11:16:07 -08:00
commit c284a7ba72
4 changed files with 356 additions and 22 deletions

View File

@ -156,10 +156,267 @@ static struct clk ** const uart_clks[] __initconst = {
NULL NULL
}; };
static int ldb_di_sel_by_clock_id(int clock_id)
{
switch (clock_id) {
case IMX6QDL_CLK_PLL5_VIDEO_DIV:
if (clk_on_imx6q() &&
imx_get_soc_revision() == IMX_CHIP_REVISION_1_0)
return -ENOENT;
return 0;
case IMX6QDL_CLK_PLL2_PFD0_352M:
return 1;
case IMX6QDL_CLK_PLL2_PFD2_396M:
return 2;
case IMX6QDL_CLK_MMDC_CH1_AXI:
return 3;
case IMX6QDL_CLK_PLL3_USB_OTG:
return 4;
default:
return -ENOENT;
}
}
static void of_assigned_ldb_sels(struct device_node *node,
unsigned int *ldb_di0_sel,
unsigned int *ldb_di1_sel)
{
struct of_phandle_args clkspec;
int index, rc, num_parents;
int parent, child, sel;
num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells");
for (index = 0; index < num_parents; index++) {
rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return;
}
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
pr_err("ccm: parent clock %d not in ccm\n", index);
return;
}
parent = clkspec.args[0];
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0)
return;
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
pr_err("ccm: child clock %d not in ccm\n", index);
return;
}
child = clkspec.args[0];
if (child != IMX6QDL_CLK_LDB_DI0_SEL &&
child != IMX6QDL_CLK_LDB_DI1_SEL)
continue;
sel = ldb_di_sel_by_clock_id(parent);
if (sel < 0) {
pr_err("ccm: invalid ldb_di%d parent clock: %d\n",
child == IMX6QDL_CLK_LDB_DI1_SEL, parent);
continue;
}
if (child == IMX6QDL_CLK_LDB_DI0_SEL)
*ldb_di0_sel = sel;
if (child == IMX6QDL_CLK_LDB_DI1_SEL)
*ldb_di1_sel = sel;
}
}
#define CCM_CCDR 0x04
#define CCM_CCSR 0x0c
#define CCM_CS2CDR 0x2c
#define CCDR_MMDC_CH1_MASK BIT(16)
#define CCSR_PLL3_SW_CLK_SEL BIT(0)
#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT 9
#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT 12
static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base)
{
unsigned int reg;
reg = readl_relaxed(ccm_base + CCM_CCDR);
reg |= CCDR_MMDC_CH1_MASK;
writel_relaxed(reg, ccm_base + CCM_CCDR);
}
/*
* The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk
* via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the
* bypass clock source, since there is no CG bit for mmdc_ch1.
*/
static void mmdc_ch1_disable(void __iomem *ccm_base)
{
unsigned int reg;
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2_CLK2_SEL],
clk[IMX6QDL_CLK_PLL3_USB_OTG]);
/*
* Handshake with mmdc_ch1 module must be masked when changing
* periph2_clk_sel.
*/
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_CLK2]);
/* Disable pll3_sw_clk by selecting the bypass clock source */
reg = readl_relaxed(ccm_base + CCM_CCSR);
reg |= CCSR_PLL3_SW_CLK_SEL;
writel_relaxed(reg, ccm_base + CCM_CCSR);
}
static void mmdc_ch1_reenable(void __iomem *ccm_base)
{
unsigned int reg;
/* Enable pll3_sw_clk by disabling the bypass */
reg = readl_relaxed(ccm_base + CCM_CCSR);
reg &= ~CCSR_PLL3_SW_CLK_SEL;
writel_relaxed(reg, ccm_base + CCM_CCSR);
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_PRE]);
}
/*
* We have to follow a strict procedure when changing the LDB clock source,
* otherwise we risk introducing a glitch that can lock up the LDB divider.
* Things to keep in mind:
*
* 1. The current and new parent clock inputs to the mux must be disabled.
* 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which
* has no CG bit.
* 3. pll2_pfd2_396m can not be gated if it is used as memory clock.
* 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four
* options are in one mux and the PLL3 option along with three unused
* inputs is in a second mux. There is a third mux with two inputs used
* to decide between the first and second 4-port mux:
*
* pll5_video_div 0 --|\
* pll2_pfd0_352m 1 --| |_
* pll2_pfd2_396m 2 --| | `-|\
* mmdc_ch1_axi 3 --|/ | |
* | |--
* pll3_usb_otg 4 --|\ | |
* 5 --| |_,-|/
* 6 --| |
* 7 --|/
*
* The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time.
* The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below
* switches the parent to the bottom mux first and then manipulates the top
* mux to ensure that no glitch will enter the divider.
*/
static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
{
unsigned int reg;
unsigned int sel[2][4];
int i;
reg = readl_relaxed(ccm_base + CCM_CS2CDR);
sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7;
sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7;
sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0];
sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0];
of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]);
for (i = 0; i < 2; i++) {
/* Warn if a glitch might have been introduced already */
if (sel[i][0] != 3) {
pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n",
i, sel[i][0]);
}
if (sel[i][0] == sel[i][3])
continue;
/* Only switch to or from pll2_pfd2_396m if it is disabled */
if ((sel[i][0] == 2 || sel[i][3] == 2) &&
(clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) ==
clk[IMX6QDL_CLK_PLL2_PFD2_396M])) {
pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n",
i);
sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0];
continue;
}
/* First switch to the bottom mux */
sel[i][1] = sel[i][0] | 4;
/* Then configure the top mux before switching back to it */
sel[i][2] = sel[i][3] | 4;
pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i,
sel[i][0], sel[i][1], sel[i][2], sel[i][3]);
}
if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3])
return;
mmdc_ch1_disable(ccm_base);
for (i = 1; i < 4; i++) {
reg = readl_relaxed(ccm_base + CCM_CS2CDR);
reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
(7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
(sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
writel_relaxed(reg, ccm_base + CCM_CS2CDR);
}
mmdc_ch1_reenable(ccm_base);
}
#define CCM_ANALOG_PLL_VIDEO 0xa0
#define CCM_ANALOG_PFD_480 0xf0
#define CCM_ANALOG_PFD_528 0x100
#define PLL_ENABLE BIT(13)
#define PFD0_CLKGATE BIT(7)
#define PFD1_CLKGATE BIT(15)
#define PFD2_CLKGATE BIT(23)
#define PFD3_CLKGATE BIT(31)
static void disable_anatop_clocks(void __iomem *anatop_base)
{
unsigned int reg;
/* Make sure PLL2 PFDs 0-2 are gated */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_528);
/* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */
if (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) ==
clk[IMX6QDL_CLK_PLL2_PFD2_396M])
reg |= PFD0_CLKGATE | PFD1_CLKGATE;
else
reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_528);
/* Make sure PLL3 PFDs 0-3 are gated */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_480);
reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_480);
/* Make sure PLL5 is disabled */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PLL_VIDEO);
reg &= ~PLL_ENABLE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PLL_VIDEO);
}
static void __init imx6q_clocks_init(struct device_node *ccm_node) static void __init imx6q_clocks_init(struct device_node *ccm_node)
{ {
struct device_node *np; struct device_node *np;
void __iomem *base; void __iomem *anatop_base, *base;
int i; int i;
int ret; int ret;
@ -172,7 +429,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0); clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0);
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
base = of_iomap(np, 0); anatop_base = base = of_iomap(np, 0);
WARN_ON(!base); WARN_ON(!base);
/* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
@ -330,8 +587,20 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); disable_anatop_clocks(anatop_base);
imx6q_mmdc_ch1_mask_handshake(base);
/*
* The LDB_DI0/1_SEL muxes are registered read-only due to a hardware
* bug. Set the muxes to the requested values before registering the
* ldb_di_sel clocks.
*/
init_ldb_clks(np, base);
clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
@ -582,12 +851,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL); clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL);
if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) ||
clk_on_imx6dl()) {
clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
clk_set_parent(clk[IMX6QDL_CLK_LDB_DI1_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
}
clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000); clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000);
if (clk_on_imx6dl()) if (clk_on_imx6dl())
clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]);

View File

@ -64,6 +64,10 @@ static const char *perclk_sels[] = { "ipg", "osc", };
static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
static const char *sim_sels[] = { "sim_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; static const char *sim_sels[] = { "sim_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
/* epdc_pre_sels, epdc_sels, esai_sels only exists on i.MX6ULL */
static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", };
static const char *esai_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", };
static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
static struct clk *clks[IMX6UL_CLK_END]; static struct clk *clks[IMX6UL_CLK_END];
static struct clk_onecell_data clk_data; static struct clk_onecell_data clk_data;
@ -102,6 +106,17 @@ static u32 share_count_audio;
static u32 share_count_sai1; static u32 share_count_sai1;
static u32 share_count_sai2; static u32 share_count_sai2;
static u32 share_count_sai3; static u32 share_count_sai3;
static u32 share_count_esai;
static inline int clk_on_imx6ul(void)
{
return of_machine_is_compatible("fsl,imx6ul");
}
static inline int clk_on_imx6ull(void)
{
return of_machine_is_compatible("fsl,imx6ull");
}
static void __init imx6ul_clocks_init(struct device_node *ccm_node) static void __init imx6ul_clocks_init(struct device_node *ccm_node)
{ {
@ -238,12 +253,19 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels)); clks[IMX6UL_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels));
clks[IMX6UL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); clks[IMX6UL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels));
clks[IMX6UL_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); clks[IMX6UL_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels));
if (clk_on_imx6ull())
clks[IMX6ULL_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, esai_sels, ARRAY_SIZE(esai_sels));
clks[IMX6UL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); clks[IMX6UL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels));
clks[IMX6UL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels, ARRAY_SIZE(enfc_sels)); clks[IMX6UL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels, ARRAY_SIZE(enfc_sels));
clks[IMX6UL_CLK_LDB_DI0_SEL] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels)); clks[IMX6UL_CLK_LDB_DI0_SEL] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels));
clks[IMX6UL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels)); clks[IMX6UL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels));
clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels)); if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels)); clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels));
clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels));
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_EPDC_PRE_SEL] = imx_clk_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels));
clks[IMX6ULL_CLK_EPDC_SEL] = imx_clk_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels));
}
clks[IMX6UL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); clks[IMX6UL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels));
clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels)); clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels));
clks[IMX6UL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); clks[IMX6UL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels));
@ -276,6 +298,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_SAI3_PODF] = imx_clk_divider("sai3_podf", "sai3_pred", base + 0x28, 16, 6); clks[IMX6UL_CLK_SAI3_PODF] = imx_clk_divider("sai3_podf", "sai3_pred", base + 0x28, 16, 6);
clks[IMX6UL_CLK_SAI1_PRED] = imx_clk_divider("sai1_pred", "sai1_sel", base + 0x28, 6, 3); clks[IMX6UL_CLK_SAI1_PRED] = imx_clk_divider("sai1_pred", "sai1_sel", base + 0x28, 6, 3);
clks[IMX6UL_CLK_SAI1_PODF] = imx_clk_divider("sai1_podf", "sai1_pred", base + 0x28, 0, 6); clks[IMX6UL_CLK_SAI1_PODF] = imx_clk_divider("sai1_podf", "sai1_pred", base + 0x28, 0, 6);
if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_ESAI_PRED] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3);
clks[IMX6ULL_CLK_ESAI_PODF] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3);
}
clks[IMX6UL_CLK_ENFC_PRED] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); clks[IMX6UL_CLK_ENFC_PRED] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3);
clks[IMX6UL_CLK_ENFC_PODF] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); clks[IMX6UL_CLK_ENFC_PODF] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6);
clks[IMX6UL_CLK_SAI2_PRED] = imx_clk_divider("sai2_pred", "sai2_sel", base + 0x2c, 6, 3); clks[IMX6UL_CLK_SAI2_PRED] = imx_clk_divider("sai2_pred", "sai2_sel", base + 0x2c, 6, 3);
@ -298,9 +324,15 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_APBHDMA] = imx_clk_gate2("apbh_dma", "bch_podf", base + 0x68, 4); clks[IMX6UL_CLK_APBHDMA] = imx_clk_gate2("apbh_dma", "bch_podf", base + 0x68, 4);
clks[IMX6UL_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); clks[IMX6UL_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6UL_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); clks[IMX6UL_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8);
clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10);
clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12);
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_DCP_CLK] = imx_clk_gate2("dcp", "ahb", base + 0x68, 10);
clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x68, 12);
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x68, 12);
}
clks[IMX6UL_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); clks[IMX6UL_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14);
clks[IMX6UL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_podf", base + 0x68, 16); clks[IMX6UL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_podf", base + 0x68, 16);
clks[IMX6UL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); clks[IMX6UL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18);
@ -309,7 +341,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_GPT2_SERIAL] = imx_clk_gate2("gpt2_serial", "perclk", base + 0x68, 26); clks[IMX6UL_CLK_GPT2_SERIAL] = imx_clk_gate2("gpt2_serial", "perclk", base + 0x68, 26);
clks[IMX6UL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28); clks[IMX6UL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28);
clks[IMX6UL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28); clks[IMX6UL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28);
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30); if (clk_on_imx6ul())
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30);
else if (clk_on_imx6ull())
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x80, 18);
/* CCGR1 */ /* CCGR1 */
clks[IMX6UL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); clks[IMX6UL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0);
@ -328,6 +363,11 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24); clks[IMX6UL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24);
/* CCGR2 */ /* CCGR2 */
if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x70, 0, &share_count_esai);
clks[IMX6ULL_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai);
clks[IMX6ULL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai);
}
clks[IMX6UL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x70, 2); clks[IMX6UL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x70, 2);
clks[IMX6UL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); clks[IMX6UL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6);
clks[IMX6UL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); clks[IMX6UL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8);
@ -340,8 +380,13 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
/* CCGR3 */ /* CCGR3 */
clks[IMX6UL_CLK_UART5_IPG] = imx_clk_gate2("uart5_ipg", "ipg", base + 0x74, 2); clks[IMX6UL_CLK_UART5_IPG] = imx_clk_gate2("uart5_ipg", "ipg", base + 0x74, 2);
clks[IMX6UL_CLK_UART5_SERIAL] = imx_clk_gate2("uart5_serial", "uart_podf", base + 0x74, 2); clks[IMX6UL_CLK_UART5_SERIAL] = imx_clk_gate2("uart5_serial", "uart_podf", base + 0x74, 2);
clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4); if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4); clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4);
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4);
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_EPDC_ACLK] = imx_clk_gate2("epdc_aclk", "axi", base + 0x74, 4);
clks[IMX6ULL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_podf", base + 0x74, 4);
}
clks[IMX6UL_CLK_UART6_IPG] = imx_clk_gate2("uart6_ipg", "ipg", base + 0x74, 6); clks[IMX6UL_CLK_UART6_IPG] = imx_clk_gate2("uart6_ipg", "ipg", base + 0x74, 6);
clks[IMX6UL_CLK_UART6_SERIAL] = imx_clk_gate2("uart6_serial", "uart_podf", base + 0x74, 6); clks[IMX6UL_CLK_UART6_SERIAL] = imx_clk_gate2("uart6_serial", "uart_podf", base + 0x74, 6);
clks[IMX6UL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); clks[IMX6UL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10);
@ -385,8 +430,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); clks[IMX6UL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0);
clks[IMX6UL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); clks[IMX6UL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2);
clks[IMX6UL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); clks[IMX6UL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4);
clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6); if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8); clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6);
clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8);
}
clks[IMX6UL_CLK_EIM] = imx_clk_gate2("eim", "eim_slow_podf", base + 0x80, 10); clks[IMX6UL_CLK_EIM] = imx_clk_gate2("eim", "eim_slow_podf", base + 0x80, 10);
clks[IMX6UL_CLK_PWM8] = imx_clk_gate2("pwm8", "perclk", base + 0x80, 16); clks[IMX6UL_CLK_PWM8] = imx_clk_gate2("pwm8", "perclk", base + 0x80, 16);
clks[IMX6UL_CLK_UART8_IPG] = imx_clk_gate2("uart8_ipg", "ipg", base + 0x80, 14); clks[IMX6UL_CLK_UART8_IPG] = imx_clk_gate2("uart8_ipg", "ipg", base + 0x80, 14);
@ -441,7 +488,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
} }
clk_set_parent(clks[IMX6UL_CLK_CAN_SEL], clks[IMX6UL_CLK_PLL3_60M]); clk_set_parent(clks[IMX6UL_CLK_CAN_SEL], clks[IMX6UL_CLK_PLL3_60M]);
clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]); if (clk_on_imx6ul())
clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]);
else if (clk_on_imx6ull())
clk_set_parent(clks[IMX6ULL_CLK_EPDC_PRE_SEL], clks[IMX6UL_CLK_PLL3_PFD2]);
clk_set_parent(clks[IMX6UL_CLK_ENFC_SEL], clks[IMX6UL_CLK_PLL2_PFD2]); clk_set_parent(clks[IMX6UL_CLK_ENFC_SEL], clks[IMX6UL_CLK_PLL2_PFD2]);
} }

View File

@ -75,6 +75,14 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate)
return clk_register_fixed_rate(NULL, name, NULL, 0, rate); return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
} }
static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, int num_parents)
{
return clk_register_mux(NULL, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg,
shift, width, CLK_MUX_READ_ONLY, &imx_ccm_lock);
}
static inline struct clk *imx_clk_fixed_factor(const char *name, static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div) const char *parent, unsigned int mult, unsigned int div)
{ {

View File

@ -236,6 +236,19 @@
#define IMX6UL_CLK_PLL3_120M 223 #define IMX6UL_CLK_PLL3_120M 223
#define IMX6UL_CLK_KPP 224 #define IMX6UL_CLK_KPP 224
#define IMX6UL_CLK_END 225 /* For i.MX6ULL */
#define IMX6ULL_CLK_ESAI_PRED 225
#define IMX6ULL_CLK_ESAI_PODF 226
#define IMX6ULL_CLK_ESAI_EXTAL 227
#define IMX6ULL_CLK_ESAI_MEM 228
#define IMX6ULL_CLK_ESAI_IPG 229
#define IMX6ULL_CLK_DCP_CLK 230
#define IMX6ULL_CLK_EPDC_PRE_SEL 231
#define IMX6ULL_CLK_EPDC_SEL 232
#define IMX6ULL_CLK_EPDC_PODF 233
#define IMX6ULL_CLK_EPDC_ACLK 234
#define IMX6ULL_CLK_EPDC_PIX 235
#define IMX6ULL_CLK_ESAI_SEL 236
#define IMX6UL_CLK_END 237
#endif /* __DT_BINDINGS_CLOCK_IMX6UL_H */ #endif /* __DT_BINDINGS_CLOCK_IMX6UL_H */