blob: 6730ad19cb4d6437822b228925ef137e595e6d22 [file] [log] [blame]
/*
* Copyright (c) 2014, 2017-2018, The Linux Foundation. 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 version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <soc/qcom/clock-local2.h>
#include <soc/qcom/clock-rpm.h>
#include <soc/qcom/clock-voter.h>
#include <soc/qcom/rpm-smd.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8909.h>
#include "clock.h"
#define GCC_DEBUG_CLK_CTL 0x74000
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
#define RPM_MEM_CLK_TYPE 0x326b6c63
#define RPM_SMD_KEY_ENABLE 0x62616E45
#define RPM_QPIC_CLK_TYPE 0x63697071
#define CXO_ID 0x0
#define QDSS_ID 0x1
#define BUS_SCALING 0x2
#define PCNOC_ID 0x0
#define SNOC_ID 0x1
#define BIMC_ID 0x0
#define QPIC_ID 0x0
/* XO clock */
#define BB_CLK1_ID 1
#define BB_CLK2_ID 2
#define BB_CLK3_ID 3
#define RF_CLK2_ID 5
static void __iomem *virt_base;
/* SMD clocks */
DEFINE_CLK_RPM_SMD(pcnoc_clk, pcnoc_a_clk, RPM_BUS_CLK_TYPE, PCNOC_ID, NULL);
DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_ID, NULL);
DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_ID, NULL);
DEFINE_CLK_RPM_SMD(qpic_clk, qpic_a_clk, RPM_QPIC_CLK_TYPE, QPIC_ID, NULL);
DEFINE_CLK_RPM_SMD_BRANCH(xo_clk_src, xo_a_clk_src,
RPM_MISC_CLK_TYPE, CXO_ID, 19200000);
DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID);
/* SMD_XO_BUFFER */
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk1, bb_clk1_a, BB_CLK1_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk2, bb_clk2_a, BB_CLK2_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk3, bb_clk3_a, BB_CLK3_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(rf_clk2, rf_clk2_a, RF_CLK2_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk1_pin, bb_clk1_a_pin, BB_CLK1_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk2_pin, bb_clk2_a_pin, BB_CLK2_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk3_pin, bb_clk3_a_pin, BB_CLK3_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(rf_clk2_pin, rf_clk2_a_pin, RF_CLK2_ID);
/* Voter clocks */
static DEFINE_CLK_VOTER(pcnoc_msmbus_clk, &pcnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_mm_msmbus_clk, &snoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pcnoc_msmbus_a_clk, &pcnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pcnoc_keepalive_a_clk, &pcnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_mm_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pcnoc_usb_a_clk, &pcnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_usb_a_clk, &snoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(bimc_usb_a_clk, &bimc_a_clk.c, LONG_MAX);
/* Branch Voter clocks */
static DEFINE_CLK_BRANCH_VOTER(xo_gcc, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_otg_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_lpm_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_pil_pronto_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_pil_mss_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_wlan_clk, &xo_clk_src.c);
static struct mux_clk rpm_debug_mux = {
.ops = &mux_reg_ops,
.offset = GCC_DEBUG_CLK_CTL,
.mask = 0x1FF,
.en_offset = GCC_DEBUG_CLK_CTL,
.en_mask = BIT(16),
.base = &virt_base,
MUX_SRC_LIST(
{&snoc_clk.c, 0x0000},
{&pcnoc_clk.c, 0x0008},
/* BIMC_CLK is 2x clock to the BIMC Core as well as DDR, while the
* axi clock is for the BIMC AXI interface. The AXI clock is 1/2 of
* the BIMC Clock. measure the gcc_bimc_apss_axi_clk.
*/
{&bimc_clk.c, 0x0155},
),
.c = {
.dbg_name = "rpm_debug_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(rpm_debug_mux.c),
},
};
/* Lookup Table */
static struct clk_lookup msm_clocks_rpm[] = {
CLK_LIST(xo_clk_src),
CLK_LIST(xo_a_clk_src),
CLK_LIST(xo_otg_clk),
CLK_LIST(xo_lpm_clk),
CLK_LIST(xo_pil_mss_clk),
CLK_LIST(xo_pil_pronto_clk),
CLK_LIST(xo_wlan_clk),
CLK_LIST(snoc_msmbus_clk),
CLK_LIST(snoc_msmbus_a_clk),
CLK_LIST(snoc_mm_msmbus_clk),
CLK_LIST(snoc_mm_msmbus_a_clk),
CLK_LIST(pcnoc_msmbus_clk),
CLK_LIST(pcnoc_msmbus_a_clk),
CLK_LIST(bimc_msmbus_clk),
CLK_LIST(bimc_msmbus_a_clk),
CLK_LIST(pcnoc_keepalive_a_clk),
CLK_LIST(pcnoc_usb_a_clk),
CLK_LIST(snoc_usb_a_clk),
CLK_LIST(bimc_usb_a_clk),
/* CoreSight clocks */
CLK_LIST(qdss_clk),
CLK_LIST(qdss_a_clk),
CLK_LIST(snoc_clk),
CLK_LIST(pcnoc_clk),
CLK_LIST(bimc_clk),
CLK_LIST(snoc_a_clk),
CLK_LIST(pcnoc_a_clk),
CLK_LIST(bimc_a_clk),
CLK_LIST(qpic_clk),
CLK_LIST(qpic_a_clk),
CLK_LIST(bb_clk1),
CLK_LIST(bb_clk2),
CLK_LIST(rf_clk2),
CLK_LIST(bb_clk1_pin),
CLK_LIST(bb_clk2_pin),
CLK_LIST(rf_clk2_pin),
/* RPM debug Mux*/
CLK_LIST(rpm_debug_mux),
};
/* Lookup Table for MSM8909w-PM660 */
static struct clk_lookup msm_clocks_rpm_8909_pm660[] = {
CLK_LIST(xo_clk_src),
CLK_LIST(xo_a_clk_src),
CLK_LIST(xo_otg_clk),
CLK_LIST(xo_lpm_clk),
CLK_LIST(xo_pil_mss_clk),
CLK_LIST(xo_pil_pronto_clk),
CLK_LIST(xo_wlan_clk),
CLK_LIST(snoc_msmbus_clk),
CLK_LIST(snoc_msmbus_a_clk),
CLK_LIST(snoc_mm_msmbus_clk),
CLK_LIST(snoc_mm_msmbus_a_clk),
CLK_LIST(pcnoc_msmbus_clk),
CLK_LIST(pcnoc_msmbus_a_clk),
CLK_LIST(bimc_msmbus_clk),
CLK_LIST(bimc_msmbus_a_clk),
CLK_LIST(pcnoc_keepalive_a_clk),
CLK_LIST(pcnoc_usb_a_clk),
CLK_LIST(snoc_usb_a_clk),
CLK_LIST(bimc_usb_a_clk),
/* CoreSight clocks */
CLK_LIST(qdss_clk),
CLK_LIST(qdss_a_clk),
CLK_LIST(snoc_clk),
CLK_LIST(pcnoc_clk),
CLK_LIST(bimc_clk),
CLK_LIST(snoc_a_clk),
CLK_LIST(pcnoc_a_clk),
CLK_LIST(bimc_a_clk),
CLK_LIST(qpic_clk),
CLK_LIST(qpic_a_clk),
CLK_LIST(bb_clk1),
CLK_LIST(bb_clk2),
CLK_LIST(bb_clk3),
CLK_LIST(rf_clk2),
CLK_LIST(bb_clk1_pin),
CLK_LIST(bb_clk2_pin),
CLK_LIST(bb_clk3_pin),
CLK_LIST(rf_clk2_pin),
/* RPM debug Mux*/
CLK_LIST(rpm_debug_mux),
};
static int msm_rpmcc_8909_probe(struct platform_device *pdev)
{
struct resource *res;
int ret, is_8909_pm660 = 0;
ret = enable_rpm_scaling();
if (ret)
return ret;
is_8909_pm660 = of_device_is_compatible(pdev->dev.of_node,
"qcom,rpmcc-8909-pm660");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
if (!res) {
dev_err(&pdev->dev, "Unable to get register base\n");
return -ENOMEM;
}
virt_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!virt_base) {
dev_err(&pdev->dev, "Failed to map CC registers\n");
return -ENOMEM;
}
if (is_8909_pm660)
ret = of_msm_clock_register(pdev->dev.of_node,
msm_clocks_rpm_8909_pm660,
ARRAY_SIZE(msm_clocks_rpm_8909_pm660));
else
ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm,
ARRAY_SIZE(msm_clocks_rpm));
if (ret) {
dev_err(&pdev->dev, "Unable to register RPM clocks\n");
return ret;
}
/*
* Hold an active set vote for PCNOC AHB source. Sleep set vote is 0.
*/
clk_set_rate(&pcnoc_keepalive_a_clk.c, 19200000);
clk_prepare_enable(&pcnoc_keepalive_a_clk.c);
clk_prepare_enable(&xo_a_clk_src.c);
dev_info(&pdev->dev, "Registered RPM clocks.\n");
return 0;
}
static const struct of_device_id msm_clk_rpm_match_table[] = {
{ .compatible = "qcom,rpmcc-8909" },
{ .compatible = "qcom,rpmcc-8909-pm660" },
{}
};
static struct platform_driver msm_clock_rpm_driver = {
.probe = msm_rpmcc_8909_probe,
.driver = {
.name = "qcom,rpmcc-8909",
.of_match_table = msm_clk_rpm_match_table,
.owner = THIS_MODULE,
},
};
static int __init msm_rpmcc_8909_init(void)
{
return platform_driver_register(&msm_clock_rpm_driver);
}
arch_initcall(msm_rpmcc_8909_init);