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