162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for IDT Versaclock 5 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * Possible optimizations: 1062306a36Sopenharmony_ci * - Use spread spectrum 1162306a36Sopenharmony_ci * - Use integer divider in FOD if applicable 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/clk-provider.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/i2c.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/property.h> 2362306a36Sopenharmony_ci#include <linux/regmap.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <dt-bindings/clock/versaclock.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* VersaClock5 registers */ 2962306a36Sopenharmony_ci#define VC5_OTP_CONTROL 0x00 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Factory-reserved register block */ 3262306a36Sopenharmony_ci#define VC5_RSVD_DEVICE_ID 0x01 3362306a36Sopenharmony_ci#define VC5_RSVD_ADC_GAIN_7_0 0x02 3462306a36Sopenharmony_ci#define VC5_RSVD_ADC_GAIN_15_8 0x03 3562306a36Sopenharmony_ci#define VC5_RSVD_ADC_OFFSET_7_0 0x04 3662306a36Sopenharmony_ci#define VC5_RSVD_ADC_OFFSET_15_8 0x05 3762306a36Sopenharmony_ci#define VC5_RSVD_TEMPY 0x06 3862306a36Sopenharmony_ci#define VC5_RSVD_OFFSET_TBIN 0x07 3962306a36Sopenharmony_ci#define VC5_RSVD_GAIN 0x08 4062306a36Sopenharmony_ci#define VC5_RSVD_TEST_NP 0x09 4162306a36Sopenharmony_ci#define VC5_RSVD_UNUSED 0x0a 4262306a36Sopenharmony_ci#define VC5_RSVD_BANDGAP_TRIM_UP 0x0b 4362306a36Sopenharmony_ci#define VC5_RSVD_BANDGAP_TRIM_DN 0x0c 4462306a36Sopenharmony_ci#define VC5_RSVD_CLK_R_12_CLK_AMP_4 0x0d 4562306a36Sopenharmony_ci#define VC5_RSVD_CLK_R_34_CLK_AMP_4 0x0e 4662306a36Sopenharmony_ci#define VC5_RSVD_CLK_AMP_123 0x0f 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Configuration register block */ 4962306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN 0x10 5062306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7) 5162306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6) 5262306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ BIT(3) 5362306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN_SP BIT(1) 5462306a36Sopenharmony_ci#define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define VC5_VCO_BAND 0x11 5762306a36Sopenharmony_ci#define VC5_XTAL_X1_LOAD_CAP 0x12 5862306a36Sopenharmony_ci#define VC5_XTAL_X2_LOAD_CAP 0x13 5962306a36Sopenharmony_ci#define VC5_REF_DIVIDER 0x15 6062306a36Sopenharmony_ci#define VC5_REF_DIVIDER_SEL_PREDIV2 BIT(7) 6162306a36Sopenharmony_ci#define VC5_REF_DIVIDER_REF_DIV(n) ((n) & 0x3f) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define VC5_VCO_CTRL_AND_PREDIV 0x16 6462306a36Sopenharmony_ci#define VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV BIT(7) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define VC5_FEEDBACK_INT_DIV 0x17 6762306a36Sopenharmony_ci#define VC5_FEEDBACK_INT_DIV_BITS 0x18 6862306a36Sopenharmony_ci#define VC5_FEEDBACK_FRAC_DIV(n) (0x19 + (n)) 6962306a36Sopenharmony_ci#define VC5_RC_CONTROL0 0x1e 7062306a36Sopenharmony_ci#define VC5_RC_CONTROL1 0x1f 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* These registers are named "Unused Factory Reserved Registers" */ 7362306a36Sopenharmony_ci#define VC5_RESERVED_X0(idx) (0x20 + ((idx) * 0x10)) 7462306a36Sopenharmony_ci#define VC5_RESERVED_X0_BYPASS_SYNC BIT(7) /* bypass_sync<idx> bit */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Output divider control for divider 1,2,3,4 */ 7762306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL(idx) (0x21 + ((idx) * 0x10)) 7862306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL_RESET BIT(7) 7962306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL_SELB_NORM BIT(3) 8062306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL_SEL_EXT BIT(2) 8162306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL_INT_MODE BIT(1) 8262306a36Sopenharmony_ci#define VC5_OUT_DIV_CONTROL_EN_FOD BIT(0) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define VC5_OUT_DIV_FRAC(idx, n) (0x22 + ((idx) * 0x10) + (n)) 8562306a36Sopenharmony_ci#define VC5_OUT_DIV_FRAC4_OD_SCEE BIT(1) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define VC5_OUT_DIV_STEP_SPREAD(idx, n) (0x26 + ((idx) * 0x10) + (n)) 8862306a36Sopenharmony_ci#define VC5_OUT_DIV_SPREAD_MOD(idx, n) (0x29 + ((idx) * 0x10) + (n)) 8962306a36Sopenharmony_ci#define VC5_OUT_DIV_SKEW_INT(idx, n) (0x2b + ((idx) * 0x10) + (n)) 9062306a36Sopenharmony_ci#define VC5_OUT_DIV_INT(idx, n) (0x2d + ((idx) * 0x10) + (n)) 9162306a36Sopenharmony_ci#define VC5_OUT_DIV_SKEW_FRAC(idx) (0x2f + ((idx) * 0x10)) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Clock control register for clock 1,2 */ 9462306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) 9562306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 9662306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) 9962306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) 10062306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) 10162306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) 10262306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) 10362306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) 10462306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 10762306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) 10862306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) 10962306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) 11062306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) 11162306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 11262306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) 11362306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) 11462306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) 11562306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) 11662306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) 11762306a36Sopenharmony_ci#define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define VC5_CLK_OE_SHDN 0x68 12062306a36Sopenharmony_ci#define VC5_CLK_OS_SHDN 0x69 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define VC5_GLOBAL_REGISTER 0x76 12362306a36Sopenharmony_ci#define VC5_GLOBAL_REGISTER_GLOBAL_RESET BIT(5) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* The minimum VCO frequency is 2.5 GHz. The maximum is variant specific. */ 12662306a36Sopenharmony_ci#define VC5_PLL_VCO_MIN 2500000000UL 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* VC5 Input mux settings */ 12962306a36Sopenharmony_ci#define VC5_MUX_IN_XIN BIT(0) 13062306a36Sopenharmony_ci#define VC5_MUX_IN_CLKIN BIT(1) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Maximum number of clk_out supported by this driver */ 13362306a36Sopenharmony_ci#define VC5_MAX_CLK_OUT_NUM 5 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Maximum number of FODs supported by this driver */ 13662306a36Sopenharmony_ci#define VC5_MAX_FOD_NUM 4 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* flags to describe chip features */ 13962306a36Sopenharmony_ci/* chip has built-in oscilator */ 14062306a36Sopenharmony_ci#define VC5_HAS_INTERNAL_XTAL BIT(0) 14162306a36Sopenharmony_ci/* chip has PFD requency doubler */ 14262306a36Sopenharmony_ci#define VC5_HAS_PFD_FREQ_DBL BIT(1) 14362306a36Sopenharmony_ci/* chip has bits to disable FOD sync */ 14462306a36Sopenharmony_ci#define VC5_HAS_BYPASS_SYNC_BIT BIT(2) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Supported IDT VC5 models. */ 14762306a36Sopenharmony_cienum vc5_model { 14862306a36Sopenharmony_ci IDT_VC5_5P49V5923, 14962306a36Sopenharmony_ci IDT_VC5_5P49V5925, 15062306a36Sopenharmony_ci IDT_VC5_5P49V5933, 15162306a36Sopenharmony_ci IDT_VC5_5P49V5935, 15262306a36Sopenharmony_ci IDT_VC6_5P49V60, 15362306a36Sopenharmony_ci IDT_VC6_5P49V6901, 15462306a36Sopenharmony_ci IDT_VC6_5P49V6965, 15562306a36Sopenharmony_ci IDT_VC6_5P49V6975, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* Structure to describe features of a particular VC5 model */ 15962306a36Sopenharmony_cistruct vc5_chip_info { 16062306a36Sopenharmony_ci const enum vc5_model model; 16162306a36Sopenharmony_ci const unsigned int clk_fod_cnt; 16262306a36Sopenharmony_ci const unsigned int clk_out_cnt; 16362306a36Sopenharmony_ci const u32 flags; 16462306a36Sopenharmony_ci const unsigned long vco_max; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct vc5_driver_data; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct vc5_hw_data { 17062306a36Sopenharmony_ci struct clk_hw hw; 17162306a36Sopenharmony_ci struct vc5_driver_data *vc5; 17262306a36Sopenharmony_ci u32 div_int; 17362306a36Sopenharmony_ci u32 div_frc; 17462306a36Sopenharmony_ci unsigned int num; 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistruct vc5_out_data { 17862306a36Sopenharmony_ci struct clk_hw hw; 17962306a36Sopenharmony_ci struct vc5_driver_data *vc5; 18062306a36Sopenharmony_ci unsigned int num; 18162306a36Sopenharmony_ci unsigned int clk_output_cfg0; 18262306a36Sopenharmony_ci unsigned int clk_output_cfg0_mask; 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct vc5_driver_data { 18662306a36Sopenharmony_ci struct i2c_client *client; 18762306a36Sopenharmony_ci struct regmap *regmap; 18862306a36Sopenharmony_ci const struct vc5_chip_info *chip_info; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci struct clk *pin_xin; 19162306a36Sopenharmony_ci struct clk *pin_clkin; 19262306a36Sopenharmony_ci unsigned char clk_mux_ins; 19362306a36Sopenharmony_ci struct clk_hw clk_mux; 19462306a36Sopenharmony_ci struct clk_hw clk_mul; 19562306a36Sopenharmony_ci struct clk_hw clk_pfd; 19662306a36Sopenharmony_ci struct vc5_hw_data clk_pll; 19762306a36Sopenharmony_ci struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; 19862306a36Sopenharmony_ci struct vc5_out_data clk_out[VC5_MAX_CLK_OUT_NUM]; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * VersaClock5 i2c regmap 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_cistatic bool vc5_regmap_is_writeable(struct device *dev, unsigned int reg) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci /* Factory reserved regs, make them read-only */ 20762306a36Sopenharmony_ci if (reg <= 0xf) 20862306a36Sopenharmony_ci return false; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Factory reserved regs, make them read-only */ 21162306a36Sopenharmony_ci if (reg == 0x14 || reg == 0x1c || reg == 0x1d) 21262306a36Sopenharmony_ci return false; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return true; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct regmap_config vc5_regmap_config = { 21862306a36Sopenharmony_ci .reg_bits = 8, 21962306a36Sopenharmony_ci .val_bits = 8, 22062306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 22162306a36Sopenharmony_ci .max_register = 0x76, 22262306a36Sopenharmony_ci .writeable_reg = vc5_regmap_is_writeable, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * VersaClock5 input multiplexer between XTAL and CLKIN divider 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic unsigned char vc5_mux_get_parent(struct clk_hw *hw) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 23162306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_mux); 23262306a36Sopenharmony_ci const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN; 23362306a36Sopenharmony_ci unsigned int src; 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src); 23762306a36Sopenharmony_ci if (ret) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci src &= mask; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (src == VC5_PRIM_SRC_SHDN_EN_XTAL) 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (src == VC5_PRIM_SRC_SHDN_EN_CLKIN) 24662306a36Sopenharmony_ci return 1; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci dev_warn(&vc5->client->dev, 24962306a36Sopenharmony_ci "Invalid clock input configuration (%02x)\n", src); 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int vc5_mux_set_parent(struct clk_hw *hw, u8 index) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 25662306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_mux); 25762306a36Sopenharmony_ci const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN; 25862306a36Sopenharmony_ci u8 src; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if ((index > 1) || !vc5->clk_mux_ins) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) { 26462306a36Sopenharmony_ci if (index == 0) 26562306a36Sopenharmony_ci src = VC5_PRIM_SRC_SHDN_EN_XTAL; 26662306a36Sopenharmony_ci if (index == 1) 26762306a36Sopenharmony_ci src = VC5_PRIM_SRC_SHDN_EN_CLKIN; 26862306a36Sopenharmony_ci } else { 26962306a36Sopenharmony_ci if (index != 0) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (vc5->clk_mux_ins == VC5_MUX_IN_XIN) 27362306a36Sopenharmony_ci src = VC5_PRIM_SRC_SHDN_EN_XTAL; 27462306a36Sopenharmony_ci else if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN) 27562306a36Sopenharmony_ci src = VC5_PRIM_SRC_SHDN_EN_CLKIN; 27662306a36Sopenharmony_ci else /* Invalid; should have been caught by vc5_probe() */ 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic const struct clk_ops vc5_mux_ops = { 28462306a36Sopenharmony_ci .determine_rate = clk_hw_determine_rate_no_reparent, 28562306a36Sopenharmony_ci .set_parent = vc5_mux_set_parent, 28662306a36Sopenharmony_ci .get_parent = vc5_mux_get_parent, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw, 29062306a36Sopenharmony_ci unsigned long parent_rate) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 29362306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_mul); 29462306a36Sopenharmony_ci unsigned int premul; 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ) 30262306a36Sopenharmony_ci parent_rate *= 2; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return parent_rate; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic long vc5_dbl_round_rate(struct clk_hw *hw, unsigned long rate, 30862306a36Sopenharmony_ci unsigned long *parent_rate) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci if ((*parent_rate == rate) || ((*parent_rate * 2) == rate)) 31162306a36Sopenharmony_ci return rate; 31262306a36Sopenharmony_ci else 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate, 31762306a36Sopenharmony_ci unsigned long parent_rate) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 32062306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_mul); 32162306a36Sopenharmony_ci u32 mask; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if ((parent_rate * 2) == rate) 32462306a36Sopenharmony_ci mask = VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ; 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci mask = 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, 32962306a36Sopenharmony_ci VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ, 33062306a36Sopenharmony_ci mask); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct clk_ops vc5_dbl_ops = { 33462306a36Sopenharmony_ci .recalc_rate = vc5_dbl_recalc_rate, 33562306a36Sopenharmony_ci .round_rate = vc5_dbl_round_rate, 33662306a36Sopenharmony_ci .set_rate = vc5_dbl_set_rate, 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw, 34062306a36Sopenharmony_ci unsigned long parent_rate) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 34362306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_pfd); 34462306a36Sopenharmony_ci unsigned int prediv, div; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv); 34862306a36Sopenharmony_ci if (ret) 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* The bypass_prediv is set, PLL fed from Ref_in directly. */ 35262306a36Sopenharmony_ci if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV) 35362306a36Sopenharmony_ci return parent_rate; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div); 35662306a36Sopenharmony_ci if (ret) 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */ 36062306a36Sopenharmony_ci if (div & VC5_REF_DIVIDER_SEL_PREDIV2) 36162306a36Sopenharmony_ci return parent_rate / 2; 36262306a36Sopenharmony_ci else 36362306a36Sopenharmony_ci return parent_rate / VC5_REF_DIVIDER_REF_DIV(div); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic long vc5_pfd_round_rate(struct clk_hw *hw, unsigned long rate, 36762306a36Sopenharmony_ci unsigned long *parent_rate) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci unsigned long idiv; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* PLL cannot operate with input clock above 50 MHz. */ 37262306a36Sopenharmony_ci if (rate > 50000000) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* CLKIN within range of PLL input, feed directly to PLL. */ 37662306a36Sopenharmony_ci if (*parent_rate <= 50000000) 37762306a36Sopenharmony_ci return *parent_rate; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci idiv = DIV_ROUND_UP(*parent_rate, rate); 38062306a36Sopenharmony_ci if (idiv > 127) 38162306a36Sopenharmony_ci return -EINVAL; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return *parent_rate / idiv; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate, 38762306a36Sopenharmony_ci unsigned long parent_rate) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct vc5_driver_data *vc5 = 39062306a36Sopenharmony_ci container_of(hw, struct vc5_driver_data, clk_pfd); 39162306a36Sopenharmony_ci unsigned long idiv; 39262306a36Sopenharmony_ci int ret; 39362306a36Sopenharmony_ci u8 div; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* CLKIN within range of PLL input, feed directly to PLL. */ 39662306a36Sopenharmony_ci if (parent_rate <= 50000000) { 39762306a36Sopenharmony_ci ret = regmap_set_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, 39862306a36Sopenharmony_ci VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV); 39962306a36Sopenharmony_ci if (ret) 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci idiv = DIV_ROUND_UP(parent_rate, rate); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* We have dedicated div-2 predivider. */ 40862306a36Sopenharmony_ci if (idiv == 2) 40962306a36Sopenharmony_ci div = VC5_REF_DIVIDER_SEL_PREDIV2; 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci div = VC5_REF_DIVIDER_REF_DIV(idiv); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ret = regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div); 41462306a36Sopenharmony_ci if (ret) 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return regmap_clear_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, 41862306a36Sopenharmony_ci VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic const struct clk_ops vc5_pfd_ops = { 42262306a36Sopenharmony_ci .recalc_rate = vc5_pfd_recalc_rate, 42362306a36Sopenharmony_ci .round_rate = vc5_pfd_round_rate, 42462306a36Sopenharmony_ci .set_rate = vc5_pfd_set_rate, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* 42862306a36Sopenharmony_ci * VersaClock5 PLL/VCO 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic unsigned long vc5_pll_recalc_rate(struct clk_hw *hw, 43162306a36Sopenharmony_ci unsigned long parent_rate) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 43462306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 43562306a36Sopenharmony_ci u32 div_int, div_frc; 43662306a36Sopenharmony_ci u8 fb[5]; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci regmap_bulk_read(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci div_int = (fb[0] << 4) | (fb[1] >> 4); 44162306a36Sopenharmony_ci div_frc = (fb[2] << 16) | (fb[3] << 8) | fb[4]; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* The PLL divider has 12 integer bits and 24 fractional bits */ 44462306a36Sopenharmony_ci return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate, 44862306a36Sopenharmony_ci unsigned long *parent_rate) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 45162306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 45262306a36Sopenharmony_ci u32 div_int; 45362306a36Sopenharmony_ci u64 div_frc; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci rate = clamp(rate, VC5_PLL_VCO_MIN, vc5->chip_info->vco_max); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Determine integer part, which is 12 bit wide */ 45862306a36Sopenharmony_ci div_int = rate / *parent_rate; 45962306a36Sopenharmony_ci if (div_int > 0xfff) 46062306a36Sopenharmony_ci rate = *parent_rate * 0xfff; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Determine best fractional part, which is 24 bit wide */ 46362306a36Sopenharmony_ci div_frc = rate % *parent_rate; 46462306a36Sopenharmony_ci div_frc *= BIT(24) - 1; 46562306a36Sopenharmony_ci do_div(div_frc, *parent_rate); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci hwdata->div_int = div_int; 46862306a36Sopenharmony_ci hwdata->div_frc = (u32)div_frc; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return (*parent_rate * div_int) + ((*parent_rate * div_frc) >> 24); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate, 47462306a36Sopenharmony_ci unsigned long parent_rate) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 47762306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 47862306a36Sopenharmony_ci u8 fb[5]; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci fb[0] = hwdata->div_int >> 4; 48162306a36Sopenharmony_ci fb[1] = hwdata->div_int << 4; 48262306a36Sopenharmony_ci fb[2] = hwdata->div_frc >> 16; 48362306a36Sopenharmony_ci fb[3] = hwdata->div_frc >> 8; 48462306a36Sopenharmony_ci fb[4] = hwdata->div_frc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return regmap_bulk_write(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic const struct clk_ops vc5_pll_ops = { 49062306a36Sopenharmony_ci .recalc_rate = vc5_pll_recalc_rate, 49162306a36Sopenharmony_ci .round_rate = vc5_pll_round_rate, 49262306a36Sopenharmony_ci .set_rate = vc5_pll_set_rate, 49362306a36Sopenharmony_ci}; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic unsigned long vc5_fod_recalc_rate(struct clk_hw *hw, 49662306a36Sopenharmony_ci unsigned long parent_rate) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 49962306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 50062306a36Sopenharmony_ci /* VCO frequency is divided by two before entering FOD */ 50162306a36Sopenharmony_ci u32 f_in = parent_rate / 2; 50262306a36Sopenharmony_ci u32 div_int, div_frc; 50362306a36Sopenharmony_ci u8 od_int[2]; 50462306a36Sopenharmony_ci u8 od_frc[4]; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_INT(hwdata->num, 0), 50762306a36Sopenharmony_ci od_int, 2); 50862306a36Sopenharmony_ci regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), 50962306a36Sopenharmony_ci od_frc, 4); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci div_int = (od_int[0] << 4) | (od_int[1] >> 4); 51262306a36Sopenharmony_ci div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) | 51362306a36Sopenharmony_ci (od_frc[2] << 6) | (od_frc[3] >> 2); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Avoid division by zero if the output is not configured. */ 51662306a36Sopenharmony_ci if (div_int == 0 && div_frc == 0) 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* The PLL divider has 12 integer bits and 30 fractional bits */ 52062306a36Sopenharmony_ci return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate, 52462306a36Sopenharmony_ci unsigned long *parent_rate) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 52762306a36Sopenharmony_ci /* VCO frequency is divided by two before entering FOD */ 52862306a36Sopenharmony_ci u32 f_in = *parent_rate / 2; 52962306a36Sopenharmony_ci u32 div_int; 53062306a36Sopenharmony_ci u64 div_frc; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Determine integer part, which is 12 bit wide */ 53362306a36Sopenharmony_ci div_int = f_in / rate; 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * WARNING: The clock chip does not output signal if the integer part 53662306a36Sopenharmony_ci * of the divider is 0xfff and fractional part is non-zero. 53762306a36Sopenharmony_ci * Clamp the divider at 0xffe to keep the code simple. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci if (div_int > 0xffe) { 54062306a36Sopenharmony_ci div_int = 0xffe; 54162306a36Sopenharmony_ci rate = f_in / div_int; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Determine best fractional part, which is 30 bit wide */ 54562306a36Sopenharmony_ci div_frc = f_in % rate; 54662306a36Sopenharmony_ci div_frc <<= 24; 54762306a36Sopenharmony_ci do_div(div_frc, rate); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci hwdata->div_int = div_int; 55062306a36Sopenharmony_ci hwdata->div_frc = (u32)div_frc; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate, 55662306a36Sopenharmony_ci unsigned long parent_rate) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); 55962306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 56062306a36Sopenharmony_ci u8 data[14] = { 56162306a36Sopenharmony_ci hwdata->div_frc >> 22, hwdata->div_frc >> 14, 56262306a36Sopenharmony_ci hwdata->div_frc >> 6, hwdata->div_frc << 2, 56362306a36Sopenharmony_ci 0, 0, 0, 0, 0, 56462306a36Sopenharmony_ci 0, 0, 56562306a36Sopenharmony_ci hwdata->div_int >> 4, hwdata->div_int << 4, 56662306a36Sopenharmony_ci 0 56762306a36Sopenharmony_ci }; 56862306a36Sopenharmony_ci int ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ret = regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), 57162306a36Sopenharmony_ci data, 14); 57262306a36Sopenharmony_ci if (ret) 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Toggle magic bit in undocumented register for unknown reason. 57762306a36Sopenharmony_ci * This is what the IDT timing commander tool does and the chip 57862306a36Sopenharmony_ci * datasheet somewhat implies this is needed, but the register 57962306a36Sopenharmony_ci * and the bit is not documented. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci ret = regmap_clear_bits(vc5->regmap, VC5_GLOBAL_REGISTER, 58262306a36Sopenharmony_ci VC5_GLOBAL_REGISTER_GLOBAL_RESET); 58362306a36Sopenharmony_ci if (ret) 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return regmap_set_bits(vc5->regmap, VC5_GLOBAL_REGISTER, 58762306a36Sopenharmony_ci VC5_GLOBAL_REGISTER_GLOBAL_RESET); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const struct clk_ops vc5_fod_ops = { 59162306a36Sopenharmony_ci .recalc_rate = vc5_fod_recalc_rate, 59262306a36Sopenharmony_ci .round_rate = vc5_fod_round_rate, 59362306a36Sopenharmony_ci .set_rate = vc5_fod_set_rate, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int vc5_clk_out_prepare(struct clk_hw *hw) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); 59962306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 60062306a36Sopenharmony_ci const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | 60162306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SEL_EXT | 60262306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_EN_FOD; 60362306a36Sopenharmony_ci unsigned int src; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * When enabling a FOD, all currently enabled FODs are briefly 60862306a36Sopenharmony_ci * stopped in order to synchronize all of them. This causes a clock 60962306a36Sopenharmony_ci * disruption to any unrelated chips that might be already using 61062306a36Sopenharmony_ci * other clock outputs. Bypass the sync feature to avoid the issue, 61162306a36Sopenharmony_ci * which is possible on the VersaClock 6E family via reserved 61262306a36Sopenharmony_ci * registers. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) { 61562306a36Sopenharmony_ci ret = regmap_set_bits(vc5->regmap, 61662306a36Sopenharmony_ci VC5_RESERVED_X0(hwdata->num), 61762306a36Sopenharmony_ci VC5_RESERVED_X0_BYPASS_SYNC); 61862306a36Sopenharmony_ci if (ret) 61962306a36Sopenharmony_ci return ret; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* 62362306a36Sopenharmony_ci * If the input mux is disabled, enable it first and 62462306a36Sopenharmony_ci * select source from matching FOD. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); 62762306a36Sopenharmony_ci if (ret) 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if ((src & mask) == 0) { 63162306a36Sopenharmony_ci src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD; 63262306a36Sopenharmony_ci ret = regmap_update_bits(vc5->regmap, 63362306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL(hwdata->num), 63462306a36Sopenharmony_ci mask | VC5_OUT_DIV_CONTROL_RESET, src); 63562306a36Sopenharmony_ci if (ret) 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Enable the clock buffer */ 64062306a36Sopenharmony_ci ret = regmap_set_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), 64162306a36Sopenharmony_ci VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); 64262306a36Sopenharmony_ci if (ret) 64362306a36Sopenharmony_ci return ret; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (hwdata->clk_output_cfg0_mask) { 64662306a36Sopenharmony_ci dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", 64762306a36Sopenharmony_ci hwdata->num, hwdata->clk_output_cfg0_mask, 64862306a36Sopenharmony_ci hwdata->clk_output_cfg0); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci ret = regmap_update_bits(vc5->regmap, 65162306a36Sopenharmony_ci VC5_CLK_OUTPUT_CFG(hwdata->num, 0), 65262306a36Sopenharmony_ci hwdata->clk_output_cfg0_mask, 65362306a36Sopenharmony_ci hwdata->clk_output_cfg0); 65462306a36Sopenharmony_ci if (ret) 65562306a36Sopenharmony_ci return ret; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void vc5_clk_out_unprepare(struct clk_hw *hw) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); 66462306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Disable the clock buffer */ 66762306a36Sopenharmony_ci regmap_clear_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), 66862306a36Sopenharmony_ci VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); 67462306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 67562306a36Sopenharmony_ci const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | 67662306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SEL_EXT | 67762306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_EN_FOD; 67862306a36Sopenharmony_ci const u8 fodclkmask = VC5_OUT_DIV_CONTROL_SELB_NORM | 67962306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_EN_FOD; 68062306a36Sopenharmony_ci const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM | 68162306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SEL_EXT; 68262306a36Sopenharmony_ci unsigned int src; 68362306a36Sopenharmony_ci int ret; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); 68662306a36Sopenharmony_ci if (ret) 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci src &= mask; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (src == 0) /* Input mux set to DISABLED */ 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD) 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (src == extclk) 69862306a36Sopenharmony_ci return 1; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci dev_warn(&vc5->client->dev, 70162306a36Sopenharmony_ci "Invalid clock output configuration (%02x)\n", src); 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); 70862306a36Sopenharmony_ci struct vc5_driver_data *vc5 = hwdata->vc5; 70962306a36Sopenharmony_ci const u8 mask = VC5_OUT_DIV_CONTROL_RESET | 71062306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SELB_NORM | 71162306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SEL_EXT | 71262306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_EN_FOD; 71362306a36Sopenharmony_ci const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM | 71462306a36Sopenharmony_ci VC5_OUT_DIV_CONTROL_SEL_EXT; 71562306a36Sopenharmony_ci u8 src = VC5_OUT_DIV_CONTROL_RESET; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (index == 0) 71862306a36Sopenharmony_ci src |= VC5_OUT_DIV_CONTROL_EN_FOD; 71962306a36Sopenharmony_ci else 72062306a36Sopenharmony_ci src |= extclk; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return regmap_update_bits(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), 72362306a36Sopenharmony_ci mask, src); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic const struct clk_ops vc5_clk_out_ops = { 72762306a36Sopenharmony_ci .prepare = vc5_clk_out_prepare, 72862306a36Sopenharmony_ci .unprepare = vc5_clk_out_unprepare, 72962306a36Sopenharmony_ci .determine_rate = clk_hw_determine_rate_no_reparent, 73062306a36Sopenharmony_ci .set_parent = vc5_clk_out_set_parent, 73162306a36Sopenharmony_ci .get_parent = vc5_clk_out_get_parent, 73262306a36Sopenharmony_ci}; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic struct clk_hw *vc5_of_clk_get(struct of_phandle_args *clkspec, 73562306a36Sopenharmony_ci void *data) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct vc5_driver_data *vc5 = data; 73862306a36Sopenharmony_ci unsigned int idx = clkspec->args[0]; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (idx >= vc5->chip_info->clk_out_cnt) 74162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return &vc5->clk_out[idx].hw; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int vc5_map_index_to_output(const enum vc5_model model, 74762306a36Sopenharmony_ci const unsigned int n) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci switch (model) { 75062306a36Sopenharmony_ci case IDT_VC5_5P49V5933: 75162306a36Sopenharmony_ci return (n == 0) ? 0 : 3; 75262306a36Sopenharmony_ci case IDT_VC5_5P49V5923: 75362306a36Sopenharmony_ci case IDT_VC5_5P49V5925: 75462306a36Sopenharmony_ci case IDT_VC5_5P49V5935: 75562306a36Sopenharmony_ci case IDT_VC6_5P49V6901: 75662306a36Sopenharmony_ci case IDT_VC6_5P49V6965: 75762306a36Sopenharmony_ci case IDT_VC6_5P49V6975: 75862306a36Sopenharmony_ci default: 75962306a36Sopenharmony_ci return n; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int vc5_update_mode(struct device_node *np_output, 76462306a36Sopenharmony_ci struct vc5_out_data *clk_out) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci u32 value; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (!of_property_read_u32(np_output, "idt,mode", &value)) { 76962306a36Sopenharmony_ci clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; 77062306a36Sopenharmony_ci switch (value) { 77162306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: 77262306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: 77362306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: 77462306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: 77562306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: 77662306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: 77762306a36Sopenharmony_ci case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: 77862306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= 77962306a36Sopenharmony_ci value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci default: 78262306a36Sopenharmony_ci return -EINVAL; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int vc5_update_power(struct device_node *np_output, 78962306a36Sopenharmony_ci struct vc5_out_data *clk_out) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci u32 value; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!of_property_read_u32(np_output, "idt,voltage-microvolt", 79462306a36Sopenharmony_ci &value)) { 79562306a36Sopenharmony_ci clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; 79662306a36Sopenharmony_ci switch (value) { 79762306a36Sopenharmony_ci case 1800000: 79862306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci case 2500000: 80162306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case 3300000: 80462306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci default: 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int vc5_map_cap_value(u32 femtofarads) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci int mapped_value; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * The datasheet explicitly states 9000 - 25000 with 0.5pF 81962306a36Sopenharmony_ci * steps, but the Programmer's guide shows the steps are 0.430pF. 82062306a36Sopenharmony_ci * After getting feedback from Renesas, the .5pF steps were the 82162306a36Sopenharmony_ci * goal, but 430nF was the actual values. 82262306a36Sopenharmony_ci * Because of this, the actual range goes to 22760 instead of 25000 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci if (femtofarads < 9000 || femtofarads > 22760) 82562306a36Sopenharmony_ci return -EINVAL; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* 82862306a36Sopenharmony_ci * The Programmer's guide shows XTAL[5:0] but in reality, 82962306a36Sopenharmony_ci * XTAL[0] and XTAL[1] are both LSB which makes the math 83062306a36Sopenharmony_ci * strange. With clarfication from Renesas, setting the 83162306a36Sopenharmony_ci * values should be simpler by ignoring XTAL[0] 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci mapped_value = DIV_ROUND_CLOSEST(femtofarads - 9000, 430); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * Since the calculation ignores XTAL[0], there is one 83762306a36Sopenharmony_ci * special case where mapped_value = 32. In reality, this means 83862306a36Sopenharmony_ci * the real mapped value should be 111111b. In other cases, 83962306a36Sopenharmony_ci * the mapped_value needs to be shifted 1 to the left. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci if (mapped_value > 31) 84262306a36Sopenharmony_ci mapped_value = 0x3f; 84362306a36Sopenharmony_ci else 84462306a36Sopenharmony_ci mapped_value <<= 1; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return mapped_value; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_cistatic int vc5_update_cap_load(struct device_node *node, struct vc5_driver_data *vc5) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u32 value; 85162306a36Sopenharmony_ci int mapped_value; 85262306a36Sopenharmony_ci int ret; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (of_property_read_u32(node, "idt,xtal-load-femtofarads", &value)) 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci mapped_value = vc5_map_cap_value(value); 85862306a36Sopenharmony_ci if (mapped_value < 0) 85962306a36Sopenharmony_ci return mapped_value; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * The mapped_value is really the high 6 bits of 86362306a36Sopenharmony_ci * VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so 86462306a36Sopenharmony_ci * shift the value 2 places. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci ret = regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03, 86762306a36Sopenharmony_ci mapped_value << 2); 86862306a36Sopenharmony_ci if (ret) 86962306a36Sopenharmony_ci return ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03, 87262306a36Sopenharmony_ci mapped_value << 2); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int vc5_update_slew(struct device_node *np_output, 87662306a36Sopenharmony_ci struct vc5_out_data *clk_out) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci u32 value; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { 88162306a36Sopenharmony_ci clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; 88262306a36Sopenharmony_ci switch (value) { 88362306a36Sopenharmony_ci case 80: 88462306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case 85: 88762306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci case 90: 89062306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci case 100: 89362306a36Sopenharmony_ci clk_out->clk_output_cfg0 |= 89462306a36Sopenharmony_ci VC5_CLK_OUTPUT_CFG0_SLEW_100; 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci default: 89762306a36Sopenharmony_ci return -EINVAL; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int vc5_get_output_config(struct i2c_client *client, 90462306a36Sopenharmony_ci struct vc5_out_data *clk_out) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct device_node *np_output; 90762306a36Sopenharmony_ci char *child_name; 90862306a36Sopenharmony_ci int ret = 0; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); 91162306a36Sopenharmony_ci if (!child_name) 91262306a36Sopenharmony_ci return -ENOMEM; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci np_output = of_get_child_by_name(client->dev.of_node, child_name); 91562306a36Sopenharmony_ci kfree(child_name); 91662306a36Sopenharmony_ci if (!np_output) 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = vc5_update_mode(np_output, clk_out); 92062306a36Sopenharmony_ci if (ret) 92162306a36Sopenharmony_ci goto output_error; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ret = vc5_update_power(np_output, clk_out); 92462306a36Sopenharmony_ci if (ret) 92562306a36Sopenharmony_ci goto output_error; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci ret = vc5_update_slew(np_output, clk_out); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cioutput_error: 93062306a36Sopenharmony_ci if (ret) { 93162306a36Sopenharmony_ci dev_err(&client->dev, 93262306a36Sopenharmony_ci "Invalid clock output configuration OUT%d\n", 93362306a36Sopenharmony_ci clk_out->num + 1); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci of_node_put(np_output); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return ret; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic const struct of_device_id clk_vc5_of_match[]; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int vc5_probe(struct i2c_client *client) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci unsigned int oe, sd, src_mask = 0, src_val = 0; 94662306a36Sopenharmony_ci struct vc5_driver_data *vc5; 94762306a36Sopenharmony_ci struct clk_init_data init; 94862306a36Sopenharmony_ci const char *parent_names[2]; 94962306a36Sopenharmony_ci unsigned int n, idx = 0; 95062306a36Sopenharmony_ci int ret; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); 95362306a36Sopenharmony_ci if (!vc5) 95462306a36Sopenharmony_ci return -ENOMEM; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci i2c_set_clientdata(client, vc5); 95762306a36Sopenharmony_ci vc5->client = client; 95862306a36Sopenharmony_ci vc5->chip_info = i2c_get_match_data(client); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci vc5->pin_xin = devm_clk_get(&client->dev, "xin"); 96162306a36Sopenharmony_ci if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER) 96262306a36Sopenharmony_ci return -EPROBE_DEFER; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci vc5->pin_clkin = devm_clk_get(&client->dev, "clkin"); 96562306a36Sopenharmony_ci if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER) 96662306a36Sopenharmony_ci return -EPROBE_DEFER; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config); 96962306a36Sopenharmony_ci if (IS_ERR(vc5->regmap)) 97062306a36Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(vc5->regmap), 97162306a36Sopenharmony_ci "failed to allocate register map\n"); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = of_property_read_u32(client->dev.of_node, "idt,shutdown", &sd); 97462306a36Sopenharmony_ci if (!ret) { 97562306a36Sopenharmony_ci src_mask |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN; 97662306a36Sopenharmony_ci if (sd) 97762306a36Sopenharmony_ci src_val |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN; 97862306a36Sopenharmony_ci } else if (ret != -EINVAL) { 97962306a36Sopenharmony_ci return dev_err_probe(&client->dev, ret, 98062306a36Sopenharmony_ci "could not read idt,shutdown\n"); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ret = of_property_read_u32(client->dev.of_node, 98462306a36Sopenharmony_ci "idt,output-enable-active", &oe); 98562306a36Sopenharmony_ci if (!ret) { 98662306a36Sopenharmony_ci src_mask |= VC5_PRIM_SRC_SHDN_SP; 98762306a36Sopenharmony_ci if (oe) 98862306a36Sopenharmony_ci src_val |= VC5_PRIM_SRC_SHDN_SP; 98962306a36Sopenharmony_ci } else if (ret != -EINVAL) { 99062306a36Sopenharmony_ci return dev_err_probe(&client->dev, ret, 99162306a36Sopenharmony_ci "could not read idt,output-enable-active\n"); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci ret = regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask, 99562306a36Sopenharmony_ci src_val); 99662306a36Sopenharmony_ci if (ret) 99762306a36Sopenharmony_ci return ret; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* Register clock input mux */ 100062306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (!IS_ERR(vc5->pin_xin)) { 100362306a36Sopenharmony_ci vc5->clk_mux_ins |= VC5_MUX_IN_XIN; 100462306a36Sopenharmony_ci parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); 100562306a36Sopenharmony_ci } else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) { 100662306a36Sopenharmony_ci vc5->pin_xin = clk_register_fixed_rate(&client->dev, 100762306a36Sopenharmony_ci "internal-xtal", NULL, 100862306a36Sopenharmony_ci 0, 25000000); 100962306a36Sopenharmony_ci if (IS_ERR(vc5->pin_xin)) 101062306a36Sopenharmony_ci return PTR_ERR(vc5->pin_xin); 101162306a36Sopenharmony_ci vc5->clk_mux_ins |= VC5_MUX_IN_XIN; 101262306a36Sopenharmony_ci parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (!IS_ERR(vc5->pin_clkin)) { 101662306a36Sopenharmony_ci vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; 101762306a36Sopenharmony_ci parent_names[init.num_parents++] = 101862306a36Sopenharmony_ci __clk_get_name(vc5->pin_clkin); 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!init.num_parents) 102262306a36Sopenharmony_ci return dev_err_probe(&client->dev, -EINVAL, 102362306a36Sopenharmony_ci "no input clock specified!\n"); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* Configure Optional Loading Capacitance for external XTAL */ 102662306a36Sopenharmony_ci if (!(vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)) { 102762306a36Sopenharmony_ci ret = vc5_update_cap_load(client->dev.of_node, vc5); 102862306a36Sopenharmony_ci if (ret) 102962306a36Sopenharmony_ci goto err_clk_register; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); 103362306a36Sopenharmony_ci if (!init.name) { 103462306a36Sopenharmony_ci ret = -ENOMEM; 103562306a36Sopenharmony_ci goto err_clk; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci init.ops = &vc5_mux_ops; 103962306a36Sopenharmony_ci init.flags = 0; 104062306a36Sopenharmony_ci init.parent_names = parent_names; 104162306a36Sopenharmony_ci vc5->clk_mux.init = &init; 104262306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); 104362306a36Sopenharmony_ci if (ret) 104462306a36Sopenharmony_ci goto err_clk_register; 104562306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { 104862306a36Sopenharmony_ci /* Register frequency doubler */ 104962306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 105062306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", 105162306a36Sopenharmony_ci client->dev.of_node); 105262306a36Sopenharmony_ci if (!init.name) { 105362306a36Sopenharmony_ci ret = -ENOMEM; 105462306a36Sopenharmony_ci goto err_clk; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci init.ops = &vc5_dbl_ops; 105762306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 105862306a36Sopenharmony_ci init.parent_names = parent_names; 105962306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_mux); 106062306a36Sopenharmony_ci init.num_parents = 1; 106162306a36Sopenharmony_ci vc5->clk_mul.init = &init; 106262306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); 106362306a36Sopenharmony_ci if (ret) 106462306a36Sopenharmony_ci goto err_clk_register; 106562306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Register PFD */ 106962306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 107062306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); 107162306a36Sopenharmony_ci if (!init.name) { 107262306a36Sopenharmony_ci ret = -ENOMEM; 107362306a36Sopenharmony_ci goto err_clk; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci init.ops = &vc5_pfd_ops; 107662306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 107762306a36Sopenharmony_ci init.parent_names = parent_names; 107862306a36Sopenharmony_ci if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) 107962306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_mul); 108062306a36Sopenharmony_ci else 108162306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_mux); 108262306a36Sopenharmony_ci init.num_parents = 1; 108362306a36Sopenharmony_ci vc5->clk_pfd.init = &init; 108462306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); 108562306a36Sopenharmony_ci if (ret) 108662306a36Sopenharmony_ci goto err_clk_register; 108762306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Register PLL */ 109062306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 109162306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); 109262306a36Sopenharmony_ci if (!init.name) { 109362306a36Sopenharmony_ci ret = -ENOMEM; 109462306a36Sopenharmony_ci goto err_clk; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci init.ops = &vc5_pll_ops; 109762306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 109862306a36Sopenharmony_ci init.parent_names = parent_names; 109962306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); 110062306a36Sopenharmony_ci init.num_parents = 1; 110162306a36Sopenharmony_ci vc5->clk_pll.num = 0; 110262306a36Sopenharmony_ci vc5->clk_pll.vc5 = vc5; 110362306a36Sopenharmony_ci vc5->clk_pll.hw.init = &init; 110462306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); 110562306a36Sopenharmony_ci if (ret) 110662306a36Sopenharmony_ci goto err_clk_register; 110762306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* Register FODs */ 111062306a36Sopenharmony_ci for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { 111162306a36Sopenharmony_ci idx = vc5_map_index_to_output(vc5->chip_info->model, n); 111262306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 111362306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", 111462306a36Sopenharmony_ci client->dev.of_node, idx); 111562306a36Sopenharmony_ci if (!init.name) { 111662306a36Sopenharmony_ci ret = -ENOMEM; 111762306a36Sopenharmony_ci goto err_clk; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci init.ops = &vc5_fod_ops; 112062306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 112162306a36Sopenharmony_ci init.parent_names = parent_names; 112262306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); 112362306a36Sopenharmony_ci init.num_parents = 1; 112462306a36Sopenharmony_ci vc5->clk_fod[n].num = idx; 112562306a36Sopenharmony_ci vc5->clk_fod[n].vc5 = vc5; 112662306a36Sopenharmony_ci vc5->clk_fod[n].hw.init = &init; 112762306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); 112862306a36Sopenharmony_ci if (ret) 112962306a36Sopenharmony_ci goto err_clk_register; 113062306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* Register MUX-connected OUT0_I2C_SELB output */ 113462306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 113562306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", 113662306a36Sopenharmony_ci client->dev.of_node); 113762306a36Sopenharmony_ci if (!init.name) { 113862306a36Sopenharmony_ci ret = -ENOMEM; 113962306a36Sopenharmony_ci goto err_clk; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci init.ops = &vc5_clk_out_ops; 114262306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 114362306a36Sopenharmony_ci init.parent_names = parent_names; 114462306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_mux); 114562306a36Sopenharmony_ci init.num_parents = 1; 114662306a36Sopenharmony_ci vc5->clk_out[0].num = idx; 114762306a36Sopenharmony_ci vc5->clk_out[0].vc5 = vc5; 114862306a36Sopenharmony_ci vc5->clk_out[0].hw.init = &init; 114962306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); 115062306a36Sopenharmony_ci if (ret) 115162306a36Sopenharmony_ci goto err_clk_register; 115262306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* Register FOD-connected OUTx outputs */ 115562306a36Sopenharmony_ci for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { 115662306a36Sopenharmony_ci idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); 115762306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); 115862306a36Sopenharmony_ci if (n == 1) 115962306a36Sopenharmony_ci parent_names[1] = clk_hw_get_name(&vc5->clk_mux); 116062306a36Sopenharmony_ci else 116162306a36Sopenharmony_ci parent_names[1] = 116262306a36Sopenharmony_ci clk_hw_get_name(&vc5->clk_out[n - 1].hw); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 116562306a36Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", 116662306a36Sopenharmony_ci client->dev.of_node, idx + 1); 116762306a36Sopenharmony_ci if (!init.name) { 116862306a36Sopenharmony_ci ret = -ENOMEM; 116962306a36Sopenharmony_ci goto err_clk; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci init.ops = &vc5_clk_out_ops; 117262306a36Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 117362306a36Sopenharmony_ci init.parent_names = parent_names; 117462306a36Sopenharmony_ci init.num_parents = 2; 117562306a36Sopenharmony_ci vc5->clk_out[n].num = idx; 117662306a36Sopenharmony_ci vc5->clk_out[n].vc5 = vc5; 117762306a36Sopenharmony_ci vc5->clk_out[n].hw.init = &init; 117862306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); 117962306a36Sopenharmony_ci if (ret) 118062306a36Sopenharmony_ci goto err_clk_register; 118162306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Fetch Clock Output configuration from DT (if specified) */ 118462306a36Sopenharmony_ci ret = vc5_get_output_config(client, &vc5->clk_out[n]); 118562306a36Sopenharmony_ci if (ret) 118662306a36Sopenharmony_ci goto err_clk; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); 119062306a36Sopenharmony_ci if (ret) { 119162306a36Sopenharmony_ci dev_err_probe(&client->dev, ret, 119262306a36Sopenharmony_ci "unable to add clk provider\n"); 119362306a36Sopenharmony_ci goto err_clk; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cierr_clk_register: 119962306a36Sopenharmony_ci dev_err_probe(&client->dev, ret, 120062306a36Sopenharmony_ci "unable to register %s\n", init.name); 120162306a36Sopenharmony_ci kfree(init.name); /* clock framework made a copy of the name */ 120262306a36Sopenharmony_cierr_clk: 120362306a36Sopenharmony_ci if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) 120462306a36Sopenharmony_ci clk_unregister_fixed_rate(vc5->pin_xin); 120562306a36Sopenharmony_ci return ret; 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic void vc5_remove(struct i2c_client *client) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct vc5_driver_data *vc5 = i2c_get_clientdata(client); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci of_clk_del_provider(client->dev.of_node); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) 121562306a36Sopenharmony_ci clk_unregister_fixed_rate(vc5->pin_xin); 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic int __maybe_unused vc5_suspend(struct device *dev) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct vc5_driver_data *vc5 = dev_get_drvdata(dev); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci regcache_cache_only(vc5->regmap, true); 122362306a36Sopenharmony_ci regcache_mark_dirty(vc5->regmap); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cistatic int __maybe_unused vc5_resume(struct device *dev) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci struct vc5_driver_data *vc5 = dev_get_drvdata(dev); 123162306a36Sopenharmony_ci int ret; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci regcache_cache_only(vc5->regmap, false); 123462306a36Sopenharmony_ci ret = regcache_sync(vc5->regmap); 123562306a36Sopenharmony_ci if (ret) 123662306a36Sopenharmony_ci dev_err(dev, "Failed to restore register map: %d\n", ret); 123762306a36Sopenharmony_ci return ret; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v5923_info = { 124162306a36Sopenharmony_ci .model = IDT_VC5_5P49V5923, 124262306a36Sopenharmony_ci .clk_fod_cnt = 2, 124362306a36Sopenharmony_ci .clk_out_cnt = 3, 124462306a36Sopenharmony_ci .flags = 0, 124562306a36Sopenharmony_ci .vco_max = 3000000000UL, 124662306a36Sopenharmony_ci}; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v5925_info = { 124962306a36Sopenharmony_ci .model = IDT_VC5_5P49V5925, 125062306a36Sopenharmony_ci .clk_fod_cnt = 4, 125162306a36Sopenharmony_ci .clk_out_cnt = 5, 125262306a36Sopenharmony_ci .flags = 0, 125362306a36Sopenharmony_ci .vco_max = 3000000000UL, 125462306a36Sopenharmony_ci}; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v5933_info = { 125762306a36Sopenharmony_ci .model = IDT_VC5_5P49V5933, 125862306a36Sopenharmony_ci .clk_fod_cnt = 2, 125962306a36Sopenharmony_ci .clk_out_cnt = 3, 126062306a36Sopenharmony_ci .flags = VC5_HAS_INTERNAL_XTAL, 126162306a36Sopenharmony_ci .vco_max = 3000000000UL, 126262306a36Sopenharmony_ci}; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v5935_info = { 126562306a36Sopenharmony_ci .model = IDT_VC5_5P49V5935, 126662306a36Sopenharmony_ci .clk_fod_cnt = 4, 126762306a36Sopenharmony_ci .clk_out_cnt = 5, 126862306a36Sopenharmony_ci .flags = VC5_HAS_INTERNAL_XTAL, 126962306a36Sopenharmony_ci .vco_max = 3000000000UL, 127062306a36Sopenharmony_ci}; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v60_info = { 127362306a36Sopenharmony_ci .model = IDT_VC6_5P49V60, 127462306a36Sopenharmony_ci .clk_fod_cnt = 4, 127562306a36Sopenharmony_ci .clk_out_cnt = 5, 127662306a36Sopenharmony_ci .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT, 127762306a36Sopenharmony_ci .vco_max = 2700000000UL, 127862306a36Sopenharmony_ci}; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v6901_info = { 128162306a36Sopenharmony_ci .model = IDT_VC6_5P49V6901, 128262306a36Sopenharmony_ci .clk_fod_cnt = 4, 128362306a36Sopenharmony_ci .clk_out_cnt = 5, 128462306a36Sopenharmony_ci .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT, 128562306a36Sopenharmony_ci .vco_max = 3000000000UL, 128662306a36Sopenharmony_ci}; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v6965_info = { 128962306a36Sopenharmony_ci .model = IDT_VC6_5P49V6965, 129062306a36Sopenharmony_ci .clk_fod_cnt = 4, 129162306a36Sopenharmony_ci .clk_out_cnt = 5, 129262306a36Sopenharmony_ci .flags = VC5_HAS_BYPASS_SYNC_BIT, 129362306a36Sopenharmony_ci .vco_max = 3000000000UL, 129462306a36Sopenharmony_ci}; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic const struct vc5_chip_info idt_5p49v6975_info = { 129762306a36Sopenharmony_ci .model = IDT_VC6_5P49V6975, 129862306a36Sopenharmony_ci .clk_fod_cnt = 4, 129962306a36Sopenharmony_ci .clk_out_cnt = 5, 130062306a36Sopenharmony_ci .flags = VC5_HAS_BYPASS_SYNC_BIT | VC5_HAS_INTERNAL_XTAL, 130162306a36Sopenharmony_ci .vco_max = 3000000000UL, 130262306a36Sopenharmony_ci}; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic const struct i2c_device_id vc5_id[] = { 130562306a36Sopenharmony_ci { "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, 130662306a36Sopenharmony_ci { "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, 130762306a36Sopenharmony_ci { "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, 130862306a36Sopenharmony_ci { "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, 130962306a36Sopenharmony_ci { "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info }, 131062306a36Sopenharmony_ci { "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, 131162306a36Sopenharmony_ci { "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, 131262306a36Sopenharmony_ci { "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, 131362306a36Sopenharmony_ci { } 131462306a36Sopenharmony_ci}; 131562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, vc5_id); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic const struct of_device_id clk_vc5_of_match[] = { 131862306a36Sopenharmony_ci { .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info }, 131962306a36Sopenharmony_ci { .compatible = "idt,5p49v5925", .data = &idt_5p49v5925_info }, 132062306a36Sopenharmony_ci { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info }, 132162306a36Sopenharmony_ci { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info }, 132262306a36Sopenharmony_ci { .compatible = "idt,5p49v60", .data = &idt_5p49v60_info }, 132362306a36Sopenharmony_ci { .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info }, 132462306a36Sopenharmony_ci { .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info }, 132562306a36Sopenharmony_ci { .compatible = "idt,5p49v6975", .data = &idt_5p49v6975_info }, 132662306a36Sopenharmony_ci { }, 132762306a36Sopenharmony_ci}; 132862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, clk_vc5_of_match); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(vc5_pm_ops, vc5_suspend, vc5_resume); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic struct i2c_driver vc5_driver = { 133362306a36Sopenharmony_ci .driver = { 133462306a36Sopenharmony_ci .name = "vc5", 133562306a36Sopenharmony_ci .pm = &vc5_pm_ops, 133662306a36Sopenharmony_ci .of_match_table = clk_vc5_of_match, 133762306a36Sopenharmony_ci }, 133862306a36Sopenharmony_ci .probe = vc5_probe, 133962306a36Sopenharmony_ci .remove = vc5_remove, 134062306a36Sopenharmony_ci .id_table = vc5_id, 134162306a36Sopenharmony_ci}; 134262306a36Sopenharmony_cimodule_i2c_driver(vc5_driver); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 134562306a36Sopenharmony_ciMODULE_DESCRIPTION("IDT VersaClock 5 driver"); 134662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1347