/* * Copyright (c) 2017, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "clk-regmap.h" #include "clk-rcg.h" #include "clk-branch.h" #include "clk-regmap-divider.h" #include "clk-regmap-mux.h" #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } enum { P_XO, P_AUDIO_PLL, P_RXMPAD_CLK, P_TXMPAD_CLK, P_PCMPAD_CLK, }; static const char * const parents_audio_rxm_clk_src[] = { "xo", "audio_pll", "audio_rxmpad_clk" }; static const struct parent_map parents_audio_rxm_clk_src_map[] = { { P_XO, 0 }, { P_AUDIO_PLL, 1 }, { P_RXMPAD_CLK, 2 }, }; struct freq_tbl ftbl_audio_rxm_clk_src[] = { F(252, P_RXMPAD_CLK, 1, 0, 0), F(16384000, P_AUDIO_PLL, 12, 0, 0), F(19200000, P_XO, 1, 0, 0), F(22579200, P_AUDIO_PLL, 8, 0, 0), F(24576000, P_AUDIO_PLL, 8, 0, 0), F(32768000, P_AUDIO_PLL, 6, 0, 0), F(45158400, P_AUDIO_PLL, 4, 0, 0), F(49152000, P_AUDIO_PLL, 4, 0, 0), F(61440000, P_AUDIO_PLL, 3, 0, 0), F(64512000, P_AUDIO_PLL, 3, 0, 0), F(65536000, P_AUDIO_PLL, 3, 0, 0), F(67737600, P_AUDIO_PLL, 3, 0, 0), F(70560000, P_AUDIO_PLL, 3, 0, 0), F(71680000, P_AUDIO_PLL, 3, 0, 0), F(86016000, P_AUDIO_PLL, 2, 0, 0), F(90316800, P_AUDIO_PLL, 2, 0, 0), F(92160000, P_AUDIO_PLL, 2, 0, 0), F(98304000, P_AUDIO_PLL, 2, 0, 0), F(98784000, P_AUDIO_PLL, 2, 0, 0), F(100352000, P_AUDIO_PLL, 2, 0, 0), F(102400000, P_AUDIO_PLL, 2, 0, 0), F(186278400, P_AUDIO_PLL, 1, 0, 0), F(196608000, P_AUDIO_PLL, 1, 0, 0), F(202752000, P_AUDIO_PLL, 1, 0, 0), { } }; struct clk_rcg2 adss_audio_rxm_clk_src = { .cmd_rcgr = 0x0020, .freq_tbl = ftbl_audio_rxm_clk_src, .hid_width = 5, .parent_map = parents_audio_rxm_clk_src_map, .flags = CLK_RCG2_HW_CONTROLLED, .clkr.hw.init = &(struct clk_init_data){ .name = "adss_audio_rxm_clk_src", .parent_names = parents_audio_rxm_clk_src, .num_parents = 3, .ops = &clk_rcg2_ops, .flags = CLK_SET_RATE_PARENT, }, }; static struct clk_regmap_div adss_audio_rxm_postdiv_clk_src = { .reg = 0x0028, .shift = 0, .width = 9, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_rxm_postdiv_clk_src", .parent_names = (const char *[]){ "adss_audio_rxm_clk_src" }, .num_parents = 1, .ops = &clk_regmap_div_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_rxm_clk = { .halt_reg = 0x002c, .halt_bit = 31, .clkr = { .enable_reg = 0x002c, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_rxm_clk", .parent_names = (const char *[]){ "adss_audio_rxm_postdiv_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_regmap_div adss_audio_rxb_postdiv_clk_src = { .reg = 0x0008, .shift = 1, .width = 8, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_rxb_postdiv_clk_src", .parent_names = (const char *[]){ "adss_audio_rxm_clk_src" }, .num_parents = 1, .ops = &clk_regmap_div_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static const char * const parent_adss_audio_rxb_clk_mux[] = { "adss_audio_rxb_postdiv_clk_src", "audio_rxbpad_clk", }; static struct clk_regmap_mux adss_audio_rxb_clk_mux = { .reg = 0x0004, .shift = 8, .width = 3, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_rxb_clk_mux", .parent_names = parent_adss_audio_rxb_clk_mux, .num_parents = 2, .ops = &clk_regmap_mux_closest_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_rxb_clk = { .halt_reg = 0x000c, .halt_bit = 31, .clkr = { .enable_reg = 0x000c, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_rxb_clk", .parent_names = (const char *[]){ "adss_audio_rxb_clk_mux" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; struct freq_tbl ftbl_audio_txm_clk_src[] = { F(252, P_TXMPAD_CLK, 1, 0, 0), F(16384000, P_AUDIO_PLL, 12, 0, 0), F(19200000, P_XO, 1, 0, 0), F(22579200, P_AUDIO_PLL, 8, 0, 0), F(24576000, P_AUDIO_PLL, 8, 0, 0), F(32768000, P_AUDIO_PLL, 6, 0, 0), F(45158400, P_AUDIO_PLL, 4, 0, 0), F(49152000, P_AUDIO_PLL, 4, 0, 0), F(61440000, P_AUDIO_PLL, 3, 0, 0), F(64512000, P_AUDIO_PLL, 3, 0, 0), F(65536000, P_AUDIO_PLL, 3, 0, 0), F(67737600, P_AUDIO_PLL, 3, 0, 0), F(70560000, P_AUDIO_PLL, 3, 0, 0), F(71680000, P_AUDIO_PLL, 3, 0, 0), F(86016000, P_AUDIO_PLL, 2, 0, 0), F(90316800, P_AUDIO_PLL, 2, 0, 0), F(92160000, P_AUDIO_PLL, 2, 0, 0), F(98304000, P_AUDIO_PLL, 2, 0, 0), F(98784000, P_AUDIO_PLL, 2, 0, 0), F(100352000, P_AUDIO_PLL, 2, 0, 0), F(102400000, P_AUDIO_PLL, 2, 0, 0), F(186278400, P_AUDIO_PLL, 1, 0, 0), F(196608000, P_AUDIO_PLL, 1, 0, 0), F(202752000, P_AUDIO_PLL, 1, 0, 0), { } }; static const char * const parents_audio_txm_clk_src[] = { "xo", "audio_pll", "audio_txmpad_clk" }; static const struct parent_map parents_audio_txm_clk_src_map[] = { { P_XO, 0 }, { P_AUDIO_PLL, 1 }, { P_TXMPAD_CLK, 2 }, }; struct clk_rcg2 adss_audio_txm_clk_src = { .cmd_rcgr = 0x0060, .freq_tbl = ftbl_audio_txm_clk_src, .hid_width = 5, .parent_map = parents_audio_txm_clk_src_map, .flags = CLK_RCG2_HW_CONTROLLED, .clkr.hw.init = &(struct clk_init_data){ .name = "adss_audio_txm_clk_src", .parent_names = parents_audio_txm_clk_src, .num_parents = 3, .ops = &clk_rcg2_ops, .flags = CLK_SET_RATE_PARENT, }, }; static struct clk_regmap_div adss_audio_txm_postdiv_clk_src = { .reg = 0x0068, .shift = 0, .width = 9, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_txm_postdiv_clk_src", .parent_names = (const char *[]){ "adss_audio_txm_clk_src" }, .num_parents = 1, .ops = &clk_regmap_div_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_txm_clk = { .halt_reg = 0x006c, .halt_bit = 31, .clkr = { .enable_reg = 0x006c, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_txm_clk", .parent_names = (const char *[]){ "adss_audio_txm_postdiv_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_regmap_div adss_audio_txb_postdiv_clk_src = { .reg = 0x0048, .shift = 1, .width = 8, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_txb_postdiv_clk_src", .parent_names = (const char *[]){ "adss_audio_txm_clk_src" }, .num_parents = 1, .ops = &clk_regmap_div_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static const char * const parent_adss_audio_txb_clk_mux[] = { "adss_audio_txb_postdiv_clk_src", "audio_txbpad_clk", }; static struct clk_regmap_mux adss_audio_txb_clk_mux = { .reg = 0x0044, .shift = 8, .width = 3, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_txb_clk_mux", .parent_names = parent_adss_audio_txb_clk_mux, .num_parents = 2, .ops = &clk_regmap_mux_closest_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_txb_clk = { .halt_reg = 0x004c, .halt_bit = 31, .clkr = { .enable_reg = 0x004c, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_txb_clk", .parent_names = (const char *[]){ "adss_audio_txb_clk_mux" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; struct freq_tbl ftbl_audio_pcm_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(49152000, P_AUDIO_PLL, 4, 0, 0), { } }; static const char * const parents_audio_pcm_clk_src[] = { "xo", "audio_pll", "audio_pcmpad_clk" }; static const struct parent_map parents_audio_pcm_clk_src_map[] = { { P_XO, 0 }, { P_AUDIO_PLL, 1 }, { P_PCMPAD_CLK, 2 }, }; struct clk_rcg2 adss_audio_pcm_clk_src = { .cmd_rcgr = 0x00a0, .freq_tbl = ftbl_audio_pcm_clk_src, .hid_width = 5, .parent_map = parents_audio_pcm_clk_src_map, .flags = CLK_RCG2_HW_CONTROLLED, .clkr.hw.init = &(struct clk_init_data){ .name = "adss_audio_pcm_clk_src", .parent_names = parents_audio_txm_clk_src, .num_parents = 3, .ops = &clk_rcg2_ops, .flags = CLK_SET_RATE_PARENT, }, }; static struct clk_regmap_div adss_audio_pcm_postdiv_clk_src = { .reg = 0x00a8, .shift = 0, .width = 9, .clkr = { .hw.init = &(struct clk_init_data){ .name = "adss_audio_pcm_postdiv_clk_src", .parent_names = (const char *[]){ "adss_audio_pcm_clk_src" }, .num_parents = 1, .ops = &clk_regmap_div_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_pcm_clk = { .halt_reg = 0x00ac, .halt_bit = 31, .clkr = { .enable_reg = 0x00ac, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_pcm_clk", .parent_names = (const char *[]){ "adss_audio_pcm_postdiv_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_SET_RATE_PARENT, }, }, }; static struct clk_branch adss_audio_i2s0_clk = { .halt_reg = 0x0104, .halt_bit = 31, .clkr = { .enable_reg = 0x0104, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_i2s0_clk", .parent_names = (const char *[]){ "pcnoc_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_IGNORE_UNUSED, }, }, }; static struct clk_branch adss_audio_i2s3_clk = { .halt_reg = 0x0108, .halt_bit = 31, .clkr = { .enable_reg = 0x0108, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_i2s3_clk", .parent_names = (const char *[]){ "pcnoc_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_IGNORE_UNUSED, }, }, }; static struct clk_branch adss_audio_mbox0_clk = { .halt_reg = 0x010c, .halt_bit = 31, .clkr = { .enable_reg = 0x010c, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_mbox0_clk", .parent_names = (const char *[]){ "pcnoc_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_IGNORE_UNUSED, }, }, }; static struct clk_branch adss_audio_mbox3_clk = { .halt_reg = 0x0110, .halt_bit = 31, .clkr = { .enable_reg = 0x0110, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "adss_audio_mbox3_clk", .parent_names = (const char *[]){ "pcnoc_clk_src" }, .num_parents = 1, .ops = &clk_branch2_ops, .flags = CLK_IGNORE_UNUSED, }, }, }; static struct clk_regmap *adss_ipq807x_clks[] = { [ADSS_AUDIO_RXM_CLK_SRC] = &adss_audio_rxm_clk_src.clkr, [ADSS_AUDIO_RXM_POSTDIV_CLK_SRC] = &adss_audio_rxm_postdiv_clk_src.clkr, [ADSS_AUDIO_RXM_CLK] = &adss_audio_rxm_clk.clkr, [ADSS_AUDIO_RXB_POSTDIV_CLK_SRC] = &adss_audio_rxb_postdiv_clk_src.clkr, [ADSS_AUDIO_RXB_CLK_MUX] = &adss_audio_rxb_clk_mux.clkr, [ADSS_AUDIO_RXB_CLK] = &adss_audio_rxb_clk.clkr, [ADSS_AUDIO_TXM_CLK_SRC] = &adss_audio_txm_clk_src.clkr, [ADSS_AUDIO_TXM_POSTDIV_CLK_SRC] = &adss_audio_txm_postdiv_clk_src.clkr, [ADSS_AUDIO_TXM_CLK] = &adss_audio_txm_clk.clkr, [ADSS_AUDIO_TXB_POSTDIV_CLK_SRC] = &adss_audio_txb_postdiv_clk_src.clkr, [ADSS_AUDIO_TXB_CLK_MUX] = &adss_audio_txb_clk_mux.clkr, [ADSS_AUDIO_TXB_CLK] = &adss_audio_txb_clk.clkr, [ADSS_AUDIO_PCM_CLK_SRC] = &adss_audio_pcm_clk_src.clkr, [ADSS_AUDIO_PCM_POSTDIV_CLK_SRC] = &adss_audio_pcm_postdiv_clk_src.clkr, [ADSS_AUDIO_PCM_CLK] = &adss_audio_pcm_clk.clkr, [ADSS_AUDIO_I2S0_CLK] = &adss_audio_i2s0_clk.clkr, [ADSS_AUDIO_I2S3_CLK] = &adss_audio_i2s3_clk.clkr, [ADSS_AUDIO_MBOX0_CLK] = &adss_audio_mbox0_clk.clkr, [ADSS_AUDIO_MBOX3_CLK] = &adss_audio_mbox3_clk.clkr, }; static const struct of_device_id adss_ipq807x_match_table[] = { { .compatible = "qcom,adss-ipq807x" }, { } }; MODULE_DEVICE_TABLE(of, adss_ipq807x_match_table); static const struct regmap_config adss_ipq807x_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = 0x1fc, .fast_io = true, }; static const struct qcom_cc_desc adss_ipq807x_desc = { .config = &adss_ipq807x_regmap_config, .clks = adss_ipq807x_clks, .num_clks = ARRAY_SIZE(adss_ipq807x_clks), }; static int adss_ipq807x_probe(struct platform_device *pdev) { int ret; ret = qcom_cc_probe(pdev, &adss_ipq807x_desc); return ret; } static int adss_ipq807x_remove(struct platform_device *pdev) { return 0; } static struct platform_driver adss_ipq807x_driver = { .probe = adss_ipq807x_probe, .remove = adss_ipq807x_remove, .driver = { .name = "qcom,adss-ipq807x", .owner = THIS_MODULE, .of_match_table = adss_ipq807x_match_table, }, }; static int __init adss_ipq807x_init(void) { return platform_driver_register(&adss_ipq807x_driver); } core_initcall(adss_ipq807x_init); static void __exit adss_ipq807x_exit(void) { platform_driver_unregister(&adss_ipq807x_driver); } module_exit(adss_ipq807x_exit); MODULE_DESCRIPTION("QCA APSS IPQ807x Driver"); MODULE_LICENSE("Dual BSD/GPLv2"); MODULE_ALIAS("platform:adss-ipq807x");