xref: /openbmc/linux/drivers/clk/clk-versaclock5.c (revision 0894f0b6)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23e1aec4eSMarek Vasut /*
33e1aec4eSMarek Vasut  * Driver for IDT Versaclock 5
43e1aec4eSMarek Vasut  *
53e1aec4eSMarek Vasut  * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
63e1aec4eSMarek Vasut  */
73e1aec4eSMarek Vasut 
83e1aec4eSMarek Vasut /*
93e1aec4eSMarek Vasut  * Possible optimizations:
103e1aec4eSMarek Vasut  * - Use spread spectrum
113e1aec4eSMarek Vasut  * - Use integer divider in FOD if applicable
123e1aec4eSMarek Vasut  */
133e1aec4eSMarek Vasut 
143e1aec4eSMarek Vasut #include <linux/clk.h>
153e1aec4eSMarek Vasut #include <linux/clk-provider.h>
163e1aec4eSMarek Vasut #include <linux/delay.h>
173e1aec4eSMarek Vasut #include <linux/i2c.h>
183e1aec4eSMarek Vasut #include <linux/interrupt.h>
193e1aec4eSMarek Vasut #include <linux/mod_devicetable.h>
203e1aec4eSMarek Vasut #include <linux/module.h>
213e1aec4eSMarek Vasut #include <linux/of.h>
223e1aec4eSMarek Vasut #include <linux/property.h>
23d3d1c5e9SMarek Vasut #include <linux/regmap.h>
243e1aec4eSMarek Vasut #include <linux/slab.h>
253e1aec4eSMarek Vasut 
263e1aec4eSMarek Vasut #include <dt-bindings/clock/versaclock.h>
27ba6165bcSLukas Bulwahn 
28260249f9SAdam Ford /* VersaClock5 registers */
293e1aec4eSMarek Vasut #define VC5_OTP_CONTROL				0x00
303e1aec4eSMarek Vasut 
313e1aec4eSMarek Vasut /* Factory-reserved register block */
323e1aec4eSMarek Vasut #define VC5_RSVD_DEVICE_ID			0x01
333e1aec4eSMarek Vasut #define VC5_RSVD_ADC_GAIN_7_0			0x02
343e1aec4eSMarek Vasut #define VC5_RSVD_ADC_GAIN_15_8			0x03
353e1aec4eSMarek Vasut #define VC5_RSVD_ADC_OFFSET_7_0			0x04
363e1aec4eSMarek Vasut #define VC5_RSVD_ADC_OFFSET_15_8		0x05
373e1aec4eSMarek Vasut #define VC5_RSVD_TEMPY				0x06
383e1aec4eSMarek Vasut #define VC5_RSVD_OFFSET_TBIN			0x07
393e1aec4eSMarek Vasut #define VC5_RSVD_GAIN				0x08
403e1aec4eSMarek Vasut #define VC5_RSVD_TEST_NP			0x09
413e1aec4eSMarek Vasut #define VC5_RSVD_UNUSED				0x0a
423e1aec4eSMarek Vasut #define VC5_RSVD_BANDGAP_TRIM_UP		0x0b
433e1aec4eSMarek Vasut #define VC5_RSVD_BANDGAP_TRIM_DN		0x0c
443e1aec4eSMarek Vasut #define VC5_RSVD_CLK_R_12_CLK_AMP_4		0x0d
453e1aec4eSMarek Vasut #define VC5_RSVD_CLK_R_34_CLK_AMP_4		0x0e
463e1aec4eSMarek Vasut #define VC5_RSVD_CLK_AMP_123			0x0f
473e1aec4eSMarek Vasut 
483e1aec4eSMarek Vasut /* Configuration register block */
493e1aec4eSMarek Vasut #define VC5_PRIM_SRC_SHDN			0x10
503e1aec4eSMarek Vasut #define VC5_PRIM_SRC_SHDN_EN_XTAL		BIT(7)
513e1aec4eSMarek Vasut #define VC5_PRIM_SRC_SHDN_EN_CLKIN		BIT(6)
523e1aec4eSMarek Vasut #define VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ	BIT(3)
538c1ebe97SMarek Vasut #define VC5_PRIM_SRC_SHDN_SP			BIT(1)
543e1aec4eSMarek Vasut #define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN		BIT(0)
553e1aec4eSMarek Vasut 
563e1aec4eSMarek Vasut #define VC5_VCO_BAND				0x11
573e1aec4eSMarek Vasut #define VC5_XTAL_X1_LOAD_CAP			0x12
583e1aec4eSMarek Vasut #define VC5_XTAL_X2_LOAD_CAP			0x13
593e1aec4eSMarek Vasut #define VC5_REF_DIVIDER				0x15
603e1aec4eSMarek Vasut #define VC5_REF_DIVIDER_SEL_PREDIV2		BIT(7)
613e1aec4eSMarek Vasut #define VC5_REF_DIVIDER_REF_DIV(n)		((n) & 0x3f)
623e1aec4eSMarek Vasut 
633e1aec4eSMarek Vasut #define VC5_VCO_CTRL_AND_PREDIV			0x16
643e1aec4eSMarek Vasut #define VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV	BIT(7)
653e1aec4eSMarek Vasut 
663e1aec4eSMarek Vasut #define VC5_FEEDBACK_INT_DIV			0x17
673e1aec4eSMarek Vasut #define VC5_FEEDBACK_INT_DIV_BITS		0x18
683e1aec4eSMarek Vasut #define VC5_FEEDBACK_FRAC_DIV(n)		(0x19 + (n))
693e1aec4eSMarek Vasut #define VC5_RC_CONTROL0				0x1e
703e1aec4eSMarek Vasut #define VC5_RC_CONTROL1				0x1f
713e1aec4eSMarek Vasut 
72fc336ae6SLuca Ceresoli /* These registers are named "Unused Factory Reserved Registers" */
73fc336ae6SLuca Ceresoli #define VC5_RESERVED_X0(idx)		(0x20 + ((idx) * 0x10))
74fc336ae6SLuca Ceresoli #define VC5_RESERVED_X0_BYPASS_SYNC	BIT(7) /* bypass_sync<idx> bit */
75fc336ae6SLuca Ceresoli 
763e1aec4eSMarek Vasut /* Output divider control for divider 1,2,3,4 */
773e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL(idx)	(0x21 + ((idx) * 0x10))
783e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL_RESET	BIT(7)
793e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL_SELB_NORM	BIT(3)
803e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL_SEL_EXT	BIT(2)
813e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL_INT_MODE	BIT(1)
823e1aec4eSMarek Vasut #define VC5_OUT_DIV_CONTROL_EN_FOD	BIT(0)
833e1aec4eSMarek Vasut 
843e1aec4eSMarek Vasut #define VC5_OUT_DIV_FRAC(idx, n)	(0x22 + ((idx) * 0x10) + (n))
853e1aec4eSMarek Vasut #define VC5_OUT_DIV_FRAC4_OD_SCEE	BIT(1)
863e1aec4eSMarek Vasut 
873e1aec4eSMarek Vasut #define VC5_OUT_DIV_STEP_SPREAD(idx, n)	(0x26 + ((idx) * 0x10) + (n))
883e1aec4eSMarek Vasut #define VC5_OUT_DIV_SPREAD_MOD(idx, n)	(0x29 + ((idx) * 0x10) + (n))
893e1aec4eSMarek Vasut #define VC5_OUT_DIV_SKEW_INT(idx, n)	(0x2b + ((idx) * 0x10) + (n))
903e1aec4eSMarek Vasut #define VC5_OUT_DIV_INT(idx, n)		(0x2d + ((idx) * 0x10) + (n))
913e1aec4eSMarek Vasut #define VC5_OUT_DIV_SKEW_FRAC(idx)	(0x2f + ((idx) * 0x10))
923e1aec4eSMarek Vasut 
933e1aec4eSMarek Vasut /* Clock control register for clock 1,2 */
943e1aec4eSMarek Vasut #define VC5_CLK_OUTPUT_CFG(idx, n)	(0x60 + ((idx) * 0x2) + (n))
953e1aec4eSMarek Vasut #define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT	5
96260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT)
97260249f9SAdam Ford 
98260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL	(VC5_LVPECL)
99260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_CMOS		(VC5_CMOS)
100260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33	(VC5_HCSL33)
101260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_LVDS		(VC5_LVDS)
102260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2		(VC5_CMOS2)
103260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD		(VC5_CMOSD)
104260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25	(VC5_HCSL25)
105260249f9SAdam Ford 
106260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT	3
107260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
108260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_PWR_18	(0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
109260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_PWR_25	(2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
110260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_PWR_33	(3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
111260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT	0
112260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
113260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_80	(0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
114260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_85	(1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
115260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_90	(2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
116260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG0_SLEW_100	(3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
117260249f9SAdam Ford #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF	BIT(0)
1183e1aec4eSMarek Vasut 
1193e1aec4eSMarek Vasut #define VC5_CLK_OE_SHDN				0x68
1203e1aec4eSMarek Vasut #define VC5_CLK_OS_SHDN				0x69
1213e1aec4eSMarek Vasut 
1223e1aec4eSMarek Vasut #define VC5_GLOBAL_REGISTER			0x76
1233e1aec4eSMarek Vasut #define VC5_GLOBAL_REGISTER_GLOBAL_RESET	BIT(5)
1243e1aec4eSMarek Vasut 
1253e1aec4eSMarek Vasut /* The minimum VCO frequency is 2.5 GHz. The maximum is variant specific. */
1264411da32SLars-Peter Clausen #define VC5_PLL_VCO_MIN				2500000000UL
1273e1aec4eSMarek Vasut 
1283e1aec4eSMarek Vasut /* VC5 Input mux settings */
1293e1aec4eSMarek Vasut #define VC5_MUX_IN_XIN		BIT(0)
1303e1aec4eSMarek Vasut #define VC5_MUX_IN_CLKIN	BIT(1)
1313e1aec4eSMarek Vasut 
1323e1aec4eSMarek Vasut /* Maximum number of clk_out supported by this driver */
1339adddb01SAlexey Firago #define VC5_MAX_CLK_OUT_NUM	5
1341193e14fSAlexey Firago 
1359adddb01SAlexey Firago /* Maximum number of FODs supported by this driver */
1369adddb01SAlexey Firago #define VC5_MAX_FOD_NUM	4
1371193e14fSAlexey Firago 
1389adddb01SAlexey Firago /* flags to describe chip features */
1399adddb01SAlexey Firago /* chip has built-in oscilator */
1409adddb01SAlexey Firago #define VC5_HAS_INTERNAL_XTAL	BIT(0)
1419adddb01SAlexey Firago /* chip has PFD requency doubler */
1428c1ebe97SMarek Vasut #define VC5_HAS_PFD_FREQ_DBL	BIT(1)
1438c1ebe97SMarek Vasut /* chip has bits to disable FOD sync */
144fc336ae6SLuca Ceresoli #define VC5_HAS_BYPASS_SYNC_BIT	BIT(2)
145fc336ae6SLuca Ceresoli 
1469adddb01SAlexey Firago /* Supported IDT VC5 models. */
1473e1aec4eSMarek Vasut enum vc5_model {
1483e1aec4eSMarek Vasut 	IDT_VC5_5P49V5923,
1493e1aec4eSMarek Vasut 	IDT_VC5_5P49V5925,
150b1911555SVladimir Barinov 	IDT_VC5_5P49V5933,
1513e1aec4eSMarek Vasut 	IDT_VC5_5P49V5935,
1521193e14fSAlexey Firago 	IDT_VC6_5P49V60,
1534411da32SLars-Peter Clausen 	IDT_VC6_5P49V6901,
154dbf6b16fSMarek Vasut 	IDT_VC6_5P49V6965,
1552bda748eSAdam Ford 	IDT_VC6_5P49V6975,
156d8473831SMatthias Fend };
1573e1aec4eSMarek Vasut 
1583e1aec4eSMarek Vasut /* Structure to describe features of a particular VC5 model */
1599adddb01SAlexey Firago struct vc5_chip_info {
1609adddb01SAlexey Firago 	const enum vc5_model	model;
1619adddb01SAlexey Firago 	const unsigned int	clk_fod_cnt;
1629adddb01SAlexey Firago 	const unsigned int	clk_out_cnt;
1639adddb01SAlexey Firago 	const u32		flags;
1649adddb01SAlexey Firago 	const unsigned long	vco_max;
1654411da32SLars-Peter Clausen };
1669adddb01SAlexey Firago 
1679adddb01SAlexey Firago struct vc5_driver_data;
1683e1aec4eSMarek Vasut 
1693e1aec4eSMarek Vasut struct vc5_hw_data {
1703e1aec4eSMarek Vasut 	struct clk_hw		hw;
1713e1aec4eSMarek Vasut 	struct vc5_driver_data	*vc5;
1723e1aec4eSMarek Vasut 	u32			div_int;
1733e1aec4eSMarek Vasut 	u32			div_frc;
1743e1aec4eSMarek Vasut 	unsigned int		num;
1753e1aec4eSMarek Vasut };
1763bca66b0SLuca Ceresoli 
1773bca66b0SLuca Ceresoli struct vc5_out_data {
1783bca66b0SLuca Ceresoli 	struct clk_hw		hw;
1793bca66b0SLuca Ceresoli 	struct vc5_driver_data	*vc5;
1803bca66b0SLuca Ceresoli 	unsigned int		num;
1813bca66b0SLuca Ceresoli 	unsigned int		clk_output_cfg0;
182260249f9SAdam Ford 	unsigned int		clk_output_cfg0_mask;
183260249f9SAdam Ford };
1843e1aec4eSMarek Vasut 
1853e1aec4eSMarek Vasut struct vc5_driver_data {
1863e1aec4eSMarek Vasut 	struct i2c_client	*client;
1873e1aec4eSMarek Vasut 	struct regmap		*regmap;
1883e1aec4eSMarek Vasut 	const struct vc5_chip_info	*chip_info;
1899adddb01SAlexey Firago 
1903e1aec4eSMarek Vasut 	struct clk		*pin_xin;
1913e1aec4eSMarek Vasut 	struct clk		*pin_clkin;
1923e1aec4eSMarek Vasut 	unsigned char		clk_mux_ins;
1933e1aec4eSMarek Vasut 	struct clk_hw		clk_mux;
1943e1aec4eSMarek Vasut 	struct clk_hw		clk_mul;
1958c1ebe97SMarek Vasut 	struct clk_hw		clk_pfd;
19655997db5SMarek Vasut 	struct vc5_hw_data	clk_pll;
1973e1aec4eSMarek Vasut 	struct vc5_hw_data	clk_fod[VC5_MAX_FOD_NUM];
1989adddb01SAlexey Firago 	struct vc5_out_data	clk_out[VC5_MAX_CLK_OUT_NUM];
1993bca66b0SLuca Ceresoli };
2003e1aec4eSMarek Vasut 
2013e1aec4eSMarek Vasut /*
2023e1aec4eSMarek Vasut  * VersaClock5 i2c regmap
2033e1aec4eSMarek Vasut  */
vc5_regmap_is_writeable(struct device * dev,unsigned int reg)2043e1aec4eSMarek Vasut static bool vc5_regmap_is_writeable(struct device *dev, unsigned int reg)
2053e1aec4eSMarek Vasut {
2063e1aec4eSMarek Vasut 	/* Factory reserved regs, make them read-only */
2073e1aec4eSMarek Vasut 	if (reg <= 0xf)
2083e1aec4eSMarek Vasut 		return false;
2093e1aec4eSMarek Vasut 
2103e1aec4eSMarek Vasut 	/* Factory reserved regs, make them read-only */
2113e1aec4eSMarek Vasut 	if (reg == 0x14 || reg == 0x1c || reg == 0x1d)
2123e1aec4eSMarek Vasut 		return false;
2133e1aec4eSMarek Vasut 
2143e1aec4eSMarek Vasut 	return true;
2153e1aec4eSMarek Vasut }
2163e1aec4eSMarek Vasut 
2173e1aec4eSMarek Vasut static const struct regmap_config vc5_regmap_config = {
2183e1aec4eSMarek Vasut 	.reg_bits = 8,
2193e1aec4eSMarek Vasut 	.val_bits = 8,
2203e1aec4eSMarek Vasut 	.cache_type = REGCACHE_RBTREE,
2213e1aec4eSMarek Vasut 	.max_register = 0x76,
2223e1aec4eSMarek Vasut 	.writeable_reg = vc5_regmap_is_writeable,
2233e1aec4eSMarek Vasut };
2243e1aec4eSMarek Vasut 
2253e1aec4eSMarek Vasut /*
2263e1aec4eSMarek Vasut  * VersaClock5 input multiplexer between XTAL and CLKIN divider
2273e1aec4eSMarek Vasut  */
vc5_mux_get_parent(struct clk_hw * hw)2283e1aec4eSMarek Vasut static unsigned char vc5_mux_get_parent(struct clk_hw *hw)
2293e1aec4eSMarek Vasut {
2303e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 =
2313e1aec4eSMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_mux);
2323e1aec4eSMarek Vasut 	const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
2333e1aec4eSMarek Vasut 	unsigned int src;
2343e1aec4eSMarek Vasut 	int ret;
235cc323782SLars-Peter Clausen 
2363e1aec4eSMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
237cc323782SLars-Peter Clausen 	if (ret)
238cc323782SLars-Peter Clausen 		return 0;
239cc323782SLars-Peter Clausen 
240cc323782SLars-Peter Clausen 	src &= mask;
2413e1aec4eSMarek Vasut 
2423e1aec4eSMarek Vasut 	if (src == VC5_PRIM_SRC_SHDN_EN_XTAL)
2433e1aec4eSMarek Vasut 		return 0;
2443e1aec4eSMarek Vasut 
2453e1aec4eSMarek Vasut 	if (src == VC5_PRIM_SRC_SHDN_EN_CLKIN)
2463e1aec4eSMarek Vasut 		return 1;
2473e1aec4eSMarek Vasut 
2483e1aec4eSMarek Vasut 	dev_warn(&vc5->client->dev,
2493e1aec4eSMarek Vasut 		 "Invalid clock input configuration (%02x)\n", src);
2503e1aec4eSMarek Vasut 	return 0;
2513e1aec4eSMarek Vasut }
2523e1aec4eSMarek Vasut 
vc5_mux_set_parent(struct clk_hw * hw,u8 index)2533e1aec4eSMarek Vasut static int vc5_mux_set_parent(struct clk_hw *hw, u8 index)
2543e1aec4eSMarek Vasut {
2553e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 =
2563e1aec4eSMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_mux);
2573e1aec4eSMarek Vasut 	const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
2583e1aec4eSMarek Vasut 	u8 src;
2593e1aec4eSMarek Vasut 
2603e1aec4eSMarek Vasut 	if ((index > 1) || !vc5->clk_mux_ins)
2613e1aec4eSMarek Vasut 		return -EINVAL;
2623e1aec4eSMarek Vasut 
2633e1aec4eSMarek Vasut 	if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) {
2643e1aec4eSMarek Vasut 		if (index == 0)
2653e1aec4eSMarek Vasut 			src = VC5_PRIM_SRC_SHDN_EN_XTAL;
2663e1aec4eSMarek Vasut 		if (index == 1)
2673e1aec4eSMarek Vasut 			src = VC5_PRIM_SRC_SHDN_EN_CLKIN;
2683e1aec4eSMarek Vasut 	} else {
2693e1aec4eSMarek Vasut 		if (index != 0)
2703e1aec4eSMarek Vasut 			return -EINVAL;
2713e1aec4eSMarek Vasut 
2723e1aec4eSMarek Vasut 		if (vc5->clk_mux_ins == VC5_MUX_IN_XIN)
2733e1aec4eSMarek Vasut 			src = VC5_PRIM_SRC_SHDN_EN_XTAL;
2743e1aec4eSMarek Vasut 		else if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN)
2752137a109SMarek Vasut 			src = VC5_PRIM_SRC_SHDN_EN_CLKIN;
2763e1aec4eSMarek Vasut 		else /* Invalid; should have been caught by vc5_probe() */
2772137a109SMarek Vasut 			return -EINVAL;
2782137a109SMarek Vasut 	}
2793e1aec4eSMarek Vasut 
2803e1aec4eSMarek Vasut 	return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src);
2813e1aec4eSMarek Vasut }
2823e1aec4eSMarek Vasut 
2833e1aec4eSMarek Vasut static const struct clk_ops vc5_mux_ops = {
28455997db5SMarek Vasut 	.determine_rate	= clk_hw_determine_rate_no_reparent,
285dcba8da5SMaxime Ripard 	.set_parent	= vc5_mux_set_parent,
28655997db5SMarek Vasut 	.get_parent	= vc5_mux_get_parent,
28755997db5SMarek Vasut };
28855997db5SMarek Vasut 
vc5_dbl_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)28955997db5SMarek Vasut static unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw,
2908c1ebe97SMarek Vasut 					 unsigned long parent_rate)
2918c1ebe97SMarek Vasut {
2928c1ebe97SMarek Vasut 	struct vc5_driver_data *vc5 =
2938c1ebe97SMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_mul);
2948c1ebe97SMarek Vasut 	unsigned int premul;
2958c1ebe97SMarek Vasut 	int ret;
296cc323782SLars-Peter Clausen 
2978c1ebe97SMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul);
298cc323782SLars-Peter Clausen 	if (ret)
299cc323782SLars-Peter Clausen 		return 0;
300cc323782SLars-Peter Clausen 
301cc323782SLars-Peter Clausen 	if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ)
3028c1ebe97SMarek Vasut 		parent_rate *= 2;
3038c1ebe97SMarek Vasut 
3048c1ebe97SMarek Vasut 	return parent_rate;
3058c1ebe97SMarek Vasut }
3068c1ebe97SMarek Vasut 
vc5_dbl_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)3078c1ebe97SMarek Vasut static long vc5_dbl_round_rate(struct clk_hw *hw, unsigned long rate,
3088c1ebe97SMarek Vasut 			       unsigned long *parent_rate)
3098c1ebe97SMarek Vasut {
3108c1ebe97SMarek Vasut 	if ((*parent_rate == rate) || ((*parent_rate * 2) == rate))
3118c1ebe97SMarek Vasut 		return rate;
3128c1ebe97SMarek Vasut 	else
3138c1ebe97SMarek Vasut 		return -EINVAL;
3148c1ebe97SMarek Vasut }
3158c1ebe97SMarek Vasut 
vc5_dbl_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)3168c1ebe97SMarek Vasut static int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate,
3178c1ebe97SMarek Vasut 			    unsigned long parent_rate)
3188c1ebe97SMarek Vasut {
3198c1ebe97SMarek Vasut 	struct vc5_driver_data *vc5 =
3208c1ebe97SMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_mul);
3218c1ebe97SMarek Vasut 	u32 mask;
3228c1ebe97SMarek Vasut 
3238c1ebe97SMarek Vasut 	if ((parent_rate * 2) == rate)
3248c1ebe97SMarek Vasut 		mask = VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ;
3258c1ebe97SMarek Vasut 	else
3268c1ebe97SMarek Vasut 		mask = 0;
3278c1ebe97SMarek Vasut 
3288c1ebe97SMarek Vasut 	return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN,
329cc323782SLars-Peter Clausen 				  VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ,
3308c1ebe97SMarek Vasut 				  mask);
3318c1ebe97SMarek Vasut }
3328c1ebe97SMarek Vasut 
3338c1ebe97SMarek Vasut static const struct clk_ops vc5_dbl_ops = {
3348c1ebe97SMarek Vasut 	.recalc_rate	= vc5_dbl_recalc_rate,
3358c1ebe97SMarek Vasut 	.round_rate	= vc5_dbl_round_rate,
3368c1ebe97SMarek Vasut 	.set_rate	= vc5_dbl_set_rate,
3378c1ebe97SMarek Vasut };
3388c1ebe97SMarek Vasut 
vc5_pfd_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)3398c1ebe97SMarek Vasut static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw,
34055997db5SMarek Vasut 					 unsigned long parent_rate)
3413e1aec4eSMarek Vasut {
3423e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 =
3433e1aec4eSMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_pfd);
34455997db5SMarek Vasut 	unsigned int prediv, div;
3453e1aec4eSMarek Vasut 	int ret;
346cc323782SLars-Peter Clausen 
3473e1aec4eSMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
348cc323782SLars-Peter Clausen 	if (ret)
349cc323782SLars-Peter Clausen 		return 0;
350cc323782SLars-Peter Clausen 
3513e1aec4eSMarek Vasut 	/* The bypass_prediv is set, PLL fed from Ref_in directly. */
3523e1aec4eSMarek Vasut 	if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV)
3533e1aec4eSMarek Vasut 		return parent_rate;
3543e1aec4eSMarek Vasut 
3553e1aec4eSMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
356cc323782SLars-Peter Clausen 	if (ret)
357cc323782SLars-Peter Clausen 		return 0;
358cc323782SLars-Peter Clausen 
3593e1aec4eSMarek Vasut 	/* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */
3603e1aec4eSMarek Vasut 	if (div & VC5_REF_DIVIDER_SEL_PREDIV2)
3613e1aec4eSMarek Vasut 		return parent_rate / 2;
3623e1aec4eSMarek Vasut 	else
3633e1aec4eSMarek Vasut 		return parent_rate / VC5_REF_DIVIDER_REF_DIV(div);
3643e1aec4eSMarek Vasut }
3653e1aec4eSMarek Vasut 
vc5_pfd_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)3663e1aec4eSMarek Vasut static long vc5_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
36755997db5SMarek Vasut 			       unsigned long *parent_rate)
3683e1aec4eSMarek Vasut {
3693e1aec4eSMarek Vasut 	unsigned long idiv;
3703e1aec4eSMarek Vasut 
3713e1aec4eSMarek Vasut 	/* PLL cannot operate with input clock above 50 MHz. */
3723e1aec4eSMarek Vasut 	if (rate > 50000000)
3733e1aec4eSMarek Vasut 		return -EINVAL;
3743e1aec4eSMarek Vasut 
3753e1aec4eSMarek Vasut 	/* CLKIN within range of PLL input, feed directly to PLL. */
3763e1aec4eSMarek Vasut 	if (*parent_rate <= 50000000)
3773e1aec4eSMarek Vasut 		return *parent_rate;
3783e1aec4eSMarek Vasut 
3793e1aec4eSMarek Vasut 	idiv = DIV_ROUND_UP(*parent_rate, rate);
3803e1aec4eSMarek Vasut 	if (idiv > 127)
3813e1aec4eSMarek Vasut 		return -EINVAL;
3823e1aec4eSMarek Vasut 
3833e1aec4eSMarek Vasut 	return *parent_rate / idiv;
3843e1aec4eSMarek Vasut }
3853e1aec4eSMarek Vasut 
vc5_pfd_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)3863e1aec4eSMarek Vasut static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
38755997db5SMarek Vasut 			    unsigned long parent_rate)
3883e1aec4eSMarek Vasut {
3893e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 =
3903e1aec4eSMarek Vasut 		container_of(hw, struct vc5_driver_data, clk_pfd);
39155997db5SMarek Vasut 	unsigned long idiv;
3923e1aec4eSMarek Vasut 	int ret;
393cc323782SLars-Peter Clausen 	u8 div;
3943e1aec4eSMarek Vasut 
3953e1aec4eSMarek Vasut 	/* CLKIN within range of PLL input, feed directly to PLL. */
3963e1aec4eSMarek Vasut 	if (parent_rate <= 50000000) {
3973e1aec4eSMarek Vasut 		ret = regmap_set_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
39801874fb2SLars-Peter Clausen 				      VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
3993e1aec4eSMarek Vasut 		if (ret)
400cc323782SLars-Peter Clausen 			return ret;
401cc323782SLars-Peter Clausen 
402cc323782SLars-Peter Clausen 		return regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
403cc323782SLars-Peter Clausen 	}
4043e1aec4eSMarek Vasut 
4053e1aec4eSMarek Vasut 	idiv = DIV_ROUND_UP(parent_rate, rate);
4063e1aec4eSMarek Vasut 
4073e1aec4eSMarek Vasut 	/* We have dedicated div-2 predivider. */
4083e1aec4eSMarek Vasut 	if (idiv == 2)
4093e1aec4eSMarek Vasut 		div = VC5_REF_DIVIDER_SEL_PREDIV2;
4103e1aec4eSMarek Vasut 	else
4113e1aec4eSMarek Vasut 		div = VC5_REF_DIVIDER_REF_DIV(idiv);
4123e1aec4eSMarek Vasut 
4133e1aec4eSMarek Vasut 	ret = regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
414cc323782SLars-Peter Clausen 	if (ret)
415cc323782SLars-Peter Clausen 		return ret;
416cc323782SLars-Peter Clausen 
4173e1aec4eSMarek Vasut 	return regmap_clear_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
41801874fb2SLars-Peter Clausen 				 VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
41901874fb2SLars-Peter Clausen }
4203e1aec4eSMarek Vasut 
4213e1aec4eSMarek Vasut static const struct clk_ops vc5_pfd_ops = {
42255997db5SMarek Vasut 	.recalc_rate	= vc5_pfd_recalc_rate,
42355997db5SMarek Vasut 	.round_rate	= vc5_pfd_round_rate,
42455997db5SMarek Vasut 	.set_rate	= vc5_pfd_set_rate,
42555997db5SMarek Vasut };
4263e1aec4eSMarek Vasut 
4273e1aec4eSMarek Vasut /*
4283e1aec4eSMarek Vasut  * VersaClock5 PLL/VCO
4293e1aec4eSMarek Vasut  */
vc5_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)4303e1aec4eSMarek Vasut static unsigned long vc5_pll_recalc_rate(struct clk_hw *hw,
4313e1aec4eSMarek Vasut 					 unsigned long parent_rate)
4323e1aec4eSMarek Vasut {
4333e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
4343e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = hwdata->vc5;
4353e1aec4eSMarek Vasut 	u32 div_int, div_frc;
4363e1aec4eSMarek Vasut 	u8 fb[5];
4373e1aec4eSMarek Vasut 
4383e1aec4eSMarek Vasut 	regmap_bulk_read(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5);
4393e1aec4eSMarek Vasut 
4403e1aec4eSMarek Vasut 	div_int = (fb[0] << 4) | (fb[1] >> 4);
4413e1aec4eSMarek Vasut 	div_frc = (fb[2] << 16) | (fb[3] << 8) | fb[4];
4423e1aec4eSMarek Vasut 
4433e1aec4eSMarek Vasut 	/* The PLL divider has 12 integer bits and 24 fractional bits */
4443e1aec4eSMarek Vasut 	return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24);
4453e1aec4eSMarek Vasut }
4463e1aec4eSMarek Vasut 
vc5_pll_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)4473e1aec4eSMarek Vasut static long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
4483e1aec4eSMarek Vasut 			       unsigned long *parent_rate)
4493e1aec4eSMarek Vasut {
4503e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
4513e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = hwdata->vc5;
4524411da32SLars-Peter Clausen 	u32 div_int;
4533e1aec4eSMarek Vasut 	u64 div_frc;
4543e1aec4eSMarek Vasut 
4553e1aec4eSMarek Vasut 	rate = clamp(rate, VC5_PLL_VCO_MIN, vc5->chip_info->vco_max);
4564411da32SLars-Peter Clausen 
4573e1aec4eSMarek Vasut 	/* Determine integer part, which is 12 bit wide */
4583e1aec4eSMarek Vasut 	div_int = rate / *parent_rate;
4593e1aec4eSMarek Vasut 	if (div_int > 0xfff)
4603e1aec4eSMarek Vasut 		rate = *parent_rate * 0xfff;
4613e1aec4eSMarek Vasut 
4623e1aec4eSMarek Vasut 	/* Determine best fractional part, which is 24 bit wide */
4633e1aec4eSMarek Vasut 	div_frc = rate % *parent_rate;
4643e1aec4eSMarek Vasut 	div_frc *= BIT(24) - 1;
4653e1aec4eSMarek Vasut 	do_div(div_frc, *parent_rate);
4663e1aec4eSMarek Vasut 
4673e1aec4eSMarek Vasut 	hwdata->div_int = div_int;
4683e1aec4eSMarek Vasut 	hwdata->div_frc = (u32)div_frc;
4693e1aec4eSMarek Vasut 
4703e1aec4eSMarek Vasut 	return (*parent_rate * div_int) + ((*parent_rate * div_frc) >> 24);
4713e1aec4eSMarek Vasut }
4723e1aec4eSMarek Vasut 
vc5_pll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)4733e1aec4eSMarek Vasut static int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate,
4743e1aec4eSMarek Vasut 			    unsigned long parent_rate)
4753e1aec4eSMarek Vasut {
4763e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
4773e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = hwdata->vc5;
4783e1aec4eSMarek Vasut 	u8 fb[5];
4793e1aec4eSMarek Vasut 
4803e1aec4eSMarek Vasut 	fb[0] = hwdata->div_int >> 4;
4813e1aec4eSMarek Vasut 	fb[1] = hwdata->div_int << 4;
4823e1aec4eSMarek Vasut 	fb[2] = hwdata->div_frc >> 16;
4833e1aec4eSMarek Vasut 	fb[3] = hwdata->div_frc >> 8;
4843e1aec4eSMarek Vasut 	fb[4] = hwdata->div_frc;
4853e1aec4eSMarek Vasut 
4863e1aec4eSMarek Vasut 	return regmap_bulk_write(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5);
4873e1aec4eSMarek Vasut }
4883e1aec4eSMarek Vasut 
4893e1aec4eSMarek Vasut static const struct clk_ops vc5_pll_ops = {
4903e1aec4eSMarek Vasut 	.recalc_rate	= vc5_pll_recalc_rate,
4913e1aec4eSMarek Vasut 	.round_rate	= vc5_pll_round_rate,
4923e1aec4eSMarek Vasut 	.set_rate	= vc5_pll_set_rate,
4933e1aec4eSMarek Vasut };
4943e1aec4eSMarek Vasut 
vc5_fod_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)4953e1aec4eSMarek Vasut static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw,
4963e1aec4eSMarek Vasut 					 unsigned long parent_rate)
4973e1aec4eSMarek Vasut {
4983e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
4993e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = hwdata->vc5;
5003e1aec4eSMarek Vasut 	/* VCO frequency is divided by two before entering FOD */
5013e1aec4eSMarek Vasut 	u32 f_in = parent_rate / 2;
5023e1aec4eSMarek Vasut 	u32 div_int, div_frc;
5033e1aec4eSMarek Vasut 	u8 od_int[2];
5043e1aec4eSMarek Vasut 	u8 od_frc[4];
5053e1aec4eSMarek Vasut 
5063e1aec4eSMarek Vasut 	regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_INT(hwdata->num, 0),
5073e1aec4eSMarek Vasut 			 od_int, 2);
5083e1aec4eSMarek Vasut 	regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
5093e1aec4eSMarek Vasut 			 od_frc, 4);
5103e1aec4eSMarek Vasut 
5113e1aec4eSMarek Vasut 	div_int = (od_int[0] << 4) | (od_int[1] >> 4);
5123e1aec4eSMarek Vasut 	div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) |
5133e1aec4eSMarek Vasut 		  (od_frc[2] << 6) | (od_frc[3] >> 2);
5143e1aec4eSMarek Vasut 
5153e1aec4eSMarek Vasut 	/* Avoid division by zero if the output is not configured. */
5163bded569SMarek Vasut 	if (div_int == 0 && div_frc == 0)
5173bded569SMarek Vasut 		return 0;
5183bded569SMarek Vasut 
5193bded569SMarek Vasut 	/* The PLL divider has 12 integer bits and 30 fractional bits */
5203e1aec4eSMarek Vasut 	return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
5213e1aec4eSMarek Vasut }
5223e1aec4eSMarek Vasut 
vc5_fod_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)5233e1aec4eSMarek Vasut static long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate,
5243e1aec4eSMarek Vasut 			       unsigned long *parent_rate)
5253e1aec4eSMarek Vasut {
5263e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
5273e1aec4eSMarek Vasut 	/* VCO frequency is divided by two before entering FOD */
5283e1aec4eSMarek Vasut 	u32 f_in = *parent_rate / 2;
5293e1aec4eSMarek Vasut 	u32 div_int;
5303e1aec4eSMarek Vasut 	u64 div_frc;
5313e1aec4eSMarek Vasut 
5323e1aec4eSMarek Vasut 	/* Determine integer part, which is 12 bit wide */
5333e1aec4eSMarek Vasut 	div_int = f_in / rate;
5343e1aec4eSMarek Vasut 	/*
5353e1aec4eSMarek Vasut 	 * WARNING: The clock chip does not output signal if the integer part
5363e1aec4eSMarek Vasut 	 *          of the divider is 0xfff and fractional part is non-zero.
5373e1aec4eSMarek Vasut 	 *          Clamp the divider at 0xffe to keep the code simple.
5383e1aec4eSMarek Vasut 	 */
5393e1aec4eSMarek Vasut 	if (div_int > 0xffe) {
5403e1aec4eSMarek Vasut 		div_int = 0xffe;
5413e1aec4eSMarek Vasut 		rate = f_in / div_int;
5423e1aec4eSMarek Vasut 	}
5433e1aec4eSMarek Vasut 
5443e1aec4eSMarek Vasut 	/* Determine best fractional part, which is 30 bit wide */
5453e1aec4eSMarek Vasut 	div_frc = f_in % rate;
5463e1aec4eSMarek Vasut 	div_frc <<= 24;
5473e1aec4eSMarek Vasut 	do_div(div_frc, rate);
5483e1aec4eSMarek Vasut 
5493e1aec4eSMarek Vasut 	hwdata->div_int = div_int;
5503e1aec4eSMarek Vasut 	hwdata->div_frc = (u32)div_frc;
5513e1aec4eSMarek Vasut 
5523e1aec4eSMarek Vasut 	return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
5533e1aec4eSMarek Vasut }
5543e1aec4eSMarek Vasut 
vc5_fod_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)5553e1aec4eSMarek Vasut static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
5563e1aec4eSMarek Vasut 			    unsigned long parent_rate)
5573e1aec4eSMarek Vasut {
5583e1aec4eSMarek Vasut 	struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
5593e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = hwdata->vc5;
5603e1aec4eSMarek Vasut 	u8 data[14] = {
5613e1aec4eSMarek Vasut 		hwdata->div_frc >> 22, hwdata->div_frc >> 14,
5623e1aec4eSMarek Vasut 		hwdata->div_frc >> 6, hwdata->div_frc << 2,
5633e1aec4eSMarek Vasut 		0, 0, 0, 0, 0,
5643e1aec4eSMarek Vasut 		0, 0,
5653e1aec4eSMarek Vasut 		hwdata->div_int >> 4, hwdata->div_int << 4,
5663e1aec4eSMarek Vasut 		0
5673e1aec4eSMarek Vasut 	};
5683e1aec4eSMarek Vasut 	int ret;
569cc323782SLars-Peter Clausen 
5703e1aec4eSMarek Vasut 	ret = regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
571cc323782SLars-Peter Clausen 				data, 14);
5723e1aec4eSMarek Vasut 	if (ret)
573cc323782SLars-Peter Clausen 		return ret;
574cc323782SLars-Peter Clausen 
5753e1aec4eSMarek Vasut 	/*
5763e1aec4eSMarek Vasut 	 * Toggle magic bit in undocumented register for unknown reason.
5773e1aec4eSMarek Vasut 	 * This is what the IDT timing commander tool does and the chip
5783e1aec4eSMarek Vasut 	 * datasheet somewhat implies this is needed, but the register
5793e1aec4eSMarek Vasut 	 * and the bit is not documented.
5803e1aec4eSMarek Vasut 	 */
5813e1aec4eSMarek Vasut 	ret = regmap_clear_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
58201874fb2SLars-Peter Clausen 				VC5_GLOBAL_REGISTER_GLOBAL_RESET);
5833e1aec4eSMarek Vasut 	if (ret)
584cc323782SLars-Peter Clausen 		return ret;
585cc323782SLars-Peter Clausen 
586cc323782SLars-Peter Clausen 	return regmap_set_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
58701874fb2SLars-Peter Clausen 			       VC5_GLOBAL_REGISTER_GLOBAL_RESET);
5883e1aec4eSMarek Vasut }
5893e1aec4eSMarek Vasut 
5903e1aec4eSMarek Vasut static const struct clk_ops vc5_fod_ops = {
5913e1aec4eSMarek Vasut 	.recalc_rate	= vc5_fod_recalc_rate,
5923e1aec4eSMarek Vasut 	.round_rate	= vc5_fod_round_rate,
5933e1aec4eSMarek Vasut 	.set_rate	= vc5_fod_set_rate,
5943e1aec4eSMarek Vasut };
5953e1aec4eSMarek Vasut 
vc5_clk_out_prepare(struct clk_hw * hw)5963e1aec4eSMarek Vasut static int vc5_clk_out_prepare(struct clk_hw *hw)
5973e1aec4eSMarek Vasut {
5983e1aec4eSMarek Vasut 	struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw);
5993bca66b0SLuca Ceresoli 	struct vc5_driver_data *vc5 = hwdata->vc5;
6003e1aec4eSMarek Vasut 	const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM |
601718f4694SMarek Vasut 			VC5_OUT_DIV_CONTROL_SEL_EXT |
602718f4694SMarek Vasut 			VC5_OUT_DIV_CONTROL_EN_FOD;
603718f4694SMarek Vasut 	unsigned int src;
604718f4694SMarek Vasut 	int ret;
605718f4694SMarek Vasut 
606718f4694SMarek Vasut 	/*
607718f4694SMarek Vasut 	 * When enabling a FOD, all currently enabled FODs are briefly
608fc336ae6SLuca Ceresoli 	 * stopped in order to synchronize all of them. This causes a clock
609fc336ae6SLuca Ceresoli 	 * disruption to any unrelated chips that might be already using
610fc336ae6SLuca Ceresoli 	 * other clock outputs. Bypass the sync feature to avoid the issue,
611fc336ae6SLuca Ceresoli 	 * which is possible on the VersaClock 6E family via reserved
612fc336ae6SLuca Ceresoli 	 * registers.
613fc336ae6SLuca Ceresoli 	 */
614fc336ae6SLuca Ceresoli 	if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) {
615fc336ae6SLuca Ceresoli 		ret = regmap_set_bits(vc5->regmap,
61601874fb2SLars-Peter Clausen 				      VC5_RESERVED_X0(hwdata->num),
617fc336ae6SLuca Ceresoli 				      VC5_RESERVED_X0_BYPASS_SYNC);
618fc336ae6SLuca Ceresoli 		if (ret)
619fc336ae6SLuca Ceresoli 			return ret;
620fc336ae6SLuca Ceresoli 	}
621fc336ae6SLuca Ceresoli 
622fc336ae6SLuca Ceresoli 	/*
623fc336ae6SLuca Ceresoli 	 * If the input mux is disabled, enable it first and
624718f4694SMarek Vasut 	 * select source from matching FOD.
625718f4694SMarek Vasut 	 */
626718f4694SMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
627cc323782SLars-Peter Clausen 	if (ret)
628cc323782SLars-Peter Clausen 		return ret;
629cc323782SLars-Peter Clausen 
630cc323782SLars-Peter Clausen 	if ((src & mask) == 0) {
631718f4694SMarek Vasut 		src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD;
632718f4694SMarek Vasut 		ret = regmap_update_bits(vc5->regmap,
633718f4694SMarek Vasut 					 VC5_OUT_DIV_CONTROL(hwdata->num),
634718f4694SMarek Vasut 					 mask | VC5_OUT_DIV_CONTROL_RESET, src);
635718f4694SMarek Vasut 		if (ret)
636718f4694SMarek Vasut 			return ret;
637718f4694SMarek Vasut 	}
638718f4694SMarek Vasut 
6393e1aec4eSMarek Vasut 	/* Enable the clock buffer */
6403e1aec4eSMarek Vasut 	ret = regmap_set_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
64101874fb2SLars-Peter Clausen 			      VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
6423e1aec4eSMarek Vasut 	if (ret)
643cc323782SLars-Peter Clausen 		return ret;
644cc323782SLars-Peter Clausen 
645cc323782SLars-Peter Clausen 	if (hwdata->clk_output_cfg0_mask) {
646260249f9SAdam Ford 		dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n",
647260249f9SAdam Ford 			hwdata->num, hwdata->clk_output_cfg0_mask,
648260249f9SAdam Ford 			hwdata->clk_output_cfg0);
649260249f9SAdam Ford 
650260249f9SAdam Ford 		ret = regmap_update_bits(vc5->regmap,
651cc323782SLars-Peter Clausen 					 VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
652260249f9SAdam Ford 					 hwdata->clk_output_cfg0_mask,
653260249f9SAdam Ford 					 hwdata->clk_output_cfg0);
654260249f9SAdam Ford 		if (ret)
655cc323782SLars-Peter Clausen 			return ret;
656cc323782SLars-Peter Clausen 	}
657260249f9SAdam Ford 
658260249f9SAdam Ford 	return 0;
6593e1aec4eSMarek Vasut }
6603e1aec4eSMarek Vasut 
vc5_clk_out_unprepare(struct clk_hw * hw)6613e1aec4eSMarek Vasut static void vc5_clk_out_unprepare(struct clk_hw *hw)
6623e1aec4eSMarek Vasut {
6633e1aec4eSMarek Vasut 	struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw);
6643bca66b0SLuca Ceresoli 	struct vc5_driver_data *vc5 = hwdata->vc5;
6653e1aec4eSMarek Vasut 
6663e1aec4eSMarek Vasut 	/* Disable the clock buffer */
667a4decf58SMarek Vasut 	regmap_clear_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
66801874fb2SLars-Peter Clausen 			  VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
66901874fb2SLars-Peter Clausen }
6703e1aec4eSMarek Vasut 
vc5_clk_out_get_parent(struct clk_hw * hw)6713e1aec4eSMarek Vasut static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
6723e1aec4eSMarek Vasut {
6733e1aec4eSMarek Vasut 	struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw);
6743bca66b0SLuca Ceresoli 	struct vc5_driver_data *vc5 = hwdata->vc5;
6753e1aec4eSMarek Vasut 	const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM |
6763e1aec4eSMarek Vasut 			VC5_OUT_DIV_CONTROL_SEL_EXT |
6773e1aec4eSMarek Vasut 			VC5_OUT_DIV_CONTROL_EN_FOD;
6783e1aec4eSMarek Vasut 	const u8 fodclkmask = VC5_OUT_DIV_CONTROL_SELB_NORM |
6793e1aec4eSMarek Vasut 			      VC5_OUT_DIV_CONTROL_EN_FOD;
6803e1aec4eSMarek Vasut 	const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
6813e1aec4eSMarek Vasut 			  VC5_OUT_DIV_CONTROL_SEL_EXT;
6823e1aec4eSMarek Vasut 	unsigned int src;
6833e1aec4eSMarek Vasut 	int ret;
684cc323782SLars-Peter Clausen 
6853e1aec4eSMarek Vasut 	ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
686cc323782SLars-Peter Clausen 	if (ret)
687cc323782SLars-Peter Clausen 		return 0;
688cc323782SLars-Peter Clausen 
689cc323782SLars-Peter Clausen 	src &= mask;
6903e1aec4eSMarek Vasut 
6913e1aec4eSMarek Vasut 	if (src == 0)	/* Input mux set to DISABLED */
692325b7b90SMarek Vasut 		return 0;
693325b7b90SMarek Vasut 
694325b7b90SMarek Vasut 	if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD)
6953e1aec4eSMarek Vasut 		return 0;
6963e1aec4eSMarek Vasut 
6973e1aec4eSMarek Vasut 	if (src == extclk)
6983e1aec4eSMarek Vasut 		return 1;
6993e1aec4eSMarek Vasut 
7003e1aec4eSMarek Vasut 	dev_warn(&vc5->client->dev,
7013e1aec4eSMarek Vasut 		 "Invalid clock output configuration (%02x)\n", src);
7023e1aec4eSMarek Vasut 	return 0;
7033e1aec4eSMarek Vasut }
7043e1aec4eSMarek Vasut 
vc5_clk_out_set_parent(struct clk_hw * hw,u8 index)7053e1aec4eSMarek Vasut static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index)
7063e1aec4eSMarek Vasut {
7073e1aec4eSMarek Vasut 	struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw);
7083bca66b0SLuca Ceresoli 	struct vc5_driver_data *vc5 = hwdata->vc5;
7093e1aec4eSMarek Vasut 	const u8 mask = VC5_OUT_DIV_CONTROL_RESET |
7103e1aec4eSMarek Vasut 			VC5_OUT_DIV_CONTROL_SELB_NORM |
7113e1aec4eSMarek Vasut 			VC5_OUT_DIV_CONTROL_SEL_EXT |
7123e1aec4eSMarek Vasut 			VC5_OUT_DIV_CONTROL_EN_FOD;
7133e1aec4eSMarek Vasut 	const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
7143e1aec4eSMarek Vasut 			  VC5_OUT_DIV_CONTROL_SEL_EXT;
7153e1aec4eSMarek Vasut 	u8 src = VC5_OUT_DIV_CONTROL_RESET;
7163e1aec4eSMarek Vasut 
7173e1aec4eSMarek Vasut 	if (index == 0)
7183e1aec4eSMarek Vasut 		src |= VC5_OUT_DIV_CONTROL_EN_FOD;
7193e1aec4eSMarek Vasut 	else
7203e1aec4eSMarek Vasut 		src |= extclk;
7213e1aec4eSMarek Vasut 
7223e1aec4eSMarek Vasut 	return regmap_update_bits(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num),
7233e1aec4eSMarek Vasut 				  mask, src);
7243e1aec4eSMarek Vasut }
7253e1aec4eSMarek Vasut 
7263e1aec4eSMarek Vasut static const struct clk_ops vc5_clk_out_ops = {
7273e1aec4eSMarek Vasut 	.prepare	= vc5_clk_out_prepare,
7283e1aec4eSMarek Vasut 	.unprepare	= vc5_clk_out_unprepare,
7293e1aec4eSMarek Vasut 	.determine_rate	= clk_hw_determine_rate_no_reparent,
730538e864fSMaxime Ripard 	.set_parent	= vc5_clk_out_set_parent,
7313e1aec4eSMarek Vasut 	.get_parent	= vc5_clk_out_get_parent,
7323e1aec4eSMarek Vasut };
7333e1aec4eSMarek Vasut 
vc5_of_clk_get(struct of_phandle_args * clkspec,void * data)7343e1aec4eSMarek Vasut static struct clk_hw *vc5_of_clk_get(struct of_phandle_args *clkspec,
7353e1aec4eSMarek Vasut 				     void *data)
7363e1aec4eSMarek Vasut {
7373e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = data;
7383e1aec4eSMarek Vasut 	unsigned int idx = clkspec->args[0];
7393e1aec4eSMarek Vasut 
7403e1aec4eSMarek Vasut 	if (idx >= vc5->chip_info->clk_out_cnt)
7419adddb01SAlexey Firago 		return ERR_PTR(-EINVAL);
7423e1aec4eSMarek Vasut 
7433e1aec4eSMarek Vasut 	return &vc5->clk_out[idx].hw;
7443e1aec4eSMarek Vasut }
7453e1aec4eSMarek Vasut 
vc5_map_index_to_output(const enum vc5_model model,const unsigned int n)7463e1aec4eSMarek Vasut static int vc5_map_index_to_output(const enum vc5_model model,
7473e1aec4eSMarek Vasut 				   const unsigned int n)
7483e1aec4eSMarek Vasut {
7493e1aec4eSMarek Vasut 	switch (model) {
7503e1aec4eSMarek Vasut 	case IDT_VC5_5P49V5933:
7513e1aec4eSMarek Vasut 		return (n == 0) ? 0 : 3;
7523e1aec4eSMarek Vasut 	case IDT_VC5_5P49V5923:
7533e1aec4eSMarek Vasut 	case IDT_VC5_5P49V5925:
754b1911555SVladimir Barinov 	case IDT_VC5_5P49V5935:
7551193e14fSAlexey Firago 	case IDT_VC6_5P49V6901:
756dbf6b16fSMarek Vasut 	case IDT_VC6_5P49V6965:
7572bda748eSAdam Ford 	case IDT_VC6_5P49V6975:
758d8473831SMatthias Fend 	default:
7593e1aec4eSMarek Vasut 		return n;
7603e1aec4eSMarek Vasut 	}
7613e1aec4eSMarek Vasut }
7623e1aec4eSMarek Vasut 
vc5_update_mode(struct device_node * np_output,struct vc5_out_data * clk_out)7633e1aec4eSMarek Vasut static int vc5_update_mode(struct device_node *np_output,
764260249f9SAdam Ford 			   struct vc5_out_data *clk_out)
7653bca66b0SLuca Ceresoli {
766260249f9SAdam Ford 	u32 value;
767260249f9SAdam Ford 
768260249f9SAdam Ford 	if (!of_property_read_u32(np_output, "idt,mode", &value)) {
769260249f9SAdam Ford 		clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK;
770260249f9SAdam Ford 		switch (value) {
771260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL:
772260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_CMOS:
773260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33:
774260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_LVDS:
775260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2:
776260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD:
777260249f9SAdam Ford 		case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25:
778260249f9SAdam Ford 			clk_out->clk_output_cfg0 |=
779260249f9SAdam Ford 			    value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT;
780260249f9SAdam Ford 			break;
781260249f9SAdam Ford 		default:
782260249f9SAdam Ford 			return -EINVAL;
783260249f9SAdam Ford 		}
784260249f9SAdam Ford 	}
785260249f9SAdam Ford 	return 0;
786260249f9SAdam Ford }
787260249f9SAdam Ford 
vc5_update_power(struct device_node * np_output,struct vc5_out_data * clk_out)788260249f9SAdam Ford static int vc5_update_power(struct device_node *np_output,
789260249f9SAdam Ford 			    struct vc5_out_data *clk_out)
7903bca66b0SLuca Ceresoli {
791260249f9SAdam Ford 	u32 value;
792260249f9SAdam Ford 
793260249f9SAdam Ford 	if (!of_property_read_u32(np_output, "idt,voltage-microvolt",
7944b003f5fSGeert Uytterhoeven 				  &value)) {
7954b003f5fSGeert Uytterhoeven 		clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK;
796260249f9SAdam Ford 		switch (value) {
797260249f9SAdam Ford 		case 1800000:
798260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18;
799260249f9SAdam Ford 			break;
800260249f9SAdam Ford 		case 2500000:
801260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25;
802260249f9SAdam Ford 			break;
803260249f9SAdam Ford 		case 3300000:
804260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33;
805260249f9SAdam Ford 			break;
806260249f9SAdam Ford 		default:
807260249f9SAdam Ford 			return -EINVAL;
808260249f9SAdam Ford 		}
809260249f9SAdam Ford 	}
810260249f9SAdam Ford 	return 0;
811260249f9SAdam Ford }
812260249f9SAdam Ford 
vc5_map_cap_value(u32 femtofarads)813260249f9SAdam Ford static int vc5_map_cap_value(u32 femtofarads)
814f3d661d6SAdam Ford {
815f3d661d6SAdam Ford 	int mapped_value;
816f3d661d6SAdam Ford 
817f3d661d6SAdam Ford 	/*
818f3d661d6SAdam Ford 	 * The datasheet explicitly states 9000 - 25000 with 0.5pF
819f3d661d6SAdam Ford 	 * steps, but the Programmer's guide shows the steps are 0.430pF.
820f3d661d6SAdam Ford 	 * After getting feedback from Renesas, the .5pF steps were the
821f3d661d6SAdam Ford 	 * goal, but 430nF was the actual values.
822f3d661d6SAdam Ford 	 * Because of this, the actual range goes to 22760 instead of 25000
823f3d661d6SAdam Ford 	 */
824f3d661d6SAdam Ford 	if (femtofarads < 9000 || femtofarads > 22760)
825f3d661d6SAdam Ford 		return -EINVAL;
826f3d661d6SAdam Ford 
827f3d661d6SAdam Ford 	/*
828f3d661d6SAdam Ford 	 * The Programmer's guide shows XTAL[5:0] but in reality,
829f3d661d6SAdam Ford 	 * XTAL[0] and XTAL[1] are both LSB which makes the math
830f3d661d6SAdam Ford 	 * strange.  With clarfication from Renesas, setting the
831f3d661d6SAdam Ford 	 * values should be simpler by ignoring XTAL[0]
832f3d661d6SAdam Ford 	 */
833f3d661d6SAdam Ford 	mapped_value = DIV_ROUND_CLOSEST(femtofarads - 9000, 430);
834f3d661d6SAdam Ford 
835f3d661d6SAdam Ford 	/*
836f3d661d6SAdam Ford 	 * Since the calculation ignores XTAL[0], there is one
837f3d661d6SAdam Ford 	 * special case where mapped_value = 32.  In reality, this means
838f3d661d6SAdam Ford 	 * the real mapped value should be 111111b.  In other cases,
839f3d661d6SAdam Ford 	 * the mapped_value needs to be shifted 1 to the left.
840f3d661d6SAdam Ford 	 */
841f3d661d6SAdam Ford 	if (mapped_value > 31)
842f3d661d6SAdam Ford 		mapped_value = 0x3f;
843f3d661d6SAdam Ford 	else
844f3d661d6SAdam Ford 		mapped_value <<= 1;
845f3d661d6SAdam Ford 
846f3d661d6SAdam Ford 	return mapped_value;
847f3d661d6SAdam Ford }
vc5_update_cap_load(struct device_node * node,struct vc5_driver_data * vc5)848f3d661d6SAdam Ford static int vc5_update_cap_load(struct device_node *node, struct vc5_driver_data *vc5)
849f3d661d6SAdam Ford {
850f3d661d6SAdam Ford 	u32 value;
851f3d661d6SAdam Ford 	int mapped_value;
852f3d661d6SAdam Ford 	int ret;
853cc323782SLars-Peter Clausen 
854f3d661d6SAdam Ford 	if (of_property_read_u32(node, "idt,xtal-load-femtofarads", &value))
855cc323782SLars-Peter Clausen 		return 0;
856cc323782SLars-Peter Clausen 
857cc323782SLars-Peter Clausen 	mapped_value = vc5_map_cap_value(value);
858f3d661d6SAdam Ford 	if (mapped_value < 0)
859f3d661d6SAdam Ford 		return mapped_value;
860f3d661d6SAdam Ford 
861f3d661d6SAdam Ford 	/*
862f3d661d6SAdam Ford 	 * The mapped_value is really the high 6 bits of
863f3d661d6SAdam Ford 	 * VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so
864f3d661d6SAdam Ford 	 * shift the value 2 places.
865f3d661d6SAdam Ford 	 */
866f3d661d6SAdam Ford 	ret = regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03,
867cc323782SLars-Peter Clausen 				 mapped_value << 2);
868cc323782SLars-Peter Clausen 	if (ret)
869cc323782SLars-Peter Clausen 		return ret;
870cc323782SLars-Peter Clausen 
871f3d661d6SAdam Ford 	return regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03,
872cc323782SLars-Peter Clausen 				  mapped_value << 2);
873cc323782SLars-Peter Clausen }
874f3d661d6SAdam Ford 
vc5_update_slew(struct device_node * np_output,struct vc5_out_data * clk_out)875f3d661d6SAdam Ford static int vc5_update_slew(struct device_node *np_output,
876260249f9SAdam Ford 			   struct vc5_out_data *clk_out)
8773bca66b0SLuca Ceresoli {
878260249f9SAdam Ford 	u32 value;
879260249f9SAdam Ford 
880260249f9SAdam Ford 	if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) {
881260249f9SAdam Ford 		clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK;
882260249f9SAdam Ford 		switch (value) {
883260249f9SAdam Ford 		case 80:
884260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80;
885260249f9SAdam Ford 			break;
886260249f9SAdam Ford 		case 85:
887260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85;
888260249f9SAdam Ford 			break;
889260249f9SAdam Ford 		case 90:
890260249f9SAdam Ford 			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90;
891260249f9SAdam Ford 			break;
892260249f9SAdam Ford 		case 100:
893260249f9SAdam Ford 			clk_out->clk_output_cfg0 |=
894260249f9SAdam Ford 			    VC5_CLK_OUTPUT_CFG0_SLEW_100;
895260249f9SAdam Ford 			break;
896260249f9SAdam Ford 		default:
897260249f9SAdam Ford 			return -EINVAL;
898260249f9SAdam Ford 		}
899260249f9SAdam Ford 	}
900260249f9SAdam Ford 	return 0;
901260249f9SAdam Ford }
902260249f9SAdam Ford 
vc5_get_output_config(struct i2c_client * client,struct vc5_out_data * clk_out)903260249f9SAdam Ford static int vc5_get_output_config(struct i2c_client *client,
904260249f9SAdam Ford 				 struct vc5_out_data *clk_out)
9053bca66b0SLuca Ceresoli {
906260249f9SAdam Ford 	struct device_node *np_output;
907260249f9SAdam Ford 	char *child_name;
908260249f9SAdam Ford 	int ret = 0;
909260249f9SAdam Ford 
910260249f9SAdam Ford 	child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1);
911260249f9SAdam Ford 	if (!child_name)
912faf29338SAdam Ford 		return -ENOMEM;
913faf29338SAdam Ford 
914faf29338SAdam Ford 	np_output = of_get_child_by_name(client->dev.of_node, child_name);
915260249f9SAdam Ford 	kfree(child_name);
916260249f9SAdam Ford 	if (!np_output)
917260249f9SAdam Ford 		return 0;
918faf29338SAdam Ford 
919260249f9SAdam Ford 	ret = vc5_update_mode(np_output, clk_out);
920260249f9SAdam Ford 	if (ret)
921260249f9SAdam Ford 		goto output_error;
922260249f9SAdam Ford 
923260249f9SAdam Ford 	ret = vc5_update_power(np_output, clk_out);
924260249f9SAdam Ford 	if (ret)
925260249f9SAdam Ford 		goto output_error;
926260249f9SAdam Ford 
927260249f9SAdam Ford 	ret = vc5_update_slew(np_output, clk_out);
928260249f9SAdam Ford 
929260249f9SAdam Ford output_error:
930260249f9SAdam Ford 	if (ret) {
931260249f9SAdam Ford 		dev_err(&client->dev,
932260249f9SAdam Ford 			"Invalid clock output configuration OUT%d\n",
933260249f9SAdam Ford 			clk_out->num + 1);
934260249f9SAdam Ford 	}
935260249f9SAdam Ford 
936260249f9SAdam Ford 	of_node_put(np_output);
937260249f9SAdam Ford 
938260249f9SAdam Ford 	return ret;
939260249f9SAdam Ford }
940260249f9SAdam Ford 
941260249f9SAdam Ford static const struct of_device_id clk_vc5_of_match[];
9423e1aec4eSMarek Vasut 
vc5_probe(struct i2c_client * client)9433e1aec4eSMarek Vasut static int vc5_probe(struct i2c_client *client)
9445bba6d37SLuca Ceresoli {
9453e1aec4eSMarek Vasut 	unsigned int oe, sd, src_mask = 0, src_val = 0;
946d83e561dSSean Anderson 	struct vc5_driver_data *vc5;
9473e1aec4eSMarek Vasut 	struct clk_init_data init;
9483e1aec4eSMarek Vasut 	const char *parent_names[2];
9493e1aec4eSMarek Vasut 	unsigned int n, idx = 0;
9509adddb01SAlexey Firago 	int ret;
9513e1aec4eSMarek Vasut 
9523e1aec4eSMarek Vasut 	vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL);
9533e1aec4eSMarek Vasut 	if (!vc5)
954faf29338SAdam Ford 		return -ENOMEM;
9553e1aec4eSMarek Vasut 
9563e1aec4eSMarek Vasut 	i2c_set_clientdata(client, vc5);
9573e1aec4eSMarek Vasut 	vc5->client = client;
9583e1aec4eSMarek Vasut 	vc5->chip_info = i2c_get_match_data(client);
959*0894f0b6SBiju Das 
9603e1aec4eSMarek Vasut 	vc5->pin_xin = devm_clk_get(&client->dev, "xin");
9613e1aec4eSMarek Vasut 	if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER)
9623e1aec4eSMarek Vasut 		return -EPROBE_DEFER;
9633e1aec4eSMarek Vasut 
9643e1aec4eSMarek Vasut 	vc5->pin_clkin = devm_clk_get(&client->dev, "clkin");
9653e1aec4eSMarek Vasut 	if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER)
9663e1aec4eSMarek Vasut 		return -EPROBE_DEFER;
9673e1aec4eSMarek Vasut 
9683e1aec4eSMarek Vasut 	vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config);
9693e1aec4eSMarek Vasut 	if (IS_ERR(vc5->regmap))
9702ef16254SSean Anderson 		return dev_err_probe(&client->dev, PTR_ERR(vc5->regmap),
9712ef16254SSean Anderson 				     "failed to allocate register map\n");
9722ef16254SSean Anderson 
9733e1aec4eSMarek Vasut 	ret = of_property_read_u32(client->dev.of_node, "idt,shutdown", &sd);
974d83e561dSSean Anderson 	if (!ret) {
975d83e561dSSean Anderson 		src_mask |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN;
976d83e561dSSean Anderson 		if (sd)
977d83e561dSSean Anderson 			src_val |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN;
978d83e561dSSean Anderson 	} else if (ret != -EINVAL) {
979d83e561dSSean Anderson 		return dev_err_probe(&client->dev, ret,
980d83e561dSSean Anderson 				     "could not read idt,shutdown\n");
981d83e561dSSean Anderson 	}
982d83e561dSSean Anderson 
983d83e561dSSean Anderson 	ret = of_property_read_u32(client->dev.of_node,
984d83e561dSSean Anderson 				   "idt,output-enable-active", &oe);
985d83e561dSSean Anderson 	if (!ret) {
986d83e561dSSean Anderson 		src_mask |= VC5_PRIM_SRC_SHDN_SP;
987d83e561dSSean Anderson 		if (oe)
988d83e561dSSean Anderson 			src_val |= VC5_PRIM_SRC_SHDN_SP;
989d83e561dSSean Anderson 	} else if (ret != -EINVAL) {
990d83e561dSSean Anderson 		return dev_err_probe(&client->dev, ret,
991d83e561dSSean Anderson 				     "could not read idt,output-enable-active\n");
992d83e561dSSean Anderson 	}
993d83e561dSSean Anderson 
994d83e561dSSean Anderson 	ret = regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask,
995cc323782SLars-Peter Clausen 				 src_val);
996cc323782SLars-Peter Clausen 	if (ret)
997cc323782SLars-Peter Clausen 		return ret;
998cc323782SLars-Peter Clausen 
999d83e561dSSean Anderson 	/* Register clock input mux */
10003e1aec4eSMarek Vasut 	memset(&init, 0, sizeof(init));
10013e1aec4eSMarek Vasut 
10023e1aec4eSMarek Vasut 	if (!IS_ERR(vc5->pin_xin)) {
10033e1aec4eSMarek Vasut 		vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
10043e1aec4eSMarek Vasut 		parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
10053e1aec4eSMarek Vasut 	} else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) {
10069adddb01SAlexey Firago 		vc5->pin_xin = clk_register_fixed_rate(&client->dev,
10073e1aec4eSMarek Vasut 						       "internal-xtal", NULL,
10083e1aec4eSMarek Vasut 						       0, 25000000);
10093e1aec4eSMarek Vasut 		if (IS_ERR(vc5->pin_xin))
10103e1aec4eSMarek Vasut 			return PTR_ERR(vc5->pin_xin);
10113e1aec4eSMarek Vasut 		vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
10123e1aec4eSMarek Vasut 		parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
10133e1aec4eSMarek Vasut 	}
10143e1aec4eSMarek Vasut 
10153e1aec4eSMarek Vasut 	if (!IS_ERR(vc5->pin_clkin)) {
10163e1aec4eSMarek Vasut 		vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN;
10173e1aec4eSMarek Vasut 		parent_names[init.num_parents++] =
10183e1aec4eSMarek Vasut 		    __clk_get_name(vc5->pin_clkin);
10193e1aec4eSMarek Vasut 	}
10203e1aec4eSMarek Vasut 
10213e1aec4eSMarek Vasut 	if (!init.num_parents)
10222ef16254SSean Anderson 		return dev_err_probe(&client->dev, -EINVAL,
10232ef16254SSean Anderson 				     "no input clock specified!\n");
10242ef16254SSean Anderson 
10253e1aec4eSMarek Vasut 	/* Configure Optional Loading Capacitance for external XTAL */
1026f3d661d6SAdam Ford 	if (!(vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)) {
1027f3d661d6SAdam Ford 		ret = vc5_update_cap_load(client->dev.of_node, vc5);
1028f3d661d6SAdam Ford 		if (ret)
1029f3d661d6SAdam Ford 			goto err_clk_register;
1030f3d661d6SAdam Ford 	}
1031f3d661d6SAdam Ford 
1032f3d661d6SAdam Ford 	init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node);
1033f491276aSAdam Ford 	if (!init.name) {
1034144601f6SClaudiu Beznea 		ret = -ENOMEM;
1035144601f6SClaudiu Beznea 		goto err_clk;
1036144601f6SClaudiu Beznea 	}
1037144601f6SClaudiu Beznea 
1038144601f6SClaudiu Beznea 	init.ops = &vc5_mux_ops;
10393e1aec4eSMarek Vasut 	init.flags = 0;
10403e1aec4eSMarek Vasut 	init.parent_names = parent_names;
10413e1aec4eSMarek Vasut 	vc5->clk_mux.init = &init;
10423e1aec4eSMarek Vasut 	ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux);
10433e1aec4eSMarek Vasut 	if (ret)
10448200597fSColin Ian King 		goto err_clk_register;
10458200597fSColin Ian King 	kfree(init.name);	/* clock framework made a copy of the name */
1046f491276aSAdam Ford 
10473e1aec4eSMarek Vasut 	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) {
10488c1ebe97SMarek Vasut 		/* Register frequency doubler */
10498c1ebe97SMarek Vasut 		memset(&init, 0, sizeof(init));
10508c1ebe97SMarek Vasut 		init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl",
1051f491276aSAdam Ford 				      client->dev.of_node);
1052f491276aSAdam Ford 		if (!init.name) {
1053144601f6SClaudiu Beznea 			ret = -ENOMEM;
1054144601f6SClaudiu Beznea 			goto err_clk;
1055144601f6SClaudiu Beznea 		}
1056144601f6SClaudiu Beznea 		init.ops = &vc5_dbl_ops;
10578c1ebe97SMarek Vasut 		init.flags = CLK_SET_RATE_PARENT;
10588c1ebe97SMarek Vasut 		init.parent_names = parent_names;
1059f491276aSAdam Ford 		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
1060f491276aSAdam Ford 		init.num_parents = 1;
10618c1ebe97SMarek Vasut 		vc5->clk_mul.init = &init;
10628c1ebe97SMarek Vasut 		ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul);
10638c1ebe97SMarek Vasut 		if (ret)
10648200597fSColin Ian King 			goto err_clk_register;
10658200597fSColin Ian King 		kfree(init.name); /* clock framework made a copy of the name */
1066f491276aSAdam Ford 	}
10678c1ebe97SMarek Vasut 
10688c1ebe97SMarek Vasut 	/* Register PFD */
106955997db5SMarek Vasut 	memset(&init, 0, sizeof(init));
107055997db5SMarek Vasut 	init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node);
1071f491276aSAdam Ford 	if (!init.name) {
1072144601f6SClaudiu Beznea 		ret = -ENOMEM;
1073144601f6SClaudiu Beznea 		goto err_clk;
1074144601f6SClaudiu Beznea 	}
1075144601f6SClaudiu Beznea 	init.ops = &vc5_pfd_ops;
107655997db5SMarek Vasut 	init.flags = CLK_SET_RATE_PARENT;
107755997db5SMarek Vasut 	init.parent_names = parent_names;
1078f491276aSAdam Ford 	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL)
10798c1ebe97SMarek Vasut 		parent_names[0] = clk_hw_get_name(&vc5->clk_mul);
1080f491276aSAdam Ford 	else
10818c1ebe97SMarek Vasut 		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
1082f491276aSAdam Ford 	init.num_parents = 1;
108355997db5SMarek Vasut 	vc5->clk_pfd.init = &init;
108455997db5SMarek Vasut 	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd);
108555997db5SMarek Vasut 	if (ret)
10868200597fSColin Ian King 		goto err_clk_register;
10878200597fSColin Ian King 	kfree(init.name);	/* clock framework made a copy of the name */
1088f491276aSAdam Ford 
108955997db5SMarek Vasut 	/* Register PLL */
10903e1aec4eSMarek Vasut 	memset(&init, 0, sizeof(init));
10913e1aec4eSMarek Vasut 	init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node);
1092f491276aSAdam Ford 	if (!init.name) {
1093144601f6SClaudiu Beznea 		ret = -ENOMEM;
1094144601f6SClaudiu Beznea 		goto err_clk;
1095144601f6SClaudiu Beznea 	}
1096144601f6SClaudiu Beznea 	init.ops = &vc5_pll_ops;
10973e1aec4eSMarek Vasut 	init.flags = CLK_SET_RATE_PARENT;
10983e1aec4eSMarek Vasut 	init.parent_names = parent_names;
1099f491276aSAdam Ford 	parent_names[0] = clk_hw_get_name(&vc5->clk_pfd);
1100f491276aSAdam Ford 	init.num_parents = 1;
11013e1aec4eSMarek Vasut 	vc5->clk_pll.num = 0;
11023e1aec4eSMarek Vasut 	vc5->clk_pll.vc5 = vc5;
11033e1aec4eSMarek Vasut 	vc5->clk_pll.hw.init = &init;
11043e1aec4eSMarek Vasut 	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw);
11053e1aec4eSMarek Vasut 	if (ret)
11068200597fSColin Ian King 		goto err_clk_register;
11078200597fSColin Ian King 	kfree(init.name); /* clock framework made a copy of the name */
1108f491276aSAdam Ford 
11093e1aec4eSMarek Vasut 	/* Register FODs */
11103e1aec4eSMarek Vasut 	for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) {
11119adddb01SAlexey Firago 		idx = vc5_map_index_to_output(vc5->chip_info->model, n);
11129adddb01SAlexey Firago 		memset(&init, 0, sizeof(init));
11133e1aec4eSMarek Vasut 		init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d",
1114f491276aSAdam Ford 				      client->dev.of_node, idx);
1115f491276aSAdam Ford 		if (!init.name) {
1116144601f6SClaudiu Beznea 			ret = -ENOMEM;
1117144601f6SClaudiu Beznea 			goto err_clk;
1118144601f6SClaudiu Beznea 		}
1119144601f6SClaudiu Beznea 		init.ops = &vc5_fod_ops;
11203e1aec4eSMarek Vasut 		init.flags = CLK_SET_RATE_PARENT;
11213e1aec4eSMarek Vasut 		init.parent_names = parent_names;
1122f491276aSAdam Ford 		parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw);
1123f491276aSAdam Ford 		init.num_parents = 1;
11243e1aec4eSMarek Vasut 		vc5->clk_fod[n].num = idx;
11253e1aec4eSMarek Vasut 		vc5->clk_fod[n].vc5 = vc5;
11263e1aec4eSMarek Vasut 		vc5->clk_fod[n].hw.init = &init;
11273e1aec4eSMarek Vasut 		ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw);
11283e1aec4eSMarek Vasut 		if (ret)
11298200597fSColin Ian King 			goto err_clk_register;
11308200597fSColin Ian King 		kfree(init.name); /* clock framework made a copy of the name */
1131f491276aSAdam Ford 	}
11323e1aec4eSMarek Vasut 
11333e1aec4eSMarek Vasut 	/* Register MUX-connected OUT0_I2C_SELB output */
11343e1aec4eSMarek Vasut 	memset(&init, 0, sizeof(init));
11353e1aec4eSMarek Vasut 	init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb",
1136f491276aSAdam Ford 			      client->dev.of_node);
1137f491276aSAdam Ford 	if (!init.name) {
1138144601f6SClaudiu Beznea 		ret = -ENOMEM;
1139144601f6SClaudiu Beznea 		goto err_clk;
1140144601f6SClaudiu Beznea 	}
1141144601f6SClaudiu Beznea 	init.ops = &vc5_clk_out_ops;
11423e1aec4eSMarek Vasut 	init.flags = CLK_SET_RATE_PARENT;
11433e1aec4eSMarek Vasut 	init.parent_names = parent_names;
1144f491276aSAdam Ford 	parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
1145f491276aSAdam Ford 	init.num_parents = 1;
11463e1aec4eSMarek Vasut 	vc5->clk_out[0].num = idx;
11473e1aec4eSMarek Vasut 	vc5->clk_out[0].vc5 = vc5;
11483e1aec4eSMarek Vasut 	vc5->clk_out[0].hw.init = &init;
11493e1aec4eSMarek Vasut 	ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw);
11503e1aec4eSMarek Vasut 	if (ret)
11518200597fSColin Ian King 		goto err_clk_register;
11528200597fSColin Ian King 	kfree(init.name); /* clock framework made a copy of the name */
1153f491276aSAdam Ford 
11543e1aec4eSMarek Vasut 	/* Register FOD-connected OUTx outputs */
11553e1aec4eSMarek Vasut 	for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) {
11569adddb01SAlexey Firago 		idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1);
11579adddb01SAlexey Firago 		parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw);
1158f491276aSAdam Ford 		if (n == 1)
11593e1aec4eSMarek Vasut 			parent_names[1] = clk_hw_get_name(&vc5->clk_mux);
1160f491276aSAdam Ford 		else
11613e1aec4eSMarek Vasut 			parent_names[1] =
1162f491276aSAdam Ford 			    clk_hw_get_name(&vc5->clk_out[n - 1].hw);
1163f491276aSAdam Ford 
11643e1aec4eSMarek Vasut 		memset(&init, 0, sizeof(init));
11653e1aec4eSMarek Vasut 		init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d",
1166f491276aSAdam Ford 				      client->dev.of_node, idx + 1);
1167f491276aSAdam Ford 		if (!init.name) {
1168144601f6SClaudiu Beznea 			ret = -ENOMEM;
1169144601f6SClaudiu Beznea 			goto err_clk;
1170144601f6SClaudiu Beznea 		}
1171144601f6SClaudiu Beznea 		init.ops = &vc5_clk_out_ops;
11723e1aec4eSMarek Vasut 		init.flags = CLK_SET_RATE_PARENT;
11733e1aec4eSMarek Vasut 		init.parent_names = parent_names;
11743e1aec4eSMarek Vasut 		init.num_parents = 2;
11753e1aec4eSMarek Vasut 		vc5->clk_out[n].num = idx;
11763e1aec4eSMarek Vasut 		vc5->clk_out[n].vc5 = vc5;
11773e1aec4eSMarek Vasut 		vc5->clk_out[n].hw.init = &init;
11783e1aec4eSMarek Vasut 		ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw);
1179f491276aSAdam Ford 		if (ret)
11808200597fSColin Ian King 			goto err_clk_register;
11818200597fSColin Ian King 		kfree(init.name); /* clock framework made a copy of the name */
1182f491276aSAdam Ford 
1183260249f9SAdam Ford 		/* Fetch Clock Output configuration from DT (if specified) */
1184260249f9SAdam Ford 		ret = vc5_get_output_config(client, &vc5->clk_out[n]);
1185260249f9SAdam Ford 		if (ret)
1186260249f9SAdam Ford 			goto err_clk;
1187260249f9SAdam Ford 	}
11883e1aec4eSMarek Vasut 
11893e1aec4eSMarek Vasut 	ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
11903e1aec4eSMarek Vasut 	if (ret) {
11913e1aec4eSMarek Vasut 		dev_err_probe(&client->dev, ret,
11922ef16254SSean Anderson 			      "unable to add clk provider\n");
11932ef16254SSean Anderson 		goto err_clk;
11943e1aec4eSMarek Vasut 	}
11953e1aec4eSMarek Vasut 
11963e1aec4eSMarek Vasut 	return 0;
11973e1aec4eSMarek Vasut 
11983e1aec4eSMarek Vasut err_clk_register:
11998200597fSColin Ian King 	dev_err_probe(&client->dev, ret,
12002ef16254SSean Anderson 		      "unable to register %s\n", init.name);
12012ef16254SSean Anderson 	kfree(init.name); /* clock framework made a copy of the name */
12028200597fSColin Ian King err_clk:
12033e1aec4eSMarek Vasut 	if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
12049adddb01SAlexey Firago 		clk_unregister_fixed_rate(vc5->pin_xin);
12053e1aec4eSMarek Vasut 	return ret;
12063e1aec4eSMarek Vasut }
12073e1aec4eSMarek Vasut 
vc5_remove(struct i2c_client * client)12083e1aec4eSMarek Vasut static void vc5_remove(struct i2c_client *client)
1209ed5c2f5fSUwe Kleine-König {
12103e1aec4eSMarek Vasut 	struct vc5_driver_data *vc5 = i2c_get_clientdata(client);
12113e1aec4eSMarek Vasut 
12123e1aec4eSMarek Vasut 	of_clk_del_provider(client->dev.of_node);
12133e1aec4eSMarek Vasut 
12143e1aec4eSMarek Vasut 	if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
12159adddb01SAlexey Firago 		clk_unregister_fixed_rate(vc5->pin_xin);
12163e1aec4eSMarek Vasut }
12173e1aec4eSMarek Vasut 
vc5_suspend(struct device * dev)12183e1aec4eSMarek Vasut static int __maybe_unused vc5_suspend(struct device *dev)
12198cbdc1f0SMarek Vasut {
12208cbdc1f0SMarek Vasut 	struct vc5_driver_data *vc5 = dev_get_drvdata(dev);
12218cbdc1f0SMarek Vasut 
12228cbdc1f0SMarek Vasut 	regcache_cache_only(vc5->regmap, true);
12238cbdc1f0SMarek Vasut 	regcache_mark_dirty(vc5->regmap);
12248cbdc1f0SMarek Vasut 
12258cbdc1f0SMarek Vasut 	return 0;
12268cbdc1f0SMarek Vasut }
12278cbdc1f0SMarek Vasut 
vc5_resume(struct device * dev)12288cbdc1f0SMarek Vasut static int __maybe_unused vc5_resume(struct device *dev)
12298cbdc1f0SMarek Vasut {
12308cbdc1f0SMarek Vasut 	struct vc5_driver_data *vc5 = dev_get_drvdata(dev);
12318cbdc1f0SMarek Vasut 	int ret;
12328cbdc1f0SMarek Vasut 
12338cbdc1f0SMarek Vasut 	regcache_cache_only(vc5->regmap, false);
12348cbdc1f0SMarek Vasut 	ret = regcache_sync(vc5->regmap);
12358cbdc1f0SMarek Vasut 	if (ret)
12368cbdc1f0SMarek Vasut 		dev_err(dev, "Failed to restore register map: %d\n", ret);
12378cbdc1f0SMarek Vasut 	return ret;
12388cbdc1f0SMarek Vasut }
12398cbdc1f0SMarek Vasut 
12408cbdc1f0SMarek Vasut static const struct vc5_chip_info idt_5p49v5923_info = {
12419adddb01SAlexey Firago 	.model = IDT_VC5_5P49V5923,
12429adddb01SAlexey Firago 	.clk_fod_cnt = 2,
12439adddb01SAlexey Firago 	.clk_out_cnt = 3,
12449adddb01SAlexey Firago 	.flags = 0,
12459adddb01SAlexey Firago 	.vco_max = 3000000000UL,
12464411da32SLars-Peter Clausen };
12479adddb01SAlexey Firago 
12489adddb01SAlexey Firago static const struct vc5_chip_info idt_5p49v5925_info = {
1249b1911555SVladimir Barinov 	.model = IDT_VC5_5P49V5925,
1250b1911555SVladimir Barinov 	.clk_fod_cnt = 4,
1251b1911555SVladimir Barinov 	.clk_out_cnt = 5,
1252b1911555SVladimir Barinov 	.flags = 0,
1253b1911555SVladimir Barinov 	.vco_max = 3000000000UL,
12544411da32SLars-Peter Clausen };
1255b1911555SVladimir Barinov 
1256b1911555SVladimir Barinov static const struct vc5_chip_info idt_5p49v5933_info = {
12579adddb01SAlexey Firago 	.model = IDT_VC5_5P49V5933,
12589adddb01SAlexey Firago 	.clk_fod_cnt = 2,
12599adddb01SAlexey Firago 	.clk_out_cnt = 3,
12609adddb01SAlexey Firago 	.flags = VC5_HAS_INTERNAL_XTAL,
12619adddb01SAlexey Firago 	.vco_max = 3000000000UL,
12624411da32SLars-Peter Clausen };
12639adddb01SAlexey Firago 
12649adddb01SAlexey Firago static const struct vc5_chip_info idt_5p49v5935_info = {
12651193e14fSAlexey Firago 	.model = IDT_VC5_5P49V5935,
12661193e14fSAlexey Firago 	.clk_fod_cnt = 4,
12671193e14fSAlexey Firago 	.clk_out_cnt = 5,
12681193e14fSAlexey Firago 	.flags = VC5_HAS_INTERNAL_XTAL,
12691193e14fSAlexey Firago 	.vco_max = 3000000000UL,
12704411da32SLars-Peter Clausen };
12714411da32SLars-Peter Clausen 
12724411da32SLars-Peter Clausen static const struct vc5_chip_info idt_5p49v60_info = {
12734411da32SLars-Peter Clausen 	.model = IDT_VC6_5P49V60,
12744411da32SLars-Peter Clausen 	.clk_fod_cnt = 4,
12754411da32SLars-Peter Clausen 	.clk_out_cnt = 5,
12764411da32SLars-Peter Clausen 	.flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT,
12774411da32SLars-Peter Clausen 	.vco_max = 2700000000UL,
12784411da32SLars-Peter Clausen };
12791193e14fSAlexey Firago 
12801193e14fSAlexey Firago static const struct vc5_chip_info idt_5p49v6901_info = {
1281dbf6b16fSMarek Vasut 	.model = IDT_VC6_5P49V6901,
1282dbf6b16fSMarek Vasut 	.clk_fod_cnt = 4,
1283dbf6b16fSMarek Vasut 	.clk_out_cnt = 5,
1284dbf6b16fSMarek Vasut 	.flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT,
1285c388cc80SSerge Semin 	.vco_max = 3000000000UL,
12864411da32SLars-Peter Clausen };
1287dbf6b16fSMarek Vasut 
1288dbf6b16fSMarek Vasut static const struct vc5_chip_info idt_5p49v6965_info = {
12892bda748eSAdam Ford 	.model = IDT_VC6_5P49V6965,
12902bda748eSAdam Ford 	.clk_fod_cnt = 4,
12912bda748eSAdam Ford 	.clk_out_cnt = 5,
12922bda748eSAdam Ford 	.flags = VC5_HAS_BYPASS_SYNC_BIT,
1293fc336ae6SLuca Ceresoli 	.vco_max = 3000000000UL,
12944411da32SLars-Peter Clausen };
12952bda748eSAdam Ford 
12962bda748eSAdam Ford static const struct vc5_chip_info idt_5p49v6975_info = {
1297d8473831SMatthias Fend 	.model = IDT_VC6_5P49V6975,
1298d8473831SMatthias Fend 	.clk_fod_cnt = 4,
1299d8473831SMatthias Fend 	.clk_out_cnt = 5,
1300d8473831SMatthias Fend 	.flags = VC5_HAS_BYPASS_SYNC_BIT | VC5_HAS_INTERNAL_XTAL,
1301d8473831SMatthias Fend 	.vco_max = 3000000000UL,
13024411da32SLars-Peter Clausen };
1303d8473831SMatthias Fend 
1304d8473831SMatthias Fend static const struct i2c_device_id vc5_id[] = {
13053e1aec4eSMarek Vasut 	{ "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info },
1306be3471c5SMarek Vasut 	{ "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info },
1307be3471c5SMarek Vasut 	{ "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info },
1308be3471c5SMarek Vasut 	{ "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info },
1309be3471c5SMarek Vasut 	{ "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info },
1310be3471c5SMarek Vasut 	{ "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info },
1311be3471c5SMarek Vasut 	{ "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info },
1312be3471c5SMarek Vasut 	{ "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info },
1313be3471c5SMarek Vasut 	{ }
13143e1aec4eSMarek Vasut };
13153e1aec4eSMarek Vasut MODULE_DEVICE_TABLE(i2c, vc5_id);
13163e1aec4eSMarek Vasut 
13173e1aec4eSMarek Vasut static const struct of_device_id clk_vc5_of_match[] = {
13183e1aec4eSMarek Vasut 	{ .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info },
13199adddb01SAlexey Firago 	{ .compatible = "idt,5p49v5925", .data = &idt_5p49v5925_info },
1320b1911555SVladimir Barinov 	{ .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info },
13219adddb01SAlexey Firago 	{ .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info },
13221193e14fSAlexey Firago 	{ .compatible = "idt,5p49v60", .data = &idt_5p49v60_info },
13234411da32SLars-Peter Clausen 	{ .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info },
1324dbf6b16fSMarek Vasut 	{ .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info },
13252bda748eSAdam Ford 	{ .compatible = "idt,5p49v6975", .data = &idt_5p49v6975_info },
1326d8473831SMatthias Fend 	{ },
13273e1aec4eSMarek Vasut };
13283e1aec4eSMarek Vasut MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
13293e1aec4eSMarek Vasut 
13303e1aec4eSMarek Vasut static SIMPLE_DEV_PM_OPS(vc5_pm_ops, vc5_suspend, vc5_resume);
13318cbdc1f0SMarek Vasut 
13328cbdc1f0SMarek Vasut static struct i2c_driver vc5_driver = {
13333e1aec4eSMarek Vasut 	.driver = {
13343e1aec4eSMarek Vasut 		.name = "vc5",
13353e1aec4eSMarek Vasut 		.pm	= &vc5_pm_ops,
13368cbdc1f0SMarek Vasut 		.of_match_table = clk_vc5_of_match,
13373e1aec4eSMarek Vasut 	},
13383e1aec4eSMarek Vasut 	.probe		= vc5_probe,
133962279db5SUwe Kleine-König 	.remove		= vc5_remove,
13403e1aec4eSMarek Vasut 	.id_table	= vc5_id,
13413e1aec4eSMarek Vasut };
13423e1aec4eSMarek Vasut module_i2c_driver(vc5_driver);
13433e1aec4eSMarek Vasut 
13443e1aec4eSMarek Vasut MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
13453e1aec4eSMarek Vasut MODULE_DESCRIPTION("IDT VersaClock 5 driver");
13463e1aec4eSMarek Vasut MODULE_LICENSE("GPL");
13473e1aec4eSMarek Vasut