/* This file is part of the depthcharge project. */ /* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include static void i2c_clk_configure(unsigned int bus, enum i2c_speed speed) { int clk_div = 0, t_high = 0, t_low = 0, t_cycle = 0; struct qup_regs *regs = qup[bus].regs; switch (speed) { case I2C_SPEED_STANDARD: clk_div = 7; t_high = 10; t_low = 11; t_cycle = 26; break; case I2C_SPEED_FAST: clk_div = 2; t_high = 5; t_low = 12; t_cycle = 24; break; case I2C_SPEED_FAST_PLUS: clk_div = 1; t_high = 3; t_low = 9; t_cycle = 18; break; default: die("Unsupported I2C speed"); } write32(®s->geni_ser_m_clk_cfg, (clk_div << 4) | 1); /* Serial clock frequency is 19.2 MHz */ write32(®s->i2c_scl_counters, ((t_high << 20) | (t_low << 10) | t_cycle)); } void i2c_init(unsigned int bus, enum i2c_speed speed) { uint32_t proto; struct qup_regs *regs = qup[bus].regs; qupv3_se_fw_load_and_init(bus, SE_PROTOCOL_I2C, MIXED); clock_enable_qup(bus); i2c_clk_configure(bus, speed); proto = ((read32(®s->geni_fw_revision_ro) & GENI_FW_REVISION_RO_PROTOCOL_MASK) >> GENI_FW_REVISION_RO_PROTOCOL_SHIFT); assert(proto == 3); /* Serial engine IO initialization */ write32(®s->geni_cgc_ctrl, DEFAULT_CGC_EN); write32(®s->dma_general_cfg, (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON | DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON)); write32(®s->geni_output_ctrl, DEFAULT_IO_OUTPUT_CTRL_MSK); write32(®s->geni_force_default_reg, FORCE_DEFAULT); /* Serial engine IO set mode */ write32(®s->se_irq_en, (GENI_M_IRQ_EN | GENI_S_IRQ_EN | DMA_TX_IRQ_EN | DMA_RX_IRQ_EN)); write32(®s->se_gsi_event_en, 0); /* Set RX and RFR watermark */ write32(®s->geni_rx_watermark_reg, 0); write32(®s->geni_rx_rfr_watermark_reg, FIFO_DEPTH - 2); /* FIFO PACKING CONFIGURATION */ write32(®s->geni_tx_packing_cfg0, PACK_VECTOR0 | (PACK_VECTOR1 << 10)); write32(®s->geni_tx_packing_cfg1, PACK_VECTOR2 | (PACK_VECTOR3 << 10)); write32(®s->geni_rx_packing_cfg0, PACK_VECTOR0 | (PACK_VECTOR1 << 10)); write32(®s->geni_rx_packing_cfg1, PACK_VECTOR2 | (PACK_VECTOR3 << 10)); write32(®s->geni_byte_granularity, (log2(BITS_PER_WORD) - 3)); /* GPIO Configuration */ gpio_configure(qup[bus].pin[0], qup[bus].func[0], GPIO_PULL_UP, GPIO_2MA, GPIO_OUTPUT_ENABLE); gpio_configure(qup[bus].pin[1], qup[bus].func[1], GPIO_PULL_UP, GPIO_2MA, GPIO_OUTPUT_ENABLE); /* Select and setup FIFO mode */ write32(®s->geni_m_irq_clear, 0xFFFFFFFF); write32(®s->geni_s_irq_clear, 0xFFFFFFFF); write32(®s->dma_tx_irq_clr, 0xFFFFFFFF); write32(®s->dma_rx_irq_clr, 0xFFFFFFFF); write32(®s->geni_m_irq_enable, (M_COMMON_GENI_M_IRQ_EN | M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN | M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)); write32(®s->geni_s_irq_enable, (S_COMMON_GENI_S_IRQ_EN | S_CMD_DONE_EN)); clrbits32(®s->geni_dma_mode_en, GENI_DMA_MODE_EN); } static int i2c_do_xfer(unsigned int bus, struct i2c_msg segment, unsigned int prams) { unsigned int cmd = (segment.flags & I2C_M_RD) ? 2 : 1; unsigned int master_cmd_reg_val = (cmd << M_OPCODE_SHFT); struct qup_regs *regs = qup[bus].regs; void *dout = NULL, *din = NULL; if (!(segment.flags & I2C_M_RD)) { write32(®s->i2c_tx_trans_len, segment.len); write32(®s->geni_tx_watermark_reg, TX_WATERMARK); dout = segment.buf; } else { write32(®s->i2c_rx_trans_len, segment.len); din = segment.buf; } master_cmd_reg_val |= (prams & M_PARAMS_MSK); write32(®s->geni_m_cmd0, master_cmd_reg_val); return qup_handle_transfer(bus, dout, din, segment.len); } int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments, int seg_count) { struct i2c_msg *seg = segments; int ret = 0; while (!ret && seg_count--) { /* Stretch means end with repeated start, not stop */ u32 stretch = (seg_count ? 1 : 0); u32 m_param = 0; m_param |= (stretch << 2); m_param |= ((seg->slave & 0x7F) << 9); ret = i2c_do_xfer(bus, *seg, m_param); seg++; } return ret; }