aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/idt/clk-idt8t49n24x-core.h
blob: 247ec070c62130a99093343d20c0fe54fab702a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/* SPDX-License-Identifier: GPL-2.0 */
/* clk-idt8t49n24x-core.h - Program 8T49N24x settings via I2C (common code)
 *
 * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com>
 *
 * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
 * This program is distributed "AS IS" and  WITHOUT ANY WARRANTY;
 * including the implied warranties of MERCHANTABILITY, FITNESS FOR
 * A PARTICULAR PURPOSE, or NON-INFRINGEMENT.
 */

#ifndef __IDT_CLK_IDT8T49N24X_CORE_H_
#define __IDT_CLK_IDT8T49N24X_CORE_H_

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>

/*
 * The configurations in the settings file have 0x317 registers (last offset
 * is 0x316).
 */
#define NUM_CONFIG_REGISTERS	0x317
#define NUM_INPUTS		2
#define NUM_OUTPUTS		4
#define DEBUGFS_BUFFER_LENGTH	200
#define WRITE_BLOCK_SIZE	32

/* Non output-specific registers */
#define IDT24x_REG_DBL_DIS		0x6C
#define IDT24x_REG_DBL_DIS_MASK		0x01
#define IDT24x_REG_DSM_INT_8		0x25
#define IDT24x_REG_DSM_INT_8_MASK	0x01
#define IDT24x_REG_DSM_INT_7_0		0x26
#define IDT24x_REG_DSMFRAC_20_16	0x28
#define IDT24x_REG_DSMFRAC_20_16_MASK	0x1F
#define IDT24x_REG_DSMFRAC_15_8		0x29
#define IDT24x_REG_DSMFRAC_7_0		0x2A
#define IDT24x_REG_OUTEN		0x39
#define IDT24x_REG_OUTMODE0_1		0x3E
#define IDT24x_REG_OUTMODE2_3		0x3D
#define IDT24x_REG_Q_DIS		0x6F

/* Q0 */
#define IDT24x_REG_OUTEN0_MASK		0x01
#define IDT24x_REG_OUTMODE0_MASK	0x0E
#define IDT24x_REG_Q0_DIS_MASK		0x01
#define IDT24x_REG_NS1_Q0		0x3F
#define IDT24x_REG_NS1_Q0_MASK		0x03
#define IDT24x_REG_NS2_Q0_15_8		0x40
#define IDT24x_REG_NS2_Q0_7_0		0x41

/* Q1 */
#define IDT24x_REG_OUTEN1_MASK		0x02
#define IDT24x_REG_OUTMODE1_MASK	0xE0
#define IDT24x_REG_Q1_DIS_MASK		0x02
#define IDT24x_REG_N_Q1_17_16		0x42
#define IDT24x_REG_N_Q1_17_16_MASK	0x03
#define IDT24x_REG_N_Q1_15_8		0x43
#define IDT24x_REG_N_Q1_7_0		0x44
#define IDT24x_REG_NFRAC_Q1_27_24	0x57
#define IDT24x_REG_NFRAC_Q1_27_24_MASK	0x0F
#define IDT24x_REG_NFRAC_Q1_23_16	0x58
#define IDT24x_REG_NFRAC_Q1_15_8	0x59
#define IDT24x_REG_NFRAC_Q1_7_0		0x5A

/* Q2 */
#define IDT24x_REG_OUTEN2_MASK		0x04
#define IDT24x_REG_OUTMODE2_MASK	0x0E
#define IDT24x_REG_Q2_DIS_MASK		0x04
#define IDT24x_REG_N_Q2_17_16		0x45
#define IDT24x_REG_N_Q2_17_16_MASK	0x03
#define IDT24x_REG_N_Q2_15_8		0x46
#define IDT24x_REG_N_Q2_7_0		0x47
#define IDT24x_REG_NFRAC_Q2_27_24	0x5B
#define IDT24x_REG_NFRAC_Q2_27_24_MASK	0x0F
#define IDT24x_REG_NFRAC_Q2_23_16	0x5C
#define IDT24x_REG_NFRAC_Q2_15_8	0x5D
#define IDT24x_REG_NFRAC_Q2_7_0		0x5E

