Patrick Georgi | 7051707 | 2020-05-10 18:47:05 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 2 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 3 | #include <device/mmio.h> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 4 | #include <console/console.h> |
| 5 | #include <delay.h> |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 6 | #include <timer.h> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 7 | #include <soc/iomap.h> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 8 | #include <soc/qup.h> |
| 9 | |
| 10 | #define TIMEOUT_CNT 100000 |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 11 | |
| 12 | //TODO: refactor the following array to iomap driver. |
Martin Roth | 57e8909 | 2019-10-23 21:45:23 -0600 | [diff] [blame] | 13 | static unsigned int gsbi_qup_base[] = { |
| 14 | (unsigned int)GSBI_QUP1_BASE, |
| 15 | (unsigned int)GSBI_QUP2_BASE, |
| 16 | (unsigned int)GSBI_QUP3_BASE, |
| 17 | (unsigned int)GSBI_QUP4_BASE, |
| 18 | (unsigned int)GSBI_QUP5_BASE, |
| 19 | (unsigned int)GSBI_QUP6_BASE, |
| 20 | (unsigned int)GSBI_QUP7_BASE, |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 21 | }; |
| 22 | |
| 23 | #define QUP_ADDR(gsbi_num, reg) ((void *)((gsbi_qup_base[gsbi_num-1]) + (reg))) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 24 | |
| 25 | static qup_return_t qup_i2c_master_status(gsbi_id_t gsbi_id) |
| 26 | { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 27 | uint32_t reg_val = read32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS)); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 28 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 29 | if (read32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS))) |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 30 | return QUP_ERR_XFER_FAIL; |
| 31 | if (reg_val & QUP_I2C_INVALID_READ_ADDR) |
| 32 | return QUP_ERR_I2C_INVALID_SLAVE_ADDR; |
| 33 | if (reg_val & QUP_I2C_FAILED_MASK) |
| 34 | return QUP_ERR_I2C_FAILED; |
| 35 | if (reg_val & QUP_I2C_ARB_LOST) |
| 36 | return QUP_ERR_I2C_ARB_LOST; |
| 37 | if (reg_val & QUP_I2C_BUS_ERROR) |
| 38 | return QUP_ERR_I2C_BUS_ERROR; |
Sourabh Banerjee | f36cffc | 2015-02-27 19:18:11 +0530 | [diff] [blame] | 39 | if (reg_val & QUP_I2C_INVALID_WRITE) |
| 40 | return QUP_ERR_I2C_INVALID_WRITE; |
| 41 | if (reg_val & QUP_I2C_PACKET_NACK) |
| 42 | return QUP_ERR_I2C_NACK; |
| 43 | if (reg_val & QUP_I2C_INVALID_TAG) |
| 44 | return QUP_ERR_I2C_INVALID_TAG; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 45 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 46 | return QUP_SUCCESS; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 47 | } |
| 48 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 49 | static int check_bit_state(uint32_t *reg, int wait_for) |
| 50 | { |
| 51 | unsigned int count = TIMEOUT_CNT; |
| 52 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 53 | while ((read32(reg) & (QUP_STATE_VALID_MASK | QUP_STATE_MASK)) != |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 54 | (QUP_STATE_VALID | wait_for)) { |
| 55 | if (count == 0) |
| 56 | return QUP_ERR_TIMEOUT; |
| 57 | count--; |
| 58 | udelay(1); |
| 59 | } |
| 60 | |
| 61 | return QUP_SUCCESS; |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | * Check whether GSBIn_QUP State is valid |
| 66 | */ |
Martin Roth | 57e8909 | 2019-10-23 21:45:23 -0600 | [diff] [blame] | 67 | static qup_return_t qup_wait_for_state(gsbi_id_t gsbi_id, unsigned int wait_for) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 68 | { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 69 | return check_bit_state(QUP_ADDR(gsbi_id, QUP_STATE), wait_for); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 70 | } |
| 71 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 72 | qup_return_t qup_reset_i2c_master_status(gsbi_id_t gsbi_id) |
| 73 | { |
| 74 | /* |
| 75 | * Writing a one clears the status bits. |
| 76 | * Bit31-25, Bit1 and Bit0 are reserved. |
| 77 | */ |
| 78 | //TODO: Define each status bit. OR all status bits in a single macro. |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 79 | write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS), 0x3FFFFFC); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 80 | return QUP_SUCCESS; |
| 81 | } |
| 82 | |
| 83 | static qup_return_t qup_reset_master_status(gsbi_id_t gsbi_id) |
| 84 | { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 85 | write32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS), 0x7C); |
| 86 | write32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS_EN), 0x7C); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 87 | qup_reset_i2c_master_status(gsbi_id); |
| 88 | return QUP_SUCCESS; |
| 89 | } |
| 90 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 91 | static qup_return_t qup_fifo_wait_for(gsbi_id_t gsbi_id, uint32_t status, |
| 92 | struct stopwatch *timeout) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 93 | { |
| 94 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 95 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 96 | while (!(read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status)) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 97 | ret = qup_i2c_master_status(gsbi_id); |
| 98 | if (ret) |
| 99 | return ret; |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 100 | if (stopwatch_expired(timeout)) |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 101 | return QUP_ERR_TIMEOUT; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 102 | } |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 103 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 104 | return QUP_SUCCESS; |
| 105 | } |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 106 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 107 | static qup_return_t qup_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status, |
| 108 | struct stopwatch *timeout) |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 109 | { |
| 110 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 111 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 112 | while (read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 113 | ret = qup_i2c_master_status(gsbi_id); |
| 114 | if (ret) |
| 115 | return ret; |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 116 | if (stopwatch_expired(timeout)) |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 117 | return QUP_ERR_TIMEOUT; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | return QUP_SUCCESS; |
| 121 | } |
| 122 | |
| 123 | static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj, |
| 124 | uint8_t stop_seq) |
| 125 | { |
| 126 | qup_return_t ret = QUP_ERR_UNDEFINED; |
| 127 | uint8_t addr = p_tx_obj->p.iic.addr; |
| 128 | uint8_t *data_ptr = p_tx_obj->p.iic.data; |
Martin Roth | 57e8909 | 2019-10-23 21:45:23 -0600 | [diff] [blame] | 129 | unsigned int data_len = p_tx_obj->p.iic.data_len; |
| 130 | unsigned int idx = 0; |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 131 | struct stopwatch timeout; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 132 | |
| 133 | qup_reset_master_status(gsbi_id); |
| 134 | qup_set_state(gsbi_id, QUP_STATE_RUN); |
| 135 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 136 | write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO), |
| 137 | (QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr))); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 138 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 139 | stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 140 | while (data_len) { |
| 141 | if (data_len == 1 && stop_seq) { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 142 | write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO), |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 143 | QUP_I2C_STOP_SEQ | QUP_I2C_DATA(data_ptr[idx])); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 144 | } else { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 145 | write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO), |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 146 | QUP_I2C_DATA_SEQ | QUP_I2C_DATA(data_ptr[idx])); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 147 | } |
| 148 | data_len--; |
| 149 | idx++; |
| 150 | if (data_len) { |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 151 | ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_FULL, |
| 152 | &timeout); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 153 | if (ret) |
| 154 | return ret; |
| 155 | } |
| 156 | /* Hardware sets the OUTPUT_SERVICE_FLAG flag to 1 when |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 157 | OUTPUT_FIFO_NOT_EMPTY flag in the QUP_OPERATIONAL |
| 158 | register changes from 1 to 0, indicating that software |
| 159 | can write more data to the output FIFO. Software should |
| 160 | set OUTPUT_SERVICE_FLAG to 1 to clear it to 0, which |
| 161 | means that software knows to return to fill the output |
| 162 | FIFO with data. |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 163 | */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 164 | if (read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 165 | OUTPUT_SERVICE_FLAG) { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 166 | write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), |
| 167 | OUTPUT_SERVICE_FLAG); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 168 | } |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 169 | } |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 170 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 171 | ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY, &timeout); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 172 | if (ret) |
| 173 | return ret; |
| 174 | |
| 175 | qup_set_state(gsbi_id, QUP_STATE_PAUSE); |
Sourabh Banerjee | f36cffc | 2015-02-27 19:18:11 +0530 | [diff] [blame] | 176 | return qup_i2c_master_status(gsbi_id); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 177 | } |
| 178 | |
| 179 | static qup_return_t qup_i2c_write(gsbi_id_t gsbi_id, uint8_t mode, |
| 180 | qup_data_t *p_tx_obj, uint8_t stop_seq) |
| 181 | { |
| 182 | qup_return_t ret = QUP_ERR_UNDEFINED; |
| 183 | |
| 184 | switch (mode) { |
| 185 | case QUP_MODE_FIFO: |
| 186 | ret = qup_i2c_write_fifo(gsbi_id, p_tx_obj, stop_seq); |
| 187 | break; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 188 | default: |
| 189 | ret = QUP_ERR_UNSUPPORTED; |
| 190 | } |
| 191 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 192 | if (ret) { |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 193 | qup_set_state(gsbi_id, QUP_STATE_RESET); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 194 | printk(BIOS_ERR, "%s() failed (%d)\n", __func__, ret); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | return ret; |
| 198 | } |
| 199 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 200 | static qup_return_t qup_i2c_read_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj) |
| 201 | { |
| 202 | qup_return_t ret = QUP_ERR_UNDEFINED; |
| 203 | uint8_t addr = p_tx_obj->p.iic.addr; |
| 204 | uint8_t *data_ptr = p_tx_obj->p.iic.data; |
Martin Roth | 57e8909 | 2019-10-23 21:45:23 -0600 | [diff] [blame] | 205 | unsigned int data_len = p_tx_obj->p.iic.data_len; |
| 206 | unsigned int idx = 0; |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 207 | struct stopwatch timeout; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 208 | |
| 209 | qup_reset_master_status(gsbi_id); |
| 210 | qup_set_state(gsbi_id, QUP_STATE_RUN); |
| 211 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 212 | write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO), |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 213 | QUP_I2C_START_SEQ | (QUP_I2C_ADDR(addr) | QUP_I2C_SLAVE_READ)); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 214 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 215 | write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO), |
Julius Werner | 9418476 | 2015-02-19 20:19:23 -0800 | [diff] [blame] | 216 | QUP_I2C_RECV_SEQ | data_len); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 217 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 218 | stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US); |
| 219 | ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY, &timeout); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 220 | if (ret) |
| 221 | return ret; |
| 222 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 223 | write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), OUTPUT_SERVICE_FLAG); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 224 | |
| 225 | while (data_len) { |
| 226 | uint32_t data; |
| 227 | |
Jes Klinke | 19baa9d | 2022-02-22 16:00:09 -0800 | [diff] [blame] | 228 | ret = qup_fifo_wait_for(gsbi_id, INPUT_SERVICE_FLAG, &timeout); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 229 | if (ret) |
| 230 | return ret; |
| 231 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 232 | data = read32(QUP_ADDR(gsbi_id, QUP_INPUT_FIFO)); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 233 | |
| 234 | /* |
| 235 | * Process tag and corresponding data value. For I2C master |
| 236 | * mini-core, data in FIFO is composed of 16 bits and is divided |
| 237 | * into an 8-bit tag for the upper bits and 8-bit data for the |
| 238 | * lower bits. The 8-bit tag indicates whether the byte is the |
| 239 | * last byte, or if a bus error happened during the receipt of |
| 240 | * the byte. |
| 241 | */ |
| 242 | if ((QUP_I2C_MI_TAG(data)) == QUP_I2C_MIDATA_SEQ) { |
| 243 | /* Tag: MIDATA = Master input data.*/ |
| 244 | data_ptr[idx] = QUP_I2C_DATA(data); |
| 245 | idx++; |
| 246 | data_len--; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 247 | write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), |
| 248 | INPUT_SERVICE_FLAG); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 249 | } else if (QUP_I2C_MI_TAG(data) == QUP_I2C_MISTOP_SEQ) { |
| 250 | /* Tag: MISTOP: Last byte of master input. */ |
| 251 | data_ptr[idx] = QUP_I2C_DATA(data); |
| 252 | idx++; |
| 253 | data_len--; |
| 254 | break; |
| 255 | } else { |
| 256 | /* Tag: MINACK: Invalid master input data.*/ |
| 257 | break; |
| 258 | } |
| 259 | } |
| 260 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 261 | write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), INPUT_SERVICE_FLAG); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 262 | p_tx_obj->p.iic.data_len = idx; |
| 263 | qup_set_state(gsbi_id, QUP_STATE_PAUSE); |
| 264 | |
| 265 | return QUP_SUCCESS; |
| 266 | } |
| 267 | |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 268 | static qup_return_t qup_i2c_read(gsbi_id_t gsbi_id, uint8_t mode, |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 269 | qup_data_t *p_tx_obj) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 270 | { |
| 271 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 272 | |
| 273 | switch (mode) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 274 | case QUP_MODE_FIFO: |
| 275 | ret = qup_i2c_read_fifo(gsbi_id, p_tx_obj); |
| 276 | break; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 277 | default: |
| 278 | ret = QUP_ERR_UNSUPPORTED; |
| 279 | } |
| 280 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 281 | if (ret) { |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 282 | qup_set_state(gsbi_id, QUP_STATE_RESET); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 283 | printk(BIOS_ERR, "%s() failed (%d)\n", __func__, ret); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | return ret; |
| 287 | } |
| 288 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 289 | qup_return_t qup_init(gsbi_id_t gsbi_id, const qup_config_t *config_ptr) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 290 | { |
| 291 | qup_return_t ret = QUP_ERR_UNDEFINED; |
| 292 | uint32_t reg_val; |
| 293 | |
| 294 | /* Reset the QUP core.*/ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 295 | write32(QUP_ADDR(gsbi_id, QUP_SW_RESET), 0x1); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 296 | |
| 297 | /*Wait till the reset takes effect */ |
| 298 | ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET); |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 299 | if (ret) |
| 300 | goto bailout; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 301 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 302 | /* Reset the config */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 303 | write32(QUP_ADDR(gsbi_id, QUP_CONFIG), 0); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 304 | |
| 305 | /*Program the config register*/ |
| 306 | /*Set N value*/ |
| 307 | reg_val = 0x0F; |
| 308 | /*Set protocol*/ |
| 309 | switch (config_ptr->protocol) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 310 | case QUP_MINICORE_I2C_MASTER: |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 311 | reg_val |= ((config_ptr->protocol & |
| 312 | QUP_MINI_CORE_PROTO_MASK) << |
| 313 | QUP_MINI_CORE_PROTO_SHFT); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 314 | break; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 315 | default: |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 316 | ret = QUP_ERR_UNSUPPORTED; |
| 317 | goto bailout; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 318 | } |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 319 | write32(QUP_ADDR(gsbi_id, QUP_CONFIG), reg_val); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 320 | |
| 321 | /*Reset i2c clk cntl register*/ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 322 | write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL), 0); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 323 | |
| 324 | /*Set QUP IO Mode*/ |
| 325 | switch (config_ptr->mode) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 326 | case QUP_MODE_FIFO: |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 327 | reg_val = QUP_OUTPUT_BIT_SHIFT_EN | |
| 328 | ((config_ptr->mode & QUP_MODE_MASK) << |
| 329 | QUP_OUTPUT_MODE_SHFT) | |
| 330 | ((config_ptr->mode & QUP_MODE_MASK) << |
| 331 | QUP_INPUT_MODE_SHFT); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 332 | break; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 333 | default: |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 334 | ret = QUP_ERR_UNSUPPORTED; |
| 335 | goto bailout; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 336 | } |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 337 | write32(QUP_ADDR(gsbi_id, QUP_IO_MODES), reg_val); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 338 | |
| 339 | /*Set i2c clk cntl*/ |
| 340 | reg_val = (QUP_DIVIDER_MIN_VAL << QUP_HS_DIVIDER_SHFT); |
| 341 | reg_val |= ((((config_ptr->src_frequency / config_ptr->clk_frequency) |
| 342 | / 2) - QUP_DIVIDER_MIN_VAL) & |
| 343 | QUP_FS_DIVIDER_MASK); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 344 | write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL), reg_val); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 345 | |
| 346 | bailout: |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 347 | if (ret) |
| 348 | printk(BIOS_ERR, "failed to init qup (%d)\n", ret); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 349 | |
| 350 | return ret; |
| 351 | } |
| 352 | |
| 353 | qup_return_t qup_set_state(gsbi_id_t gsbi_id, uint32_t state) |
| 354 | { |
| 355 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Martin Roth | 57e8909 | 2019-10-23 21:45:23 -0600 | [diff] [blame] | 356 | unsigned int curr_state = read32(QUP_ADDR(gsbi_id, QUP_STATE)); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 357 | |
Julius Werner | 7c712bb | 2019-05-01 16:51:20 -0700 | [diff] [blame] | 358 | if (state <= QUP_STATE_PAUSE && (curr_state & QUP_STATE_VALID_MASK)) { |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 359 | /* |
| 360 | * For PAUSE_STATE to RESET_STATE transition, |
| 361 | * two writes of 10[binary]) are required for the |
| 362 | * transition to complete. |
| 363 | */ |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 364 | if (QUP_STATE_PAUSE == curr_state && QUP_STATE_RESET == state) { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 365 | write32(QUP_ADDR(gsbi_id, QUP_STATE), 0x2); |
| 366 | write32(QUP_ADDR(gsbi_id, QUP_STATE), 0x2); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 367 | } else { |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 368 | write32(QUP_ADDR(gsbi_id, QUP_STATE), state); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 369 | } |
| 370 | ret = qup_wait_for_state(gsbi_id, state); |
| 371 | } |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 372 | |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 373 | return ret; |
| 374 | } |
| 375 | |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 376 | static qup_return_t qup_i2c_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj, |
| 377 | uint8_t stop_seq) |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 378 | { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 379 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 380 | uint8_t mode = (read32(QUP_ADDR(gsbi_id, QUP_IO_MODES)) >> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 381 | QUP_OUTPUT_MODE_SHFT) & QUP_MODE_MASK; |
| 382 | |
| 383 | ret = qup_i2c_write(gsbi_id, mode, p_tx_obj, stop_seq); |
| 384 | if (0) { |
| 385 | int i; |
| 386 | printk(BIOS_DEBUG, "i2c tx bus %d device %2.2x:", |
| 387 | gsbi_id, p_tx_obj->p.iic.addr); |
| 388 | for (i = 0; i < p_tx_obj->p.iic.data_len; i++) |
| 389 | printk(BIOS_DEBUG, " %2.2x", p_tx_obj->p.iic.data[i]); |
| 390 | printk(BIOS_DEBUG, "\n"); |
| 391 | } |
| 392 | |
| 393 | return ret; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 394 | } |
| 395 | |
| 396 | qup_return_t qup_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj, |
| 397 | uint8_t stop_seq) |
| 398 | { |
| 399 | qup_return_t ret = QUP_ERR_UNDEFINED; |
| 400 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 401 | if (p_tx_obj->protocol == ((read32(QUP_ADDR(gsbi_id, QUP_CONFIG)) >> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 402 | QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) { |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 403 | switch (p_tx_obj->protocol) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 404 | case QUP_MINICORE_I2C_MASTER: |
| 405 | ret = qup_i2c_send_data(gsbi_id, p_tx_obj, stop_seq); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 406 | break; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 407 | default: |
| 408 | ret = QUP_ERR_UNSUPPORTED; |
| 409 | } |
| 410 | } |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 411 | |
| 412 | return ret; |
| 413 | } |
| 414 | |
| 415 | static qup_return_t qup_i2c_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj) |
| 416 | { |
| 417 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 418 | uint8_t mode = (read32(QUP_ADDR(gsbi_id, QUP_IO_MODES)) >> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 419 | QUP_INPUT_MODE_SHFT) & QUP_MODE_MASK; |
| 420 | |
| 421 | ret = qup_i2c_read(gsbi_id, mode, p_rx_obj); |
| 422 | if (0) { |
| 423 | int i; |
| 424 | printk(BIOS_DEBUG, "i2c rxed on bus %d device %2.2x:", |
| 425 | gsbi_id, p_rx_obj->p.iic.addr); |
| 426 | for (i = 0; i < p_rx_obj->p.iic.data_len; i++) |
| 427 | printk(BIOS_DEBUG, " %2.2x", p_rx_obj->p.iic.data[i]); |
| 428 | printk(BIOS_DEBUG, "\n"); |
| 429 | } |
| 430 | |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 431 | return ret; |
| 432 | } |
| 433 | |
| 434 | qup_return_t qup_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj) |
| 435 | { |
| 436 | qup_return_t ret = QUP_ERR_UNDEFINED; |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 437 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 438 | if (p_rx_obj->protocol == ((read32(QUP_ADDR(gsbi_id, QUP_CONFIG)) >> |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 439 | QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) { |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 440 | switch (p_rx_obj->protocol) { |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 441 | case QUP_MINICORE_I2C_MASTER: |
| 442 | ret = qup_i2c_recv_data(gsbi_id, p_rx_obj); |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 443 | break; |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 444 | default: |
| 445 | ret = QUP_ERR_UNSUPPORTED; |
| 446 | } |
| 447 | } |
Vadim Bendebury | 6fe4e5e | 2014-12-06 10:44:58 -0800 | [diff] [blame] | 448 | |
Daisuke Nojiri | 0594914 | 2014-11-21 15:33:26 -0800 | [diff] [blame] | 449 | return ret; |
| 450 | } |