1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2012 Freescale Semiconductor, Inc.
4  */
5 
6 #include "vsc3316_3308.h"
7 
8 #define REVISION_ID_REG		0x7E
9 #define INTERFACE_MODE_REG		0x79
10 #define CURRENT_PAGE_REGISTER		0x7F
11 #define CONNECTION_CONFIG_PAGE		0x00
12 #define INPUT_STATE_REG		0x13
13 #define GLOBAL_INPUT_ISE1		0x51
14 #define GLOBAL_INPUT_ISE2		0x52
15 #define GLOBAL_INPUT_GAIN		0x53
16 #define GLOBAL_INPUT_LOS		0x55
17 #define GLOBAL_OUTPUT_PE1		0x56
18 #define GLOBAL_OUTPUT_PE2		0x57
19 #define GLOBAL_OUTPUT_LEVEL		0x58
20 #define GLOBAL_OUTPUT_TERMINATION	0x5A
21 #define GLOBAL_CORE_CNTRL		0x5D
22 #define OUTPUT_MODE_PAGE		0x23
23 #define CORE_CONTROL_PAGE		0x25
24 #define CORE_CONFIG_REG		0x75
25 
26 int vsc_if_enable(unsigned int vsc_addr)
27 {
28 	u8 data;
29 
30 	debug("VSC:Configuring VSC at I2C address 0x%2x"
31 			" for 2-wire interface\n", vsc_addr);
32 
33 	/* enable 2-wire Serial InterFace (I2C) */
34 	data = 0x02;
35 	return i2c_write(vsc_addr, INTERFACE_MODE_REG, 1, &data, 1);
36 }
37 
38 int vsc3316_config(unsigned int vsc_addr, int8_t con_arr[][2],
39 		unsigned int num_con)
40 {
41 	unsigned int i;
42 	u8 rev_id = 0;
43 	int ret;
44 
45 	debug("VSC:Initializing VSC3316 at I2C address 0x%2x"
46 		" for Tx\n", vsc_addr);
47 
48 	ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1);
49 	if (ret < 0) {
50 		printf("VSC:0x%x could not read REV_ID from device.\n",
51 			vsc_addr);
52 		return ret;
53 	}
54 
55 	if (rev_id != 0xab) {
56 		printf("VSC: device at address 0x%x is not VSC3316/3308.\n",
57 			vsc_addr);
58 		return -ENODEV;
59 	}
60 
61 	ret = vsc_if_enable(vsc_addr);
62 	if (ret) {
63 		printf("VSC:0x%x could not configured for 2-wire I/F.\n",
64 			vsc_addr);
65 		return ret;
66 	}
67 
68 	/* config connections - page 0x00 */
69 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE);
70 
71 	/* Making crosspoint connections, by connecting required
72 	 * input to output */
73 	for (i = 0; i < num_con ; i++)
74 		i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]);
75 
76 	/* input state - page 0x13 */
77 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG);
78 	/* Configuring the required input of the switch */
79 	for (i = 0; i < num_con ; i++)
80 		i2c_reg_write(vsc_addr, con_arr[i][0], 0x80);
81 
82 	/* Setting Global Input LOS threshold value */
83 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0x60);
84 
85 	/* config output mode - page 0x23 */
86 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE);
87 	/* Turn ON the Output driver correspond to required output*/
88 	for (i = 0; i < num_con ; i++)
89 		i2c_reg_write(vsc_addr,  con_arr[i][1], 0);
90 
91 	/* configure global core control register, Turn on Global core power */
92 	i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0);
93 
94 	vsc_wp_config(vsc_addr);
95 
96 	return 0;
97 }
98 
99 #ifdef CONFIG_SYS_FSL_B4860QDS_XFI_ERR
100 int vsc3308_config_adjust(unsigned int vsc_addr, const int8_t con_arr[][2],
101 		unsigned int num_con)
102 {
103 	unsigned int i;
104 	u8 rev_id = 0;
105 	int ret;
106 
107 	debug("VSC:Initializing VSC3308 at I2C address 0x%x for Tx\n",
108 	      vsc_addr);
109 
110 	ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1);
111 	if (ret < 0) {
112 		printf("VSC:0x%x could not read REV_ID from device.\n",
113 		       vsc_addr);
114 		return ret;
115 	}
116 
117 	if (rev_id != 0xab) {
118 		printf("VSC: device at address 0x%x is not VSC3316/3308.\n",
119 		       vsc_addr);
120 		return -ENODEV;
121 	}
122 
123 	ret = vsc_if_enable(vsc_addr);
124 	if (ret) {
125 		printf("VSC:0x%x could not configured for 2-wire I/F.\n",
126 		       vsc_addr);
127 		return ret;
128 	}
129 
130 	/* config connections - page 0x00 */
131 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE);
132 
133 	/* Configure Global Input ISE */
134 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE1, 0);
135 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE2, 0);
136 
137 	/* Configure Tx/Rx Global Output PE1 */
138 	i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_PE1, 0);
139 
140 	/* Configure Tx/Rx Global Output PE2 */
141 	i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_PE2, 0);
142 
143 	/* Configure Tx/Rx Global Input GAIN */
144 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_GAIN, 0x3F);
145 
146 	/* Setting Global Input LOS threshold value */
147 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0xE0);
148 
149 	/* Setting Global output termination */
150 	i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_TERMINATION, 0);
151 
152 	/* Configure Tx/Rx Global Output level */
153 	if (vsc_addr == VSC3308_TX_ADDRESS)
154 		i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_LEVEL, 4);
155 	else
156 		i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_LEVEL, 2);
157 
158 	/* Making crosspoint connections, by connecting required
159 	 * input to output */
160 	for (i = 0; i < num_con ; i++)
161 		i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]);
162 
163 	/* input state - page 0x13 */
164 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG);
165 	/* Turning off all the required input of the switch */
166 	for (i = 0; i < num_con; i++)
167 		i2c_reg_write(vsc_addr, con_arr[i][0], 1);
168 
169 	/* only turn on specific Tx/Rx requested by the XFI erratum */
170 	if (vsc_addr == VSC3308_TX_ADDRESS) {
171 		i2c_reg_write(vsc_addr, 2, 0);
172 		i2c_reg_write(vsc_addr, 3, 0);
173 	} else {
174 		i2c_reg_write(vsc_addr, 0, 0);
175 		i2c_reg_write(vsc_addr, 1, 0);
176 	}
177 
178 	/* config output mode - page 0x23 */
179 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE);
180 	/* Turn off the Output driver correspond to required output*/
181 	for (i = 0; i < num_con ; i++)
182 		i2c_reg_write(vsc_addr,  con_arr[i][1], 1);
183 
184 	/* only turn on specific Tx/Rx requested by the XFI erratum */
185 	if (vsc_addr == VSC3308_TX_ADDRESS) {
186 		i2c_reg_write(vsc_addr, 0, 0);
187 		i2c_reg_write(vsc_addr, 1, 0);
188 	} else {
189 		i2c_reg_write(vsc_addr, 3, 0);
190 		i2c_reg_write(vsc_addr, 4, 0);
191 	}
192 
193 	/* configure global core control register, Turn on Global core power */
194 	i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0);
195 
196 	vsc_wp_config(vsc_addr);
197 
198 	return 0;
199 }
200 #endif
201 
202 int vsc3308_config(unsigned int vsc_addr, const int8_t con_arr[][2],
203 		unsigned int num_con)
204 {
205 	unsigned int i;
206 	u8 rev_id = 0;
207 	int ret;
208 
209 	debug("VSC:Initializing VSC3308 at I2C address 0x%x"
210 		" for Tx\n", vsc_addr);
211 
212 	ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1);
213 	if (ret < 0) {
214 		printf("VSC:0x%x could not read REV_ID from device.\n",
215 			vsc_addr);
216 		return ret;
217 	}
218 
219 	if (rev_id != 0xab) {
220 		printf("VSC: device at address 0x%x is not VSC3316/3308.\n",
221 			vsc_addr);
222 		return -ENODEV;
223 	}
224 
225 	ret = vsc_if_enable(vsc_addr);
226 	if (ret) {
227 		printf("VSC:0x%x could not configured for 2-wire I/F.\n",
228 			vsc_addr);
229 		return ret;
230 	}
231 
232 	/* config connections - page 0x00 */
233 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE);
234 
235 	/* Making crosspoint connections, by connecting required
236 	 * input to output */
237 	for (i = 0; i < num_con ; i++)
238 		i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]);
239 
240 	/*Configure Global Input ISE and gain */
241 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE1, 0x12);
242 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE2, 0x12);
243 
244 	/* input state - page 0x13 */
245 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG);
246 	/* Turning ON the required input of the switch */
247 	for (i = 0; i < num_con ; i++)
248 		i2c_reg_write(vsc_addr, con_arr[i][0], 0);
249 
250 	/* Setting Global Input LOS threshold value */
251 	i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0x60);
252 
253 	/* config output mode - page 0x23 */
254 	i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE);
255 	/* Turn ON the Output driver correspond to required output*/
256 	for (i = 0; i < num_con ; i++)
257 		i2c_reg_write(vsc_addr,  con_arr[i][1], 0);
258 
259 	/* configure global core control register, Turn on Global core power */
260 	i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0);
261 
262 	vsc_wp_config(vsc_addr);
263 
264 	return 0;
265 }
266 
267 void vsc_wp_config(unsigned int vsc_addr)
268 {
269 	debug("VSC:Configuring VSC at address:0x%x for WP\n", vsc_addr);
270 
271 	/* For new crosspoint configuration to occur, WP bit of
272 	 * CORE_CONFIG_REG should be set 1 and then reset to 0 */
273 	i2c_reg_write(vsc_addr, CORE_CONFIG_REG, 0x01);
274 	i2c_reg_write(vsc_addr, CORE_CONFIG_REG, 0x0);
275 }
276