| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| #include <stdbool.h> |
| #include <assert.h> |
| |
| #include <platsupport/io.h> |
| #include <platsupport/i2c.h> |
| #include <platsupport/plat/i2c.h> |
| |
| #include <utils/arith.h> |
| #include <utils/fence.h> |
| #include <utils/attribute.h> |
| #include "../../services.h" |
| |
| /** @file TK1 I2C driver. |
| * |
| * This driver only supports master mode. It doesn't support acting as a slave. |
| * It also doesn't support multi-master (masters other than itself connected |
| * to the same bus). |
| * |
| * We also don't support UF mode (ultra-fast mode). |
| * |
| * To use, just follow the API in `arch_include/arm/platsupport/i2c.h`. |
| * |
| * PREREQUISITES: |
| * This driver currently assumes that the MMIO registers it accesses are mapped |
| * as strongly ordered and uncached. The driver makes no attempts whatsoever at |
| * managing the write buffer or managing ordering of reads and writes. |
| * |
| * KNOWN BUGS: |
| * * Attempting to read or write more than 32bytes will cause the driver to |
| * freeze. Specifically, the first 32 or so will be transmitted, but then |
| * the IRQ to tell the controller that the TX FIFO is empty will fail to come |
| * in, so the driver will just sit there waiting for the next IRQ. |
| */ |
| |
| /* Notes from TK1 TRM: |
| * Section 33.0: |
| * "The host interface runs on the APB clock (pclk). The APB clock is derived |
| * from the system clock and can be 1.0, 1/2, 1/3 or 1/4 times the system clock |
| * (sclk)" |
| * "The I2C interface controller clock runs on a frequency up to 136 MHz." |
| * "The 136 MHz clock is derived from PLLP. Even though you can mux between |
| * PLLP, PLLC, PLLM and OSC clocks, PLLP is always selected and used in normal |
| * operation" |
| */ |
| |
| /* Notes from I2C specification rev 6: |
| * |
| * Section 5.1, "Fast mode": |
| * "If the power supply to a Fast-mode device is switched off, the SDA and SCL |
| * I/O pins must be floating so that they do not obstruct the bus lines." |
| * |
| * I.e, mux them off and leave them floating when they are not switched on. |
| */ |
| #define CHECK_FOR_SDA_LOW |
| |
| #define PREFIX "I2C %p: " |
| |
| #define TK1I2C_DATA_MAX_NBYTES (4096) |
| #define TK1I2C_NBYTES_PER_FIFO_WORD (4) |
| |
| #define TK1I2C_SLAVE_FLAGS_XFER_IN_PROGRESS_BIT (BIT(1)) |
| |
| #define TK1I2C_BUS_CLEAR_STATUS_SDA_NORMAL_BIT (BIT(0)) |
| |
| #define TK1I2C_BUS_CLEAR_CONFIG_IN_PROGRESS_BIT (BIT(0)) |
| #define TK1I2C_BUS_CLEAR_CONFIG_TERMINATE_IMMEDIATE_BIT (BIT(1)) |
| #define TK1I2C_BUS_CLEAR_CONFIG_SEND_STOP_BIT (BIT(2)) |
| #define TK1I2C_BUS_CLEAR_CONFIG_THRESHOLD_SHIFT (16) |
| #define TK1I2C_BUS_CLEAR_CONFIG_THRESHOLD_MASK (0xFF) |
| |
| #define TK1I2C_INTSTATUS_MMODE_RX_FIFO_DATA_REQ_BIT (BIT(0)) |
| #define TK1I2C_INTSTATUS_MMODE_TX_FIFO_DATA_REQ_BIT (BIT(1)) |
| #define TK1I2C_INTSTATUS_MMODE_ARBITRATION_LOST_BIT (BIT(2)) |
| #define TK1I2C_INTSTATUS_MMODE_NO_ACK_BIT (BIT(3)) |
| #define TK1I2C_INTSTATUS_MMODE_RX_FIFO_UNR_BIT (BIT(4)) |
| #define TK1I2C_INTSTATUS_MMODE_TX_FIFO_OVF_BIT (BIT(5)) |
| #define TK1I2C_INTSTATUS_MMODE_ALL_PACKETS_XFER_COMPLETE_BIT (BIT(6)) |
| #define TK1I2C_INTSTATUS_MMODE_PACKET_XFER_COMPLETE_BIT (BIT(7)) |
| |
| #define TK1I2C_INTSTATUS_SMODE_RX_FIFO_DATA_REQ_BIT (BIT(16)) |
| #define TK1I2C_INTSTATUS_SMODE_TX_FIFO_DATA_REQ_BIT (BIT(17)) |
| #define TK1I2C_INTSTATUS_SMODE_RX_FIFO_UNR_BIT (BIT(20)) |
| #define TK1I2C_INTSTATUS_SMODE_TX_FIFO_OVF_BIT (BIT(21)) |
| #define TK1I2C_INTSTATUS_SMODE_PACKET_XFER_COMPLETE_BIT (BIT(22)) |
| #define TK1I2C_INTSTATUS_SMODE_RX_BUFFER_FULL_BIT (BIT(23)) |
| #define TK1I2C_INTSTATUS_SMODE_TX_BUFFER_REQ_BIT (BIT(24)) |
| #define TK1I2C_INTSTATUS_SMODE_PACKET_XFER_ERROR_BIT (BIT(25)) |
| #define TK1I2C_INTSTATUS_SMODE_SWITCHED_WRITE2READ_BIT (BIT(26)) |
| #define TK1I2C_INTSTATUS_SMODE_SWITCHED_READ2WRITE_BIT (BIT(27)) |
| #define TK1I2C_INTSTATUS_SMODE_ACK_WITHHELD_BIT (BIT(28)) |
| |
| #define TK1I2C_INTMASK_MMODE_RX_FIFO_DATA_REQ_BIT (BIT(0)) |
| #define TK1I2C_INTMASK_MMODE_TX_FIFO_DATA_REQ_BIT (BIT(1)) |
| #define TK1I2C_INTMASK_MMODE_ARBITRATION_LOST_BIT (BIT(2)) |
| #define TK1I2C_INTMASK_MMODE_NO_ACK_BIT (BIT(3)) |
| #define TK1I2C_INTMASK_MMODE_RX_FIFO_UNR_BIT (BIT(4)) |
| #define TK1I2C_INTMASK_MMODE_TX_FIFO_OVF_BIT (BIT(5)) |
| #define TK1I2C_INTMASK_MMODE_ALL_PACKETS_XFER_COMPLETE_BIT (BIT(6)) |
| #define TK1I2C_INTMASK_MMODE_PACKET_XFER_COMPLETE_BIT (BIT(7)) |
| #define TK1I2C_INTMASK_MMODE_BUS_CLEAR_DONE_BIT (BIT(11)) |
| |
| #define TK1I2C_INTMASK_SMODE_RX_FIFO_DATA_REQ_BIT (BIT(16)) |
| #define TK1I2C_INTMASK_SMODE_TX_FIFO_DATA_REQ_BIT (BIT(17)) |
| #define TK1I2C_INTMASK_SMODE_RX_FIFO_UNR_BIT (BIT(20)) |
| #define TK1I2C_INTMASK_SMODE_TX_FIFO_OVF_BIT (BIT(21)) |
| #define TK1I2C_INTMASK_SMODE_PACKET_XFER_COMPLETE_BIT (BIT(22)) |
| #define TK1I2C_INTMASK_SMODE_RX_BUFFER_FULL_BIT (BIT(23)) |
| #define TK1I2C_INTMASK_SMODE_TX_BUFFER_REQ_BIT (BIT(24)) |
| #define TK1I2C_INTMASK_SMODE_PACKET_XFER_ERROR_BIT (BIT(25)) |
| #define TK1I2C_INTMASK_SMODE_SWITCHED_WRITE2READ_BIT (BIT(26)) |
| #define TK1I2C_INTMASK_SMODE_SWITCHED_READ2WRITE_BIT (BIT(27)) |
| #define TK1I2C_INTMASK_SMODE_ACK_WITHHELD_BIT (BIT(28)) |
| |
| #define TK1I2C_CONFIG_LOAD_MASTER_BIT (BIT(0)) |
| #define TK1I2C_CONFIG_LOAD_SLAVE_BIT (BIT(1)) |
| #define TK1I2C_CONFIG_LOAD_TIMEOUT_BIT (BIT(2)) |
| #define COMMIT_MASTER_BIT (TK1I2C_CONFIG_LOAD_MASTER_BIT) |
| #define COMMIT_SLAVE_BIT (TK1I2C_CONFIG_LOAD_SLAVE_BIT) |
| #define COMMIT_TIMEOUT_BIT (TK1I2C_CONFIG_LOAD_TIMEOUT_BIT) |
| |
| #define TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_SHIFT (5) |
| #define TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_MASK (0x7) |
| #define TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_SHIFT (2) |
| #define TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_MASK (0x7) |
| |
| #define TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_SHIFT (13) |
| #define TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_MASK (0x7) |
| #define TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_SHIFT (10) |
| #define TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_MASK (0x7) |
| |
| #define TK1I2C_FIFO_CONTROL_MMODE_FLUSH_TX_BIT (BIT(1)) |
| #define TK1I2C_FIFO_CONTROL_MMODE_FLUSH_RX_BIT (BIT(0)) |
| #define TK1I2C_FIFO_CONTROL_SMODE_FLUSH_TX_BIT (BIT(9)) |
| #define TK1I2C_FIFO_CONTROL_SMODE_FLUSH_RX_BIT (BIT(8)) |
| |
| #define TK1I2C_CNFG_PACKET_MODE_EN_BIT (BIT(10)) |
| #define TK1I2C_CNFG_ADDRESS_10BIT_EN_BIT (BIT(0)) |
| #define TK1I2C_CNFG_2_SLAVE_BIT (BIT(4)) |
| #define TK1I2C_CNFG_SEND_START_BIT (BIT(5)) |
| #define TK1I2C_CNFG_NOACK_BIT (BIT(8)) |
| #define TK1I2C_CNFG_BEGIN_XFER_BIT (BIT(9)) |
| #define TK1I2C_CNFG_XFER_LENGTH_SHIFT (1) |
| #define TK1I2C_CNFG_XFER_LENGTH_MASK (0x7) |
| #define TK1I2C_CNFG_DEBOUNCE_COUNT_SHIFT (12) |
| #define TK1I2C_CNFG_DEBOUNCE_COUNT_MASK (0x7) |
| |
| #define TK1I2C_SL_CNFG_SLAVE_EN_BIT (BIT(3)) |
| #define TK1I2C_SL_CNFG_PACKET_MODE_EN_BIT (BIT(4)) |
| #define TK1I2C_SL_CNFG_FIFO_XFER_EN_BIT (BIT(20)) |
| |
| #define TK1I2C_FIFO_STATUS_MMODE_TX_EMPTY_SHIFT (4) |
| #define TK1I2C_FIFO_STATUS_MMODE_TX_EMPTY_MASK (0xF) |
| #define TK1I2C_FIFO_STATUS_MMODE_RX_FULL_SHIFT (0) |
| #define TK1I2C_FIFO_STATUS_MMODE_RX_FULL_MASK (0xF) |
| #define TK1I2C_FIFO_STATUS_SMODE_TX_EMPTY_SHIFT (20) |
| #define TK1I2C_FIFO_STATUS_SMODE_TX_EMPTY_MASK (0xF) |
| #define TK1I2C_FIFO_STATUS_SMODE_RX_FULL_SHIFT (16) |
| #define TK1I2C_FIFO_STATUS_SMODE_RX_FULL_MASK (0xF) |
| |
| #define TK1I2C_FIFO_DEPTH (8) |
| |
| /* These constants are used by tk1_i2c_prepare_mmode_xfer_headers(). |
| * They are the values for the bit positions in the header words used by the |
| * controller. |
| */ |
| #define TK1I2C_TXPKT0_PKTTYPE_SHIFT (0) |
| #define TK1I2C_TXPKT0_PKTTYPE_MASK (0x7) |
| |
| #define TK1I2C_TXPKT0_PROTOCOL_SHIFT (4) |
| #define TK1I2C_TXPKT0_PROTOCOL_MASK (0xF) |
| #define TK1I2C_TXPKT0_PROTOCOL_I2C (1) |
| |
| #define TK1I2C_TXPKT0_CONTROLLER_ID_SHIFT (12) |
| #define TK1I2C_TXPKT0_CONTROLLER_ID_MASK (0xF) |
| |
| #define TK1I2C_TXPKT0_PKTID_SHIFT (16) |
| #define TK1I2C_TXPKT0_PKTID_MASK (0xFF) |
| |
| #define TK1I2C_TXPKT0_PROTHDR_SIZE_SHIFT (28) |
| #define TK1I2C_TXPKT0_PROTHDR_SIZE_MASK (0x3) |
| |
| #define TK1I2C_TXPKT1_PAYLOAD_SIZE_SHIFT (0) |
| #define TK1I2C_TXPKT1_PAYLOAD_SIZE_MASK (0xFFF) |
| |
| #define TK1I2C_I2CPKT_SLAVE_ADDR_10BIT_SHIFT (0) |
| #define TK1I2C_I2CPKT_SLAVE_ADDR_10BIT_MASK (0x3FF) |
| |
| #define TK1I2C_I2CPKT_SLAVE_ADDR_7BIT_SHIFT (1) |
| #define TK1I2C_I2CPKT_SLAVE_ADDR_7BIT_MASK (0x7F) |
| |
| #define TK1I2C_I2CPKT_HS_MASTER_ADDR_SHIFT (12) |
| #define TK1I2C_I2CPKT_HS_MASTER_ADDR_MASK (0x7) |
| |
| #define TK1I2C_I2CPKT_CONTINUE_XFER_BIT (BIT(15)) |
| #define TK1I2C_I2CPKT_SEND_REPEAT_START_NOT_STOP_BIT (BIT(16)) |
| #define TK1I2C_I2CPKT_IRQ_ON_COMPLETION_BIT (BIT(17)) |
| #define TK1I2C_I2CPKT_10_BIT_ADDRESS_BIT (BIT(18)) |
| #define TK1I2C_I2CPKT_READ_XFER_BIT (BIT(19)) |
| #define TK1I2C_I2CPKT_SEND_START_BYTE_BIT (BIT(20)) |
| #define TK1I2C_I2CPKT_CONTINUE_ON_NACK_BIT (BIT(21)) |
| #define TK1I2C_I2CPKT_HS_MODE_BIT (BIT(22)) |
| #define TK1I2C_I2CPKT_RESPONSE_PKT_ENABLE_BIT (BIT(24)) |
| #define TK1I2C_I2CPKT_RESPONSE_PKT_FREQ_BIT (BIT(25)) |
| |
| /* These next few #defines pertain to tk1_i2c_set_speed() and its helper |
| * functions tk1_i2c_calc_divisor_value_for() and |
| * tk1_i2c_calc_baud_resulting_from(). |
| */ |
| #define TK1_CAR_PLLP_INPUT_FREQ_HZ (408000000) |
| #define TK1_CAR_PLLP_DIVISOR (1) |
| |
| #define TK1I2C_IFACE_TIMING0_TLOW_SHIFT (0) |
| #define TK1I2C_IFACE_TIMING0_TLOW_MASK (0x3F) |
| #define TK1I2C_IFACE_TIMING0_THIGH_SHIFT (8) |
| #define TK1I2C_IFACE_TIMING0_THIGH_MASK (0x3F) |
| |
| #define TK1I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT (16) |
| #define TK1I2C_CLK_DIVISOR_STD_FAST_MODE_MASK (0xFFFF) |
| |
| #define TK1I2C_HS_IFACE_TIMING0_TLOW_SHIFT (0) |
| #define TK1I2C_HS_IFACE_TIMING0_TLOW_MASK (0x3F) |
| #define TK1I2C_HS_IFACE_TIMING0_THIGH_SHIFT (8) |
| #define TK1I2C_HS_IFACE_TIMING0_THIGH_MASK (0x3F) |
| |
| #define TK1I2C_CLK_DIVISOR_HS_MODE_SHIFT (0) |
| #define TK1I2C_CLK_DIVISOR_HS_MODE_MASK (0xFFFF) |
| |
| enum i2c_xfer_mode { |
| I2C_MODE_MASTER, |
| I2C_MODE_SLAVE |
| }; |
| |
| typedef volatile struct tk1_i2c_regs_ { |
| uint32_t cnfg; |
| uint32_t cmd_addr0, cmd_addr1; |
| uint32_t cmd_data0, cmd_data1; |
| PAD_STRUCT_BETWEEN(0x10, 0x1c, uint32_t); |
| uint32_t status; |
| PAD_STRUCT_BETWEEN(0x1c, 0x20, uint32_t); |
| uint32_t sl_cnfg; |
| uint32_t sl_rcvd, sl_status; |
| uint32_t sl_addr0, sl_addr1; |
| uint32_t tlow_sext; |
| PAD_STRUCT_BETWEEN(0x34, 0x3c, uint32_t); |
| uint32_t sl_delay_count; |
| uint32_t sl_interrupt_mask, sl_interrupt_source, sl_interrupt_set; |
| PAD_STRUCT_BETWEEN(0x48, 0x50, uint32_t); |
| uint32_t tx_packet_fifo, rx_fifo, packet_transfer_status; |
| uint32_t fifo_control, fifo_status; |
| uint32_t interrupt_mask, interrupt_status; |
| uint32_t clk_divisor; |
| uint32_t interrupt_source, interrupt_set; |
| uint32_t sl_tx_packet_fifo, sl_rx_fifo, sl_packet_status; |
| uint32_t bus_clear_config, bus_clear_status; |
| uint32_t config_load; |
| PAD_STRUCT_BETWEEN(0x8c, 0x94, uint32_t); |
| uint32_t interface_timing0, interface_timing1; |
| uint32_t hs_interface_timing0, hs_interface_timing1; |
| } tk1_i2c_regs_t; |
| |
| typedef struct tk1_i2c_state_ { |
| tk1_i2c_regs_t *regs; |
| int controller_id; |
| |
| struct { |
| int hsmode_master_address; |
| int slave_id; |
| bool is_write; |
| uint8_t *buff; |
| size_t nbytes; |
| ssize_t xfer_cursor; |
| } master; |
| struct { |
| int self_id; |
| bool is_write; |
| uint8_t *buff; |
| size_t nbytes; |
| ssize_t xfer_cursor; |
| } slave; |
| } tk1_i2c_state_t; |
| |
| static inline tk1_i2c_state_t * |
| tk1_i2c_get_state(i2c_bus_t *ib) |
| { |
| assert(ib != NULL); |
| assert(ib->priv != NULL); |
| return (tk1_i2c_state_t *)ib->priv; |
| } |
| |
| static inline tk1_i2c_regs_t * |
| tk1_i2c_get_priv(i2c_bus_t *ib) |
| { |
| assert(tk1_i2c_get_state(ib)->regs != NULL); |
| return (tk1_i2c_regs_t *)tk1_i2c_get_state(ib)->regs; |
| } |
| |
| typedef struct tk1_i2c_pktheaders_ { |
| uint32_t io0; |
| uint32_t io1; |
| uint32_t i2c; |
| } tk1_i2c_pktheaders_t; |
| |
| static void |
| tk1_i2c_set_packet_mode(i2c_bus_t *ib, bool enabled) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| if (enabled) { |
| r->cnfg |= TK1I2C_CNFG_PACKET_MODE_EN_BIT; |
| } else { |
| r->cnfg &= ~TK1I2C_CNFG_PACKET_MODE_EN_BIT; |
| } |
| } |
| |
| static void |
| tk1_i2c_config_commit(i2c_bus_t *ib, uint8_t which_configs) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| uint32_t val = 0; |
| |
| if (which_configs & COMMIT_MASTER_BIT) { |
| val |= TK1I2C_CONFIG_LOAD_MASTER_BIT; |
| } |
| if (which_configs & COMMIT_SLAVE_BIT) { |
| val |= TK1I2C_CONFIG_LOAD_SLAVE_BIT; |
| } |
| if (which_configs & COMMIT_TIMEOUT_BIT) { |
| val |= TK1I2C_CONFIG_LOAD_TIMEOUT_BIT; |
| } |
| |
| r->config_load = val; |
| |
| /* The commit bits are hardware auto-cleared when the hardware is done |
| * performing the operation. |
| */ |
| while (r->config_load != 0) { |
| /* Do nothing */ |
| } |
| } |
| |
| static void |
| tk1_i2c_flush_fifos(i2c_bus_t *ib, enum i2c_xfer_mode mode) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| uint32_t mask; |
| |
| if (mode == I2C_MODE_MASTER) { |
| mask = TK1I2C_FIFO_CONTROL_MMODE_FLUSH_RX_BIT |
| | TK1I2C_FIFO_CONTROL_MMODE_FLUSH_TX_BIT; |
| } else { |
| mask = TK1I2C_FIFO_CONTROL_SMODE_FLUSH_RX_BIT |
| | TK1I2C_FIFO_CONTROL_SMODE_FLUSH_TX_BIT; |
| } |
| |
| r->fifo_control |= mask; |
| while ((r->fifo_control & mask) != 0) { |
| /* Poll bits waiting for them to be auto-cleared by hardware. */ |
| } |
| } |
| |
| static void |
| tk1_i2c_set_mode(i2c_bus_t *ib, enum i2c_xfer_mode mode) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| if (mode == I2C_MODE_SLAVE) { |
| r->sl_cnfg |= TK1I2C_SL_CNFG_SLAVE_EN_BIT; |
| } else { |
| r->sl_cnfg &= ~TK1I2C_SL_CNFG_SLAVE_EN_BIT; |
| } |
| |
| tk1_i2c_config_commit(ib, COMMIT_MASTER_BIT); |
| } |
| |
| static int |
| tk1_i2c_handle_arbitration_loss(i2c_bus_t *ib) |
| { |
| UNUSED tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| /* I2C Reference rev6, 3.1.8: "Arbitration": |
| * "If a master also incorporates a slave function and it loses arbitration |
| * during the addressing stage, it is possible that the winning master is |
| * trying to address it. The losing master must therefore switch over |
| * immediately to its slave mode" |
| */ |
| tk1_i2c_set_mode(ib, I2C_MODE_SLAVE); |
| return 0; |
| } |
| |
| static int |
| tk1_i2c_handle_nack(i2c_bus_t *ib) |
| { |
| /* I2C Reference rev6, 33.4.4: "Error Handling": |
| * "When an error occurs due to NACK, a stop condition is put on the bus |
| * and an interrupt is generated. |
| * The status register is updated with the current error packet ID at which |
| * the error occured. |
| * Software should reset the controller. |
| * In case of a DMA transfer, DMA needs to be restarted." |
| * |
| * ^ This handling makes sense, but only if we are the master-sender. |
| * If we are the slave-sender, we should not be sending a STOP signal. |
| * |
| * "In packet mode ... if an error occurs during packet transfer then the |
| * controller should be reset ... and the entire packet needs to be resent |
| * since there is no accurate way of knowing how many bytes made it to the |
| * I2C slave." |
| * |
| * I believe that the controller which must be reset is the receiver and |
| * not the transmitter, in this case, because the receiver may be held in |
| * limbo expecting a resend. There is no reason to think the transmitter |
| * needs to be reset. |
| */ |
| UNUSED tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| return 0; |
| } |
| |
| static bool |
| tk1_i2c_bus_is_locked_up(i2c_bus_t *ib) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| return !(r->bus_clear_status & TK1I2C_BUS_CLEAR_STATUS_SDA_NORMAL_BIT); |
| } |
| |
| /** Forces CLK pulses down the CLK line to trick a slave device that is holding |
| * SDA low, to release SDA eventually. The slave is expected to release SDA |
| * within 9 CLK pulses. |
| * |
| * Reasons for an arbitration failure for the SDA line: |
| * * Line isn't muxed |
| * * High-Z is asserted |
| * * Actual bus lockup caused by slave holding SDA low. |
| * |
| * @param[in] send_stop_afterward Whether or not to automatically append a |
| * STOP signal after the bus clear operation. |
| * @return true if the bus clear operation was successful. |
| */ |
| static bool |
| tk1_i2c_bus_clear(i2c_bus_t *ib, bool send_stop_afterward) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| ZF_LOGW(PREFIX"Performing bus clear operation.", r); |
| |
| while (r->bus_clear_config & TK1I2C_BUS_CLEAR_CONFIG_IN_PROGRESS_BIT) { |
| /* Just in case the bit was set before we began. */ |
| } |
| |
| r->bus_clear_config = 9 << TK1I2C_BUS_CLEAR_CONFIG_THRESHOLD_SHIFT |
| | (send_stop_afterward |
| ? TK1I2C_BUS_CLEAR_CONFIG_SEND_STOP_BIT : 0); |
| |
| tk1_i2c_config_commit(ib, COMMIT_MASTER_BIT); |
| |
| r->bus_clear_config |= TK1I2C_BUS_CLEAR_CONFIG_IN_PROGRESS_BIT; |
| while (r->bus_clear_config & TK1I2C_BUS_CLEAR_CONFIG_IN_PROGRESS_BIT) { |
| /* Do nothing until the device clears the bit to signal that it's done. |
| */ |
| }; |
| |
| return !tk1_i2c_bus_is_locked_up(ib); |
| } |
| |
| static const uint32_t i2c_speed_freqs[] = { |
| [I2C_SLAVE_SPEED_STANDARD] = 100000, |
| [I2C_SLAVE_SPEED_FAST] = 400000, |
| [I2C_SLAVE_SPEED_FASTPLUS] = 1000000, |
| [I2C_SLAVE_SPEED_HIGHSPEED] = 3400000 |
| }; |
| |
| static const char *i2c_speed_names[] = { |
| [I2C_SLAVE_SPEED_STANDARD] = "standard", |
| [I2C_SLAVE_SPEED_FAST] = "fast", |
| [I2C_SLAVE_SPEED_FASTPLUS] = "fast+", |
| [I2C_SLAVE_SPEED_HIGHSPEED] = "high-speed" |
| }; |
| |
| static int32_t |
| tk1_i2c_calc_divisor_value_for(i2c_bus_t *bus, |
| enum i2c_slave_speed speed, |
| uint32_t variant_constant) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(bus); |
| uint32_t tlow, thigh; |
| uint32_t target_scl_freq_hz; |
| |
| assert(variant_constant == 2 || variant_constant == 3); |
| |
| /* Section 33.1.2.1 has the instructions for setting the bus speed based |
| * on the CAR controller's divisors. |
| * |
| * See also 33.3.1. |
| * |
| * FIXME: |
| * |
| * This driver works with the assumption that the input clock source is |
| * PLLP, and furthermore that the PLLP divisor value is 0. |
| * |
| * This will have to be the case until we have a proper driver API. |
| * |
| * EXPLANATION: |
| * |
| * The SCL line's frequency is set according to the following equation: |
| * SCL = 408000000 / ((TLOW+THIGH+VC) * (CLK_DIVISOR + 1) * TK1_CAR_PLLP_DIVISOR) |
| * |
| * VC = variant_constant, either 2 or 3. This is just a magic number that |
| * is given in the TK1 manual with no explanation. |
| * TLOW = the value in I2C_I2C_INTERFACE_TIMING_0_0. |
| * THIGH = the value in I2C_I2C_INTERFACE_TIMING_0_0. |
| * CLK_DIVISOR = the value in I2C_I2C_CLK_DIVISOR_REGISTER_0. |
| * TK1_CAR_PLLP_DIVISOR = the divisor value in the clock and reset |
| * controller's PLLP clock source. Specifically, the resultant value that |
| * the divider will divide the input frequency by. I.e, N+1, not N. |
| * So for example, right now, this driver depends on us using '0' as the |
| * literal divisor value, and a value of '0' in the divisor register will |
| * cause the divider to divide the input frequency by 1 (N+1). |
| * So I2C_FREQENCY_DIVISOR in this equation should be 1, not 0. |
| * |
| * But we need to derive CLK_DIVISOR. So we rearrange that (algebra!) to |
| * solve for CLK_DIVISOR, and get: |
| * CLK_DIVISOR = (408000000 / (SCL * TK1_CAR_PLLP_DIVISOR * (TLOW+THIGH+VC))) - 1 |
| */ |
| switch (speed) { |
| case I2C_SLAVE_SPEED_HIGHSPEED: |
| tlow = read_masked(&r->hs_interface_timing0, |
| (TK1I2C_HS_IFACE_TIMING0_TLOW_MASK << TK1I2C_HS_IFACE_TIMING0_TLOW_SHIFT)) |
| >> TK1I2C_HS_IFACE_TIMING0_TLOW_SHIFT; |
| thigh = read_masked(&r->hs_interface_timing0, |
| (TK1I2C_HS_IFACE_TIMING0_THIGH_MASK << TK1I2C_HS_IFACE_TIMING0_THIGH_SHIFT)) |
| >> TK1I2C_HS_IFACE_TIMING0_THIGH_SHIFT; |
| |
| ZF_LOGD("Reading highspeed tlow/high: tLOW %d, tHIGH %d.", |
| tlow, thigh); |
| break; |
| |
| case I2C_SLAVE_SPEED_STANDARD: |
| case I2C_SLAVE_SPEED_FAST: |
| case I2C_SLAVE_SPEED_FASTPLUS: |
| tlow = read_masked(&r->interface_timing0, |
| (TK1I2C_IFACE_TIMING0_TLOW_MASK << TK1I2C_IFACE_TIMING0_TLOW_SHIFT)) |
| >> TK1I2C_IFACE_TIMING0_TLOW_SHIFT; |
| thigh = read_masked(&r->interface_timing0, |
| (TK1I2C_IFACE_TIMING0_THIGH_MASK << TK1I2C_IFACE_TIMING0_THIGH_SHIFT)) |
| >> TK1I2C_IFACE_TIMING0_THIGH_SHIFT; |
| |
| ZF_LOGD("Reading std/fast/f+ tlow/high: tLOW %d, tHIGH %d.", |
| tlow, thigh); |
| break; |
| |
| default: |
| return -1; |
| } |
| |
| ZF_LOGD("tLOW is %d, tHIGH is %d.", tlow, thigh); |
| target_scl_freq_hz = i2c_speed_freqs[speed]; |
| return ((TK1_CAR_PLLP_INPUT_FREQ_HZ / target_scl_freq_hz) |
| / (tlow+thigh+variant_constant)) - 1; |
| } |
| |
| static uint32_t |
| tk1_i2c_calc_baud_resulting_from(i2c_bus_t *ib, |
| uint32_t divisor, |
| enum i2c_slave_speed speed, |
| uint32_t vc) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| uint32_t tlow, thigh; |
| |
| switch (speed) { |
| case I2C_SLAVE_SPEED_HIGHSPEED: |
| tlow = read_masked(&r->hs_interface_timing0, |
| (TK1I2C_HS_IFACE_TIMING0_TLOW_MASK << TK1I2C_HS_IFACE_TIMING0_TLOW_SHIFT)) |
| >> TK1I2C_HS_IFACE_TIMING0_TLOW_SHIFT; |
| thigh = read_masked(&r->hs_interface_timing0, |
| (TK1I2C_HS_IFACE_TIMING0_THIGH_MASK << TK1I2C_HS_IFACE_TIMING0_THIGH_SHIFT)) |
| >> TK1I2C_HS_IFACE_TIMING0_THIGH_SHIFT; |
| break; |
| |
| case I2C_SLAVE_SPEED_STANDARD: |
| case I2C_SLAVE_SPEED_FAST: |
| case I2C_SLAVE_SPEED_FASTPLUS: |
| tlow = read_masked(&r->interface_timing0, |
| (TK1I2C_IFACE_TIMING0_TLOW_MASK << TK1I2C_IFACE_TIMING0_TLOW_SHIFT)) |
| >> TK1I2C_IFACE_TIMING0_TLOW_SHIFT; |
| thigh = read_masked(&r->interface_timing0, |
| (TK1I2C_IFACE_TIMING0_THIGH_MASK << TK1I2C_IFACE_TIMING0_THIGH_SHIFT)) |
| >> TK1I2C_IFACE_TIMING0_THIGH_SHIFT; |
| break; |
| |
| default: |
| return -1; |
| } |
| |
| return TK1_CAR_PLLP_INPUT_FREQ_HZ / ((tlow+thigh+vc) * (divisor)); |
| } |
| |
| static long |
| tk1_i2c_set_speed(i2c_bus_t* bus, enum i2c_slave_speed speed) |
| { |
| int32_t divisor; |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(bus); |
| uint8_t variant_constant = 3; |
| |
| divisor = tk1_i2c_calc_divisor_value_for(bus, speed, variant_constant); |
| if (divisor < 0 || divisor > UINT16_MAX) { |
| return -1; |
| } |
| |
| if (divisor > 3) { |
| variant_constant = 2; |
| divisor = tk1_i2c_calc_divisor_value_for(bus, speed, variant_constant); |
| if (divisor < 0 || divisor > UINT16_MAX) { |
| return -1; |
| } |
| } |
| |
| while (tk1_i2c_calc_baud_resulting_from(bus, divisor, |
| speed, variant_constant) |
| > i2c_speed_freqs[speed]) { |
| ZF_LOGV(PREFIX"Resulting baud is %d. Recalculating divisor up from %d to %d.", |
| r, tk1_i2c_calc_baud_resulting_from(bus, divisor, speed, variant_constant), |
| divisor, divisor+1); |
| divisor++; |
| } |
| ZF_LOGV(PREFIX"Calculated I2C divisor at %d. Effective baud is %d. Previous values: std %d, hs %d.", |
| r, divisor, |
| tk1_i2c_calc_baud_resulting_from(bus, divisor, speed, variant_constant), |
| (r->clk_divisor >> 16 & 0xFFFF), |
| (r->clk_divisor & 0xFFFF)); |
| |
| switch (speed) { |
| case I2C_SLAVE_SPEED_HIGHSPEED: |
| write_masked(&r->clk_divisor, |
| 0xFFFF, |
| ((divisor & TK1I2C_CLK_DIVISOR_HS_MODE_MASK) |
| << TK1I2C_CLK_DIVISOR_HS_MODE_SHIFT)); |
| break; |
| |
| case I2C_SLAVE_SPEED_STANDARD: |
| case I2C_SLAVE_SPEED_FAST: |
| case I2C_SLAVE_SPEED_FASTPLUS: |
| write_masked(&r->clk_divisor, |
| 0xFFFF0000, |
| ((divisor & TK1I2C_CLK_DIVISOR_STD_FAST_MODE_MASK) |
| << TK1I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT)); |
| break; |
| |
| default: |
| return -1; |
| } |
| |
| ZF_LOGD("For I2C speed %s, divisor was calculated to be %d.", |
| i2c_speed_names[speed], divisor); |
| |
| tk1_i2c_config_commit(bus, COMMIT_MASTER_BIT); |
| return i2c_speed_freqs[speed]; |
| } |
| |
| static inline void |
| tk1_i2c_toggle_irq(i2c_bus_t *ib, bool enable, uint32_t irqs) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| if (enable) { |
| r->interrupt_mask |= irqs; |
| } else { |
| r->interrupt_mask &= ~irqs; |
| } |
| } |
| |
| static inline void |
| tk1_i2c_acknowledge_irq(i2c_bus_t *ib, uint32_t irqs) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| r->interrupt_status |= irqs; |
| } |
| |
| static inline void |
| tk1_i2c_callback(i2c_bus_t *ib, enum i2c_stat stat, size_t nbytes) |
| { |
| if (ib->cb != NULL) { |
| ib->cb(ib, stat, nbytes, ib->token); |
| } |
| } |
| |
| static tk1_i2c_pktheaders_t |
| tk1_i2c_prepare_mmode_xfer_headers(i2c_slave_t *sl, size_t nbytes, |
| bool is_write, |
| bool send_repeat_start_not_stop) |
| { |
| tk1_i2c_pktheaders_t headers; |
| tk1_i2c_state_t *state; |
| |
| assert(sl != NULL); |
| assert(sl->bus != NULL); |
| assert(nbytes > 0); |
| |
| state = tk1_i2c_get_state(sl->bus); |
| |
| headers.io0 = headers.io1 = headers.i2c = 0; |
| |
| headers.io0 = (0 << TK1I2C_TXPKT0_PKTTYPE_SHIFT) |
| | (TK1I2C_TXPKT0_PROTOCOL_I2C << TK1I2C_TXPKT0_PROTOCOL_SHIFT) |
| /* Linux uses a 0-based device index for the ID. */ |
| | (state->controller_id << TK1I2C_TXPKT0_CONTROLLER_ID_SHIFT) |
| /* The PktID is optional, AFAICT. Linux uses the value "1" always. */ |
| | (1 << TK1I2C_TXPKT0_PKTID_SHIFT) |
| /* TK1 TRM Section 33.2.2.1: Table 133: |
| * "For I2C, ProtHdrSize = 0 for request packets and 1 for response |
| * packets" |
| * |
| * Values: |
| * 0 = 1 word; 1 = 2 words; 3 = 4 words. |
| */ |
| | (0 << TK1I2C_TXPKT0_PROTHDR_SIZE_SHIFT); |
| |
| headers.io1 = ((nbytes - 1) & TK1I2C_TXPKT1_PAYLOAD_SIZE_MASK) |
| << TK1I2C_TXPKT1_PAYLOAD_SIZE_SHIFT; |
| |
| headers.i2c = TK1I2C_I2CPKT_IRQ_ON_COMPLETION_BIT |
| | (send_repeat_start_not_stop ? TK1I2C_I2CPKT_SEND_REPEAT_START_NOT_STOP_BIT : 0) |
| | (is_write ? 0 : TK1I2C_I2CPKT_READ_XFER_BIT) |
| | TK1I2C_I2CPKT_RESPONSE_PKT_ENABLE_BIT; |
| |
| if (sl->address_size == I2C_SLAVE_ADDR_10BIT) { |
| headers.i2c |= TK1I2C_I2CPKT_10_BIT_ADDRESS_BIT; |
| headers.i2c |= (sl->address & TK1I2C_I2CPKT_SLAVE_ADDR_10BIT_MASK) |
| << TK1I2C_I2CPKT_SLAVE_ADDR_10BIT_SHIFT; |
| } else { |
| /* TK1 TRM Section 33.2.5, Table 135: |
| * "Slave Address. Bit 0 is ignored for 7-bit addressing, but should |
| * always match bit 19 (READ/WRITE)." |
| */ |
| headers.i2c |= (is_write ? 0 : 1); |
| headers.i2c |= (sl->address & TK1I2C_I2CPKT_SLAVE_ADDR_7BIT_MASK) |
| << TK1I2C_I2CPKT_SLAVE_ADDR_7BIT_SHIFT; |
| } |
| |
| if (sl->max_speed == I2C_SLAVE_SPEED_HIGHSPEED) { |
| /* Enable HS mode bit and set HS master mode address. */ |
| headers.i2c |= TK1I2C_I2CPKT_HS_MODE_BIT; |
| /* Shouldn't need to mask it because we masked it in |
| * set_hsmode_master_addr. |
| */ |
| headers.i2c |= state->master.hsmode_master_address |
| << TK1I2C_I2CPKT_HS_MASTER_ADDR_SHIFT; |
| } |
| |
| if (sl->i2c_opts & I2C_SLAVE_OPTS_DEVICE_DOES_NOT_ACK) { |
| /* Enable handling of NACK when ACK is expected. */ |
| headers.i2c |= TK1I2C_I2CPKT_CONTINUE_ON_NACK_BIT; |
| } |
| |
| if (sl->i2c_opts & TK1I2C_SLAVE_FLAGS_XFER_IN_PROGRESS_BIT) { |
| headers.i2c |= TK1I2C_I2CPKT_CONTINUE_XFER_BIT; |
| } |
| |
| return headers; |
| } |
| |
| /** Takes a buffer and attempts to write as much of it as it can to the master |
| * or slave mode TX FIFO. |
| * |
| * Always begins reading the data from index 0 in the buffer. |
| * |
| * @return number of bytes that were written out to the FIFO from the buffer. |
| */ |
| static size_t |
| tk1_i2c_fill_tx_fifo(i2c_bus_t *ib, enum i2c_xfer_mode mode, |
| const void *const _data, size_t buffsize) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| volatile uint32_t *tx_fifo_reg; |
| size_t i, empty_fifo_words, buff_remaining; |
| uint32_t *data = (uint32_t *)_data; |
| |
| if (mode == I2C_MODE_MASTER) { |
| tx_fifo_reg = &r->tx_packet_fifo; |
| empty_fifo_words = r->fifo_status >> TK1I2C_FIFO_STATUS_MMODE_TX_EMPTY_SHIFT |
| & TK1I2C_FIFO_STATUS_MMODE_TX_EMPTY_MASK; |
| } else { |
| tx_fifo_reg = &r->tx_packet_fifo; |
| empty_fifo_words = r->fifo_status >> TK1I2C_FIFO_STATUS_SMODE_TX_EMPTY_SHIFT |
| & TK1I2C_FIFO_STATUS_SMODE_TX_EMPTY_MASK; |
| } |
| |
| buff_remaining = buffsize; |
| for (i = 0; i < empty_fifo_words && buff_remaining >= TK1I2C_NBYTES_PER_FIFO_WORD; i++) { |
| *tx_fifo_reg = data[i]; |
| buff_remaining -= TK1I2C_NBYTES_PER_FIFO_WORD; |
| } |
| |
| if (i < empty_fifo_words && buff_remaining > 0) { |
| uint8_t *last_word_data = (uint8_t *)&data[i]; |
| uint32_t last_word = 0; |
| |
| if (buff_remaining >= TK1I2C_NBYTES_PER_FIFO_WORD) { |
| ZF_LOGF("Bug: shifting last word with %d bytes remaining in buffer.", |
| buff_remaining); |
| } |
| |
| for (int j = 0; j < buff_remaining; j++) { |
| last_word |= last_word_data[j] << (8 * j); |
| } |
| *tx_fifo_reg = last_word; |
| buff_remaining -= buff_remaining; |
| } |
| |
| return buffsize - buff_remaining; |
| } |
| |
| /** Takes a buffer and attempts to read as much data from the master or slave |
| * mode RX FIFO into that buffer. |
| * |
| * Always begins writing into the buffer at index 0. |
| * |
| * @return number of bytes read from the FIFO into the buffer. |
| */ |
| static size_t |
| tk1_i2c_drain_rx_fifo(i2c_bus_t *ib, enum i2c_xfer_mode mode, |
| void *const _data, size_t buffsize) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| volatile uint32_t *rx_fifo_reg; |
| size_t i, avail_fifo_words, buff_remaining; |
| uint32_t *data32 = (uint32_t *)_data; |
| |
| if (mode == I2C_MODE_MASTER) { |
| rx_fifo_reg = &r->rx_fifo; |
| avail_fifo_words = r->fifo_status >> TK1I2C_FIFO_STATUS_MMODE_RX_FULL_SHIFT |
| & TK1I2C_FIFO_STATUS_MMODE_RX_FULL_MASK; |
| } else { |
| rx_fifo_reg = &r->rx_fifo; |
| avail_fifo_words = r->fifo_status >> TK1I2C_FIFO_STATUS_SMODE_RX_FULL_SHIFT |
| & TK1I2C_FIFO_STATUS_SMODE_RX_FULL_MASK; |
| } |
| |
| buff_remaining = buffsize; |
| for (i = 0; i < avail_fifo_words && buff_remaining >= TK1I2C_NBYTES_PER_FIFO_WORD; i++) { |
| data32[i] = *rx_fifo_reg; |
| buff_remaining -= TK1I2C_NBYTES_PER_FIFO_WORD; |
| } |
| |
| if (i < avail_fifo_words && buff_remaining > 0) { |
| uint8_t *last_word_data = (uint8_t *)&data32[i]; |
| uint32_t last_word = *rx_fifo_reg; |
| |
| if (buff_remaining >= TK1I2C_NBYTES_PER_FIFO_WORD) { |
| ZF_LOGF("Bug: shifting last word out of RX fifo when it's not the " |
| "last word."); |
| } |
| |
| for (int j = 0; j < buff_remaining; j++) { |
| last_word_data[j] = last_word & 0xFF; |
| last_word >>= 8; |
| } |
| buff_remaining -= buff_remaining; |
| /* Increment i for the case where there were more words in the FIFO. |
| * This case will be picked up below. |
| */ |
| i++; |
| } |
| |
| /* Handle the case where the user provided a buffer that is smaller than the |
| * amount of data that the device will actually send. We flush the FIFOS |
| * here to deal with this case. |
| * |
| * There is always 1 extra word in the RX FIFO at the end, so ignore it. |
| */ |
| if (i < avail_fifo_words - 1 && buff_remaining == 0) { |
| ZF_LOGW("Bug: usermode buffer is too small for received data. %d of %d words read from fifo.\n" |
| "\tOther word was 0x%x. pkt trans status 0x%x. After reading, fifo status is %d", |
| i, avail_fifo_words, *rx_fifo_reg, r->packet_transfer_status, |
| (r->fifo_status >> TK1I2C_FIFO_STATUS_MMODE_RX_FULL_SHIFT |
| & TK1I2C_FIFO_STATUS_MMODE_RX_FULL_MASK)); |
| tk1_i2c_flush_fifos(ib, mode); |
| } |
| |
| return buffsize - buff_remaining; |
| } |
| |
| /** Handle a master-mode IRQ. |
| * |
| * Basically either fill the Tx fifo for an ongoing send operation, or empty the |
| * RX fifo for an ongoing receive operation. |
| */ |
| #define IRQPREFIX "I2C %p: IRQ: " |
| static void |
| tk1_i2c_mmode_handle_irq(i2c_bus_t* ib) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| tk1_i2c_state_t *s = tk1_i2c_get_state(ib); |
| UNUSED uint32_t int_status; |
| int err, do_callback=0, callback_status, callback_nbytes=0, reason_known=0; |
| |
| const uint32_t error_int_statuses_mask = |
| TK1I2C_INTSTATUS_MMODE_TX_FIFO_OVF_BIT |
| | TK1I2C_INTSTATUS_MMODE_RX_FIFO_UNR_BIT |
| | TK1I2C_INTSTATUS_MMODE_NO_ACK_BIT |
| | TK1I2C_INTSTATUS_MMODE_ARBITRATION_LOST_BIT; |
| |
| const uint32_t data_available_int_statuses_mask = |
| TK1I2C_INTSTATUS_MMODE_TX_FIFO_DATA_REQ_BIT |
| | TK1I2C_INTSTATUS_MMODE_RX_FIFO_DATA_REQ_BIT; |
| |
| const uint32_t xfer_successful_completion_statuses_mask = |
| TK1I2C_INTSTATUS_MMODE_PACKET_XFER_COMPLETE_BIT |
| | TK1I2C_INTSTATUS_MMODE_ALL_PACKETS_XFER_COMPLETE_BIT; |
| |
| int_status = r->interrupt_status; |
| |
| /* Regardless of whether or not an error occured on the line, we should |
| * unconditionally read the data that did get transferred before the error |
| * occured, if any such data exists. |
| */ |
| if (int_status & data_available_int_statuses_mask) { |
| reason_known = 1; |
| |
| if (int_status & TK1I2C_INTSTATUS_MMODE_TX_FIFO_DATA_REQ_BIT |
| && s->master.is_write) { |
| |
| /* Refill TX fifo. */ |
| if (s->master.xfer_cursor <= s->master.nbytes) { |
| err = tk1_i2c_fill_tx_fifo(ib, I2C_MODE_MASTER, |
| &s->master.buff[s->master.xfer_cursor], |
| s->master.nbytes - s->master.xfer_cursor); |
| |
| if (err < 0) { |
| ZF_LOGF(IRQPREFIX "TX FIFO trigger IRQ came in, but " |
| "TX FIFO is full.", r); |
| } |
| |
| s->master.xfer_cursor += err; |
| if (s->master.xfer_cursor > s->master.nbytes) { |
| ZF_LOGF("Bug: TX userspace buffer has been overshot by driver!"); |
| } |
| |
| if (s->master.xfer_cursor == s->master.nbytes) { |
| /* Disable TX FIFO trigger IRQ and then when the XFER_COMPLETE |
| * IRQ comes in, callback. |
| */ |
| tk1_i2c_toggle_irq(ib, false, TK1I2C_INTMASK_MMODE_TX_FIFO_DATA_REQ_BIT); |
| } |
| } else { |
| ZF_LOGF(IRQPREFIX "TX FIFO data req IRQ has read beyond " |
| "userspace buffer!\n" |
| "\txfer_cursor %d, nbytes %d.", |
| r, s->master.xfer_cursor, s->master.nbytes); |
| } |
| } |
| |
| if (int_status & TK1I2C_INTSTATUS_MMODE_RX_FIFO_DATA_REQ_BIT |
| && !s->master.is_write) { |
| |
| /* Drain RX fifo. */ |
| if (s->master.xfer_cursor <= s->master.nbytes) { |
| err = tk1_i2c_drain_rx_fifo(ib, I2C_MODE_MASTER, |
| &s->master.buff[s->master.xfer_cursor], |
| s->master.nbytes - s->master.xfer_cursor); |
| |
| if (err < 0) { |
| ZF_LOGF(IRQPREFIX "RX FIFO trigger IRQ came in, but " |
| "RX FIFO is empty.", r); |
| } |
| |
| s->master.xfer_cursor += err; |
| if (s->master.xfer_cursor > s->master.nbytes) { |
| ZF_LOGF("Bug: RX userspace buffer has been overshot by driver!"); |
| } |
| |
| if (s->master.xfer_cursor == s->master.nbytes) { |
| /* Disable RX FIFO trigger IRQ and then when the XFER_COMPLETE |
| * IRQ comes in, callback. |
| */ |
| tk1_i2c_toggle_irq(ib, false, TK1I2C_INTMASK_MMODE_RX_FIFO_DATA_REQ_BIT); |
| } |
| } else { |
| ZF_LOGF(IRQPREFIX "RX FIFO data req IRQ has overrun userspace " |
| "buffer.\n" |
| "\txfer_cursor %d, nbytes %d.", |
| r, s->master.xfer_cursor, s->master.nbytes); |
| } |
| } |
| } |
| |
| /* If an error did occur, we need to decide what error condition to return. |
| * We want error conditions to take precedence over the completion |
| * condition. |
| */ |
| if (int_status & error_int_statuses_mask) { |
| reason_known = 1; |
| do_callback = 1; |
| callback_status = I2CSTAT_ERROR; |
| callback_nbytes = s->master.xfer_cursor; |
| |
| /* We would ideally like to return a bitfield with all the errors that |
| * occured, but since the API doesn't allow this, we arbitrarily |
| * choose the first one that we come across and return that one. |
| */ |
| if (int_status & TK1I2C_INTSTATUS_MMODE_TX_FIFO_OVF_BIT) { |
| ZF_LOGF(IRQPREFIX "TX FIFO has been overflowed.", r); |
| // Use I2CSTAT_ERROR. |
| } else if (int_status & TK1I2C_INTSTATUS_MMODE_RX_FIFO_UNR_BIT) { |
| ZF_LOGF(IRQPREFIX "RX FIFO has been underrun.", r); |
| // Use I2CSTAT_ERROR. |
| } else if (int_status & TK1I2C_INTSTATUS_MMODE_NO_ACK_BIT) { |
| ZF_LOGE(IRQPREFIX "Slave failed to send ACK. Does this slave " |
| "require continue_on_nack?", r); |
| tk1_i2c_handle_nack(ib); |
| callback_status = I2CSTAT_NACK; |
| } else if (int_status & TK1I2C_INTSTATUS_MMODE_ARBITRATION_LOST_BIT) { |
| ZF_LOGW(IRQPREFIX "Lost arbitration.", r); |
| tk1_i2c_handle_arbitration_loss(ib); |
| callback_status = I2CSTAT_ARBITRATION_LOST; |
| } |
| } |
| |
| if (int_status & xfer_successful_completion_statuses_mask) { |
| reason_known = 1; |
| do_callback = 1; |
| callback_nbytes = s->master.xfer_cursor; |
| |
| if (s->master.xfer_cursor < s->master.nbytes) { |
| ZF_LOGF(IRQPREFIX"Bug: Got \"PKT_XFER_COMPLETE\" IRQ when there " |
| "were bytes remaining to be xmitted.", r); |
| } |
| |
| if (int_status & TK1I2C_INTSTATUS_MMODE_PACKET_XFER_COMPLETE_BIT) { |
| ZF_LOGD(IRQPREFIX"Xfer complete, pktstatus is 0x%x, i2cstatus is 0x%x.", r, |
| r->packet_transfer_status, r->status); |
| /* XFER_COMPLETE IRQ has occured: callback into userspace. |
| * Keep in mind that the controller can set both the RX/TX trigger |
| * IRQ status bits and the XFER_COMPLETE IRQ status bit, |
| * so the XFER_COMPLETE IRQ doesn't actually have to be a separate |
| * IRQ. |
| */ |
| callback_status = I2CSTAT_COMPLETE; |
| } |
| if (int_status & TK1I2C_INTSTATUS_MMODE_ALL_PACKETS_XFER_COMPLETE_BIT) { |
| ZF_LOGD(IRQPREFIX"Got an \"ALL_PKTS_XFER_COMPLETE\" IRQ.", r); |
| } |
| } |
| |
| if (!reason_known) { |
| ZF_LOGE(IRQPREFIX"IRQ occurred for unknown reason.", r); |
| } |
| |
| /* TK1 TRM Section 33.5.23: |
| * "This register indicates the status bit for which the interrupt is |
| * set. If the interrupt is set, write a 1 to clear it." |
| */ |
| if (do_callback == 1) { |
| tk1_i2c_callback(ib, callback_status, callback_nbytes); |
| } |
| tk1_i2c_acknowledge_irq(ib, int_status); |
| return; |
| } |
| |
| UNUSED static int |
| tk1_i2c_mmode_xfer(i2c_slave_t *sl, void *data, size_t nbytes, bool is_write) |
| { |
| return 0; |
| } |
| |
| UNUSED static int |
| tk1_i2c_smode_xfer(void *data, size_t nbytes, bool is_write) |
| { |
| return 0; |
| } |
| |
| static int |
| tk1_i2c_mmode_read(i2c_slave_t* slave, void* buf, size_t size, |
| bool end_with_repeat_start, |
| i2c_callback_fn cb, void* token) |
| { |
| assert(slave != NULL); |
| assert(slave->bus != NULL); |
| int error; |
| |
| tk1_i2c_state_t *s = tk1_i2c_get_state(slave->bus); |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(slave->bus); |
| tk1_i2c_pktheaders_t headers; |
| |
| if (size == 0) { |
| tk1_i2c_callback(slave->bus, I2CSTAT_COMPLETE, 0); |
| return 0; |
| } |
| |
| error = tk1_i2c_set_speed(slave->bus, slave->max_speed); |
| if (error < 0) { |
| ZF_LOGE(PREFIX"Failed to set speed to %d for slave 0x%x.", |
| r, i2c_speed_freqs[slave->max_speed], slave->address); |
| return -1; |
| } |
| |
| tk1_i2c_flush_fifos(slave->bus, I2C_MODE_MASTER); |
| |
| if (tk1_i2c_bus_is_locked_up(slave->bus)) { |
| if (!tk1_i2c_bus_clear(slave->bus, true)) { |
| ZF_LOGE(PREFIX"slave is holding SDA line low, and bus clear " |
| "failed.", r); |
| return -1; |
| } |
| } |
| |
| s->master.slave_id = slave->address; |
| s->master.is_write = false; |
| s->master.buff = buf; |
| s->master.nbytes = size; |
| s->master.xfer_cursor = 0; |
| |
| slave->bus->cb = cb; |
| slave->bus->token = token; |
| |
| tk1_i2c_set_mode(slave->bus, I2C_MODE_MASTER); |
| tk1_i2c_config_commit(slave->bus, COMMIT_MASTER_BIT); |
| |
| headers = tk1_i2c_prepare_mmode_xfer_headers(slave, size, false, |
| end_with_repeat_start); |
| r->tx_packet_fifo = headers.io0; |
| r->tx_packet_fifo = headers.io1; |
| r->tx_packet_fifo = headers.i2c; |
| |
| /* Enable RX FIFO trigger IRQ. */ |
| tk1_i2c_toggle_irq(slave->bus, true, TK1I2C_INTMASK_SMODE_RX_FIFO_DATA_REQ_BIT); |
| |
| return 0; |
| } |
| |
| static int |
| tk1_i2c_mmode_write(i2c_slave_t *slave, const void* buf, size_t size, |
| bool end_with_repeat_start, |
| i2c_callback_fn cb, void* token) |
| { |
| assert(slave != NULL); |
| assert(slave->bus != NULL); |
| int error; |
| |
| size_t ret; |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(slave->bus); |
| tk1_i2c_state_t *s = tk1_i2c_get_state(slave->bus); |
| tk1_i2c_pktheaders_t headers; |
| |
| if (size == 0) { |
| tk1_i2c_callback(slave->bus, I2CSTAT_COMPLETE, 0); |
| return 0; |
| } |
| |
| if (size > TK1I2C_DATA_MAX_NBYTES) { |
| ZF_LOGE(PREFIX"Write: xfer sizes > %d not supported.", |
| r, TK1I2C_DATA_MAX_NBYTES); |
| return -1; |
| } |
| |
| error = tk1_i2c_set_speed(slave->bus, slave->max_speed); |
| if (error < 0) { |
| ZF_LOGE(PREFIX"Failed to set speed to %d for slave 0x%x.", |
| r, i2c_speed_freqs[slave->max_speed], slave->address); |
| return -1; |
| } |
| |
| tk1_i2c_flush_fifos(slave->bus, I2C_MODE_MASTER); |
| |
| if (tk1_i2c_bus_is_locked_up(slave->bus)) { |
| if (!tk1_i2c_bus_clear(slave->bus, true)) { |
| ZF_LOGE(PREFIX"slave is holding SDA line low, and bus clear " |
| "failed.", r); |
| return -1; |
| } |
| } |
| |
| s->master.slave_id = slave->address; |
| s->master.is_write = true; |
| s->master.buff = (uint8_t *)buf; |
| s->master.nbytes = size; |
| s->master.xfer_cursor = 0; |
| |
| slave->bus->cb = cb; |
| slave->bus->token = token; |
| |
| tk1_i2c_set_mode(slave->bus, I2C_MODE_MASTER); |
| tk1_i2c_config_commit(slave->bus, COMMIT_MASTER_BIT); |
| |
| headers = tk1_i2c_prepare_mmode_xfer_headers(slave, size, true, |
| end_with_repeat_start); |
| r->tx_packet_fifo = headers.io0; |
| r->tx_packet_fifo = headers.io1; |
| r->tx_packet_fifo = headers.i2c; |
| ret = tk1_i2c_fill_tx_fifo(slave->bus, I2C_MODE_MASTER, buf, size); |
| if (ret < 1) { |
| /* Keep in mind that not being able to write bytes out to the FIFO |
| * doesn't mean an error occured. |
| */ |
| ZF_LOGE(PREFIX"Failed to write out bytes to line.", r); |
| return -1; |
| } else { |
| s->master.xfer_cursor = ret; |
| } |
| |
| /* Enable TX FIFO trigger IRQ */ |
| tk1_i2c_toggle_irq(slave->bus, true, TK1I2C_INTMASK_MMODE_TX_FIFO_DATA_REQ_BIT); |
| |
| return s->master.xfer_cursor; |
| } |
| |
| static void |
| tk1_i2c_set_hsmode_master_address(i2c_bus_t* ib, int addr) |
| { |
| tk1_i2c_state_t *s = tk1_i2c_get_state(ib); |
| |
| s->master.hsmode_master_address = addr & TK1I2C_I2CPKT_HS_MASTER_ADDR_MASK; |
| } |
| |
| static void |
| tk1_i2c_register_slave_event_handler(i2c_bus_t* ib, |
| i2c_aas_callback_fn aas_cb, void *token) |
| { |
| ib->aas_cb = aas_cb; |
| ib->aas_token = token; |
| } |
| |
| static int |
| tk1_i2c_slave_init(i2c_bus_t* ib, int address, |
| enum i2c_slave_address_size address_size, |
| enum i2c_slave_speed max_speed, |
| uint32_t flags, i2c_slave_t* sl) |
| { |
| assert(sl != NULL); |
| |
| if (address_size == I2C_SLAVE_ADDR_7BIT) { |
| address = i2c_extract_address(address); |
| } |
| |
| /* Internally in this driver, we discard the RW bit */ |
| sl->address = address; |
| sl->address_size = address_size; |
| sl->max_speed = max_speed; |
| sl->i2c_opts = flags; |
| sl->bus = ib; |
| |
| sl->slave_read = &tk1_i2c_mmode_read; |
| sl->slave_write = &tk1_i2c_mmode_write; |
| |
| return 0; |
| } |
| |
| static void |
| tk1_i2c_set_mmode_trigger_levels(i2c_bus_t *ib, |
| uint8_t tx_trig, uint8_t rx_trig) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| r->fifo_control &= ~((TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_MASK << TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_SHIFT) |
| | (TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_MASK << TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_SHIFT)); |
| |
| r->fifo_control |= ((tx_trig & TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_MASK) |
| << TK1I2C_FIFO_CONTROL_MMODE_TX_TRIG_SHIFT) |
| | ((rx_trig & TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_MASK) |
| << TK1I2C_FIFO_CONTROL_MMODE_RX_TRIG_SHIFT); |
| } |
| |
| UNUSED static void |
| tk1_i2c_set_smode_trigger_levels(i2c_bus_t *ib, |
| uint8_t tx_trig, uint8_t rx_trig) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| r->fifo_control &= ~((TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_MASK << TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_SHIFT) |
| | (TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_MASK << TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_SHIFT)); |
| |
| r->fifo_control |= ((tx_trig & TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_MASK) |
| << TK1I2C_FIFO_CONTROL_SMODE_TX_TRIG_SHIFT) |
| | ((rx_trig & TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_MASK) |
| << TK1I2C_FIFO_CONTROL_SMODE_RX_TRIG_SHIFT); |
| } |
| |
| static int |
| tk1_i2c_master_stop(i2c_bus_t *ib) |
| { |
| return tk1_i2c_bus_clear(ib, true); |
| } |
| |
| static int |
| tk1_i2c_initialize_controller(i2c_bus_t *ib) |
| { |
| tk1_i2c_regs_t *r = tk1_i2c_get_priv(ib); |
| |
| tk1_i2c_set_packet_mode(ib, true); |
| |
| if (tk1_i2c_bus_is_locked_up(ib)) { |
| if (!tk1_i2c_bus_clear(ib, true)) { |
| ZF_LOGE(PREFIX"slave is holding SDA line low, and bus clear " |
| "failed.", r); |
| return -1; |
| } |
| } |
| |
| tk1_i2c_set_mmode_trigger_levels(ib, 7, 0); |
| tk1_i2c_flush_fifos(ib, I2C_MODE_MASTER); |
| tk1_i2c_flush_fifos(ib, I2C_MODE_SLAVE); |
| |
| /* ACK all IRQs that the chip currently has active, in case one was |
| * asserted. Perhaps the driver is being restarted, for example. |
| */ |
| tk1_i2c_acknowledge_irq(ib, r->interrupt_status); |
| |
| /* Disable those IRQs we either will never want, or will conditionally need |
| * to enable later. |
| * |
| * Master mode IRQs. |
| */ |
| tk1_i2c_toggle_irq(ib, false, |
| TK1I2C_INTMASK_MMODE_TX_FIFO_DATA_REQ_BIT |
| | TK1I2C_INTMASK_MMODE_RX_FIFO_DATA_REQ_BIT |
| | TK1I2C_INTMASK_MMODE_BUS_CLEAR_DONE_BIT |
| | TK1I2C_INTMASK_MMODE_ALL_PACKETS_XFER_COMPLETE_BIT); |
| |
| tk1_i2c_toggle_irq(ib, true, |
| TK1I2C_INTMASK_MMODE_ARBITRATION_LOST_BIT |
| | TK1I2C_INTMASK_MMODE_NO_ACK_BIT |
| | TK1I2C_INTMASK_MMODE_TX_FIFO_OVF_BIT |
| | TK1I2C_INTMASK_MMODE_RX_FIFO_UNR_BIT |
| | TK1I2C_INTMASK_MMODE_PACKET_XFER_COMPLETE_BIT); |
| |
| /* Slave mode IRQs. */ |
| tk1_i2c_toggle_irq(ib, false, |
| TK1I2C_INTMASK_SMODE_TX_FIFO_DATA_REQ_BIT |
| | TK1I2C_INTMASK_SMODE_RX_FIFO_DATA_REQ_BIT |
| | TK1I2C_INTMASK_SMODE_TX_BUFFER_REQ_BIT |
| | TK1I2C_INTMASK_SMODE_RX_BUFFER_FULL_BIT); |
| |
| tk1_i2c_toggle_irq(ib, true, |
| TK1I2C_INTMASK_SMODE_TX_FIFO_OVF_BIT |
| | TK1I2C_INTMASK_SMODE_RX_FIFO_UNR_BIT |
| | TK1I2C_INTMASK_SMODE_PACKET_XFER_COMPLETE_BIT |
| | TK1I2C_INTMASK_SMODE_PACKET_XFER_ERROR_BIT |
| | TK1I2C_INTMASK_SMODE_SWITCHED_READ2WRITE_BIT |
| | TK1I2C_INTMASK_SMODE_SWITCHED_WRITE2READ_BIT |
| | TK1I2C_INTMASK_SMODE_ACK_WITHHELD_BIT); |
| |
| tk1_i2c_config_commit(ib, COMMIT_MASTER_BIT | COMMIT_SLAVE_BIT); |
| return 0; |
| } |
| |
| int |
| tegra_i2c_init(int controller_id, void *vaddr, ps_io_ops_t *io_ops, |
| i2c_bus_t *ib) |
| { |
| tk1_i2c_state_t *s; |
| int error; |
| |
| error = ps_malloc(&io_ops->malloc_ops, sizeof(*s), (void **)&s); |
| if (error != 0 || s == NULL) { |
| ZF_LOGE(PREFIX"Failed to malloc internal state.", vaddr); |
| return -1; |
| }; |
| |
| s->regs = vaddr; |
| s->controller_id = controller_id; |
| ib->priv = s; |
| |
| ib->cb = NULL; |
| ib->token = NULL; |
| ib->aas_cb = NULL; |
| ib->aas_token = NULL; |
| |
| ib->slave_init = &tk1_i2c_slave_init; |
| ib->handle_irq = &tk1_i2c_mmode_handle_irq; |
| ib->set_speed = &tk1_i2c_set_speed; |
| ib->set_self_slave_address = NULL; |
| ib->register_slave_event_handler= &tk1_i2c_register_slave_event_handler; |
| ib->set_hsmode_master_address = &tk1_i2c_set_hsmode_master_address; |
| /* Bus clear */ |
| ib->master_stop = &tk1_i2c_master_stop; |
| |
| error = tk1_i2c_initialize_controller(ib); |
| if (error != 0) { |
| ZF_LOGE(PREFIX"Failed to initialize I2C controller.", vaddr); |
| ib->priv = NULL; |
| ps_free(&io_ops->malloc_ops, sizeof(*s), s); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int |
| i2c_init(enum i2c_id id, ps_io_ops_t* io_ops, i2c_bus_t* i2c_bus) |
| { |
| void *vaddr; |
| |
| assert(io_ops != NULL); |
| assert(i2c_bus != NULL); |
| |
| switch (id) { |
| case TK1_I2C0: |
| vaddr = RESOURCE(io_ops, TK1_I2C0); |
| break; |
| |
| case TK1_I2C1: |
| vaddr = RESOURCE(io_ops, TK1_I2C1); |
| break; |
| |
| case TK1_I2C2: |
| vaddr = RESOURCE(io_ops, TK1_I2C2); |
| break; |
| |
| case TK1_I2C3: |
| vaddr = RESOURCE(io_ops, TK1_I2C3); |
| break; |
| |
| case TK1_I2C4: |
| vaddr = RESOURCE(io_ops, TK1_I2C4); |
| break; |
| |
| case TK1_I2C5: |
| vaddr = RESOURCE(io_ops, TK1_I2C5); |
| break; |
| |
| default: |
| ZF_LOGE("Unknown I2C controller %d.", id); |
| return -1; |
| } |
| |
| if (vaddr == NULL) { |
| ZF_LOGE("Failed to map in TK1 I2C controller %d.", id); |
| return -1; |
| } |
| |
| return tegra_i2c_init(id, vaddr, io_ops, i2c_bus); |
| } |