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