1/***********************license start***************
2 * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17
18 *   * Neither the name of Cavium Networks nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22
23 * This Software, including technical data, may be subject to U.S. export  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46/**
47 * @file
48 *
49 * Interface to the TWSI / I2C bus
50 *
51 * <hr>$Revision: 49448 $<hr>
52 *
53 */
54#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
55#include <linux/i2c.h>
56
57#include <asm/octeon/cvmx.h>
58#include <asm/octeon/cvmx-twsi.h>
59#else
60#include "cvmx.h"
61#include "cvmx-twsi.h"
62#include "cvmx-csr-db.h"
63#endif
64
65//#define PRINT_TWSI_CONFIG
66#ifdef PRINT_TWSI_CONFIG
67#define twsi_printf printf
68#else
69#define twsi_printf(...)
70#define cvmx_csr_db_decode(...)
71#endif
72
73#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
74static struct i2c_adapter *__cvmx_twsix_get_adapter(int twsi_id)
75{
76# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
77	struct octeon_i2c {
78		wait_queue_head_t queue;
79		struct i2c_adapter adap;
80		int irq;
81		int twsi_freq;
82		int sys_freq;
83		resource_size_t twsi_phys;
84		void __iomem *twsi_base;
85		resource_size_t regsize;
86		struct device *dev;
87		int broken_irq_mode;
88	};
89	struct i2c_adapter *adapter;
90	struct octeon_i2c *i2c;
91
92	adapter = i2c_get_adapter(0);
93	if (adapter == NULL)
94		return NULL;
95	i2c = container_of(adapter, struct octeon_i2c, adap);
96	return &i2c[twsi_id].adap;
97#else
98	return NULL;
99#endif
100}
101#endif
102
103
104/**
105 * Do a twsi read from a 7 bit device address using an (optional) internal address.
106 * Up to 8 bytes can be read at a time.
107 *
108 * @param twsi_id   which Octeon TWSI bus to use
109 * @param dev_addr  Device address (7 bit)
110 * @param internal_addr
111 *                  Internal address.  Can be 0, 1 or 2 bytes in width
112 * @param num_bytes Number of data bytes to read
113 * @param ia_width_bytes
114 *                  Internal address size in bytes (0, 1, or 2)
115 * @param data      Pointer argument where the read data is returned.
116 *
117 * @return read data returned in 'data' argument
118 *         Number of bytes read on success
119 *         -1 on failure
120 */
121int cvmx_twsix_read_ia(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t *data)
122{
123#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
124# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
125	struct i2c_adapter *adapter;
126	u8 data_buf[8];
127	u8 addr_buf[8];
128	struct i2c_msg msg[2];
129	uint64_t r;
130	int i, j;
131
132	if (ia_width_bytes == 0)
133		return cvmx_twsix_read(twsi_id, dev_addr, num_bytes, data);
134
135	BUG_ON(ia_width_bytes > 2);
136	BUG_ON(num_bytes > 8 || num_bytes < 1);
137
138	adapter = __cvmx_twsix_get_adapter(twsi_id);
139	if (adapter == NULL)
140		return -1;
141
142	for (j = 0, i = ia_width_bytes - 1; i >= 0; i--, j++)
143		addr_buf[j] = (u8)(internal_addr >> (i * 8));
144
145	msg[0].addr = dev_addr;
146	msg[0].flags = 0;
147	msg[0].len = ia_width_bytes;
148	msg[0].buf = addr_buf;
149
150	msg[1].addr = dev_addr;
151	msg[1].flags = I2C_M_RD;
152	msg[1].len = num_bytes;
153	msg[1].buf = data_buf;
154
155	i = i2c_transfer(adapter, msg, 2);
156
157	i2c_put_adapter(adapter);
158
159	if (i == 2) {
160		r = 0;
161		for (i = 0; i < num_bytes; i++)
162			r = (r << 8) | data_buf[i];
163		*data = r;
164		return num_bytes;
165	} else {
166		return -1;
167	}
168# else
169	BUG(); /* The I2C driver is not compiled in */
170# endif
171#else
172	cvmx_mio_twsx_sw_twsi_t sw_twsi_val;
173	cvmx_mio_twsx_sw_twsi_ext_t twsi_ext;
174
175	if (num_bytes < 1 || num_bytes > 8 || !data || ia_width_bytes < 0 || ia_width_bytes > 2)
176		return -1;
177
178	twsi_ext.u64 = 0;
179	sw_twsi_val.u64 = 0;
180	sw_twsi_val.s.v = 1;
181	sw_twsi_val.s.r = 1;
182	sw_twsi_val.s.sovr = 1;
183	sw_twsi_val.s.size = num_bytes - 1;
184	sw_twsi_val.s.a = dev_addr;
185
186	if (ia_width_bytes > 0) {
187		sw_twsi_val.s.op = 1;
188		sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f;
189		sw_twsi_val.s.eop_ia = internal_addr & 0x7;
190	}
191	if (ia_width_bytes == 2) {
192		sw_twsi_val.s.eia = 1;
193		twsi_ext.s.ia = internal_addr >> 8;
194		cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64);
195	}
196
197	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
198	cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
199	while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v)
200		;
201	twsi_printf("Results:\n");
202	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
203	if (!sw_twsi_val.s.r)
204		return -1;
205
206	*data = (sw_twsi_val.s.d & (0xFFFFFFFF >> (32 - num_bytes*8)));
207	if (num_bytes > 4) {
208		twsi_ext.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id));
209		*data |= ((unsigned long long)(twsi_ext.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))) << 32);
210	}
211	return num_bytes;
212#endif
213}
214
215/**
216 * Read from a TWSI device (7 bit device address only) without generating any
217 * internal addresses.
218 * Read from 1-8 bytes and returns them in the data pointer.
219 *
220 * @param twsi_id   TWSI interface on Octeon to use
221 * @param dev_addr  TWSI device address (7 bit only)
222 * @param num_bytes number of bytes to read
223 * @param data      Pointer to data read from TWSI device
224 *
225 * @return Number of bytes read on success
226 *         -1 on error
227 */
228int cvmx_twsix_read(int twsi_id, uint8_t dev_addr, int num_bytes, uint64_t *data)
229{
230#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
231# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
232	struct i2c_adapter *adapter;
233	u8 data_buf[8];
234	struct i2c_msg msg[1];
235	uint64_t r;
236	int i;
237
238	BUG_ON(num_bytes > 8 || num_bytes < 1);
239
240	adapter = __cvmx_twsix_get_adapter(twsi_id);
241	if (adapter == NULL)
242		return -1;
243
244	msg[0].addr = dev_addr;
245	msg[0].flags = I2C_M_RD;
246	msg[0].len = num_bytes;
247	msg[0].buf = data_buf;
248
249	i = i2c_transfer(adapter, msg, 1);
250
251	i2c_put_adapter(adapter);
252
253	if (i == 1) {
254		r = 0;
255		for (i = 0; i < num_bytes; i++)
256			r = (r << 8) | data_buf[i];
257		*data = r;
258		return num_bytes;
259	} else {
260		return -1;
261	}
262# else
263	BUG(); /* The I2C driver is not compiled in */
264# endif
265#else
266	cvmx_mio_twsx_sw_twsi_t sw_twsi_val;
267	cvmx_mio_twsx_sw_twsi_ext_t twsi_ext;
268
269	if (num_bytes > 8 || num_bytes < 1)
270		return -1;
271
272	sw_twsi_val.u64 = 0;
273	sw_twsi_val.s.v = 1;
274	sw_twsi_val.s.r = 1;
275	sw_twsi_val.s.a = dev_addr;
276	sw_twsi_val.s.sovr = 1;
277	sw_twsi_val.s.size = num_bytes - 1;
278
279	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
280	cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
281	while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v)
282		;
283	twsi_printf("Results:\n");
284	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
285	if (!sw_twsi_val.s.r)
286		return -1;
287
288	*data = (sw_twsi_val.s.d & (0xFFFFFFFF >> (32 - num_bytes*8)));
289	if (num_bytes > 4) {
290		twsi_ext.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id));
291		*data |= ((unsigned long long)(twsi_ext.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))) << 32);
292	}
293	return num_bytes;
294#endif
295}
296
297/**
298 * Perform a twsi write operation to a 7 bit device address.
299 *
300 * Note that many eeprom devices have page restrictions regarding address boundaries
301 * that can be crossed in one write operation.  This is device dependent, and this routine
302 * does nothing in this regard.
303 * This command does not generate any internal addressess.
304 *
305 * @param twsi_id   Octeon TWSI interface to use
306 * @param dev_addr  TWSI device address
307 * @param num_bytes Number of bytes to write (between 1 and 8 inclusive)
308 * @param data      Data to write
309 *
310 * @return 0 on success
311 *         -1 on failure
312 */
313int cvmx_twsix_write(int twsi_id, uint8_t dev_addr, int num_bytes, uint64_t data)
314{
315#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
316# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
317	struct i2c_adapter *adapter;
318	u8 data_buf[8];
319	struct i2c_msg msg[1];
320	int i, j;
321
322	BUG_ON(num_bytes > 8 || num_bytes < 1);
323
324	adapter = __cvmx_twsix_get_adapter(twsi_id);
325	if (adapter == NULL)
326		return -1;
327
328	for (j = 0, i = num_bytes - 1; i >= 0; i--, j++)
329		data_buf[j] = (u8)(data >> (i * 8));
330
331	msg[1].addr = dev_addr;
332	msg[1].flags = 0;
333	msg[1].len = num_bytes;
334	msg[1].buf = data_buf;
335
336	i = i2c_transfer(adapter, msg, 1);
337
338	i2c_put_adapter(adapter);
339
340	if (i == 1)
341		return num_bytes;
342	else
343		return -1;
344# else
345	BUG(); /* The I2C driver is not compiled in */
346# endif
347#else
348	cvmx_mio_twsx_sw_twsi_t sw_twsi_val;
349
350	if (num_bytes > 8 || num_bytes < 1)
351		return -1;
352
353	sw_twsi_val.u64 = 0;
354	sw_twsi_val.s.v = 1;
355	sw_twsi_val.s.a = dev_addr;
356	sw_twsi_val.s.d = data & 0xffffffff;
357	sw_twsi_val.s.sovr = 1;
358	sw_twsi_val.s.size = num_bytes - 1;
359	if (num_bytes > 4) {
360		/* Upper four bytes go into a separate register */
361		cvmx_mio_twsx_sw_twsi_ext_t twsi_ext;
362		twsi_ext.u64 = 0;
363		twsi_ext.s.d = data >> 32;
364		cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64);
365	}
366	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
367	cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
368	while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v)
369		;
370	twsi_printf("Results:\n");
371	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
372	if (!sw_twsi_val.s.r)
373		return -1;
374
375	return 0;
376#endif
377}
378
379/**
380 * Write 1-8 bytes to a TWSI device using an internal address.
381 *
382 * @param twsi_id   which TWSI interface on Octeon to use
383 * @param dev_addr  TWSI device address (7 bit only)
384 * @param internal_addr
385 *                  TWSI internal address (0, 8, or 16 bits)
386 * @param num_bytes Number of bytes to write (1-8)
387 * @param ia_width_bytes
388 *                  internal address width, in bytes (0, 1, 2)
389 * @param data      Data to write.  Data is written MSB first on the twsi bus, and only the lower
390 *                  num_bytes bytes of the argument are valid.  (If a 2 byte write is done, only
391 *                  the low 2 bytes of the argument is used.
392 *
393 * @return Number of bytes read on success,
394 *         -1 on error
395 */
396int cvmx_twsix_write_ia(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t data)
397{
398#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
399# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
400	struct i2c_adapter *adapter;
401	u8 data_buf[8];
402	u8 addr_buf[8];
403	struct i2c_msg msg[2];
404	int i, j;
405
406	if (ia_width_bytes == 0)
407		return cvmx_twsix_write(twsi_id, dev_addr, num_bytes, data);
408
409	BUG_ON(ia_width_bytes > 2);
410	BUG_ON(num_bytes > 8 || num_bytes < 1);
411
412	adapter = __cvmx_twsix_get_adapter(twsi_id);
413	if (adapter == NULL)
414		return -1;
415
416
417	for (j = 0, i = ia_width_bytes - 1; i >= 0; i--, j++)
418		addr_buf[j] = (u8)(internal_addr >> (i * 8));
419
420	for (j = 0, i = num_bytes - 1; i >= 0; i--, j++)
421		data_buf[j] = (u8)(data >> (i * 8));
422
423	msg[0].addr = dev_addr;
424	msg[0].flags = 0;
425	msg[0].len = ia_width_bytes;
426	msg[0].buf = addr_buf;
427
428	msg[1].addr = dev_addr;
429	msg[1].flags = 0;
430	msg[1].len = num_bytes;
431	msg[1].buf = data_buf;
432
433	i = i2c_transfer(adapter, msg, 2);
434
435	i2c_put_adapter(adapter);
436
437	if (i == 2) {
438		/* Poll until reads succeed, or polling times out */
439		int to = 100;
440		while (to-- > 0) {
441			uint64_t data;
442			if (cvmx_twsix_read(twsi_id, dev_addr, 1, &data) >= 0)
443				break;
444		}
445	}
446
447	if (i == 2)
448		return num_bytes;
449	else
450		return -1;
451# else
452	BUG(); /* The I2C driver is not compiled in */
453# endif
454#else
455	cvmx_mio_twsx_sw_twsi_t sw_twsi_val;
456	cvmx_mio_twsx_sw_twsi_ext_t twsi_ext;
457	int to;
458
459	if (num_bytes < 1 || num_bytes > 8 || ia_width_bytes < 0 || ia_width_bytes > 2)
460		return -1;
461
462	twsi_ext.u64 = 0;
463
464	sw_twsi_val.u64 = 0;
465	sw_twsi_val.s.v = 1;
466	sw_twsi_val.s.sovr = 1;
467	sw_twsi_val.s.size = num_bytes - 1;
468	sw_twsi_val.s.a = dev_addr;
469	sw_twsi_val.s.d = 0xFFFFFFFF & data;
470
471	if (ia_width_bytes > 0) {
472		sw_twsi_val.s.op = 1;
473		sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f;
474		sw_twsi_val.s.eop_ia = internal_addr & 0x7;
475	}
476	if (ia_width_bytes == 2) {
477		sw_twsi_val.s.eia = 1;
478		twsi_ext.s.ia = internal_addr >> 8;
479	}
480	if (num_bytes > 4)
481		twsi_ext.s.d = data >> 32;
482
483	twsi_printf("%s: twsi_id=%x, dev_addr=%x, internal_addr=%x\n\tnum_bytes=%d, ia_width_bytes=%d, data=%lx\n",
484		    __FUNCTION__, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, data);
485	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64);
486	cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64);
487	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
488	cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
489	while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v)
490		;
491	twsi_printf("Results:\n");
492	cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64);
493
494	/* Poll until reads succeed, or polling times out */
495	to = 100;
496	while (to-- > 0) {
497		uint64_t data;
498		if (cvmx_twsix_read(twsi_id, dev_addr, 1, &data) >= 0)
499			break;
500	}
501	if (to <= 0)
502		return -1;
503
504	return num_bytes;
505#endif
506}
507