xref: /dpdk/drivers/net/ntnic/nthw/core/nthw_i2cm.c (revision 7917b0d38e92e8b9ec5a870415b791420e10f11a)
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright(c) 2023 Napatech A/S
4  */
5 
6 #include "nt_util.h"
7 #include "ntlog.h"
8 
9 #include "nthw_drv.h"
10 #include "nthw_register.h"
11 
12 #include "nthw_i2cm.h"
13 
14 #define NT_I2C_CMD_START 0x80
15 #define NT_I2C_CMD_STOP 0x40
16 #define NT_I2C_CMD_RD 0x20
17 #define NT_I2C_CMD_WR 0x10
18 #define NT_I2C_CMD_NACK 0x08
19 #define NT_I2C_CMD_IRQ_ACK 0x01
20 
21 #define NT_I2C_STATUS_NACK 0x80
22 #define NT_I2C_STATUS_TIP 0x02
23 
24 #define NT_I2C_TRANSMIT_WR 0x00
25 #define NT_I2C_TRANSMIT_RD 0x01
26 
27 #define NUM_RETRIES 50U
28 #define SLEEP_USECS 100U/* 0.1 ms */
29 
30 
31 static bool nthw_i2cm_ready(nthw_i2cm_t *p, bool wait_for_ack)
32 {
33 	uint32_t flags = NT_I2C_STATUS_TIP | (wait_for_ack ? NT_I2C_STATUS_NACK : 0U);
34 
35 	for (uint32_t i = 0U; i < NUM_RETRIES; i++) {
36 		uint32_t status = nthw_field_get_updated(p->mp_fld_cmd_status_cmd_status);
37 		uint32_t ready = (status & flags) == 0U;
38 		/* MUST have a short break to avoid time-outs, even if ready == true */
39 		nt_os_wait_usec(SLEEP_USECS);
40 
41 		if (ready)
42 			return true;
43 	}
44 
45 	return false;
46 }
47 
48 static int nthw_i2cm_write_internal(nthw_i2cm_t *p, uint8_t value)
49 {
50 	/* Write data to data register */
51 	nthw_field_set_val_flush32(p->mp_fld_data_data, value);
52 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
53 		NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
54 
55 	if (!nthw_i2cm_ready(p, true)) {
56 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
57 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
58 		NT_LOG(ERR, NTHW, "%s: Time-out writing data %u", __PRETTY_FUNCTION__, value);
59 		return 1;
60 	}
61 
62 	/* Generate stop condition and clear interrupt */
63 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
64 		NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
65 
66 	if (!nthw_i2cm_ready(p, true)) {
67 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
68 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
69 		NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", __PRETTY_FUNCTION__);
70 		return 1;
71 	}
72 
73 	return 0;
74 }
75 
76 static int nthw_i2cm_write_reg_addr_internal(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr,
77 	bool send_stop)
78 {
79 	/* Write device address to data register */
80 	nthw_field_set_val_flush32(p->mp_fld_data_data,
81 		(uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_WR));
82 
83 	/* #Set start condition along with secondary I2C dev_addr */
84 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
85 		NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
86 
87 	if (!nthw_i2cm_ready(p, true)) {
88 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
89 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
90 		NT_LOG(ERR, NTHW, "%s: Time-out writing device address %u, reg_addr=%u",
91 			__PRETTY_FUNCTION__, dev_addr, reg_addr);
92 		return 1;
93 	}
94 
95 	/* Writing I2C register address */
96 	nthw_field_set_val_flush32(p->mp_fld_data_data, reg_addr);
97 
98 	if (send_stop) {
99 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
100 			NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK | NT_I2C_CMD_STOP);
101 
102 	} else {
103 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
104 			NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
105 	}
106 
107 	if (!nthw_i2cm_ready(p, true)) {
108 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
109 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
110 		NT_LOG(ERR, NTHW, "%s: Time-out writing register address %u", __PRETTY_FUNCTION__,
111 			reg_addr);
112 		return 1;
113 	}
114 
115 	return 0;
116 }
117 
118 static int nthw_i2cm_read_internal(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t *value)
119 {
120 	/* Write I2C device address - with LSBit set to READ */
121 
122 	nthw_field_set_val_flush32(p->mp_fld_data_data,
123 		(uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_RD));
124 	/* #Send START condition along with secondary I2C dev_addr */
125 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
126 		NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
127 
128 	if (!nthw_i2cm_ready(p, true)) {
129 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
130 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
131 		NT_LOG(ERR, NTHW, "%s: Time-out rewriting device address %u", __PRETTY_FUNCTION__,
132 			dev_addr);
133 		return 1;
134 	}
135 
136 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
137 		NT_I2C_CMD_RD | NT_I2C_CMD_NACK | NT_I2C_CMD_IRQ_ACK);
138 
139 	if (!nthw_i2cm_ready(p, false)) {
140 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
141 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
142 		NT_LOG(ERR, NTHW, "%s: Time-out during read operation", __PRETTY_FUNCTION__);
143 		return 1;
144 	}
145 
146 	*value = (uint8_t)nthw_field_get_updated(p->mp_fld_data_data);
147 
148 	/* Generate stop condition and clear interrupt */
149 	nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
150 		NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
151 
152 	if (!nthw_i2cm_ready(p, false)) {
153 		nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
154 			NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
155 		NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", __PRETTY_FUNCTION__);
156 		return 1;
157 	}
158 
159 	return 0;
160 }
161 
162 int nthw_i2cm_read(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t *value)
163 {
164 	int status;
165 	status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, false);
166 
167 	if (status != 0)
168 		return status;
169 
170 	status = nthw_i2cm_read_internal(p, dev_addr, value);
171 
172 	if (status != 0)
173 		return status;
174 
175 	return 0;
176 }
177 
178 int nthw_i2cm_write(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t value)
179 {
180 	int status;
181 	status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, false);
182 
183 	if (status != 0)
184 		return status;
185 
186 	status = nthw_i2cm_write_internal(p, value);
187 
188 	if (status != 0)
189 		return status;
190 
191 	return 0;
192 }
193