blob: 70605220d9053ebc6a99d3f6dcef3befbdb67485 [file] [log] [blame]
/*
* drivers/amlogic/clk/tm2/tm2.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <dt-bindings/clock/amlogic,tl1-clkc.h>
#include "../clkc.h"
#include "../tl1/tl1.h"
#include "tm2.h"
static const struct pll_rate_table tm2_pcie_pll_rate_table[] = {
PLL_RATE(100000000, 200, 1, 12),
{ /* sentinel */ },
};
static struct meson_clk_pll tm2_pcie_pll = {
.m = {
.reg_off = HHI_PCIE_PLL_CNTL0,
.shift = 0,
.width = 8,
},
.n = {
.reg_off = HHI_PCIE_PLL_CNTL0,
.shift = 10,
.width = 5,
},
.od = {
.reg_off = HHI_PCIE_PLL_CNTL0,
.shift = 16,
.width = 5,
},
.frac = {
.reg_off = HHI_PCIE_PLL_CNTL1,
.shift = 0,
.width = 12,
},
.rate_table = tm2_pcie_pll_rate_table,
.rate_count = ARRAY_SIZE(tm2_pcie_pll_rate_table),
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "pcie_pll",
.ops = &meson_tl1_pll_ops,
.parent_names = (const char *[]){ "xtal" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
},
};
static struct clk_gate tm2_pcie01_enable = {
.reg = (void *)HHI_PCIE_PLL_CNTL1,
.bit_idx = 29,
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "tm2_pcie01",
.ops = &clk_gate_ops,
.parent_names = (const char *[]){ "pcie_pll" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
static struct clk_gate tm2_pcie0_gate = {
.reg = (void *)HHI_PCIE_PLL_CNTL5,
.bit_idx = 3,
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "tm2_pcie0_gate",
.ops = &clk_gate_ops,
.parent_names = (const char *[]){ "tm2_pcie01" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
static struct clk_gate tm2_pcie1_gate = {
.reg = (void *)HHI_PCIE_PLL_CNTL1,
.bit_idx = 28,
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "tm2_pcie1_gate",
.ops = &clk_gate_ops,
.parent_names = (const char *[]){ "tm2_pcie01" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
/* clk81 gate for tm2 */
static MESON_GATE(tm2_vipnanoq, HHI_GCLK_MPEG1, 19);
static MESON_GATE(tm2_pcie1, HHI_GCLK_MPEG1, 24);
static MESON_GATE(tm2_pcie1phy, HHI_GCLK_MPEG1, 27);
static MESON_GATE(tm2_parserl, HHI_GCLK_MPEG1, 28);
static MESON_GATE(tm2_hdcp22_pclk, HHI_GCLK_MPEG2, 3);
static MESON_GATE(tm2_hdmitx_pclk, HHI_GCLK_MPEG2, 4);
static MESON_GATE(tm2_pcie0, HHI_GCLK_MPEG2, 6);
static MESON_GATE(tm2_pcie0phy, HHI_GCLK_MPEG2, 7);
static MESON_GATE(tm2_hdmirx_axi_pclk, HHI_GCLK_MPEG2, 12);
static MESON_GATE(tm2_dspb, HHI_GCLK_MPEG2, 26);
static MESON_GATE(tm2_dspa, HHI_GCLK_MPEG2, 27);
PNAME(dsp_parent_names) = { "fclk_div2", "fclk_div3",
"fclk_div5", "fclk_div7", "xtal", "gp0_pll", "gp1_pll", "hifi_pll" };
static MUX(dspa_clk_a_mux, HHI_DSP_CLK_CNTL, 0x7, 4,
dsp_parent_names, CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED);
static DIV(dspa_clk_a_div, HHI_DSP_CLK_CNTL, 0, 4, "dspa_clk_a_mux",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static GATE(dspa_clk_a_gate, HHI_DSP_CLK_CNTL, 7, "dspa_clk_a_div",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static MUX(dspa_clk_b_mux, HHI_DSP_CLK_CNTL, 0x7, 12,
dsp_parent_names, CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED);
static DIV(dspa_clk_b_div, HHI_DSP_CLK_CNTL, 8, 4, "dspa_clk_b_mux",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static GATE(dspa_clk_b_gate, HHI_DSP_CLK_CNTL, 7, "dspa_clk_b_div",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
PNAME(dspa_parent_names) = { "dspa_clk_a_gate",
"dspa_clk_b_gate" };
static MESON_MUX(dspa_clk_mux, HHI_DSP_CLK_CNTL, 0x1, 15,
dspa_parent_names, CLK_GET_RATE_NOCACHE);
static MUX(dspb_clk_a_mux, HHI_DSP_CLK_CNTL, 0x7, 20,
dsp_parent_names, CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED);
static DIV(dspb_clk_a_div, HHI_DSP_CLK_CNTL, 16, 4, "dspb_clk_a_mux",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static GATE(dspb_clk_a_gate, HHI_DSP_CLK_CNTL, 23, "dspb_clk_a_div",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static MUX(dspb_clk_b_mux, HHI_DSP_CLK_CNTL, 0x7, 28,
dsp_parent_names, CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED);
static DIV(dspb_clk_b_div, HHI_DSP_CLK_CNTL, 24, 4, "dspb_clk_b_mux",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
static GATE(dspb_clk_b_gate, HHI_DSP_CLK_CNTL, 23, "dspb_clk_b_div",
CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT);
PNAME(dspb_parent_names) = { "dspb_clk_a_gate",
"dspb_clk_b_gate" };
static MESON_MUX(dspb_clk_mux, HHI_DSP_CLK_CNTL, 0x1, 31,
dspb_parent_names, CLK_GET_RATE_NOCACHE);
static struct clk_gate *tm2_clk_gates[] = {
&tm2_vipnanoq,
&tm2_pcie1,
&tm2_pcie1phy,
&tm2_parserl,
&tm2_hdcp22_pclk,
&tm2_hdmitx_pclk,
&tm2_pcie0phy,
&tm2_hdmirx_axi_pclk,
&tm2_dspb,
&tm2_dspa,
&dspa_clk_a_gate,
&dspa_clk_b_gate,
&dspb_clk_a_gate,
&dspb_clk_b_gate,
&tm2_pcie0_gate,
&tm2_pcie1_gate,
&tm2_pcie0,
&tm2_pcie01_enable,
};
static struct clk_mux *tm2_clk_mux[] = {
&dspa_clk_a_mux,
&dspa_clk_b_mux,
&dspb_clk_a_mux,
&dspb_clk_b_mux,
&dspa_clk_mux,
&dspb_clk_mux,
};
static struct clk_divider *tm2_clk_divs[] = {
&dspa_clk_a_div,
&dspa_clk_b_div,
&dspb_clk_a_div,
&dspb_clk_b_div,
};
/* Array of all clocks provided by this provider */
static struct clk_hw *tm2_clk_hws[] = {
[CLKID_PCIE_PLL] = &tm2_pcie_pll.hw,
[CLKID_VIPNANOQ] = &tm2_vipnanoq.hw,
[CLKID_PCIE0] = &tm2_pcie0.hw,
[CLKID_PCIE1] = &tm2_pcie1.hw,
[CLKID_PCIE1PHY] = &tm2_pcie1phy.hw,
[CLKID_PARSER1] = &tm2_parserl.hw,
[CLKID_HDCP22_PCLK] = &tm2_hdcp22_pclk.hw,
[CLKID_HDMITX_PCLK] = &tm2_hdmitx_pclk.hw,
[CLKID_PCIE0PHY] = &tm2_pcie0phy.hw,
[CLKID_HDMITX_AXI_PCLK] = &tm2_hdmirx_axi_pclk.hw,
[CLKID_DSPB] = &tm2_dspb.hw,
[CLKID_DSPA] = &tm2_dspa.hw,
[CLKID_DSPA_MUX_A] = &dspa_clk_a_mux.hw,
[CLKID_DSPA_DIV_A] = &dspa_clk_a_div.hw,
[CLKID_DSPA_GATE_A] = &dspa_clk_a_gate.hw,
[CLKID_DSPA_MUX_B] = &dspa_clk_b_mux.hw,
[CLKID_DSPA_DIV_B] = &dspa_clk_b_div.hw,
[CLKID_DSPA_GATE_B] = &dspa_clk_b_gate.hw,
[CLKID_DSPA_MUX] = &dspa_clk_mux.hw,
[CLKID_DSPB_MUX_A] = &dspb_clk_a_mux.hw,
[CLKID_DSPB_DIV_A] = &dspb_clk_a_div.hw,
[CLKID_DSPB_GATE_A] = &dspb_clk_a_gate.hw,
[CLKID_DSPB_MUX_B] = &dspb_clk_b_mux.hw,
[CLKID_DSPB_DIV_B] = &dspb_clk_b_div.hw,
[CLKID_DSPB_GATE_B] = &dspb_clk_b_gate.hw,
[CLKID_DSPB_MUX] = &dspb_clk_mux.hw,
[CLKID_PCIE01_ENABLE] = &tm2_pcie01_enable.hw,
[CLKID_PCIE0_GATE] = &tm2_pcie0_gate.hw,
[CLKID_PCIE1_GATE] = &tm2_pcie1_gate.hw,
};
static void __init tm2_clkc_init(struct device_node *np)
{
int clkid, i;
if (!clk_base) {
pr_err("tm2 clock basic clock driver not prepare\n");
WARN_ON(IS_ERR(clk_base));
return;
}
/* Populate base address for pcie pll */
tm2_pcie_pll.base = clk_base;
/* Populate base address for media muxes */
for (i = 0; i < ARRAY_SIZE(tm2_clk_mux); i++)
tm2_clk_mux[i]->reg = clk_base +
(unsigned long)tm2_clk_mux[i]->reg;
/* Populate base address for media divs */
for (i = 0; i < ARRAY_SIZE(tm2_clk_divs); i++)
tm2_clk_divs[i]->reg = clk_base +
(unsigned long)tm2_clk_divs[i]->reg;
/* Populate base address for gates */
for (i = 0; i < ARRAY_SIZE(tm2_clk_gates); i++)
tm2_clk_gates[i]->reg = clk_base +
(unsigned long)tm2_clk_gates[i]->reg;
/* register tm2 clks, pcie pll is the first clock index */
for (clkid = CLKID_PCIE_PLL; clkid < GATE_BASE0; clkid++) {
if (tm2_clk_hws[clkid]) {
clks[clkid] = clk_register(NULL, tm2_clk_hws[clkid]);
WARN_ON(IS_ERR(clks[clkid]));
}
}
}
CLK_OF_DECLARE(tm2, "amlogic,tm2-clkc", tm2_clkc_init);