/* * This file is part of the coreboot project. * * Copyright (C) 2014 - 2015, 2019 The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <device/mmio.h> #include <console/console.h> #include <delay.h> #include <soc/gpio.h> #include <soc/iomap.h> #include <stdlib.h> #include <soc/qup.h> #define TIMEOUT_CNT 100 #define QUP_ADDR(id, reg) (blsp_qup_base(id) + (reg)) #define QUP_DEBUG 0 #define QUPDBG BIOS_ERR, "\t-> " #if QUP_DEBUG #define qup_write32(a, v) do { \ write32(a, v); \ printk(QUPDBG "%s(%d): write32(0x%p, 0x%x)\n", \ __func__, __LINE__, a, v); \ } while (0) #else #define qup_write32 write32 #endif struct i2c_clk_div_fld { u32 clk_freq_out; u8 fs_div; u8 ht_div; }; static struct i2c_clk_div_fld i2c_clk_div_map[] = { {100000, 124, 62}, {400000, 28, 14}, {1000000, 8, 5}, }; static void i2c_set_mstr_clk_ctl(unsigned int id, unsigned int hz) { int i; struct i2c_clk_div_fld *itr = i2c_clk_div_map; u8 fs_div = 0; u8 ht_div = 0; u32 mstr_clk_ctl; for (i = 0; i < ARRAY_SIZE(i2c_clk_div_map); ++i, ++itr) { if (hz == itr->clk_freq_out) { if (!fs_div) fs_div = itr->fs_div; if (!ht_div) ht_div = itr->ht_div; break; } } /* format values in clk-ctl cache */ mstr_clk_ctl = ((ht_div & 0xff) << 16) | (fs_div & 0xff); qup_write32(QUP_ADDR(id, QUP_I2C_MASTER_CLK_CTL), mstr_clk_ctl); } static qup_return_t qup_i2c_master_status(blsp_qup_id_t id) { uint32_t reg_val = read32(QUP_ADDR(id, QUP_I2C_MASTER_STATUS)); if (read32(QUP_ADDR(id, QUP_ERROR_FLAGS))) return QUP_ERR_XFER_FAIL; #if QUP_DEBUG printk(QUPDBG "%s: 0x%x\n", __func__, reg_val); #endif if (reg_val & QUP_I2C_INVALID_READ_ADDR) return QUP_ERR_I2C_INVALID_SLAVE_ADDR; if (reg_val & QUP_I2C_FAILED_MASK) return QUP_ERR_I2C_FAILED; if (reg_val & QUP_I2C_ARB_LOST) return QUP_ERR_I2C_ARB_LOST; if (reg_val & QUP_I2C_BUS_ERROR) return QUP_ERR_I2C_BUS_ERROR; if (reg_val & QUP_I2C_INVALID_WRITE) return QUP_ERR_I2C_INVALID_WRITE; if (reg_val & QUP_I2C_PACKET_NACK) return QUP_ERR_I2C_NACK; if (reg_val & QUP_I2C_INVALID_TAG) return QUP_ERR_I2C_INVALID_TAG; return QUP_SUCCESS; } static int check_bit_state(uint32_t *reg, int wait_for) { unsigned int count = TIMEOUT_CNT; while ((read32(reg) & (QUP_STATE_VALID_MASK | QUP_STATE_MASK)) != (QUP_STATE_VALID | wait_for)) { if (count == 0) return QUP_ERR_TIMEOUT; count--; } return QUP_SUCCESS; } /* * Check whether GSBIn_QUP State is valid */ static qup_return_t qup_wait_for_state(blsp_qup_id_t id, unsigned int wait_for) { return check_bit_state(QUP_ADDR(id, QUP_STATE), wait_for); } qup_return_t qup_reset_i2c_master_status(blsp_qup_id_t id) { /* * The I2C_STATUS is a status register. * Writing any value clears the status bits. */ qup_write32(QUP_ADDR(id, QUP_I2C_MASTER_STATUS), 0); return QUP_SUCCESS; } static qup_return_t qup_reset_master_status(blsp_qup_id_t id) { qup_write32(QUP_ADDR(id, QUP_ERROR_FLAGS), 0x3C); qup_write32(QUP_ADDR(id, QUP_ERROR_FLAGS_EN), 0x3C); qup_reset_i2c_master_status(id); return QUP_SUCCESS; } static qup_return_t qup_fifo_wait_for(blsp_qup_id_t id, uint32_t status) { qup_return_t ret = QUP_ERR_UNDEFINED; unsigned int count = TIMEOUT_CNT; while (!(read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status)) { ret = qup_i2c_master_status(id); if (ret) return ret; if (count == 0) return QUP_ERR_TIMEOUT; count--; } return QUP_SUCCESS; } static qup_return_t qup_fifo_wait_while(blsp_qup_id_t id, uint32_t status) { qup_return_t ret = QUP_ERR_UNDEFINED; unsigned int count = TIMEOUT_CNT; while (read32(QUP_ADDR(id, QUP_OPERATIONAL)) & status) { ret = qup_i2c_master_status(id); if (ret) return ret; if (count == 0) return QUP_ERR_TIMEOUT; count--; } return QUP_SUCCESS; } static inline uint32_t qup_i2c_create_output_tag(int stop, u8 data) { uint32_t tag; if (stop) tag = QUP_I2C_STOP_SEQ | QUP_I2C_DATA(data); else tag = QUP_I2C_DATA_SEQ | QUP_I2C_DATA(data); return tag; } static inline qup_return_t qup_i2c_write_fifo_flush(blsp_qup_id_t id) { qup_return_t ret = QUP_ERR_UNDEFINED; qup_write32(QUP_ADDR(id, QUP_OPERATIONAL), OUTPUT_SERVICE_FLAG); mdelay(4); /* TPM seems to need this */ ret = qup_fifo_wait_while(id, OUTPUT_FIFO_NOT_EMPTY); if (ret) return ret; ret = qup_i2c_master_status(id); if (ret) printk(BIOS_DEBUG, "%s: error\n", __func__); return ret; } static qup_return_t qup_i2c_write_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj, uint8_t stop_seq) { qup_return_t ret = QUP_ERR_UNDEFINED; uint8_t addr = p_tx_obj->p.iic.addr; uint8_t *data_ptr = p_tx_obj->p.iic.data; unsigned int data_len = p_tx_obj->p.iic.data_len; unsigned int idx = 0; uint32_t tag, *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO); qup_reset_master_status(id); qup_write32(QUP_ADDR(id, QUP_MX_OUTPUT_COUNT), data_len + 1); qup_set_state(id, QUP_STATE_RUN); /* * Since UNPACK enable is set in io mode register, populate 2 tags * for each fifo register. * * Create the first tag as follows, with the start tag and first byte * of the data to be written * +--------+--------+--------+--------+ * | STOP / | data | START | ADDR | * |DATA tag| byte | tag | << 1 | * +--------+--------+--------+--------+ * rest will be created in the following while loop. */ tag = qup_i2c_create_output_tag(data_len == 1 && stop_seq, data_ptr[idx]); tag = ((tag << 16) & 0xffff0000) | (QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)); data_len--; idx++; qup_write32(fifo, tag); while (data_len) { tag = qup_i2c_create_output_tag(data_len == 1 && stop_seq, data_ptr[idx]); data_len--; idx++; if (data_len) { tag |= qup_i2c_create_output_tag( data_len == 1 && stop_seq, data_ptr[idx]) << 16; data_len--; idx++; } qup_write32(fifo, tag); ret = qup_i2c_write_fifo_flush(id); if (ret) { printk(QUPDBG "%s: error\n", __func__); return ret; } } ret = qup_i2c_write_fifo_flush(id); qup_set_state(id, QUP_STATE_RESET); return ret; } static qup_return_t qup_i2c_write(blsp_qup_id_t id, uint8_t mode, qup_data_t *p_tx_obj, uint8_t stop_seq) { qup_return_t ret = QUP_ERR_UNDEFINED; switch (mode) { case QUP_MODE_FIFO: case QUP_MODE_BLOCK: ret = qup_i2c_write_fifo(id, p_tx_obj, stop_seq); break; default: ret = QUP_ERR_UNSUPPORTED; } if (ret) { qup_set_state(id, QUP_STATE_RESET); printk(QUPDBG "%s() failed (%d)\n", __func__, ret); } return ret; } static int qup_i2c_parse_tag(uint32_t data, uint8_t *data_ptr, uint32_t len) { int i, idx = 0; int max = (len > 2) ? 2 : len; for (i = 0; i < max; i++) { switch (QUP_I2C_MI_TAG(data)) { case QUP_I2C_MIDATA_SEQ: data_ptr[idx] = QUP_I2C_DATA(data); idx++; break; case QUP_I2C_MISTOP_SEQ: data_ptr[idx] = QUP_I2C_DATA(data); idx++; return idx; default: printk(QUPDBG "%s: Unexpected tag (0x%x)\n", __func__, QUP_I2C_MI_TAG(data)); return -1; } data = (data >> 16); } return idx; } static qup_return_t qup_i2c_read_fifo(blsp_qup_id_t id, qup_data_t *p_tx_obj) { qup_return_t ret = QUP_ERR_UNDEFINED; uint8_t addr = p_tx_obj->p.iic.addr; uint8_t *data_ptr = p_tx_obj->p.iic.data; unsigned int data_len = p_tx_obj->p.iic.data_len; unsigned int idx = 0; uint32_t *fifo = QUP_ADDR(id, QUP_OUTPUT_FIFO); qup_reset_master_status(id); qup_write32(QUP_ADDR(id, QUP_IO_MODES), QUP_UNPACK_EN | QUP_PACK_EN | ((QUP_MODE_BLOCK & QUP_MODE_MASK) << QUP_OUTPUT_MODE_SHFT) | ((QUP_MODE_BLOCK & QUP_MODE_MASK) << QUP_INPUT_MODE_SHFT)); qup_write32(QUP_ADDR(id, QUP_MX_INPUT_COUNT), data_len); qup_set_state(id, QUP_STATE_RUN); qup_write32(fifo, (QUP_I2C_START_SEQ | (QUP_I2C_ADDR(addr) | QUP_I2C_SLAVE_READ)) | ((QUP_I2C_RECV_SEQ | data_len) << 16)); ret = qup_i2c_write_fifo_flush(id); if (ret) { printk(QUPDBG "%s: OUTPUT_FIFO_NOT_EMPTY\n", __func__); return ret; } ret = qup_fifo_wait_for(id, INPUT_SERVICE_FLAG); if (ret) { printk(QUPDBG "%s: INPUT_SERVICE_FLAG\n", __func__); return ret; } fifo = QUP_ADDR(id, QUP_INPUT_FIFO); while (data_len) { uint32_t data; int count; data = read32(fifo); mdelay(1); count = qup_i2c_parse_tag(data, data_ptr + idx, data_len); if (count < 0) { printk(QUPDBG "%s: Cannot parse tag 0x%x\n", __func__, data); qup_set_state(id, QUP_STATE_PAUSE); return QUP_ERR_I2C_INVALID_TAG; } idx += count; data_len -= count; qup_write32(QUP_ADDR(id, QUP_OPERATIONAL), INPUT_SERVICE_FLAG); } p_tx_obj->p.iic.data_len = idx; qup_write32(QUP_ADDR(id, QUP_MX_READ_COUNT), 0); qup_set_state(id, QUP_STATE_RESET); return QUP_SUCCESS; } static qup_return_t qup_i2c_read(blsp_qup_id_t id, uint8_t mode, qup_data_t *p_tx_obj) { qup_return_t ret = QUP_ERR_UNDEFINED; qup_set_state(id, QUP_STATE_RESET); switch (mode) { case QUP_MODE_FIFO: case QUP_MODE_BLOCK: ret = qup_i2c_read_fifo(id, p_tx_obj); break; default: ret = QUP_ERR_UNSUPPORTED; } if (ret) { qup_set_state(id, QUP_STATE_RESET); printk(QUPDBG "%s() failed (%d)\n", __func__, ret); } return ret; } qup_return_t qup_init(blsp_qup_id_t id, const qup_config_t *config_ptr) { qup_return_t ret = QUP_ERR_UNDEFINED; uint32_t reg_val; /* Reset the QUP core.*/ qup_write32(QUP_ADDR(id, QUP_SW_RESET), 0x1); /* Wait till the reset takes effect */ ret = qup_wait_for_state(id, QUP_STATE_RESET); if (ret) goto bailout; /* Reset the config */ qup_write32(QUP_ADDR(id, QUP_CONFIG), 0); /* Program the config register */ /* Set N value */ reg_val = 0x0F; /* Set protocol */ switch (config_ptr->protocol) { case QUP_MINICORE_I2C_MASTER: reg_val |= ((config_ptr->protocol & QUP_MINI_CORE_PROTO_MASK) << QUP_MINI_CORE_PROTO_SHFT); break; default: ret = QUP_ERR_UNSUPPORTED; goto bailout; } reg_val |= QUP_APP_CLK_ON_EN | QUP_CORE_CLK_ON_EN; qup_write32(QUP_ADDR(id, QUP_CONFIG), reg_val); /* Choose version 1 tag */ qup_write32(QUP_ADDR(id, QUP_I2C_MASTER_CONFIG), 0); /* Reset i2c clk cntl register */ qup_write32(QUP_ADDR(id, QUP_I2C_MASTER_CLK_CTL), 0); /* Set QUP IO Mode */ switch (config_ptr->mode) { case QUP_MODE_FIFO: case QUP_MODE_BLOCK: reg_val = QUP_UNPACK_EN | QUP_PACK_EN | ((config_ptr->mode & QUP_MODE_MASK) << QUP_OUTPUT_MODE_SHFT) | ((config_ptr->mode & QUP_MODE_MASK) << QUP_INPUT_MODE_SHFT); break; default: ret = QUP_ERR_UNSUPPORTED; goto bailout; } qup_write32(QUP_ADDR(id, QUP_IO_MODES), reg_val); /*Set i2c clk cntl*/ i2c_set_mstr_clk_ctl(id, 400000); qup_set_state(id, QUP_STATE_RESET); bailout: if (ret) printk(QUPDBG "failed to init qup (%d)\n", ret); return ret; } qup_return_t qup_set_state(blsp_qup_id_t id, uint32_t state) { qup_return_t ret = QUP_ERR_UNDEFINED; unsigned int curr_state = read32(QUP_ADDR(id, QUP_STATE)); if ((state <= QUP_STATE_PAUSE) && (curr_state & QUP_STATE_VALID_MASK)) { /* * For PAUSE_STATE to RESET_STATE transition, * two writes of 10[binary]) are required for the * transition to complete. */ if (curr_state == QUP_STATE_PAUSE && state == QUP_STATE_RESET) { qup_write32(QUP_ADDR(id, QUP_STATE), 0x2); qup_write32(QUP_ADDR(id, QUP_STATE), 0x2); } else { qup_write32(QUP_ADDR(id, QUP_STATE), state); } ret = qup_wait_for_state(id, state); } return ret; } static qup_return_t qup_i2c_send_data(blsp_qup_id_t id, qup_data_t *p_tx_obj, uint8_t stop_seq) { qup_return_t ret = QUP_ERR_UNDEFINED; uint8_t mode = (read32(QUP_ADDR(id, QUP_IO_MODES)) >> QUP_OUTPUT_MODE_SHFT) & QUP_MODE_MASK; ret = qup_i2c_write(id, mode, p_tx_obj, stop_seq); if (QUP_DEBUG) { int i; printk(BIOS_DEBUG, "i2c tx bus %d device %2.2x:", id, p_tx_obj->p.iic.addr); for (i = 0; i < p_tx_obj->p.iic.data_len; i++) printk(BIOS_DEBUG, " %2.2x", p_tx_obj->p.iic.data[i]); printk(BIOS_DEBUG, "\n"); } return ret; } qup_return_t qup_send_data(blsp_qup_id_t id, qup_data_t *p_tx_obj, uint8_t stop_seq) { qup_return_t ret = QUP_ERR_UNDEFINED; if (p_tx_obj->protocol == ((read32(QUP_ADDR(id, QUP_CONFIG)) >> QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) { switch (p_tx_obj->protocol) { case QUP_MINICORE_I2C_MASTER: ret = qup_i2c_send_data(id, p_tx_obj, stop_seq); break; default: ret = QUP_ERR_UNSUPPORTED; } } return ret; } static qup_return_t qup_i2c_recv_data(blsp_qup_id_t id, qup_data_t *p_rx_obj) { qup_return_t ret = QUP_ERR_UNDEFINED; uint8_t mode = (read32(QUP_ADDR(id, QUP_IO_MODES)) >> QUP_INPUT_MODE_SHFT) & QUP_MODE_MASK; ret = qup_i2c_read(id, mode, p_rx_obj); if (QUP_DEBUG) { int i; printk(BIOS_DEBUG, "i2c rxed on bus %d device %2.2x:", id, p_rx_obj->p.iic.addr); for (i = 0; i < p_rx_obj->p.iic.data_len; i++) printk(BIOS_DEBUG, " %2.2x", p_rx_obj->p.iic.data[i]); printk(BIOS_DEBUG, "\n"); } return ret; } qup_return_t qup_recv_data(blsp_qup_id_t id, qup_data_t *p_rx_obj) { qup_return_t ret = QUP_ERR_UNDEFINED; if (p_rx_obj->protocol == ((read32(QUP_ADDR(id, QUP_CONFIG)) >> QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) { switch (p_rx_obj->protocol) { case QUP_MINICORE_I2C_MASTER: ret = qup_i2c_recv_data(id, p_rx_obj); break; default: ret = QUP_ERR_UNSUPPORTED; } } return ret; }