/* Q3 */
#define IDT24x_REG_OUTEN3_MASK		0x08
#define IDT24x_REG_OUTMODE3_MASK	0xE0
#define IDT24x_REG_Q3_DIS_MASK		0x08
#define IDT24x_REG_N_Q3_17_16		0x48
#define IDT24x_REG_N_Q3_17_16_MASK	0x03
#define IDT24x_REG_N_Q3_15_8		0x49
#define IDT24x_REG_N_Q3_7_0		0x4A
#define IDT24x_REG_NFRAC_Q3_27_24	0x5F
#define IDT24x_REG_NFRAC_Q3_27_24_MASK	0x0F
#define IDT24x_REG_NFRAC_Q3_23_16	0x60
#define IDT24x_REG_NFRAC_Q3_15_8	0x61
#define IDT24x_REG_NFRAC_Q3_7_0		0x62

/**
 * struct idt24x_output - device output information
 * @hw:		hw registration info for this specific output clcok. This gets
 *		passed as an argument to CCF api calls (e.g., set_rate).
 *		container_of can then be used to get the reference to this
 *		struct.
 * @chip:	store a reference to the parent device structure. container_of
 *		cannot be used to get to the parent device structure from
 *		idt24x_output, because clk_idt24x_chip contains an array of
 *		output structs (for future enhancements to support devices
 *		with different numbers of output clocks).
 * @index:	identifies output on the chip; used in debug statements
 * @requested:	requested output clock frequency (in Hz)
 * @actual:	actual output clock frequency (in Hz). Will only be set after
 *		successful update of the device.
 * @debug_freq:	stores value for debugfs file. Use this instead of requested
 *		struct var because debugfs expects u64, not u32.
 */
struct idt24x_output {
	struct clk_hw hw;
	struct clk_idt24x_chip *chip;
	u8 index;
	u32 requested;
	u32 actual;
	u64 debug_freq;
};

/**
 * struct idt24x_dividers - output dividers
 * @dsmint:	int component of feedback divider for VCO (2-stage divider)
 * @dsmfrac:	fractional component of feedback divider for VCO
 * @ns1_q0:	ns1 divider component for Q0
 * @ns2_q0:	ns2 divider component for Q0
 * @nint:	int divider component for Q1-3
 * @nfrac:	fractional divider component for Q1-3
 */
struct idt24x_dividers {
	u16 dsmint;
	u32 dsmfrac;

	u8 ns1_q0;
	u16 ns2_q0;

	u32 nint[3];
	u32 nfrac[3];
};

/**
 * struct clk_idt24x_chip - device info for chip
 * @regmap:		register map used to perform i2c writes to the chip
 * @i2c_client:		i2c_client struct passed to probe
 * @min_freq:		min frequency for this chip
 * @max_freq:		max frequency for this chip
 * @settings:		filled in if full register map is specified in the DT
 * @has_settings:	true if settings array is valid
 * @input_clk:		ptr to input clock specified in DT
 * @input_clk_num:	which input clock was specified. 0-based. A value of
 *			NUM_INPUTS indicates that a XTAL is used as the input.
 * @input_clk_nb:	notification support (if input clk changes)
 * @input_clk_freq:	current freq of input_clk
 * @doubler_disabled:	whether input doubler is enabled. This value is read
 *			from the hw on probe (in case it is set in @settings).
 * @clk:		array of outputs. One entry per output supported by the
 *			chip. Frequencies requested via the ccf api will be
 *			recorded in this array.
 * @reg_dsm_int_8:	record current value from hw to avoid modifying
 *			when writing register values
 * @reg_dsm_frac_20_16:	record current value
 * @reg_out_en_x:	record current value
 * @reg_out_mode_0_1:	record current value
 * @reg_out_mode_2_3:	record current value
 * @reg_qx_dis:		record current value
 * @reg_ns1_q0:		record current value
 * @reg_n_qx_17_16:	record current value
 * @reg_nfrac_qx_27_24:	record current value
 * @divs:		output divider values for all outputs
 * @debugfs_dirroot:	debugfs support
 * @debugfs_fileaction:	debugfs support
 * @debugfs_filei2c:	debugfs support
 * @debugfs_map:	debugfs support
 * @dbg_cache:		debugfs support
 * @debugfs_fileqfreq:	debugfs support
 */
