/* <:copyright-BRCM:2018:DUAL/GPL:standard Copyright (c) 2018 Broadcom 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, as published by the Free Software Foundation (the "GPL"). 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. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ /* * Clock driver for bcm63158 based devices * * Kevin Li * */ #include #include #include #include #include #define I2S_TX_CFG ( 0x0000 ) /* 0x00 -- 0x2080 cfg */ #define I2S_CLKDENOM_CFG ( 0x0070 ) /* 0x70 -- 0x20f0 clk divider Denominator */ #define I2S_CLKNUMER_CFG ( 0x0074 ) /* 0x74 -- 0x20f4 clk numerator */ #define I2S_MCLK_DISABLE ( 0x0078 ) /* 0x78 -- 0x20f8 mclk disable */ #define I2S_TX_MCLK_RATIO_MASK 0x00F00000 #define I2S_TX_MCLK_RATIO_SHIFT 20 #define I2S_TX_SCLKS_PER_1FS_DIV32_SHIFT 4 #define I2S_TX_64BITS_PERFRAME ( 2<i2s_data; regmap_update_bits(pdata->regmap_i2scfg,I2S_TX_CFG, I2S_TX_SCLKS_PER_1FS_DIV32_MASK, I2S_TX_64BITS_PERFRAME ); regmap_update_bits(pdata->regmap_i2scfg,I2S_TX_CFG, I2S_TX_MCLK_RATIO_MASK, (rate <= 48000 ? 8 : 1) << I2S_TX_MCLK_RATIO_SHIFT); #if (CONFIG_BRCM_CHIP_REV == 0x63158A0) regmap_update_bits(pdata->regmap_reset, NTR_REET_REG, CLK_DIV_RST_N, 0 ); // Low regmap_update_bits(pdata->regmap_reset, NTR_REET_REG, CLK_DIV_RST_N, CLK_DIV_RST_N ); //High regmap_update_bits(pdata->regmap_numer, NTR_CLK_I2S_PRGDIVCFG1, NTR_CLK_SEL_MASK | CLK_DIV_MASK, (CLK_SEL_250MHZ_SYNCE_PLL << CLK_SEL_SHIFT) | (CLK_NUMERATOR(parent_rate,rate) & CLK_DIV_MASK) ); regmap_update_bits(pdata->regmap_denom, NTR_CLK_I2S_PRGDIVCFG2, CLK_DENOM_MASK, CLK_DENOM & CLK_DENOM_MASK ); regmap_update_bits(pdata->regmap_i2scfg,I2S_TX_CFG, CLK_SRC_MASK, CLK_SRC_PLL << CLK_SRC_SHIFT); #else regmap_update_bits(pdata->regmap_i2scfg,I2S_CLKDENOM_CFG, CLK_DENOM_MASK,CLK_DENOM); regmap_update_bits(pdata->regmap_i2scfg,I2S_CLKNUMER_CFG, CLK_NOMIN_MASK,CLK_NUMERATOR(parent_rate,rate)); #endif return 0; } /* based on parent rate, re-calculate the rate and returns*/ static unsigned long bcm_i2s_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_i2s *i2s = to_clk_i2s(hw); struct clk_i2s_data *pdata = i2s->i2s_data; unsigned int numerator,mclk_rate; #if (CONFIG_BRCM_CHIP_REV == 0x63158A0) regmap_read(pdata->regmap_numer, NTR_CLK_I2S_PRGDIVCFG1,&numerator); numerator = numerator & CLK_DIV_MASK; #else regmap_read(pdata->regmap_i2scfg,I2S_CLKNUMER_CFG, &numerator); #endif regmap_read(pdata->regmap_i2scfg,I2S_TX_CFG, &mclk_rate); mclk_rate = ( mclk_rate & I2S_TX_MCLK_RATIO_MASK ) >> I2S_TX_MCLK_RATIO_SHIFT; return ( parent_rate * CLK_DENOM )/( 128*mclk_rate )/numerator ; } static long bcm_i2s_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { return rate; } static const struct clk_ops clk_i2s_ops = { .set_rate = bcm_i2s_clk_set_rate, .recalc_rate = bcm_i2s_clk_recalc_rate, .round_rate = bcm_i2s_clk_round_rate, }; static struct clk *bcm_i2s_clk_register(struct device *dev, const char *name, const char *parent_name, struct clk_i2s_data *i2s_data) { struct clk_init_data init; struct clk_i2s *clki2s; struct clk *clk; clki2s = kzalloc(sizeof(*clki2s), GFP_KERNEL); if (!clki2s) { pr_err("%s: Out of memory\n", __func__); return ERR_PTR(-ENOMEM); } init.name = name; init.ops = &clk_i2s_ops; init.flags = 0; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); clki2s->i2s_data = i2s_data; clki2s->hw.init = &init; clk = clk_register(NULL, &clki2s->hw); if (IS_ERR(clk)) { kfree(clki2s); } return clk; } static void __init bcm_i2s_clk_init(struct device_node *node) { const char *clk_name = node->name; const char *parent_name; struct clk_i2s_data *data; struct clk *clk; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { pr_err("%s: Out of memory\n", __func__); return; } data->regmap_reset = syscon_regmap_lookup_by_phandle(node, "clk-reset-syscon"); if (IS_ERR(data->regmap_reset)) { goto out ; } data->regmap_numer = syscon_regmap_lookup_by_phandle(node, "clk-numerator-syscon"); if (IS_ERR(data->regmap_numer)) { goto out ; } data->regmap_denom = syscon_regmap_lookup_by_phandle(node, "clk-denominator-syscon"); if (IS_ERR(data->regmap_denom)) { goto out ; } data->regmap_i2scfg = syscon_regmap_lookup_by_phandle(node, "clk-mclk-syscon"); if (IS_ERR(data->regmap_i2scfg)) { goto out ; } of_property_read_string(node, "clock-output-names", &clk_name); parent_name = of_clk_get_parent_name(node, 0); if (!parent_name) { pr_err("%s(): %s: of_clk_get_parent_name() failed\n", __func__, node->name); goto out; } clk = bcm_i2s_clk_register(NULL, clk_name, parent_name, data); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); pr_err("%s(): %s: clock register success\n", __func__, node->name); return; } out: kfree(data); return; } static void __init of_bcm_i2s_clk_init(struct device_node *node) { bcm_i2s_clk_init(node); } CLK_OF_DECLARE(bcm_gate_clk, "brcm,i2s-clock", of_bcm_i2s_clk_init);