162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CS2000 -- CIRRUS LOGIC Fractional-N Clock Synthesizer & Clock Multiplier 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Renesas Electronics Corporation 662306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/i2c.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define CH_MAX 4 1762306a36Sopenharmony_ci#define RATIO_REG_SIZE 4 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DEVICE_ID 0x1 2062306a36Sopenharmony_ci#define DEVICE_CTRL 0x2 2162306a36Sopenharmony_ci#define DEVICE_CFG1 0x3 2262306a36Sopenharmony_ci#define DEVICE_CFG2 0x4 2362306a36Sopenharmony_ci#define GLOBAL_CFG 0x5 2462306a36Sopenharmony_ci#define Ratio_Add(x, nth) (6 + (x * 4) + (nth)) 2562306a36Sopenharmony_ci#define Ratio_Val(x, nth) ((x >> (24 - (8 * nth))) & 0xFF) 2662306a36Sopenharmony_ci#define Val_Ratio(x, nth) ((x & 0xFF) << (24 - (8 * nth))) 2762306a36Sopenharmony_ci#define FUNC_CFG1 0x16 2862306a36Sopenharmony_ci#define FUNC_CFG2 0x17 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* DEVICE_ID */ 3162306a36Sopenharmony_ci#define REVISION_MASK (0x7) 3262306a36Sopenharmony_ci#define REVISION_B2_B3 (0x4) 3362306a36Sopenharmony_ci#define REVISION_C1 (0x6) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* DEVICE_CTRL */ 3662306a36Sopenharmony_ci#define PLL_UNLOCK (1 << 7) 3762306a36Sopenharmony_ci#define AUXOUTDIS (1 << 1) 3862306a36Sopenharmony_ci#define CLKOUTDIS (1 << 0) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* DEVICE_CFG1 */ 4162306a36Sopenharmony_ci#define RSEL(x) (((x) & 0x3) << 3) 4262306a36Sopenharmony_ci#define RSEL_MASK RSEL(0x3) 4362306a36Sopenharmony_ci#define AUXOUTSRC(x) (((x) & 0x3) << 1) 4462306a36Sopenharmony_ci#define AUXOUTSRC_MASK AUXOUTSRC(0x3) 4562306a36Sopenharmony_ci#define ENDEV1 (0x1) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* DEVICE_CFG2 */ 4862306a36Sopenharmony_ci#define AUTORMOD (1 << 3) 4962306a36Sopenharmony_ci#define LOCKCLK(x) (((x) & 0x3) << 1) 5062306a36Sopenharmony_ci#define LOCKCLK_MASK LOCKCLK(0x3) 5162306a36Sopenharmony_ci#define FRACNSRC_MASK (1 << 0) 5262306a36Sopenharmony_ci#define FRACNSRC_STATIC (0 << 0) 5362306a36Sopenharmony_ci#define FRACNSRC_DYNAMIC (1 << 0) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* GLOBAL_CFG */ 5662306a36Sopenharmony_ci#define FREEZE (1 << 7) 5762306a36Sopenharmony_ci#define ENDEV2 (0x1) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* FUNC_CFG1 */ 6062306a36Sopenharmony_ci#define CLKSKIPEN (1 << 7) 6162306a36Sopenharmony_ci#define REFCLKDIV(x) (((x) & 0x3) << 3) 6262306a36Sopenharmony_ci#define REFCLKDIV_MASK REFCLKDIV(0x3) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* FUNC_CFG2 */ 6562306a36Sopenharmony_ci#define LFRATIO_MASK (1 << 3) 6662306a36Sopenharmony_ci#define LFRATIO_20_12 (0 << 3) 6762306a36Sopenharmony_ci#define LFRATIO_12_20 (1 << 3) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define CH_SIZE_ERR(ch) ((ch < 0) || (ch >= CH_MAX)) 7062306a36Sopenharmony_ci#define hw_to_priv(_hw) container_of(_hw, struct cs2000_priv, hw) 7162306a36Sopenharmony_ci#define priv_to_client(priv) (priv->client) 7262306a36Sopenharmony_ci#define priv_to_dev(priv) (&(priv_to_client(priv)->dev)) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define CLK_IN 0 7562306a36Sopenharmony_ci#define REF_CLK 1 7662306a36Sopenharmony_ci#define CLK_MAX 2 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic bool cs2000_readable_reg(struct device *dev, unsigned int reg) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return reg > 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic bool cs2000_writeable_reg(struct device *dev, unsigned int reg) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return reg != DEVICE_ID; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool cs2000_volatile_reg(struct device *dev, unsigned int reg) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return reg == DEVICE_CTRL; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic const struct regmap_config cs2000_regmap_config = { 9462306a36Sopenharmony_ci .reg_bits = 8, 9562306a36Sopenharmony_ci .val_bits = 8, 9662306a36Sopenharmony_ci .max_register = FUNC_CFG2, 9762306a36Sopenharmony_ci .readable_reg = cs2000_readable_reg, 9862306a36Sopenharmony_ci .writeable_reg = cs2000_writeable_reg, 9962306a36Sopenharmony_ci .volatile_reg = cs2000_volatile_reg, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct cs2000_priv { 10362306a36Sopenharmony_ci struct clk_hw hw; 10462306a36Sopenharmony_ci struct i2c_client *client; 10562306a36Sopenharmony_ci struct clk *clk_in; 10662306a36Sopenharmony_ci struct clk *ref_clk; 10762306a36Sopenharmony_ci struct regmap *regmap; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci bool dynamic_mode; 11062306a36Sopenharmony_ci bool lf_ratio; 11162306a36Sopenharmony_ci bool clk_skip; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* suspend/resume */ 11462306a36Sopenharmony_ci unsigned long saved_rate; 11562306a36Sopenharmony_ci unsigned long saved_parent_rate; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct of_device_id cs2000_of_match[] = { 11962306a36Sopenharmony_ci { .compatible = "cirrus,cs2000-cp", }, 12062306a36Sopenharmony_ci {}, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cs2000_of_match); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct i2c_device_id cs2000_id[] = { 12562306a36Sopenharmony_ci { "cs2000-cp", }, 12662306a36Sopenharmony_ci {} 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, cs2000_id); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int cs2000_enable_dev_config(struct cs2000_priv *priv, bool enable) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, DEVICE_CFG1, ENDEV1, 13562306a36Sopenharmony_ci enable ? ENDEV1 : 0); 13662306a36Sopenharmony_ci if (ret < 0) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, GLOBAL_CFG, ENDEV2, 14062306a36Sopenharmony_ci enable ? ENDEV2 : 0); 14162306a36Sopenharmony_ci if (ret < 0) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, FUNC_CFG1, CLKSKIPEN, 14562306a36Sopenharmony_ci (enable && priv->clk_skip) ? CLKSKIPEN : 0); 14662306a36Sopenharmony_ci if (ret < 0) 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int cs2000_ref_clk_bound_rate(struct cs2000_priv *priv, 15362306a36Sopenharmony_ci u32 rate_in) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 val; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (rate_in >= 32000000 && rate_in < 56000000) 15862306a36Sopenharmony_ci val = 0x0; 15962306a36Sopenharmony_ci else if (rate_in >= 16000000 && rate_in < 28000000) 16062306a36Sopenharmony_ci val = 0x1; 16162306a36Sopenharmony_ci else if (rate_in >= 8000000 && rate_in < 14000000) 16262306a36Sopenharmony_ci val = 0x2; 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, FUNC_CFG1, 16762306a36Sopenharmony_ci REFCLKDIV_MASK, 16862306a36Sopenharmony_ci REFCLKDIV(val)); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int cs2000_wait_pll_lock(struct cs2000_priv *priv) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct device *dev = priv_to_dev(priv); 17462306a36Sopenharmony_ci unsigned int i, val; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 17862306a36Sopenharmony_ci ret = regmap_read(priv->regmap, DEVICE_CTRL, &val); 17962306a36Sopenharmony_ci if (ret < 0) 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci if (!(val & PLL_UNLOCK)) 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci udelay(1); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dev_err(dev, "pll lock failed\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return -ETIMEDOUT; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int cs2000_clk_out_enable(struct cs2000_priv *priv, bool enable) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci /* enable both AUX_OUT, CLK_OUT */ 19462306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, DEVICE_CTRL, 19562306a36Sopenharmony_ci (AUXOUTDIS | CLKOUTDIS), 19662306a36Sopenharmony_ci enable ? 0 : 19762306a36Sopenharmony_ci (AUXOUTDIS | CLKOUTDIS)); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out, bool lf_ratio) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u64 ratio; 20362306a36Sopenharmony_ci u32 multiplier = lf_ratio ? 12 : 20; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * ratio = rate_out / rate_in * 2^multiplier 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * To avoid over flow, rate_out is u64. 20962306a36Sopenharmony_ci * The result should be u32. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci ratio = (u64)rate_out << multiplier; 21262306a36Sopenharmony_ci do_div(ratio, rate_in); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return ratio; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in, bool lf_ratio) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci u64 rate_out; 22062306a36Sopenharmony_ci u32 multiplier = lf_ratio ? 12 : 20; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * ratio = rate_out / rate_in * 2^multiplier 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * To avoid over flow, rate_out is u64. 22662306a36Sopenharmony_ci * The result should be u32 or unsigned long. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci rate_out = (u64)ratio * rate_in; 23062306a36Sopenharmony_ci return rate_out >> multiplier; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int cs2000_ratio_set(struct cs2000_priv *priv, 23462306a36Sopenharmony_ci int ch, u32 rate_in, u32 rate_out) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci u32 val; 23762306a36Sopenharmony_ci unsigned int i; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (CH_SIZE_ERR(ch)) 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci val = cs2000_rate_to_ratio(rate_in, rate_out, priv->lf_ratio); 24462306a36Sopenharmony_ci for (i = 0; i < RATIO_REG_SIZE; i++) { 24562306a36Sopenharmony_ci ret = regmap_write(priv->regmap, 24662306a36Sopenharmony_ci Ratio_Add(ch, i), 24762306a36Sopenharmony_ci Ratio_Val(val, i)); 24862306a36Sopenharmony_ci if (ret < 0) 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic u32 cs2000_ratio_get(struct cs2000_priv *priv, int ch) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci unsigned int tmp, i; 25862306a36Sopenharmony_ci u32 val; 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci val = 0; 26262306a36Sopenharmony_ci for (i = 0; i < RATIO_REG_SIZE; i++) { 26362306a36Sopenharmony_ci ret = regmap_read(priv->regmap, Ratio_Add(ch, i), &tmp); 26462306a36Sopenharmony_ci if (ret < 0) 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci val |= Val_Ratio(tmp, i); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return val; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int cs2000_ratio_select(struct cs2000_priv *priv, int ch) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int ret; 27662306a36Sopenharmony_ci u8 fracnsrc; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (CH_SIZE_ERR(ch)) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, DEVICE_CFG1, RSEL_MASK, RSEL(ch)); 28262306a36Sopenharmony_ci if (ret < 0) 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci fracnsrc = priv->dynamic_mode ? FRACNSRC_DYNAMIC : FRACNSRC_STATIC; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, DEVICE_CFG2, 28862306a36Sopenharmony_ci AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK, 28962306a36Sopenharmony_ci LOCKCLK(ch) | fracnsrc); 29062306a36Sopenharmony_ci if (ret < 0) 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic unsigned long cs2000_recalc_rate(struct clk_hw *hw, 29762306a36Sopenharmony_ci unsigned long parent_rate) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 30062306a36Sopenharmony_ci int ch = 0; /* it uses ch0 only at this point */ 30162306a36Sopenharmony_ci u32 ratio; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ratio = cs2000_ratio_get(priv, ch); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return cs2000_ratio_to_rate(ratio, parent_rate, priv->lf_ratio); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic long cs2000_round_rate(struct clk_hw *hw, unsigned long rate, 30962306a36Sopenharmony_ci unsigned long *parent_rate) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 31262306a36Sopenharmony_ci u32 ratio; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ratio = cs2000_rate_to_ratio(*parent_rate, rate, priv->lf_ratio); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return cs2000_ratio_to_rate(ratio, *parent_rate, priv->lf_ratio); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int cs2000_select_ratio_mode(struct cs2000_priv *priv, 32062306a36Sopenharmony_ci unsigned long rate, 32162306a36Sopenharmony_ci unsigned long parent_rate) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * From the datasheet: 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * | It is recommended that the 12.20 High-Resolution format be 32762306a36Sopenharmony_ci * | utilized whenever the desired ratio is less than 4096 since 32862306a36Sopenharmony_ci * | the output frequency accuracy of the PLL is directly proportional 32962306a36Sopenharmony_ci * | to the accuracy of the timing reference clock and the resolution 33062306a36Sopenharmony_ci * | of the R_UD. 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * This mode is only available in dynamic mode. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci priv->lf_ratio = priv->dynamic_mode && ((rate / parent_rate) > 4096); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, FUNC_CFG2, LFRATIO_MASK, 33762306a36Sopenharmony_ci priv->lf_ratio ? LFRATIO_20_12 : LFRATIO_12_20); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int __cs2000_set_rate(struct cs2000_priv *priv, int ch, 34162306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate) 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, GLOBAL_CFG, FREEZE, FREEZE); 34762306a36Sopenharmony_ci if (ret < 0) 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ret = cs2000_select_ratio_mode(priv, rate, parent_rate); 35162306a36Sopenharmony_ci if (ret < 0) 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ret = cs2000_ratio_set(priv, ch, parent_rate, rate); 35562306a36Sopenharmony_ci if (ret < 0) 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = cs2000_ratio_select(priv, ch); 35962306a36Sopenharmony_ci if (ret < 0) 36062306a36Sopenharmony_ci return ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, GLOBAL_CFG, FREEZE, 0); 36362306a36Sopenharmony_ci if (ret < 0) 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci priv->saved_rate = rate; 36762306a36Sopenharmony_ci priv->saved_parent_rate = parent_rate; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int cs2000_set_rate(struct clk_hw *hw, 37362306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 37662306a36Sopenharmony_ci int ch = 0; /* it uses ch0 only at this point */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return __cs2000_set_rate(priv, ch, rate, parent_rate); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int cs2000_set_saved_rate(struct cs2000_priv *priv) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int ch = 0; /* it uses ch0 only at this point */ 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return __cs2000_set_rate(priv, ch, 38662306a36Sopenharmony_ci priv->saved_rate, 38762306a36Sopenharmony_ci priv->saved_parent_rate); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int cs2000_enable(struct clk_hw *hw) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 39362306a36Sopenharmony_ci int ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = cs2000_enable_dev_config(priv, true); 39662306a36Sopenharmony_ci if (ret < 0) 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ret = cs2000_clk_out_enable(priv, true); 40062306a36Sopenharmony_ci if (ret < 0) 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = cs2000_wait_pll_lock(priv); 40462306a36Sopenharmony_ci if (ret < 0) 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void cs2000_disable(struct clk_hw *hw) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci cs2000_enable_dev_config(priv, false); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci cs2000_clk_out_enable(priv, false); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic u8 cs2000_get_parent(struct clk_hw *hw) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct cs2000_priv *priv = hw_to_priv(hw); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * In dynamic mode, output rates are derived from CLK_IN. 42562306a36Sopenharmony_ci * In static mode, CLK_IN is ignored, so we return REF_CLK instead. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci return priv->dynamic_mode ? CLK_IN : REF_CLK; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct clk_ops cs2000_ops = { 43162306a36Sopenharmony_ci .get_parent = cs2000_get_parent, 43262306a36Sopenharmony_ci .recalc_rate = cs2000_recalc_rate, 43362306a36Sopenharmony_ci .round_rate = cs2000_round_rate, 43462306a36Sopenharmony_ci .set_rate = cs2000_set_rate, 43562306a36Sopenharmony_ci .prepare = cs2000_enable, 43662306a36Sopenharmony_ci .unprepare = cs2000_disable, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int cs2000_clk_get(struct cs2000_priv *priv) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct device *dev = priv_to_dev(priv); 44262306a36Sopenharmony_ci struct clk *clk_in, *ref_clk; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci clk_in = devm_clk_get(dev, "clk_in"); 44562306a36Sopenharmony_ci /* not yet provided */ 44662306a36Sopenharmony_ci if (IS_ERR(clk_in)) 44762306a36Sopenharmony_ci return -EPROBE_DEFER; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ref_clk = devm_clk_get(dev, "ref_clk"); 45062306a36Sopenharmony_ci /* not yet provided */ 45162306a36Sopenharmony_ci if (IS_ERR(ref_clk)) 45262306a36Sopenharmony_ci return -EPROBE_DEFER; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci priv->clk_in = clk_in; 45562306a36Sopenharmony_ci priv->ref_clk = ref_clk; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int cs2000_clk_register(struct cs2000_priv *priv) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct device *dev = priv_to_dev(priv); 46362306a36Sopenharmony_ci struct device_node *np = dev->of_node; 46462306a36Sopenharmony_ci struct clk_init_data init; 46562306a36Sopenharmony_ci const char *name = np->name; 46662306a36Sopenharmony_ci static const char *parent_names[CLK_MAX]; 46762306a36Sopenharmony_ci u32 aux_out = 0; 46862306a36Sopenharmony_ci int ref_clk_rate; 46962306a36Sopenharmony_ci int ch = 0; /* it uses ch0 only at this point */ 47062306a36Sopenharmony_ci int ret; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci of_property_read_string(np, "clock-output-names", &name); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci priv->dynamic_mode = of_property_read_bool(np, "cirrus,dynamic-mode"); 47562306a36Sopenharmony_ci dev_info(dev, "operating in %s mode\n", 47662306a36Sopenharmony_ci priv->dynamic_mode ? "dynamic" : "static"); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci of_property_read_u32(np, "cirrus,aux-output-source", &aux_out); 47962306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, DEVICE_CFG1, 48062306a36Sopenharmony_ci AUXOUTSRC_MASK, AUXOUTSRC(aux_out)); 48162306a36Sopenharmony_ci if (ret < 0) 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci priv->clk_skip = of_property_read_bool(np, "cirrus,clock-skip"); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ref_clk_rate = clk_get_rate(priv->ref_clk); 48762306a36Sopenharmony_ci ret = cs2000_ref_clk_bound_rate(priv, ref_clk_rate); 48862306a36Sopenharmony_ci if (ret < 0) 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (priv->dynamic_mode) { 49262306a36Sopenharmony_ci /* Default to low-frequency mode to allow for large ratios */ 49362306a36Sopenharmony_ci priv->lf_ratio = true; 49462306a36Sopenharmony_ci } else { 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * set default rate as 1/1. 49762306a36Sopenharmony_ci * otherwise .set_rate which setup ratio 49862306a36Sopenharmony_ci * is never called if user requests 1/1 rate 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci ret = __cs2000_set_rate(priv, ch, ref_clk_rate, ref_clk_rate); 50162306a36Sopenharmony_ci if (ret < 0) 50262306a36Sopenharmony_ci return ret; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci parent_names[CLK_IN] = __clk_get_name(priv->clk_in); 50662306a36Sopenharmony_ci parent_names[REF_CLK] = __clk_get_name(priv->ref_clk); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci init.name = name; 50962306a36Sopenharmony_ci init.ops = &cs2000_ops; 51062306a36Sopenharmony_ci init.flags = CLK_SET_RATE_GATE; 51162306a36Sopenharmony_ci init.parent_names = parent_names; 51262306a36Sopenharmony_ci init.num_parents = ARRAY_SIZE(parent_names); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci priv->hw.init = &init; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = clk_hw_register(dev, &priv->hw); 51762306a36Sopenharmony_ci if (ret) 51862306a36Sopenharmony_ci return ret; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw); 52162306a36Sopenharmony_ci if (ret < 0) { 52262306a36Sopenharmony_ci clk_hw_unregister(&priv->hw); 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int cs2000_version_print(struct cs2000_priv *priv) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct device *dev = priv_to_dev(priv); 53262306a36Sopenharmony_ci const char *revision; 53362306a36Sopenharmony_ci unsigned int val; 53462306a36Sopenharmony_ci int ret; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci ret = regmap_read(priv->regmap, DEVICE_ID, &val); 53762306a36Sopenharmony_ci if (ret < 0) 53862306a36Sopenharmony_ci return ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* CS2000 should be 0x0 */ 54162306a36Sopenharmony_ci if (val >> 3) 54262306a36Sopenharmony_ci return -EIO; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci switch (val & REVISION_MASK) { 54562306a36Sopenharmony_ci case REVISION_B2_B3: 54662306a36Sopenharmony_ci revision = "B2 / B3"; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci case REVISION_C1: 54962306a36Sopenharmony_ci revision = "C1"; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci default: 55262306a36Sopenharmony_ci return -EIO; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci dev_info(dev, "revision - %s\n", revision); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return 0; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic void cs2000_remove(struct i2c_client *client) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct cs2000_priv *priv = i2c_get_clientdata(client); 56362306a36Sopenharmony_ci struct device *dev = priv_to_dev(priv); 56462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci of_clk_del_provider(np); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci clk_hw_unregister(&priv->hw); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int cs2000_probe(struct i2c_client *client) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct cs2000_priv *priv; 57462306a36Sopenharmony_ci struct device *dev = &client->dev; 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 57862306a36Sopenharmony_ci if (!priv) 57962306a36Sopenharmony_ci return -ENOMEM; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci priv->client = client; 58262306a36Sopenharmony_ci i2c_set_clientdata(client, priv); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci priv->regmap = devm_regmap_init_i2c(client, &cs2000_regmap_config); 58562306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) 58662306a36Sopenharmony_ci return PTR_ERR(priv->regmap); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = cs2000_clk_get(priv); 58962306a36Sopenharmony_ci if (ret < 0) 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = cs2000_clk_register(priv); 59362306a36Sopenharmony_ci if (ret < 0) 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret = cs2000_version_print(priv); 59762306a36Sopenharmony_ci if (ret < 0) 59862306a36Sopenharmony_ci goto probe_err; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ciprobe_err: 60362306a36Sopenharmony_ci cs2000_remove(client); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int __maybe_unused cs2000_resume(struct device *dev) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct cs2000_priv *priv = dev_get_drvdata(dev); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return cs2000_set_saved_rate(priv); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic const struct dev_pm_ops cs2000_pm_ops = { 61662306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, cs2000_resume) 61762306a36Sopenharmony_ci}; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic struct i2c_driver cs2000_driver = { 62062306a36Sopenharmony_ci .driver = { 62162306a36Sopenharmony_ci .name = "cs2000-cp", 62262306a36Sopenharmony_ci .pm = &cs2000_pm_ops, 62362306a36Sopenharmony_ci .of_match_table = cs2000_of_match, 62462306a36Sopenharmony_ci }, 62562306a36Sopenharmony_ci .probe = cs2000_probe, 62662306a36Sopenharmony_ci .remove = cs2000_remove, 62762306a36Sopenharmony_ci .id_table = cs2000_id, 62862306a36Sopenharmony_ci}; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cimodule_i2c_driver(cs2000_driver); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciMODULE_DESCRIPTION("CS2000-CP driver"); 63362306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 63462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 635