// SPDX-License-Identifier: GPL-2.0-only /* * i3c-hci-exynos.c - Samsung Exynos I3C HCI Controller Driver * * Copyright (C) 2018 Samsung Electronics Co., Ltd. * Copyright (C) 2021 Samsung Electronics Co., Ltd. * * This program is free software; You can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../pinctrl/core.h" #include #include "i3c-hci-exynos.h" #ifdef CONFIG_CPU_IDLE #include #endif /* Register Map */ #define I3C_HCI_VERSION 0x00 #define I3C_HC_CONTROL 0x04 #define I3C_MASTER_DEVICE_ADDR 0x08 #define I3C_HC_CAPABILITIES 0x0C #define I3C_RESET_CTRL 0x10 #define I3C_PRESENT_STATE 0x14 #define I3C_DAT_SECTION_OFFSET 0x30 #define I3C_DCT_SECTION_OFFSET 0x34 #define I3C_RH_SECTION_OFFSET 0x38 /* not support */ #define I3C_PIO_SECTION_OFFSET 0x3C #define I3C_EXT_CAPS_SECTION_OFFSET 0x40 #define I3C_IBI_NOTIFY_CTRL 0x58 #define I3C_DEV_CTX_BASE_LO 0x60 #define I3C_DEV_CTX_BASE_HI 0x64 #define I3C_DEV_ADDR_TABLE(x) ((x) * 4 + 0x100) #define I3C_DEV_CHAR_TABLE_0(x) ((x) * 16 + 0x200) #define I3C_DEV_CHAR_TABLE_1(x) ((x) * 16 + 0x204) #define I3C_DEV_CHAR_TABLE_2(x) ((x) * 16 + 0x208) #define I3C_DEV_CHAR_TABLE_3(x) ((x) * 16 + 0x20C) #define I3C_CMD_QUEUE 0x300 #define I3C_RESP_QUEUE 0x304 #define I3C_XFER_DATA_BUF 0x308 #define I3C_IBI_PORT 0x30C #define I3C_QUEUE_THLD_CTRL 0x310 #define I3C_DATA_BUFFER_THLD_CTRL 0x314 #define I3C_QUEUE_SIZE 0x318 #define I3C_PIO_INTR_STATUS 0x320 #define I3C_PIO_INTR_EN 0x324 #define I3C_PIO_INTR_SIGNAL_EN 0x328 #define I3C_PIO_INTR_FORCE 0x32C /* EXT Register Map */ #define I3C_EXTCAP_HDR1 0x2000 #define I3C_COMP_MANUF 0x2004 #define I3C_COMP_VERSION 0x2008 #define I3C_COMP_TYPE 0x200C #define I3C_EXTCAP_HDR2 0x2010 #define I3C_MASTER_CONFIG 0x2014 #define I3C_EXTCAP_HDR3 0x2018 #define I3C_BUS_INSTANCE_COUNT 0x201C #define I3C_BUS_INSTANCE_OFFSET 0x2020 #define I3C_EXTCAP_HDR4 0x2024 #define I3C_QUEUE_STATUS_LEVEL 0x2028 #define I3C_DATA_BUFFER_THLD_STATUS_LEVEL 0x202C #define I3C_PRESENT_STATE_DEBUG 0x2030 #define I3C_MX_ERROR_COUNTERS 0x2034 #define I3C_EXTCAP_HDR5 0x20FC #define I3C_EXT_DEV_ADDR_TABLE(x) ((x) * 4 + 0x2100) #define I3C_EXTCAP_HDR6 0x2180 #define I3C_EXT_PIO_INTR_STATUS 0x2184 #define I3C_EXT_PIO_INTR_EN 0x2188 #define I3C_EXT_PIO_INTR_SIGNAL_EN 0x218C #define I3C_EXT_PIO_INTR_FORCE 0x2190 #define I3C_EXTCAP_HDR_7 0x219C #define I3C_EXT_DEVICE_CTRL 0x2198 #define I3C_EXTCAP_HDR_8 0x219C #define I3C_EXT_RESET_CTRL 0x21A0 #define I3C_EXTCAP_HDR_9 0x21A4 #define I3C_TRAILING_CYCLE 0x21A8 #define I3C_SCL_QUARTER_PERIOD 0x21AC #define I3C_SCL_CTRL 0x21B0 #define I3C_TSU_STA 0x21B4 #define I3C_TCBP 0x21B8 #define I3C_TLOW 0x21BC #define I3C_THD_STA 0x21C0 #define I3C_TBUF 0x21C4 #define I3C_BUS_CONDITION_CYCLE 0x21C8 #define I3C_SAMPLE_CYCLE 0x21CC #define I3C_EXTCAP_HDR_10 0x21D0 #define I3C_SCL_LOW_FORCE_STEP 0x21D4 #define I3C_EXTCAP_HDR_11 0x21D8 #define I3C_FILTER_CTRL 0x21DC #define I3C_EXTCAP_HDR_12 0x21E0 #define I3C_VGPIO_TX_CONFIG 0x21E4 #define I3C_VGPIO_TX_RESP 0x21E8 #define I3C_EXTCAP_HDR_13 0x21EC #define I3C_VGPIO_RX_MASK(x) ((x) * 4 + 0x21F0) #define I3C_EXTCAP_HDR_14 0x2210 #define I3C_HDR_DDR_RD_ABORT 0x2214 #define I3C_EXTCAP_HDR_15 0x2218 #define I3C_EVENT_COMMAND 0x221C #define I3C_ENTER_ACTIVITY_STATE 0x2220 #define I3C_MAX_WRITE_LENGTH 0x2224 #define I3C_MAX_READ_LENGTH 0x2228 #define I3C_DEVICE_COUNT 0x222C #define I3C_EXT_CURRENT_MASTER 0x2230 #define I3C_TEST_MODE_BYTE 0x2234 #define I3C_HDR_MODE 0x2238 #define I3C_CHAR_REG0 0x223C #define I3C_CHAR_REG1 0x2240 #define I3C_MAX_SPEED 0x2244 #define I3C_MAX_RD_TURN 0x2248 #define I3C_HDR_CAP 0x224C #define I3C_DATA_LEN_SLAVE_HDR 0x2250 #define I3C_DATA_SLAVE_HDR_TS_TX_PERIOD 0x2254 #define I3C_EXTCAP_HDR_16 0x22FC #define I3C_IBI_TX_DATA_PORT 0x2300 #define I3C_IBI_TX_BUF_LEVEL 0x2304 #define I3C_EXTCAP_HDR_17 0x230C #define I3C_DEBUG_CMD_QUEUE_0(x) ((x) * 8 + 0x2310) #define I3C_DEBUG_CMD_QUEUE_1(x) ((x) * 8 + 0x2314) #define I3C_DEBUG_RESP_STAT_QUEUE(x) ((x) * 4 + 0x2330) #define I3C_DEBUG_TX_DATA_BUF(x) ((x) * 4 + 0x2340) #define I3C_DEBUG_RX_DATA_BUF(x) ((x) * 4 + 0x2380) #define I3C_DEBUG_IBI_QUEUE(x) ((x) * 4 + 0x23C0) #define I3C_DEBUG_IBI_TX_BUF(x) ((x) + 0x2400) #define I3C_VGPIO_TX_MONITOR 0x2410 #define I3C_VGPIO_RX_PAYLOAD 0x2414 #define I3C_MASTER_STAT_0 0x2418 #define I3C_MASTER_STAT_1 0x241C #define I3C_SLAVE_STAT 0x2420 #define I3C_EXTCAP_HDR18 0x2424 #define I3C_TS_OE_DISABLE 0x2428 #define I3C_CHICKEN 0x242C /* I3C_HC_CONTROL(0x4) Register bits */ #define I3C_ENABLE BIT(31) #define I3C_ABORT BIT(29) #define I3C_HOT_JOIN_CTRL BIT(8) #define I3C_I2C_SLV_PRESENT BIT(7) #define I3C_IBA_INCLUDE BIT(0) /* I3C_EXT_DEV_CTRL(0x2198) */ #define I3C_HWACG_DISABLE_S BIT(31) #define I3C_VGPIO_TX_REQ_FORCE BIT(22) #define I3C_NOTIFY_VGPIO_RX BIT(21) #define I3C_VGPIO_ENABLE BIT(20) #define I3C_IBI_REQ BIT(15) #define I3C_HJ_REQ BIT(14) #define I3C_MASTERSHIP_REQ BIT(13) #define I3C_SLAVE_MODE BIT(12) #define I3C_DATA_SWAP BIT(10) #define I3C_GETMXDS_5BYTE BIT(9) #define I3C_TX_DMA_FREE_RUN BIT(8) #define I3C_RX_STALL_EN BIT(7) #define I3C_TX_STALL_EN BIT(6) #define I3C_RX_DMA_EN BIT(5) #define I3C_TX_DMA_EN BIT(4) #define I3C_ADDR_OPT_EN BIT(2) #define I3C_SLOW_BUS BIT(0) /* I3C_MASTER_DEVICE_ADDR(0x8) */ #define I3C_DYNAMIC_ADDR_VALID BIT(31) #define I3C_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(22, 16)) /* I3C_HC_CAPABILITIES(0xC) */ #define I3C_HDR_TS_EN BIT(7) #define I3C_HDR_DDR_EN BIT(6) #define I3C_NON_CURRENT_MASTER_CAP BIT(5) #define I3C_AUTO_CMD BIT(3) #define I3C_COMB_CMD BIT(2) /* I3C_RESET_CTRL(0x10) */ #define I3C_IBI_QUEUE_RST BIT(5) #define I3C_RX_FIFO_RST BIT(4) #define I3C_TX_FIFO_RST BIT(3) #define I3C_RESP_QUEUE_RST BIT(2) #define I3C_CMD_QUEUE_RST BIT(1) #define I3C_SOFT_RST BIT(0) /* I3C_EXT_RESET_CTRL(0x21A0) */ #define I3C_SCL_LOW_FORCE BTT(8) #define I3C_IBI_TX_FIFO_RST BIT(5) /* I3C_PRESENT_STATE(0x14) */ #define I3C_CURRENT_MASTER BIT(2) /* I3C_PRESENT_STATE_DEBUG(0x2030) */ #define I3C_CMD_TID_STATUS GENMASK(27, 24) #define I3C_CM_TFR_ST_STATUS GENMASK(21, 16) #define I3C_CM_TFR_STATUS GENMASK(13, 8) #define I3C_SCL_LINE_SIGNAL_LV BIT(0) #define I3C_SDA_LINE_SIGNAL_LV BIT(1) /* I3C_PIO_INTR_STATUS(0x320) */ #define I3C_TRANSFER_ERR_STAT BIT(9) #define I3C_TRANSFER_ABORT_STAT BIT(5) #define I3C_RESP_READY_STAT BIT(4) #define I3C_CMD_QUEUE_READY_STAT BIT(3) #define I3C_IBI_THLD_STAT BIT(2) #define I3C_RX_THLD_STAT BIT(1) #define I3C_TX_THLD_STAT BIT(0) /* I3C_EXT_PIO_INTR_STATUS(0x2184) */ #define I3C_SCL_LOW_FORCE_DONE_STAT BIT(31) #define I3C_VGPIO_TX_PARITY_ERR_STAT BIT(22) #define I3C_VGPIO_RX_STAT BIT(21) #define I3C_VGPIO_TX_STAT BIT(20) #define I3C_SLAVE_INT_ACKED BIT(18) #define I3C_GETACCMST_STAT BIT(17) #define I3C_BUS_AVAIL_STAT BIT(16) #define I3C_BUS_IDLE_STAT BIT(15) #define I3C_TRAILING_STAT BIT(12) #define I3C_BUS_AVAIL_VIOLATION_STAT BIT(11) #define I3C_BUS_IDLE_VIOLATION_STAT BIT(10) #define I3C_INTR_ALL (I3C_RESP_READY_STAT | \ I3C_TRANSFER_ABORT_STAT | \ I3C_TRANSFER_ERR_STAT) #define I3C_EXT_INTR_ALL (I3C_BUS_IDLE_VIOLATION_STAT | \ I3C_BUS_AVAIL_VIOLATION_STAT | \ I3C_TRAILING_STAT | \ I3C_BUS_IDLE_VIOLATION_STAT | \ I3C_BUS_AVAIL_VIOLATION_STAT | \ I3C_GETACCMST_STAT | \ I3C_SLAVE_INT_ACKED | \ I3C_VGPIO_TX_STAT | \ I3C_VGPIO_RX_STAT | \ I3C_VGPIO_TX_PARITY_ERR_STAT | \ I3C_SCL_LOW_FORCE_DONE_STAT) /* I3C_PIO_INTR_EN(0x324) */ #define I3C_TRANSFER_ERR_STAT_EN BIT(9) #define I3C_TRANSFER_ABORT_STAT_EN BIT(5) #define I3C_RESP_READY_STAT_EN BIT(4) #define I3C_CMD_QUEUE_READY_STAT_EN BIT(3) #define I3C_IBI_THLD_STAT_EN BIT(2) #define I3C_RX_THLD_STAT_EN BIT(1) #define I3C_TX_THLD_STAT_EN BIT(0) /* I3C_EXT_PIO_INTR_EN(0x2188) */ #define I3C_SCL_LOW_FORCE_DONE_STAT_EN BIT(31) #define I3C_VGPIO_TX_PARITY_STAT_EN BIT(22) #define I3C_VGPIO_RX_STAT_EN BIT(21) #define I3C_VGPIO_TX_STAT_EN BIT(20) #define I3C_SLAVE_INT_ACKED_EN BIT(18) #define I3C_GETACCMST_STAT_EN BIT(17) #define I3C_BUS_AVAIL_STAT_EN BIT(16) #define I3C_BUS_IDLE_STAT_EN BIT(15) #define I3C_TRAILING_STAT_EN BIT(12) #define I3C_BUS_AVAIL_VIOLATION_STAT_EN BIT(11) #define I3C_BUS_IDLE_VIOLATION_STAT_EN BIT(10) /* I3C_PIO_INTR_SIGNAL_EN(0x328) */ #define I3C_TRANSFER_ERR_SIG_EN BIT(9) #define I3C_TRANSFER_ABORT_SIG_EN BIT(5) #define I3C_RESP_READY_SIG_EN BIT(4) #define I3C_CMD_QUEUE_READY_SIG_EN BIT(3) #define I3C_IBI_THLD_SIG_EN BIT(2) #define I3C_RX_THLD_SIG_EN BIT(1) #define I3C_TX_THLD_SIG_EN BIT(0) /* I3C_EXT_PIO_INTR_SIGNAL_EN(0x218C) */ #define I3C_SCL_LOW_FORCE_DONE_SIG_EN BIT(31) #define I3C_VGPIO_TX_PARITY_SIG_EN BIT(22) #define I3C_VGPIO_RX_SIG_EN BIT(21) #define I3C_VGPIO_TX_SIG_EN BIT(20) #define I3C_SLAVE_INT_ACKED_SIG_EN BIT(18) #define I3C_GETACCMST_SIG_EN BIT(17) #define I3C_BUS_AVAIL_SIG_EN BIT(16) #define I3C_BUS_IDLE_SIG_EN BIT(15) #define I3C_TRAILING_SIG_EN BIT(12) #define I3C_BUS_AVAIL_VIOLATION_SIG_EN BIT(11) #define I3C_BUS_IDLE_VIOLATION_SIG_EN BIT(10) /* I3C_PIO_INTR_FORCE(0x32C) */ #define I3C_TRANSFER_ERR_FORCE BIT(9) #define I3C_TRANSFER_ABORT_FORCE BIT(5) #define I3C_RESP_READY_FORCE BIT(4) #define I3C_CMD_QUEU_READY_FORCE BIT(3) #define I3C_IBI_THLD_FORCE BIT(2) #define I3C_RX_THLD_FORCE BIT(1) #define I3C_TX_THLD_FORCE BIT(0) /* I3C_EXT_PIO_INTR_FORCE(0x2190) */ #define I3C_SCL_LOW_FORCE_DONE_FORCE BIT(31) #define I3C_VGPIO_TX_PARITY_FORCE BIT(22) #define I3C_VGPIO_RX_FORCE BIT(21) #define I3C_VGPIO_TX_FORCE BIT(20) #define I3C_SLAVE_INT_ACKED_FORCE BIT(18) #define I3C_GETACCMST_FORCE BIT(17) #define I3C_BUS_AVAIL_FORCE BIT(16) #define I3C_BUS_IDLE_FORCE BIT(15) #define I3C_TRAILING_FORCE BIT(12) #define I3C_BUS_AVAIL_VIOLATION_FORCE BIT(11) #define I3C_BUS_IDLE_VIOLATION_FORCE BIT(10) /* I3C_QUEUE_THLD_CTRL(0x310) */ #define I3C_IBI_STAT_QUEUE_THLD(x) (((x) << 24) & GENMASK(31, 24)) #define IBI_DATA_THLD(x) (((x) << 16) & GENMASK(23, 16)) #define RESP_BUF_THLD(x) (((x) << 8) & GENMASK(15, 8)) #define RESP_BUF_THLD_MASK GENMASK(15, 8) #define I3C_CMD_EMPTY_BUF_THLD(x) (((x) << 0) & GENMASK(7, 0)) /* I3C_DATA_BUFFER_THLD_CTRL(0x314) */ #define I3C_RX_START_THLD(x) (((x) << 24) & GENMASK(26, 24)) #define I3C_TX_START_THLD(x) (((x) << 16) & GENMASK(18, 16)) #define I3C_RX_BUF_THLD(x) (((x) << 8) & GENMASK(10, 8)) #define I3C_TX_BUF_THLD(x) (((x) << 0) & GENMASK(2, 0)) /* I3C_QUEUE_SIZE(0x318) */ #define I3C_TX_DATA_BUFF_SIZE GENMASK(31, 24) #define I3C_RX_DATA_BUFF_SIZE GENMASK(23, 16) #define I3C_IBI_STATUS_SIZE GENMASK(15, 8) /* I3C_QUEUE_STATUS_LEVEL(0x2028) */ #define I3C_IBI_STAT_CNT_LEVEL GENMASK(28, 24) #define I3C_IBI_BUF_LEVEL GENMASK(23, 16) #define RESP_BUFFER_LEVEL GENMASK(15, 8) #define I3C_CMD_QUEUE_FREE_LEVEL GENMASK(7, 0) /* I3C_DATA_BUF_LEVEL(0x2304) */ #define I3C_TX_BUF_LEVEL GENMASK(20, 16) /* I3C_DATA_BUF_STATUS_LEVEL(0x202C) */ #define I3C_RX_BUF_LVL GENMASK(15, 8) #define I3C_TX_BUF_FREE_LVL GENMASK(7, 0) /* I3C_IBI_NOTIFY_CTRL(0x58) */ #define I3C_NOTIFY_SIR_REJECTED BIT(3) #define I3C_NOTIFY_MR_REJECTED BIT(1) #define I3C_NOTIFY_HJ_REJECTED BIT(0) /* I3C_SCL_QUARTER_PERIOD(0x21AC) */ #define I3C_FM GENMASK(31, 24) #define I3C_FM_PLUS GENMASK(23, 16) #define I3C_OPEN_DRAIN GENMASK(15, 8) #define I3C_PUSH_PULL GENMASK(7, 0) /* I3C_SCL_CTRL(0x21B0) */ #define I3C_PP_HCNT GENMASK(7, 0) /* I3C_TSU_STA(0x21B4) */ #define I3C_FM_tSU_STA GENMASK(23, 16) #define I3C_FM_PLUS_tSU_STA GENMASK(15, 8) #define I3C_tCBSr GENMASK(7, 0) /* I3C_TCBP(0x21B0) */ #define I3C_TCBP_MIXED GENMASK(23, 16) #define I3C_TCBP_PURE GENMASK(7, 0) /* I3C_TLOW(0x21BC) */ #define I3C_FM_tLOW GENMASK(24, 16) #define I3C_FM_PLUS_tLOW GENMASK(15, 8) #define I3C_tLOW_OD GENMASK(7, 0) /* I3C_THD_STA(0x21C0) */ #define I3C_FM_tHD_STA GENMASK(23, 16) #define I3C_FM_PLUS_tHD_STA GENMASK(15, 8) #define I3C_tCAS GENMASK(7, 0) /* I3C_TBUF(0x21C4) */ #define I3C_TBUF_MIXED GENMASK(24, 16) #define I3C_TBUF_PURE GENMASK(8, 0) /* I3C_BUS_CONDITION_CYCLE(0x21C8) */ #define I3C_BUS_AVAIL_CYCLE GENMASK(31, 24) #define I3C_BUS_IDLE_CYCLE GENMASK(23, 0) /* I3C_SAMPLE_CYCLE(0x21CC) */ #define I3C_HDR_TS_SDA_SAMPLE_CYCLE_FOR_OE GENMASK(13, 12) #define I3C_HDR_TS_SCL_SAMPLE_CYCLE_FOR_OE GENMASK(9, 8) #define I3C_HDR_TS_SAMPLE_CYCLE GENMASK(6, 4) #define I3C_SDA_SAMPLE_CYCLE GENMASK(1, 0) /* I3C_FILTER_CTRL(0x218D) */ #define I3C_FILTER_CYCLE_SDA GENMASK(23, 20) #define I3C_FILTER_EN_SDA BIT(16) #define I3C_FILTER_CYCLE_SCL GENMASK(7, 4) #define I3C_FILTER_EN_SCL BIT(0) /* I3C_VGPIO_TX_CONFIG(0x21E4) */ #define I3C_M_VGPIO_TX_DATA_LEN GENMASK(24, 23) #define I3C_S_VGPIO_TX_DATA_LEN GENMASK(22, 21) #define I3C_VGPIO_TX_DELAY_STEP GENMASK(20, 16) #define I3C_VGPIO_TX_PARITY_ERR_IDX GENMASK(12, 8) #define I3C_VGPIO_TX_CCC GENMASK(7, 0) /* I3C_DEV_ADDR_TABLE(0x100 ~ )*/ #define I3C_DEVICE_TYPE BIT(31) #define I3C_NACK_RETRY_CNT(x) (((x) << 29) & GENMASK(30, 29)) #define I3C_NACK_RETRY_CNT_MASK GENMASK(30, 29) #define I3C_DYNAMIC_ADDRESS(x) (((x) << 16) & GENMASK(26, 16)) #define I3C_DYNAMIC_ADDRESS_MASK GENMASK(23, 16) #define I3C_GET_DYNAMIC_ADDRESS(x) ((x) >> 16 & GENMASK(7, 0)) #define I3C_MR_REJECT BIT(14) #define I3C_SIR_REJECT BIT(13) #define I3C_IBI_PAYLOAD BIT(12) #define I3C_STATIC_ADDRESS_MASK GENMASK(6, 0) #define I3C_STATIC_ADDRESS(x) (((x) << 0) & GENMASK(6, 0)) /* I3C_EXT_DEV_ADDR_TABLE(0x2100 ~ )*/ #define I3C_IBI_PAYLOAD_SIZE(x) (((x) << 16) & GENMASK(23, 16)) #define I3C_IS_VGPIO_ALIVE BIT(12) #define I3C_DYNAMIC_ADDR_ASSIGNED BIT(7) /* I3C_DEV_CHAR_TABLE_1(0x204 ~ ) */ #define I3C_PID_1 GENMASK(31, 0) /* I3C_DEV_CHAR_TABLE_1(0x204 ~ ) */ #define I3C_PID_0 GENMASK(15, 0) /* I3C_DEV_CHAR_TABLE_2(0x208 ~ */ #define I3C_DCR GENMASK(15, 8) #define I3C_BCR GENMASK(7, 0) /* I3C_DEV_CHAR_TABLE_3(0x20C ~ */ #define I3C_CHAR_DYNAMIC_ADDR GENMASK(7, 0) /* I3C_CMD_QUEUE(0x300) */ #define I3C_TOC BIT(31) #define I3C_ROC BIT(30) #define I3C_RNW BIT(29) #define I3C_CP BIT(15) #define I3C_DEV_COUNT(x) (((x) << 26) & GENMASK(29, 26)) #define I3C_MODE(x) (((x) << 26) & GENMASK(28, 26)) #define I3C_BYTE_CNT(x) (((x) << 23) & GENMASK(25, 23)) #define I3C_DEVICE_INDEX(x) (((x) << 16) & GENMASK(20, 16)) #define I3C_CMD_CODE(x) (((x) << 7) & GENMASK(14, 7)) #define I3C_CMD_TID(x) (((x) << 3) & GENMASK(6, 3)) #define I3C_CMD_ATTR(x) ((x) & GENMASK(2, 0)) #define I3C_CMD_ATTR_R 0 #define I3C_CMD_ATTR_IM 1 #define I3C_CMD_ATTR_A 2 #define I3C_CMD_ATTR_C 3 #define I3C_CMD_ATTR_IC 7 //#define I3C_HDR_DDR BIT(1) //#define I3C_HDR_TS BIT(2) #define I3C_CMD_MAX_LEN (127) /* I3C_HDR_DDR_RD_ABORT(0x2214) */ #define HDR_EXIT BIT(31) #define HDR_DDR_RD_ABORT BIT(0) /* I3C_RESP_QUEUE(0x304) */ #define RESP_ERR_STAT(x) (((x) & GENMASK(31, 28)) >> 28) #define RESP_CMDID(x) (((x) & GENMASK(27, 24)) >> 24) #define RESP_DATA_LENGTH(x) ((x) & GENMASK(15, 0)) /* I3C_IBI_QUEUE */ #define I3C_IBI_STAT BIT(0) #define I3C_DATA_LENGTH(x) ((x) & 0xff) #define I3C_IBI_ID(x) (((x) >> 8) & 0xff) #define I3C_IBI_ADDR(x) (((x) >> 9) & 0x7f) #define I3C_IBI_RNW(x) (((x) >> 8) & 0x1) #define I3C_RX_CRC_ERROR 1 #define I3C_RX_PARITY_ERROR 2 #define I3C_RX_FRAME_ERROR 3 #define I3C_ADDR_HEADER_NACK_M2 4 #define I3C_NACK 5 #define I3C_OVERFLOW_OR_UNDERFLOW 6 #define I3C_ABORTED_BY_USING_ABORT 8 #define I3C_PURE_BUS 0 #define I3C_MIXED_FAST_BUS 2 #define I3C_MIXED_SLOW_BUS 3 #define CMD_QUEUE_DEPTH 4 #define RESP_STAT_QUEUE_DEPTH 4 #define RX_FIFO_DEPTH 16 #define TX_FIFO_DEPTH 16 #define IBI_STAT_QUEUE_DEPTH 16 #define IBI_DATA_BUFFER_DEPTH 16 #define I3C_CCC 0 #define I3C_READ 1 #define I3C_WRITE 2 #define EXYNOS_I3C_TIMEOUT (msecs_to_jiffies(1000)) #define EXYNOS_FIFO_SIZE 32 #define DEFAULT_CLK 200000000 #define RX_FRAME_ERROR_HDR_DDR 0 #define RX_CRC_ERROR_HDR_DDR 1 #define RX_PARITY_ERROR_HDR 2 #define TX_DATA_MISMATCH_M1 3 #define ADDR_HEADER_NACK_M2 4 #define ADDR_NACK 5 #define SYMBOL_2_ERROR_HDR_TS 6 #define ABORT 7 #define RX_OVERFLOW_TX_UNDERFLOW 8 #define I3C_OPEN_DRAIN_CLOCK 1000000 static void exynos_i3c_hci_master_enable(struct exynos_i3c_hci_master *master); static int exynos_i3c_hci_master_disable(struct exynos_i3c_hci_master *master); static inline void dump_i3c_dat(struct exynos_i3c_hci_master *i3c) { dev_info(i3c->dev, "register DAT\n" "DAT0 0x%08x " "DAT1 0x%08x " "DAT2 0x%08x " "DAT3 0x%08x\n " "DAT4 0x%08x " "DAT5 0x%08x " "DAT6 0x%08x " "DAT7 0x%08x\n " "EXTDAT0 0x%08x " "EXTDAT1 0x%08x " "EXTDAT2 0x%08x " "EXTDAT3 0x%08x\n " "EXTDAT4 0x%08x " "EXTDAT5 0x%08x " "EXTDAT6 0x%08x " "EXTDAT7 0x%08x\n " "free_rr_slots 0x%08x " , readl(i3c->regs + I3C_DEV_ADDR_TABLE(0)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(1)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(2)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(3)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(4)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(5)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(6)) , readl(i3c->regs + I3C_DEV_ADDR_TABLE(7)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(0)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(1)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(2)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(3)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(4)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(5)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(6)) , readl(i3c->regs + I3C_EXT_DEV_ADDR_TABLE(7)) , i3c->free_rr_slots ); } static inline void dump_i3c_register(struct exynos_i3c_hci_master *i3c) { dev_info(i3c->dev, "register dump(suspended : %d)\n" "HC_CONTROL 0x%08x " "MASTER_DEVICE_ADDR 0x%08x " "RESET_CTRL 0x%08x " "PRESENT_STATE 0x%08x \n" "INT_STATUS 0x%08x " "INT_EN 0x%08x " "INT_SIGNAL_EN 0x%08x " "QUEUE_THLD_CTRL 0x%08x \n" "DATA_BUFFER_THLD_CTRL 0x%08x " "QUEUE_STATUS_LV 0x%08x " "DATA_BUF_LEVEL 0x%08x " "IBI_QUEUE_CTRL 0x%08x \n" "TRAILING_CYCLE 0x%08x " "SCL_QUARTER_PERIOD 0x%08x " "SCL_CTRL 0x%08x " "TSU_STA 0x%08x \n" "TCBP 0x%08x " "TLOW 0x%08x " "THD_STA 0x%08x " "TBUF 0x%08x \n" "BUS_CONDITION_CYCLE 0x%08x " "SAMPLE_CYCLE 0x%08x " "FILTER_CTRL 0x%08x " "PRESENT_STATE_DEBUG 0x%08x \n" , i3c->suspended , readl(i3c->regs + I3C_HC_CONTROL) , readl(i3c->regs + I3C_MASTER_DEVICE_ADDR) , readl(i3c->regs + I3C_RESET_CTRL) , readl(i3c->regs + I3C_PRESENT_STATE) , readl(i3c->regs + I3C_PIO_INTR_STATUS) , readl(i3c->regs + I3C_PIO_INTR_EN) , readl(i3c->regs + I3C_PIO_INTR_SIGNAL_EN) , readl(i3c->regs + I3C_QUEUE_THLD_CTRL) , readl(i3c->regs + I3C_DATA_BUFFER_THLD_CTRL) , readl(i3c->regs + I3C_QUEUE_STATUS_LEVEL) , readl(i3c->regs + I3C_DATA_BUFFER_THLD_STATUS_LEVEL) , readl(i3c->regs + I3C_IBI_NOTIFY_CTRL) , readl(i3c->regs + I3C_TRAILING_CYCLE) , readl(i3c->regs + I3C_SCL_QUARTER_PERIOD) , readl(i3c->regs + I3C_SCL_CTRL) , readl(i3c->regs + I3C_TSU_STA) , readl(i3c->regs + I3C_TCBP) , readl(i3c->regs + I3C_TLOW) , readl(i3c->regs + I3C_THD_STA) , readl(i3c->regs + I3C_TBUF) , readl(i3c->regs + I3C_BUS_CONDITION_CYCLE) , readl(i3c->regs + I3C_SAMPLE_CYCLE) , readl(i3c->regs + I3C_FILTER_CTRL) , readl(i3c->regs + I3C_PRESENT_STATE_DEBUG) ); dump_i3c_dat(i3c); } static void exynos_i3c_hci_master_wr_to_tx_fifo(struct exynos_i3c_hci_master *master, const u8 *tx_buf, int nbytes) { writesl(master->regs + I3C_XFER_DATA_BUF, tx_buf, nbytes / 4); if (nbytes & 3) { u32 tmp = 0; memcpy(&tmp, tx_buf + (nbytes & ~3), nbytes & 3); writesl(master->regs + I3C_XFER_DATA_BUF, &tmp, 1); } } static void exynos_i3c_hci_master_rd_from_rx_fifo(struct exynos_i3c_hci_master *master, u8 *rx_buf, int nbytes) { if (nbytes == 0) return; readsl(master->regs + I3C_XFER_DATA_BUF, rx_buf, nbytes / 4); if (nbytes & 3) { u32 tmp; readsl(master->regs + I3C_XFER_DATA_BUF, &tmp, 1); memcpy(rx_buf + (nbytes & ~3), &tmp, nbytes & 3); } } static void exynos_i3c_hci_master_start_xfer_locked(struct exynos_i3c_hci_master *master) { struct exynos_i3c_hci_xfer *xfer = master->xfer_queue.xfer; unsigned int i; unsigned int queue_thld; dev_dbg(master->dev, "Start %s\n", __func__); if (!xfer) return; for (i = 0; i < xfer->ncmds; i++) { struct exynos_i3c_hci_cmd *cmd = &xfer->cmds[i]; exynos_i3c_hci_master_wr_to_tx_fifo(master, cmd->tx_buf, cmd->tx_len); } writel(I3C_RESP_READY_STAT_EN | I3C_TRANSFER_ERR_STAT_EN | I3C_TRANSFER_ABORT_STAT_EN | I3C_IBI_THLD_STAT_EN, master->regs + I3C_PIO_INTR_EN); writel(I3C_RESP_READY_STAT_EN | I3C_TRANSFER_ERR_STAT_EN | I3C_TRANSFER_ABORT_STAT_EN | I3C_IBI_THLD_STAT_EN, master->regs + I3C_PIO_INTR_SIGNAL_EN); queue_thld = readl(master->regs + I3C_QUEUE_THLD_CTRL); queue_thld &= ~RESP_BUF_THLD_MASK; queue_thld |= RESP_BUF_THLD(xfer->ncmds); writel(queue_thld, master->regs + I3C_QUEUE_THLD_CTRL); for (i = 0; i < xfer->ncmds; i++) { struct exynos_i3c_hci_cmd *cmd = &xfer->cmds[i]; writel(cmd->cmd | I3C_CMD_TID(i), master->regs + I3C_CMD_QUEUE); /* For Address Assign command, do not send data length */ if(I3C_CMD_ATTR(cmd->cmd) == I3C_CMD_ATTR_A) continue; if(cmd->cmd & I3C_RNW) { writel(cmd->rx_len << 16, master->regs + I3C_CMD_QUEUE); } else{ writel(cmd->tx_len << 16, master->regs + I3C_CMD_QUEUE); } } dev_dbg(master->dev, "End %s\n", __func__); } static void exynos_i3c_hci_master_unqueue_xfer_locked(struct exynos_i3c_hci_master *master, struct exynos_i3c_hci_xfer *xfer) { if (master->xfer_queue.xfer == xfer) { exynos_i3c_hci_master_disable(master); master->xfer_queue.xfer = NULL; writel(I3C_CMD_QUEUE_RST | I3C_RESP_QUEUE_RST | I3C_TX_FIFO_RST | I3C_RX_FIFO_RST, master->regs + I3C_RESET_CTRL); writel(0, master->regs + I3C_PIO_INTR_EN); exynos_i3c_hci_master_enable(master); } else { list_del_init(&xfer->node); } } static void exynos_i3c_hci_master_unqueue_xfer(struct exynos_i3c_hci_master *master, struct exynos_i3c_hci_xfer *xfer) { unsigned long flags; spin_lock_irqsave(&master->xfer_queue.lock, flags); exynos_i3c_hci_master_unqueue_xfer_locked(master, xfer); spin_unlock_irqrestore(&master->xfer_queue.lock, flags); } static void exynos_i3c_hci_master_end_xfer_locked(struct exynos_i3c_hci_master *master, u32 isr) { struct exynos_i3c_hci_xfer *xfer = master->xfer_queue.xfer; int i, ret = 0; struct exynos_i3c_hci_cmd *cmd; unsigned int queue_status; unsigned int rx_len; unsigned int resp; dev_dbg(master->dev, "Start %s\n", __func__); if (!xfer) return; /* TODO : Dequeueing RESP until RESP is EMPTY because there are multiple cmd in one Xfer */ for (queue_status = readl(master->regs + I3C_QUEUE_STATUS_LEVEL); (queue_status & RESP_BUFFER_LEVEL); queue_status = readl(master->regs + I3C_QUEUE_STATUS_LEVEL)) { resp = readl(master->regs + I3C_RESP_QUEUE); cmd = &xfer->cmds[RESP_CMDID(resp)]; dev_dbg(master->dev, "RESP 0x%08x\n", resp); rx_len = min_t(unsigned int, RESP_DATA_LENGTH(resp), cmd->rx_len); cmd->error = RESP_ERR_STAT(resp); if (cmd->rx_len && !cmd->error) exynos_i3c_hci_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len); } for (i = 0; i < xfer->ncmds; i++) { unsigned int err = xfer->cmds[i].error; if (!err) continue; dev_err(master->dev, "CMD[%d] Error detected: 0x%08x\n", i, err); switch(err){ case I3C_NACK: dev_err(master->dev, "Error: NOACK\n"); ret = -EIO; break; case I3C_RX_FRAME_ERROR: dev_err(master->dev, "Error: I3C_HDR_DDR_RX_FRAME_ERROR\n"); ret = -EIO; break; case I3C_RX_CRC_ERROR: dev_err(master->dev, "Error: I3C_HDR_DDR_RX_CRC_ERROR\n"); ret = -EIO; break; case I3C_RX_PARITY_ERROR: dev_err(master->dev, "Error: I3C_HDR_RX_PARITY_ERROR\n"); ret = -EIO; break; case I3C_ADDR_HEADER_NACK_M2: dev_err(master->dev, "Error: I3C_ADDR_HEADER_NOACK_M2_ERROR\n"); ret = -EINVAL; break; case I3C_ABORTED_BY_USING_ABORT: dev_err(master->dev, "Error: I3C_ABORTED_BY_USING_ABORT\n"); ret = -EIO; break; case I3C_OVERFLOW_OR_UNDERFLOW: dev_err(master->dev, "Error: I3C_OVERFLOW_OR_UNDERFLOW\n"); ret = -ENOSPC; break; default: dev_err(master->dev, "Error: Reserved or Not defined\n"); ret = -EINVAL; break; } } xfer->ret = ret; complete(&xfer->comp); if (ret < 0) exynos_i3c_hci_master_unqueue_xfer_locked(master, xfer); xfer = list_first_entry_or_null(&master->xfer_queue.list, struct exynos_i3c_hci_xfer, node); if (xfer) list_del_init(&xfer->node); master->xfer_queue.xfer = xfer; exynos_i3c_hci_master_start_xfer_locked(master); dev_dbg(master->dev, "End %s\n", __func__); } static void exynos_i3c_hci_master_queue_xfer(struct exynos_i3c_hci_master *master, struct exynos_i3c_hci_xfer *xfer) { unsigned long flags; dev_dbg(master->dev, "Start %s\n", __func__); init_completion(&xfer->comp); spin_lock_irqsave(&master->xfer_queue.lock, flags); if (master->xfer_queue.xfer) { list_add_tail(&xfer->node, &master->xfer_queue.list); } else { master->xfer_queue.xfer = xfer; exynos_i3c_hci_master_start_xfer_locked(master); } spin_unlock_irqrestore(&master->xfer_queue.lock, flags); dev_dbg(master->dev, "End %s\n", __func__); } static inline struct exynos_i3c_hci_master * to_exynos_i3c_hci_master(struct i3c_master_controller *master) { return container_of(master, struct exynos_i3c_hci_master, base); } static void exynos_i3c_hci_master_enable(struct exynos_i3c_hci_master *master) { writel(readl(master->regs + I3C_HC_CONTROL) | I3C_ENABLE, master->regs + I3C_HC_CONTROL); } static int exynos_i3c_hci_master_disable(struct exynos_i3c_hci_master *master) { u32 status; writel(readl(master->regs + I3C_HC_CONTROL) & ~I3C_ENABLE, master->regs + I3C_HC_CONTROL); return readl_poll_timeout_atomic(master->regs + I3C_PRESENT_STATE_DEBUG, status, (status & I3C_CM_TFR_ST_STATUS) == 0, 10, 1000000); } static enum i3c_error_code exynos_i3c_hci_cmd_get_err(struct exynos_i3c_hci_cmd *cmd) { switch (cmd->error) { case TX_DATA_MISMATCH_M1: return I3C_ERROR_M1; case ADDR_HEADER_NACK_M2: return I3C_ERROR_M2; default: break; } return I3C_ERROR_UNKNOWN; } static struct exynos_i3c_hci_xfer* exynos_i3c_hci_master_alloc_xfer(struct exynos_i3c_hci_master *master, int nxfers) { struct exynos_i3c_hci_xfer *xfer; xfer = kzalloc(struct_size(xfer, cmds, nxfers), GFP_KERNEL); if (!xfer) return NULL; INIT_LIST_HEAD(&xfer->node); xfer->ncmds = nxfers; xfer->ret = -ETIMEDOUT; return xfer; } static void exynos_i3c_hci_master_free_xfer(struct exynos_i3c_hci_xfer *xfer) { kfree(xfer); } static int exynos_i3c_hci_master_get_free_slot(struct exynos_i3c_hci_master *master) { if (!(master->free_rr_slots & GENMASK(master->maxdevs - 1, 0))) return -ENOSPC; return ffs(master->free_rr_slots) - 1; } static int exynos_i3c_hci_master_get_addr_slot(struct exynos_i3c_hci_master *master, u8 dyn_addr) { u32 rr0, rr1; int i; for (i = 0; i < master->maxdevs; i++) { rr0 = readl(master->regs + I3C_DEV_ADDR_TABLE(i)); rr1 = readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(i)); if (!(rr1 & I3C_DYNAMIC_ADDR_ASSIGNED)) continue; if ((rr0 & I3C_DEVICE_TYPE) || I3C_GET_DYNAMIC_ADDRESS(rr0) != dyn_addr) continue; return i; } return -EINVAL; } static void exynos_i3c_hci_master_upd_i3c_scl_lim(struct exynos_i3c_hci_master *master) { struct i3c_master_controller *m = &master->base; // unsigned long i3c_lim_period, pres_step, ncycles, ipclk_rate; unsigned long new_i3c_scl_lim = 0; struct i3c_dev_desc *dev; // u32 prescl1, ctrl; /* TODO : i3c_master_get_bus로 전환 */ i3c_bus_for_each_i3cdev(&m->bus, dev) { unsigned long max_fscl; max_fscl = max(I3C_CCC_MAX_SDR_FSCL(dev->info.max_read_ds), I3C_CCC_MAX_SDR_FSCL(dev->info.max_write_ds)); switch (max_fscl) { case I3C_SDR1_FSCL_8MHZ: max_fscl = 8000000; break; case I3C_SDR2_FSCL_6MHZ: max_fscl = 6000000; break; case I3C_SDR3_FSCL_4MHZ: max_fscl = 4000000; break; case I3C_SDR4_FSCL_2MHZ: max_fscl = 2000000; break; case I3C_SDR0_FSCL_MAX: default: max_fscl = 0; break; } if (max_fscl && (new_i3c_scl_lim > max_fscl || !new_i3c_scl_lim)) new_i3c_scl_lim = max_fscl; } /* Only update PRESCL_CTRL1 if the I3C SCL limitation has changed. */ if (new_i3c_scl_lim == master->i3c_scl_lim) return; master->i3c_scl_lim = new_i3c_scl_lim; if (!new_i3c_scl_lim) return; /* Todo : set_timing * We only use 200MHz ipclk and sfr reset value for test * */ // ipclk_rate = clk_get_rate(master->ipclk); /* pres_step = ipclk_rate / (master->base.bus->scl_rate.i3c * 4); prescl1 = readl(master->regs + PRESCL_CTRL1) & ~PRESCL_CTRL1_PP_LOW_MASK; ctrl = readl(master->regs + CTRL); i3c_lim_period = DIV_ROUND_UP(1000000000, master->i3c_scl_lim); ncycles = DIV_ROUND_UP(i3c_lim_period, pres_step); if (ncycles < 4) ncycles = 0; else ncycles -= 4; prescl1 |= PRESCL_CTRL1_PP_LOW(ncycles); if (ctrl & CTRL_DEV_EN) exynos_i3c_hci_master_disable(master); writel(prescl1, master->regs + PRESCL_CTRL1); if (ctrl & CTRL_DEV_EN) exynos_i3c_hci_master_enable(master); */ } static void exynos_i3c_hci_master_upd_i3c_addr(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); u32 addr; u32 dat; dev_dbg(master->dev, "Start %s\n", __func__); addr = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr; dat = readl(master->regs + I3C_DEV_ADDR_TABLE(data->id)); if (dev->info.dyn_addr) { dat &= ~(I3C_DYNAMIC_ADDRESS_MASK | I3C_DEVICE_TYPE); dat |= I3C_DYNAMIC_ADDRESS(addr); writel(dat, master->regs + I3C_DEV_ADDR_TABLE(data->id)); } else { dat &= ~(I3C_STATIC_ADDRESS_MASK | I3C_DEVICE_TYPE); dat |= I3C_STATIC_ADDRESS(addr); writel(dat, master->regs + I3C_DEV_ADDR_TABLE(data->id)); } master->addrs[data->id] = addr; dev_dbg(master->dev, "End %s\n", __func__); } static int exynos_i3c_hci_master_attach_i3c_dev(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data; int slot; dev_dbg(master->dev, "Start %s dyn_addr : 0x%02X\n", __func__, dev->info.dyn_addr); slot = exynos_i3c_hci_master_get_free_slot(master); if (slot < 0) { dev_err(master->dev, "%s: failed to get slot\n", __func__); return slot; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { dev_err(master->dev, "%s: kzalloc fail\n", __func__); return -ENOMEM; } data->ibi = -1; data->id = slot; master->addrs[slot] = dev->info.dyn_addr ? : dev->info.static_addr; i3c_dev_set_master_data(dev, data); master->free_rr_slots &= ~BIT(slot); if (!dev->info.dyn_addr) { exynos_i3c_hci_master_upd_i3c_addr(dev); writel(readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(data->id)) | I3C_DYNAMIC_ADDR_ASSIGNED, master->regs + I3C_EXT_DEV_ADDR_TABLE(data->id)); } dev_dbg(master->dev, "End %s\n", __func__); return 0; } static void exynos_i3c_hci_master_detach_i3c_dev(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); writel(0, master->regs + I3C_DEV_CHAR_TABLE_0(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_1(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_2(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_3(data->id)); i3c_dev_set_master_data(dev, NULL); master->addrs[data->id] = 0; master->free_rr_slots |= BIT(data->id); kfree(data); } static int exynos_i3c_hci_master_attach_i2c_dev(struct i2c_dev_desc *dev) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data; int slot; unsigned dat; dev_dbg(master->dev, "Start %s i2c addr: 0x%02X\n", __func__, dev->boardinfo->base.addr); slot = exynos_i3c_hci_master_get_free_slot(master); if (slot < 0) return slot; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->id = slot; master->addrs[slot] = dev->boardinfo->base.addr; master->free_rr_slots &= ~BIT(slot); i2c_dev_set_master_data(dev, data); /* exynos cannot support 10bit address of i2c normal i2c device */ /* Write DAT */ dat = I3C_DEVICE_TYPE | I3C_NACK_RETRY_CNT(master->nack_retry_cnt) | I3C_STATIC_ADDRESS(dev->boardinfo->base.addr); dev_dbg(master->dev, "DAT(%d) : %lx\n", slot, dat); writel(dat, master->regs + I3C_DEV_ADDR_TABLE(data->id)); master->i2c_slv_present = true; dev_dbg(master->dev, "End %s\n", __func__); return 0; } static void exynos_i3c_hci_master_detach_i2c_dev(struct i2c_dev_desc *dev) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i2c_dev_get_master_data(dev); writel(0, master->regs + I3C_DEV_ADDR_TABLE(data->id)); writel(0, master->regs + I3C_EXT_DEV_ADDR_TABLE(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_0(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_1(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_2(data->id)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_3(data->id)); i2c_dev_set_master_data(dev, NULL); master->addrs[data->id] = 0; master->free_rr_slots |= BIT(data->id); kfree(data); } static int exynos_i3c_hci_master_do_daa(struct i3c_master_controller *m) { struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); u32 olddevs = 0, newdevs = 0; int ret, slot; u8 last_addr = 0; u32 dat, exdat; dev_dbg(master->dev, "Start %s\n", __func__); for (slot = 0; slot < master->maxdevs; slot++) { dat = readl(master->regs + I3C_DEV_ADDR_TABLE(slot)); exdat = readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(slot)); /* skip i2c device */ if (dat & I3C_DEVICE_TYPE || exdat & I3C_DYNAMIC_ADDR_ASSIGNED) olddevs |= BIT(slot); } master->dat_idx = master->maxdevs; /* Prepare RR slots before launching DAA. */ for (slot = 0; slot < master->maxdevs; slot++) { if (olddevs & BIT(slot)) continue; ret = i3c_master_get_free_addr(m, last_addr + 1); if (ret < 0) { dev_err(master->dev, "%s: Failed to get free\n", __func__); return -ENOSPC; } last_addr = ret; master->addrs[slot] = last_addr; if(master->dat_idx > slot) master->dat_idx = slot; writel(I3C_DYNAMIC_ADDRESS(last_addr) | I3C_NACK_RETRY_CNT(master->nack_retry_cnt), master->regs + I3C_DEV_ADDR_TABLE(slot)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_0(slot)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_1(slot)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_2(slot)); writel(0, master->regs + I3C_DEV_CHAR_TABLE_3(slot)); } writel((master->dat_idx << 19), master->regs + I3C_DCT_SECTION_OFFSET); ret = i3c_master_entdaa_locked(&master->base); if (ret && ret != I3C_ERROR_M2) { dev_err(master->dev, "%s: Failed to ENTDAA CCC\n", __func__); return ret; } for (slot = 0; slot < master->maxdevs; slot++) { if (readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(slot)) & I3C_DYNAMIC_ADDR_ASSIGNED) newdevs |= BIT(slot); } newdevs &= ~olddevs; /* * Clear all retaining registers filled during DAA. We already * have the addressed assigned to them in the addrs array. */ for (slot = 0; slot < master->maxdevs; slot++) { if (newdevs & BIT(slot)) i3c_master_add_i3c_dev_locked(m, master->addrs[slot]); } /* * Clear slots that ended up not being used. Can be caused by I3C * device creation failure or when the I3C device was already known * by the system but with a different address (in this case the device * already has a slot and does not need a new one). */ for (slot = 0; slot < master->maxdevs; slot++) { dat = readl(master->regs + I3C_DEV_ADDR_TABLE(slot)); exdat = readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(slot)); if (dat & I3C_DEVICE_TYPE) continue; if (master->free_rr_slots & BIT(slot) && (exdat & I3C_DYNAMIC_ADDR_ASSIGNED)) { writel(0, master->regs + I3C_DEV_ADDR_TABLE(slot)); writel(0, master->regs + I3C_EXT_DEV_ADDR_TABLE(slot)); } } i3c_master_defslvs_locked(&master->base); exynos_i3c_hci_master_upd_i3c_scl_lim(master); /* Mask Hot-Join and Mastership request interrupts. */ i3c_master_enec_locked(m, I3C_BROADCAST_ADDR, I3C_CCC_EVENT_HJ | I3C_CCC_EVENT_MR); dev_dbg(master->dev, "End %s\n", __func__); return 0; } #ifdef CONFIG_OF static int exynos_i3c_hci_parse_dt(struct exynos_i3c_hci_master *i3c, struct device *dev) { u32 temp; /* Mode of operation High/Fast/Fast+ Speed mode */ if (of_property_read_u32(dev->of_node, "samsung,fs-clock", &i3c->fs_clock)) i3c->fs_clock = I3C_BUS_I2C_FM_SCL_RATE; if (of_property_read_u32(dev->of_node, "samsung,fs-plus-clock", &i3c->fs_plus_clock)) i3c->fs_plus_clock = I3C_BUS_I2C_FM_PLUS_SCL_RATE; if (of_property_read_u32(dev->of_node, "samsung,open-drain-clock", &i3c->open_drain_clock)) i3c->open_drain_clock = I3C_OPEN_DRAIN_CLOCK; if (of_property_read_u32(dev->of_node, "samsung,push-pull-clock", &i3c->push_pull_clock)) i3c->push_pull_clock = I3C_BUS_TYP_I3C_SCL_RATE; if (of_get_property(dev->of_node, "samsung,disable_hold_initiating_command", NULL)) i3c->hold_initiating_command = 0; else i3c->hold_initiating_command = 1; if (of_property_read_u32(dev->of_node, "samsung,nack_retry_cnt", &temp)) i3c->nack_retry_cnt = 2; else i3c->nack_retry_cnt = temp; if (of_get_property(dev->of_node, "dma-mode", NULL)) i3c->dma_mode = 1; else i3c->dma_mode = 0; if (of_get_property(dev->of_node, "samsung,hot_join_nack", NULL)) i3c->hot_join_nack = 1; else i3c->hot_join_nack = 0; if (of_get_property(dev->of_node, "samsung,mixed-fast", NULL)) i3c->i3c_bus_mode = I3C_MIXED_FAST_BUS; else if (of_get_property(dev->of_node, "samsung,mixed-slow", NULL)) i3c->i3c_bus_mode = I3C_MIXED_SLOW_BUS; else i3c->i3c_bus_mode = I3C_PURE_BUS; if (of_get_property(dev->of_node, "samsung,notify_sir_rejected", NULL)) i3c->notify_sir_rejected = 1; else i3c->notify_sir_rejected = 0; if (of_get_property(dev->of_node, "samsung,notify_mr_rejected", NULL)) i3c->notify_mr_rejected = 1; else i3c->notify_mr_rejected = 0; if (of_get_property(dev->of_node, "samsung,notify_hj_rejected", NULL)) i3c->notify_hj_rejected = 1; else i3c->notify_hj_rejected = 0; if (of_get_property(dev->of_node, "samsung,mr_req_nack", NULL)) i3c->mr_req_nack = 1; else i3c->mr_req_nack = 0; if (of_get_property(dev->of_node, "samsung,sir_req_nack", NULL)) i3c->sir_req_nack = 1; else i3c->sir_req_nack = 0; if (of_get_property(dev->of_node, "samsung,filter_disable_sda", NULL)) i3c->filter_en_sda = 0; else i3c->filter_en_sda = 1; if (of_get_property(dev->of_node, "samsung,filter_disable_scl", NULL)) i3c->filter_en_scl = 0; else i3c->filter_en_scl = 1; if (of_get_property(dev->of_node, "samsung, iba_exclude", NULL)) i3c->iba_include = 0; else i3c->iba_include = 1; /* EXT_DEVICE_CTRL */ if (of_get_property(dev->of_node, "samsung,hwacg_enable_s", NULL)) i3c->hwacg_enable_s = 1; else i3c->hwacg_enable_s = 0; if (of_get_property(dev->of_node, "samsung,slave_mode", NULL)) i3c->slave_mode = 1; else i3c->slave_mode = 0; if (of_get_property(dev->of_node, "samsung,data_swap", NULL)) i3c->data_swap = 1; else i3c->data_swap = 0; if (of_get_property(dev->of_node, "samsung,tx_dma_free_run", NULL)) i3c->tx_dma_free_run = 1; else i3c->tx_dma_free_run = 0; if (of_get_property(dev->of_node, "samsung,rx_stall_dis", NULL)) i3c->rx_stall_en = 0; else i3c->rx_stall_en = 1; if (of_get_property(dev->of_node, "samsung,tx_stall_dis", NULL)) i3c->tx_stall_en = 0; else i3c->tx_stall_en = 1; if (of_get_property(dev->of_node, "samsung,addr_opt_en", NULL)) i3c->addr_opt_en = 1; else i3c->addr_opt_en = 0; return 0; } #endif static bool exynos_i3c_hci_broadcast_ccc(const struct i3c_ccc_cmd *cmd) { switch (cmd->id) { /* Broadcast CCC w/ extra data */ case I3C_CCC_ENEC(true): fallthrough; case I3C_CCC_DISEC(true): fallthrough; case I3C_CCC_SETMWL(true): fallthrough; case I3C_CCC_SETMRL(true): fallthrough; case I3C_CCC_DEFSLVS: fallthrough; case I3C_CCC_ENTHDR(0): return true; default: break; } return false; } static int exynos_i3c_hci_find_first_i3c_dev(struct exynos_i3c_hci_master *master, int slot) { int i; u32 reg; for (i = slot; i < master->maxdevs; i++) { reg = readl(master->regs + I3C_DEV_ADDR_TABLE(i)); if ((reg & I3C_DEVICE_TYPE) == 0) return i; } return 0; } static int exynos_i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *cmd) { struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_xfer *xfer; struct exynos_i3c_hci_cmd *ccmd; int ret; int slot; dev_dbg(master->dev, "Start %s\n", __func__); dev_info(master->dev, "addr: 0x%02X CCC: 0x%02X\n", cmd->dests[0].addr, cmd->id); xfer = exynos_i3c_hci_master_alloc_xfer(master, 1); if (!xfer) { dev_err(master->dev, "%s: failed to alloc xfer\n", __func__); return -ENOMEM; } ccmd = xfer->cmds; ccmd->cmd = I3C_CMD_CODE(cmd->id) | I3C_CP | I3C_ROC | I3C_TOC; if (cmd->id == I3C_CCC_ENTDAA || cmd->id == I3C_CCC_SETDASA){ if (master->dat_idx < 0 || master->dat_idx >= master->maxdevs){ dev_err(master->dev, "Invalid DAT Index\n", master->dat_idx); return -EINVAL; } ccmd->cmd |= I3C_DEVICE_INDEX(master->dat_idx); ccmd->cmd |= I3C_DEV_COUNT(master->maxdevs - master->dat_idx); ccmd->cmd |= I3C_CMD_ATTR(I3C_CMD_ATTR_A); } if (cmd->id & I3C_CCC_DIRECT) { slot = exynos_i3c_hci_master_get_addr_slot(master, cmd->dests[0].addr); if (slot < 0) { dev_err(master->dev, "send CCC to invalid addr. ret:%d\n", slot); return slot; } ccmd->cmd |= I3C_DEVICE_INDEX(slot); } if (exynos_i3c_hci_broadcast_ccc(cmd)) { /* * For a case that I2C and I3C slaves in same bus. * To send Broadcast CCC w/ extra data, DEV_INDEX should be one of I3C devices. * If DEV_INDEX is I2C device, master makes NOACK response. */ slot = exynos_i3c_hci_find_first_i3c_dev(master, 0); ccmd->cmd |= I3C_DEVICE_INDEX(slot); } if (cmd->rnw) { ccmd->cmd |= I3C_RNW; ccmd->rx_buf = cmd->dests[0].payload.data; ccmd->rx_len = cmd->dests[0].payload.len; } else { ccmd->tx_buf = cmd->dests[0].payload.data; ccmd->tx_len = cmd->dests[0].payload.len; } exynos_i3c_hci_master_queue_xfer(master, xfer); if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) { dev_err(master->dev, "I3C CCC Transfer timeout\n"); dump_i3c_register(master); exynos_i3c_hci_master_unqueue_xfer(master, xfer); } ret = xfer->ret; cmd->err = exynos_i3c_hci_cmd_get_err(&xfer->cmds[0]); exynos_i3c_hci_master_free_xfer(xfer); dev_dbg(master->dev, "End %s\n", __func__); return ret; } static bool exynos_i3c_hci_supports_ccc_cmd(struct i3c_master_controller *m, const struct i3c_ccc_cmd *cmd) { if (cmd->ndests > 1) return false; switch (cmd->id) { case I3C_CCC_ENEC(true): case I3C_CCC_ENEC(false): case I3C_CCC_DISEC(true): case I3C_CCC_DISEC(false): case I3C_CCC_ENTAS(0, true): case I3C_CCC_ENTAS(0, false): case I3C_CCC_RSTDAA(true): case I3C_CCC_RSTDAA(false): case I3C_CCC_ENTDAA: case I3C_CCC_SETMWL(true): case I3C_CCC_SETMWL(false): case I3C_CCC_SETMRL(true): case I3C_CCC_SETMRL(false): case I3C_CCC_DEFSLVS: case I3C_CCC_ENTHDR(0): case I3C_CCC_SETDASA: case I3C_CCC_SETNEWDA: case I3C_CCC_GETMWL: case I3C_CCC_GETMRL: case I3C_CCC_GETPID: case I3C_CCC_GETBCR: case I3C_CCC_GETDCR: case I3C_CCC_GETSTATUS: case I3C_CCC_GETACCMST: case I3C_CCC_GETMXDS: case I3C_CCC_GETHDRCAP: return true; default: break; } return false; } static int exynos_i3c_hci_xfer_priv(struct i3c_dev_desc *dev, struct i3c_priv_xfer *xfers, int nxfers) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); int i = 0; int ret = 0; int txslots = 0, rxslots = 0; struct exynos_i3c_hci_xfer *xfer; int slot; dev_dbg(master->dev, "Start %s\n", __func__); dev_dbg(master->dev, "addr : 0x%02X\n", dev->info.dyn_addr); if (!nxfers) return 0; if (nxfers > master->caps.cmdfifodepth) return -ENOTSUPP; /* max lenth config */ for (i = 0; i < nxfers; i++) { if (xfers[i].len > I3C_CMD_MAX_LEN) { dev_err(master->dev, "Xfer size %u is more than MAX_LEN %u\n", xfers[i].len, I3C_CMD_MAX_LEN); return -ENOTSUPP; } } for (i = 0; i < nxfers; i++) { if (xfers[i].rnw) rxslots += DIV_ROUND_UP(xfers[i].len, 4); else txslots += DIV_ROUND_UP(xfers[i].len, 4); } if (rxslots > master->caps.rxfifodepth || txslots > master->caps.txfifodepth) return -ENOTSUPP; xfer = exynos_i3c_hci_master_alloc_xfer(master, nxfers); if (!xfer) { dev_err(master->dev, "%s: failed to alloc xfer\n", __func__); return -ENOMEM; } for (i = 0; i < nxfers; i++) { struct exynos_i3c_hci_cmd *ccmd = &xfer->cmds[i]; u32 pl_len = xfers[i].len; slot = exynos_i3c_hci_master_get_addr_slot(master, dev->info.dyn_addr); ccmd->cmd |= I3C_DEVICE_INDEX(slot); ccmd->cmd |= I3C_BYTE_CNT(pl_len); if (xfers[i].rnw) { ccmd->cmd |= I3C_RNW; ccmd->rx_buf = xfers[i].data.in; ccmd->rx_len = xfers[i].len; } else { ccmd->tx_buf = xfers[i].data.out; ccmd->tx_len = xfers[i].len; } ccmd->cmd |= I3C_ROC | I3C_CMD_TID(i); if (i == (nxfers - 1)) ccmd->cmd |= I3C_TOC; } exynos_i3c_hci_master_queue_xfer(master, xfer); if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) { dev_err(master->dev, "I3C priv_xfer Transfer timeout\n"); dump_i3c_register(master); exynos_i3c_hci_master_unqueue_xfer(master, xfer); } ret = xfer->ret; for (i = 0; i < nxfers; i++) xfers[i].err = exynos_i3c_hci_cmd_get_err(&xfer->cmds[i]); exynos_i3c_hci_master_free_xfer(xfer); dev_dbg(master->dev, "End %s\n", __func__); return ret; } static int exynos_i3c_hci_master_i2c_xfers(struct i2c_dev_desc *dev, const struct i2c_msg *xfers, int nxfers) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct exynos_i3c_hci_i2c_dev_data *data = i2c_dev_get_master_data(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); unsigned int nrxwords = 0, ntxwords = 0; struct exynos_i3c_hci_xfer *xfer; int i, ret = 0; int slot; dev_dbg(master->dev, "Start %s\n", __func__); dev_dbg(master->dev, "addr : 0x%02X\n", xfers[0].addr); if (!nxfers) return 0; if (nxfers > master->caps.cmdfifodepth) return -ENOTSUPP; /* max length config */ for (i = 0; i < nxfers; i++) { if (xfers[i].len > I3C_CMD_MAX_LEN) return -ENOTSUPP; } for (i = 0; i < nxfers; i++) { if (xfers[i].flags & I2C_M_RD) nrxwords += DIV_ROUND_UP(xfers[i].len, 4); else ntxwords += DIV_ROUND_UP(xfers[i].len, 4); } if (ntxwords > master->caps.txfifodepth || nrxwords > master->caps.rxfifodepth) return -ENOTSUPP; xfer = exynos_i3c_hci_master_alloc_xfer(master, nxfers); if (!xfer) return -ENOMEM; for (i = 0; i < nxfers; i++) { struct exynos_i3c_hci_cmd *ccmd = &xfer->cmds[i]; slot = data->id; ccmd->cmd |= I3C_DEVICE_INDEX(slot); if (xfers[i].flags & I2C_M_RD) { ccmd->cmd |= I3C_RNW; ccmd->rx_buf = xfers[i].buf; ccmd->rx_len = xfers[i].len; } else { ccmd->tx_buf = xfers[i].buf; ccmd->tx_len = xfers[i].len; } ccmd->cmd |= I3C_CMD_TID(i) | I3C_ROC; if (i == (nxfers - 1)) ccmd->cmd |= I3C_TOC; dev_dbg(master->dev, "CMD %d 0x%x\n", i, ccmd->cmd); } /* Exclude 0x7e for i2c private transfers */ writel(readl(master->regs + I3C_HC_CONTROL) & ~I3C_IBA_INCLUDE, master->regs+I3C_HC_CONTROL); exynos_i3c_hci_master_queue_xfer(master, xfer); if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) { dev_err(master->dev, "I3C i2c xfer Transfer timeout\n"); dump_i3c_register(master); exynos_i3c_hci_master_unqueue_xfer(master, xfer); } ret = xfer->ret; exynos_i3c_hci_master_free_xfer(xfer); writel(readl(master->regs + I3C_HC_CONTROL) | I3C_IBA_INCLUDE, master->regs+I3C_HC_CONTROL); dev_dbg(master->dev, "End %s\n", __func__); return ret; } static int exynos_i3c_hci_master_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) { exynos_i3c_hci_master_upd_i3c_addr(dev); return 0; } static int exynos_i3c_hci_master_clk_cfg(struct exynos_i3c_hci_master *master) { unsigned long ipclk_rate; //u32 tbuf; int ret; __u32 fs_clock_div_quat; __u32 fs_plus_clock_div_quat; __u32 open_drain_clock_div_quat; __u32 push_pull_clock_div_quat; ipclk_rate = clk_get_rate(master->ipclk); if (!ipclk_rate) { dev_err(master->dev, "Failed to get clock %d\n"); return -EINVAL; } if (ipclk_rate != DEFAULT_CLK) { ret = clk_set_rate(master->ipclk, DEFAULT_CLK); if (ret < 0) { dev_err(master->dev, "Failed to set clock %d\n", DEFAULT_CLK); return -EINVAL; } ipclk_rate = clk_get_rate(master->ipclk); } dev_info(master->dev, "ipclk: %luHz SCL_QUAR_PER 0x%x\n", ipclk_rate, readl(master->regs + I3C_SCL_QUARTER_PERIOD)); /* Tbuf reset value 0x09 is too short to make STOP completely */ //tbuf = readl(master->regs + I3C_TBUF); //writel((tbuf & ~0xff) | 0x30, master->regs + I3C_TBUF); /* Todo : setting timing parameters */ /* But We have no calculating equiation * We only use default reset value for 200MHz ipclk and 400KHz FM, 1MHz FM+ mode */ fs_clock_div_quat = ipclk_rate / master->fs_clock / 4; fs_plus_clock_div_quat = ipclk_rate / master->fs_plus_clock / 4; open_drain_clock_div_quat = ipclk_rate / master->open_drain_clock / 4; push_pull_clock_div_quat = ipclk_rate / master->push_pull_clock / 4; writel((fs_clock_div_quat & 0xff) << 24 | (fs_plus_clock_div_quat & 0xff) << 16 | (open_drain_clock_div_quat & 0xff) << 8 | (push_pull_clock_div_quat & 0xff) << 0, master->regs + I3C_SCL_QUARTER_PERIOD); /* Todo : setting PP Low, TSU_STA, TCBP, TLOW, THD_STA, TBUF, BUS_AVAIL * I3C_PP_HCNT = 2 * PP_CLK - 1 * writel((push_pull_clock_div_quat * 2 - 1) & 0xff, i3c->regs + I3C_SCL_CTRL); * TSU_STA = 600ns in 400KHz, 260ns in 1MHz, tCBSr = 1/4T? * TCBP_MIXED = tSU_STA in FM, TCBP_PURE = 1/2*tCBSr * TLOW = 1300ns in 400KHz, 500ns in 1MHz, 200ns in OD * THD = 600ns in 400KHz, 260ns in 1MHz, 38.4n in OD * TBUF = 1300ns in 400Khz, 45ns in OD * tAVAL = 1000ns in OD, tIDLE = 1ms in OD * */ return 0; } static void exynos_i3c_hci_master_config(struct exynos_i3c_hci_master *m) { u32 ctrl = readl(m->regs + I3C_HC_CONTROL); /* clear optional bits */ ctrl &= (I3C_ENABLE | I3C_ABORT | I3C_HOT_JOIN_CTRL); if (m->i2c_slv_present) ctrl |= I3C_I2C_SLV_PRESENT; if (m->iba_include) ctrl |= I3C_IBA_INCLUDE; writel(ctrl, m->regs + I3C_HC_CONTROL); } static void exynos_i3c_hci_master_extconfig(struct exynos_i3c_hci_master *m) { u32 ctrl = readl(m->regs + I3C_EXT_DEVICE_CTRL); /* clear optional bits */ ctrl &= (I3C_VGPIO_TX_REQ_FORCE | I3C_IBI_REQ | I3C_HJ_REQ | I3C_MASTERSHIP_REQ); if (!m->hwacg_enable_s) ctrl |= I3C_HWACG_DISABLE_S; if (m->notify_vgpio_rx) ctrl |= I3C_NOTIFY_VGPIO_RX; if (m->vgpio_en) ctrl |= I3C_VGPIO_ENABLE; if (m->slave_mode) ctrl |= I3C_SLAVE_MODE; if (m->data_swap) ctrl |= I3C_DATA_SWAP; if (m->getmxds_5byte) ctrl |= I3C_GETMXDS_5BYTE; if (m->tx_dma_free_run) ctrl |= I3C_TX_DMA_FREE_RUN; if (m->rx_stall_en) ctrl |= I3C_RX_STALL_EN; if (m->tx_stall_en) ctrl |= I3C_TX_STALL_EN; if (m->addr_opt_en) ctrl |= I3C_ADDR_OPT_EN; if (m->i3c_bus_mode == I3C_MIXED_SLOW_BUS) ctrl |= I3C_SLOW_BUS; writel(ctrl, m->regs + I3C_EXT_DEVICE_CTRL); } static int exynos_i3c_hci_master_bus_init(struct i3c_master_controller *m) { struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct i3c_device_info info = { }; u32 ctrl; int ret; unsigned int char_reg0, char_reg1; // unsigned int queue_thld, buffer_thld; dev_dbg(master->dev, "Start %s\n", __func__); /* Setting I3C_DEVICE_CTRL * For now, we set sfr reset value */ switch (m->bus.mode) { case I3C_BUS_MODE_PURE: ctrl = I3C_PURE_BUS; break; case I3C_BUS_MODE_MIXED_FAST: ctrl = I3C_MIXED_FAST_BUS; break; case I3C_BUS_MODE_MIXED_SLOW: ctrl = I3C_MIXED_SLOW_BUS; break; default: dev_err(master->dev, "%s: Not configured bus mode\n", __func__); return -EINVAL; } exynos_i3c_hci_master_extconfig(master); /* Set Timing */ exynos_i3c_hci_master_clk_cfg(master); writel(I3C_EXT_INTR_ALL, master->regs + I3C_EXT_PIO_INTR_STATUS); writel(I3C_INTR_ALL, master->regs + I3C_PIO_INTR_STATUS); writel(0, master->regs + I3C_PIO_INTR_EN); writel(0, master->regs + I3C_EXT_PIO_INTR_EN); writel(0, master->regs + I3C_PIO_INTR_SIGNAL_EN); writel(0, master->regs + I3C_EXT_PIO_INTR_SIGNAL_EN); /* Setting I3C_DEVICE_ADDR * Get an address for the master. */ ret = i3c_master_get_free_addr(m, 0); if (ret < 0) { dev_err(master->dev, "%s: Failed to get free addr for MST\n", __func__); return ret; } writel(I3C_DYNAMIC_ADDR_VALID | I3C_DYNAMIC_ADDR(ret), master->regs + I3C_MASTER_DEVICE_ADDR); info.dyn_addr = ret; char_reg0 = readl(master->regs + I3C_CHAR_REG0); char_reg1 = readl(master->regs + I3C_CHAR_REG1); info.dcr = char_reg0 & 0xff; info.bcr = (char_reg0 >> 8) & 0xff; info.pid = (char_reg0 >> 16) & 0xffff; info.pid |= (u64)char_reg0 << 16; if (info.bcr & I3C_BCR_HDR_CAP) info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR); ret = i3c_master_set_info(&master->base, &info); if (ret) return ret; /* S/W Reset cannot be called to prevent clearing DAT */ writel(0x3e, master->regs + I3C_RESET_CTRL); writel(0x120, master->regs + I3C_EXT_RESET_CTRL); /* * Enable Hot-Join, and, when a Hot-Join request happens, disable all * events coming from this device. * * We will issue ENTDAA afterwards from the threaded IRQ handler. */ exynos_i3c_hci_master_config(master); exynos_i3c_hci_master_enable(master); dev_dbg(master->dev, "End %s\n", __func__); return 0; } static void exynos_i3c_hci_store_dat(struct exynos_i3c_hci_master *master) { int i; for(i = 0; i < MAX_DEVS ; i++){ master->DAT[i] = readl(master->regs + I3C_DEV_ADDR_TABLE(i)); master->EXT_DAT[i] = readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(i)); master->DCT[i][0] = readl(master->regs + I3C_DEV_CHAR_TABLE_0(i)); master->DCT[i][1] = readl(master->regs + I3C_DEV_CHAR_TABLE_1(i)); master->DCT[i][2] = readl(master->regs + I3C_DEV_CHAR_TABLE_2(i)); master->DCT[i][3] = readl(master->regs + I3C_DEV_CHAR_TABLE_3(i)); } } static void exynos_i3c_hci_load_dat(struct exynos_i3c_hci_master *master) { int i; for(i = 0; i < MAX_DEVS ; i++){ writel(master->DAT[i], master->regs + I3C_DEV_ADDR_TABLE(i)); writel(master->EXT_DAT[i], master->regs + I3C_EXT_DEV_ADDR_TABLE(i)); writel(master->DCT[i][0], master->regs + I3C_DEV_CHAR_TABLE_0(i)); writel(master->DCT[i][1], master->regs + I3C_DEV_CHAR_TABLE_1(i)); writel(master->DCT[i][2], master->regs + I3C_DEV_CHAR_TABLE_2(i)); writel(master->DCT[i][3],master->regs + I3C_DEV_CHAR_TABLE_3(i)); } } static int exynos_i3c_hci_runtime_suspend(struct device *dev) { dev_dbg(dev, "runtime suspending\n", __func__); /* struct platform_device *pdev = to_platform_device(dev); struct exynos_i3c_hci_master *master = platform_get_drvdata(pdev); clk_disable(master->ipclk); clk_disable(master->pclk); master->runtime_resumed = 0; */ return 0; } static int exynos_i3c_hci_runtime_resume(struct device *dev) { /* struct platform_device *pdev = to_platform_device(dev); struct exynos_i3c_hci_master *master = platform_get_drvdata(pdev); int ret = 0; clk_enable(master->ipclk); if (ret) { return ret; } clk_enable(master->pclk); if (ret) { return ret; } master->runtime_resumed = 1; */ dev_dbg(dev, "runtime resumed\n", __func__); return 0; } static int exynos_i3c_hci_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct exynos_i3c_hci_master *master = platform_get_drvdata(pdev); dev_dbg(master->dev, "suspend_noirq\n", __func__); master->current_master = readl(master->regs + I3C_MASTER_DEVICE_ADDR); exynos_i3c_hci_store_dat(master); /* S/W reset while entering suspend */ writel(I3C_SOFT_RST, master->regs + I3C_RESET_CTRL); exynos_i3c_hci_master_disable(master); clk_disable(master->ipclk); clk_disable(master->gate_clk); master->suspended = 1; return 0; } static int exynos_i3c_hci_resume_noirq(struct device *dev) { int ret = 0; struct platform_device *pdev = to_platform_device(dev); struct exynos_i3c_hci_master *master = platform_get_drvdata(pdev); dev_dbg(master->dev, "resume_noirq\n", __func__); ret = clk_enable(master->ipclk); if (ret) { dev_err(master->dev, "ipclk enable fail\n"); return ret; } ret = clk_enable(master->gate_clk); if (ret) { dev_err(master->dev, "gate_clk enable fail\n"); clk_disable(master->ipclk); return ret; } writel(master->current_master, master->regs + I3C_MASTER_DEVICE_ADDR); exynos_i3c_hci_load_dat(master); exynos_i3c_hci_master_extconfig(master); /* Set Timing */ exynos_i3c_hci_master_clk_cfg(master); exynos_i3c_hci_master_config(master); exynos_i3c_hci_master_enable(master); master->suspended = 0; return 0; } static void exynos_i3c_hci_master_bus_cleanup(struct i3c_master_controller *m) { struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); exynos_i3c_hci_master_disable(master); } static void exynos_i3c_hci_master_handle_ibi(struct exynos_i3c_hci_master *master, unsigned int ibiq) { struct exynos_i3c_hci_i2c_dev_data *data; bool data_consumed = false; struct i3c_ibi_slot *slot; u32 addr = I3C_IBI_ADDR(ibiq); struct i3c_dev_desc *dev; size_t nbytes; u8 *buf; unsigned int i; /* * FIXME: maybe we should report the FIFO OVF errors to the upper * layer. * Todo: dynamic addr to ibi slot number */ for (i = 0; i < master->ibi.num_slots; i++) { dev = master->ibi.slots[i]; if (dev && dev->info.dyn_addr == addr) break; } if (i == master->ibi.num_slots) { dev_err(master->dev, "Invalid ibi slot\n"); return; } if (!dev) { dev_err(master->dev, "ibi slot(%x) not found\n", addr); return; } spin_lock(&master->ibi.lock); data = i3c_dev_get_master_data(dev); slot = i3c_generic_ibi_get_free_slot(data->ibi_pool); if (!slot) goto out_unlock; buf = slot->data; nbytes = I3C_DATA_LENGTH(ibiq); for (i = 0; i < nbytes; i++) { buf[i] = readl(master->regs + I3C_IBI_PORT); } slot->len = min_t(unsigned int, I3C_DATA_LENGTH(ibiq), dev->ibi->max_payload_len); i3c_master_queue_ibi(dev, slot); data_consumed = true; out_unlock: spin_unlock(&master->ibi.lock); /* Consume data from the FIFO if it's not been done already. */ if (!data_consumed) { for (i = 0; i < I3C_DATA_LENGTH(ibiq); i++) readl(master->regs + I3C_IBI_PORT); } } static void exynos_i3c_hci_master_demux_ibis(struct exynos_i3c_hci_master *master) { unsigned int queue_status; unsigned int ibiq; for (queue_status = readl(master->regs + I3C_QUEUE_STATUS_LEVEL); (queue_status & I3C_IBI_STAT_CNT_LEVEL); queue_status = readl(master->regs + I3C_QUEUE_STATUS_LEVEL)) { ibiq = readl(master->regs + I3C_IBI_PORT); switch(I3C_IBI_ID(ibiq)) { case 0x2: /* HJ Case */ queue_work(master->base.wq, &master->hj_work); break; default: exynos_i3c_hci_master_handle_ibi(master, ibiq); break; } } } static irqreturn_t exynos_i3c_hci_master_interrupt(int irq, void *data) { struct exynos_i3c_hci_master *master = data; u32 status; dev_dbg(master->dev, "Start %s\n", __func__); status = readl(master->regs + I3C_PIO_INTR_STATUS); if (!(status & readl(master->regs + I3C_PIO_INTR_EN))) { writel(status, master->regs + I3C_PIO_INTR_STATUS); return IRQ_NONE; } spin_lock(&master->xfer_queue.lock); dev_dbg(master->dev, "INT STATUS 0x%08x\n", status); if (status & I3C_TRANSFER_ERR_STAT) dev_err(master->dev, "INT STATUS 0x%08x Transfer ERR Interrupt detected\n", status); if (status & I3C_TRANSFER_ABORT_STAT) dev_err(master->dev, "INT STATUS 0x%08x Transfer ABORT Interrupt detected\n", status); if (status & I3C_RESP_READY_STAT) exynos_i3c_hci_master_end_xfer_locked(master, status); else if (!(status & I3C_IBI_THLD_STAT)) dev_err(master->dev, "INT STATUS 0x%08x Wrong with transaction\n", status); /* Pending clear */ writel(status & ~I3C_IBI_THLD_STAT, master->regs + I3C_PIO_INTR_STATUS); spin_unlock(&master->xfer_queue.lock); if (status & I3C_IBI_THLD_STAT) { exynos_i3c_hci_master_demux_ibis(master); writel(I3C_IBI_THLD_STAT, master->regs + I3C_PIO_INTR_STATUS); } dev_dbg(master->dev, "End %s\n", __func__); return IRQ_HANDLED; } static int exynos_i3c_hci_master_disable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); int ret; ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); if (ret) dev_err(master->dev, "Fail to DISEC CCC while disable_ibi, ret = %d\n", ret); return ret; } static int exynos_i3c_hci_master_enable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); unsigned long flags; // u32 sircfg, sirmap; unsigned int dat, exdat; int ret; spin_lock_irqsave(&master->ibi.lock, flags); /* CDNS SIR_MAP contains DEV_ROLE(slv or mst), DEV_DA, DEV_PL, ACK and DEV_SLOW. * Exynos uses DAT as IBI MAP register. DAT includes DYNAMIC_ADDRESS, IBI_PAYLOAD_SIZE, SIR_REG_NACK bits. */ dat = readl(master->regs + I3C_DEV_ADDR_TABLE(data->id)); exdat = readl(master->regs + I3C_EXT_DEV_ADDR_TABLE(data->id)); if (!(exdat & I3C_DYNAMIC_ADDR_ASSIGNED)) { dev_err(master->dev, "Enable_ibi fails: DYNAMIC ADDR wasn't assigned\n"); return -ENODEV; } else if (dat & I3C_DEVICE_TYPE) { dev_err(master->dev, "Enable_ibi fails: dev is I2C slave\n"); return -ENODEV; }; exdat |= I3C_IBI_PAYLOAD_SIZE(dev->info.max_ibi_len); dat |= I3C_IBI_PAYLOAD; dat &= ~I3C_SIR_REJECT; writel(dat, master->regs + I3C_DEV_ADDR_TABLE(data->id)); writel(exdat, master->regs + I3C_EXT_DEV_ADDR_TABLE(data->id)); spin_unlock_irqrestore(&master->ibi.lock, flags); ret = i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); if (ret) { dev_err(master->dev, "Fail to ENEC CCC while enable_ibi, ret = %d\n", ret); } return ret; } static int exynos_i3c_hci_master_request_ibi(struct i3c_dev_desc *dev, const struct i3c_ibi_setup *req) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); unsigned long flags; unsigned int i; data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req); if (IS_ERR(data->ibi_pool)) return PTR_ERR(data->ibi_pool); spin_lock_irqsave(&master->ibi.lock, flags); for (i = 0; i < master->ibi.num_slots; i++) { if (!master->ibi.slots[i]) { data->ibi = i; master->ibi.slots[i] = dev; break; } } spin_unlock_irqrestore(&master->ibi.lock, flags); if (i < master->ibi.num_slots) return 0; i3c_generic_ibi_free_pool(data->ibi_pool); data->ibi_pool = NULL; return -ENOSPC; } static void exynos_i3c_hci_master_free_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct exynos_i3c_hci_master *master = to_exynos_i3c_hci_master(m); struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); unsigned long flags; spin_lock_irqsave(&master->ibi.lock, flags); master->ibi.slots[data->ibi] = NULL; data->ibi = -1; spin_unlock_irqrestore(&master->ibi.lock, flags); i3c_generic_ibi_free_pool(data->ibi_pool); } static void exynos_i3c_hci_master_recycle_ibi_slot(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot) { struct exynos_i3c_hci_i2c_dev_data *data = i3c_dev_get_master_data(dev); i3c_generic_ibi_recycle_slot(data->ibi_pool, slot); } static const struct i3c_master_controller_ops exynos_i3c_hci_master_ops = { .bus_init = exynos_i3c_hci_master_bus_init, .bus_cleanup = exynos_i3c_hci_master_bus_cleanup, .priv_xfers = exynos_i3c_hci_xfer_priv, .i2c_xfers = exynos_i3c_hci_master_i2c_xfers, .attach_i3c_dev = exynos_i3c_hci_master_attach_i3c_dev, .reattach_i3c_dev = exynos_i3c_hci_master_reattach_i3c_dev, .detach_i3c_dev = exynos_i3c_hci_master_detach_i3c_dev, .attach_i2c_dev = exynos_i3c_hci_master_attach_i2c_dev, .detach_i2c_dev = exynos_i3c_hci_master_detach_i2c_dev, .do_daa = exynos_i3c_hci_master_do_daa, .send_ccc_cmd = exynos_i3c_hci_send_ccc_cmd, .supports_ccc_cmd = exynos_i3c_hci_supports_ccc_cmd, .enable_ibi = exynos_i3c_hci_master_enable_ibi, .disable_ibi = exynos_i3c_hci_master_disable_ibi, .request_ibi = exynos_i3c_hci_master_request_ibi, .free_ibi = exynos_i3c_hci_master_free_ibi, .recycle_ibi_slot = exynos_i3c_hci_master_recycle_ibi_slot, }; static void exynos_i3c_hci_master_hj(struct work_struct *work) { struct exynos_i3c_hci_master *master = container_of(work, struct exynos_i3c_hci_master, hj_work); i3c_master_do_daa(&master->base); } static int exynos_i3c_hci_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct exynos_i3c_hci_master *master; struct resource *mem; int ret, irq; dev_info(&pdev->dev, "Entering I3C probe\n"); if (!np) { dev_err(&pdev->dev, "No device node\n"); return -ENOENT; } master = devm_kzalloc(&pdev->dev, sizeof(struct exynos_i3c_hci_master), GFP_KERNEL); if (!master) { dev_err(&pdev->dev, "No memory for I3C device\n"); return -ENOMEM; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); master->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(master->regs)) { dev_err(&pdev->dev, "faile to ioremap\n"); return PTR_ERR(master->regs); } master->ipclk = devm_clk_get(&pdev->dev, "ipclk"); if (IS_ERR(master->ipclk)) { dev_err(&pdev->dev, "cannot get ipclk(sysclk)\n"); return PTR_ERR(master->ipclk); } master->gate_clk = devm_clk_get(&pdev->dev, "gate_clk"); if (IS_ERR(master->gate_clk)) { dev_err(&pdev->dev, "cannot get gateclk(pclk)\n"); return PTR_ERR(master->gate_clk); } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "cannot get I3C IRQ\n"); return irq; } ret = clk_prepare_enable(master->gate_clk); if (ret) { dev_err(&pdev->dev, "failed to gateclk enable\n"); return ret; } ret = clk_prepare_enable(master->ipclk); if (ret) { dev_err(&pdev->dev, "failed to ipclk enable\n"); goto err_enable_ipclk; } if(pdev->dev.of_node) exynos_i3c_hci_parse_dt(master, &pdev->dev); spin_lock_init(&master->xfer_queue.lock); INIT_LIST_HEAD(&master->xfer_queue.list); INIT_WORK(&master->hj_work, exynos_i3c_hci_master_hj); ret = devm_request_irq(&pdev->dev, irq, exynos_i3c_hci_master_interrupt, 0, dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); goto err_disable_pclk; } /* TOTO : load from DAT entries */ master->maxdevs = 8; master->free_rr_slots = GENMASK(master->maxdevs, 0); platform_set_drvdata(pdev, master); master->dev = &pdev->dev; /* TODO : IBI work */ spin_lock_init(&master->ibi.lock); master->ibi.num_slots = IBI_STAT_QUEUE_DEPTH; master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots, sizeof(*master->ibi.slots), GFP_KERNEL); if (!master->ibi.slots) goto err_disable_pclk; master->caps.cmdfifodepth = CMD_QUEUE_DEPTH; master->caps.respfifodepth = RESP_STAT_QUEUE_DEPTH; master->caps.rxfifodepth = RX_FIFO_DEPTH; master->caps.txfifodepth = TX_FIFO_DEPTH; master->caps.ibiqfifodepth = IBI_STAT_QUEUE_DEPTH; master->caps.ibidfifodepth = IBI_DATA_BUFFER_DEPTH; // writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL); writel(I3C_IBI_THLD_STAT_EN, master->regs + I3C_PIO_INTR_EN); writel(I3C_IBI_THLD_SIG_EN, master->regs + I3C_PIO_INTR_SIGNAL_EN); ret = i3c_master_register(&master->base, &pdev->dev, &exynos_i3c_hci_master_ops, false); if (ret) { dev_err(&pdev->dev, "failed to i3c master register ret(%d)\n", ret); goto err_disable_pclk; } dev_info(&pdev->dev, "I3C probe success\n"); return 0; err_disable_pclk: clk_disable_unprepare(master->ipclk); err_enable_ipclk: clk_disable_unprepare(master->gate_clk); return ret; } static int exynos_i3c_hci_remove(struct platform_device *pdev) { struct exynos_i3c_hci_master *master = platform_get_drvdata(pdev); int ret; ret = i3c_master_unregister(&master->base); if (ret) return ret; clk_disable_unprepare(master->ipclk); clk_disable_unprepare(master->gate_clk); return 0; } static const struct dev_pm_ops exynos_i3c_hci_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos_i3c_hci_suspend_noirq, exynos_i3c_hci_resume_noirq) SET_RUNTIME_PM_OPS(exynos_i3c_hci_runtime_suspend, exynos_i3c_hci_runtime_resume, NULL) }; static const struct of_device_id exynos_i3c_hci_match[] = { { .compatible = "samsung,exynos-i3c-hci" }, {}, }; MODULE_DEVICE_TABLE(of, exynos_i3c_hci_match); static struct platform_driver exynos_i3c_hci_driver = { .probe = exynos_i3c_hci_probe, .remove = exynos_i3c_hci_remove, .driver = { .name = "exynos-i3c-hci", .pm = &exynos_i3c_hci_pm, .of_match_table = of_match_ptr(exynos_i3c_hci_match), }, }; module_platform_driver(exynos_i3c_hci_driver); MODULE_AUTHOR("Kang Kyungwoo \ Cha Myungsu "); MODULE_DESCRIPTION("Exynos I3C HCI master driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:exynos-i3c-hci-master");