struct clk_idt24x_chip {
	struct regmap *regmap;
	struct i2c_client *i2c_client;

	u32 min_freq;
	u32 max_freq;

	u8 settings[NUM_CONFIG_REGISTERS];

	bool has_settings;

	struct clk *input_clk;
	int input_clk_num;
	struct notifier_block input_clk_nb;
	u32 input_clk_freq;

	bool doubler_disabled;

	struct idt24x_output clk[NUM_OUTPUTS];

	unsigned int reg_dsm_int_8;
	unsigned int reg_dsm_frac_20_16;
	unsigned int reg_out_en_x;
	unsigned int reg_out_mode_0_1;
	unsigned int reg_out_mode_2_3;
	unsigned int reg_qx_dis;
	unsigned int reg_ns1_q0;
	unsigned int reg_n_qx_17_16[3];
	unsigned int reg_nfrac_qx_27_24[3];

	struct idt24x_dividers divs;

	struct dentry *debugfs_dirroot, *debugfs_fileaction, *debugfs_filei2c,
		*debugfs_map;
	char dbg_cache[DEBUGFS_BUFFER_LENGTH];
	struct dentry *debugfs_fileqfreq[4];
};

#define to_idt24x_output(_hw) \
	container_of(_hw, struct idt24x_output, hw)
#define to_clk_idt24x_from_client(_client) \
	container_of(_client, struct clk_idt24x_chip, i2c_client)
#define to_clk_idt24x_from_nb(_nb) \
	container_of(_nb, struct clk_idt24x_chip, input_clk_nb)

/**
 * struct clk_register_offsets - register offsets for current context
 * @oe_offset:		offset for current output enable and mode
 * @oe_mask:		mask for current output enable
 * @dis_mask:		mask for current output disable
 * @n_17_16_offset:	offset for current output int divider (bits 17:16)
 * @n_17_16_mask:	mask for current output int divider (bits 17:16)
 * @n_15_8_offset:	offset for current output int divider (bits 15:8)
 * @n_7_0_offset:	offset for current output int divider (bits 7:0)
 * @nfrac_27_24_offset:	offset for current output frac divider (bits 27:24)
 * @nfrac_27_24_mask:	mask for current output frac divider (bits 27:24)
 * @nfrac_23_16_offset:	offset for current output frac divider (bits 23:16)
 * @nfrac_15_8_offset:	offset for current output frac divider (bits 15:8)
 * @nfrac_7_0_offset:	offset for current output frac divider (bits 7:0)
 * @ns1_offset:		offset for stage 1 div for output Q0
 * @ns1_offset_mask:	mask for stage 1 div for output Q0
 * @ns2_15_8_offset:	offset for stage 2 div for output Q0 (bits 15:8)
 * @ns2_7_0_offset:	offset for stage 2 div for output Q0 (bits 7:0)
 */
struct clk_register_offsets {
	u16 oe_offset;
	u8 oe_mask;
	u8 dis_mask;

	u16 n_17_16_offset;
	u8 n_17_16_mask;
	u16 n_15_8_offset;
	u16 n_7_0_offset;
	u16 nfrac_27_24_offset;
	u8 nfrac_27_24_mask;
	u16 nfrac_23_16_offset;
	u16 nfrac_15_8_offset;
	u16 nfrac_7_0_offset;

	u16 ns1_offset;
	u8 ns1_offset_mask;
	u16 ns2_15_8_offset;
	u16 ns2_7_0_offset;
};

int bits_to_shift(unsigned int mask);
int i2cwritebulk(
	struct i2c_client *client, struct regmap *map,
	unsigned int reg, u8 val[], size_t val_count);
int idt24x_get_offsets(
	u8 output_num,
	struct clk_register_offsets *offsets);
int idt24x_set_frequency(struct clk_idt24x_chip *chip);

#endif /* __IDT_CLK_IDT8T49N24X_CORE_H_ */