// SPDX-License-Identifier: GPL-2.0 /* * arch/sw_64/chip/chip3/i2c-lib.c * * Copyright (C) 2020 Platform Software * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * The drivers in this file are synchronous/blocking. In addition, * use poll mode to read/write slave devices on the I2C bus instead * of the interrupt mode. */ #include #include #include #include #define CPLD_BUSNR 2 #ifndef _I2C_DEBUG_FLAG_ #define _I2C_DEBUG_FLAG_ 0 #endif #define ICC_DEBUG(fmt, ...) \ do { \ if (_I2C_DEBUG_FLAG_) { \ printk(fmt, ##__VA_ARGS__); \ } \ } while (0) #define IC_CLK_KHZ 25000 /* I2C register definitions */ #define DW_IC_CON 0x0 #define DW_IC_STATUS 0x3800 #define DW_IC_DATA_CMD 0x0800 #define DW_IC_TAR 0x00200 #define DW_IC_ENABLE 0x3600 #define DW_IC_CMD 0x0100 #define DW_IC_STOP 0x0200 #define DW_IC_SDA_HOLD 0x3e00 #define DW_IC_SDA_SETUP 0x4a00 #define DW_IC_SS_SCL_HCNT 0x0a00 #define DW_IC_SS_SCL_LCNT 0x0c00 #define DW_IC_FS_SCL_HCNT 0x0e00 #define DW_IC_FS_SCL_LCNT 0x1000 #define DW_IC_TX_TL 0x1e00 #define DW_IC_RX_TL 0x1c00 #define DW_IC_INTR_MASK 0x1800 #define MAX_RETRY 10000000 #define DW_IC_STATUS_ACTIVITY 0x1 #define DW_IC_STATUS_TFNF 0x2 #define DW_IC_STATUS_TFE 0x4 #define DW_IC_STATUS_RFNE 0x8 #define DW_IC_STATUS_RFF 0x10 #define DW_IC_CON_MASTER 0x1 #define DW_IC_CON_SPEED_STD 0x2 #define DW_IC_CON_SPEED_FAST 0x4 #define DW_IC_CON_10BITADDR_MASTER 0x10 #define DW_IC_CON_RESTART_EN 0x20 #define DW_IC_CON_SLAVE_DISABLE 0x40 #define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \ DW_IC_CON_SLAVE_DISABLE | \ DW_IC_CON_RESTART_EN) #define DW_IC_INTR_RX_UNDER 0x001 #define DW_IC_INTR_RX_OVER 0x002 #define DW_IC_INTR_RX_FULL 0x004 #define DW_IC_INTR_TX_OVER 0x008 #define DW_IC_INTR_TX_EMPTY 0x010 #define DW_IC_INTR_RD_REQ 0x020 #define DW_IC_INTR_TX_ABRT 0x040 #define DW_IC_INTR_RX_DONE 0x080 #define DW_IC_INTR_ACTIVITY 0x100 #define DW_IC_INTR_STOP_DET 0x200 #define DW_IC_INTR_START_DET 0x400 #define DW_IC_INTR_GEN_CALL 0x800 #define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ DW_IC_INTR_TX_EMPTY | \ DW_IC_INTR_TX_ABRT | \ DW_IC_INTR_STOP_DET) enum i2c_bus_operation { I2C_BUS_READ, I2C_BUS_WRITE, }; static uint64_t m_i2c_base_address; /* * This function get I2Cx controller base address * * @param i2c_controller_index Bus Number of I2C controller. * @return I2C BAR. */ uint64_t get_i2c_bar_addr(uint8_t i2c_controller_index) { uint64_t base_addr = 0; if (i2c_controller_index == 0) base_addr = PAGE_OFFSET | IO_BASE | IIC0_BASE; else if (i2c_controller_index == 1) base_addr = PAGE_OFFSET | IO_BASE | IIC1_BASE; else if (i2c_controller_index == 2) base_addr = PAGE_OFFSET | IO_BASE | IIC2_BASE; return base_addr; } void write_cpu_i2c_controller(uint64_t offset, uint32_t data) { mb(); *(volatile uint32_t *)(m_i2c_base_address + offset) = data; } uint32_t read_cpu_i2c_controller(uint64_t offset) { uint32_t data; data = *(volatile uint32_t *)(m_i2c_base_address + offset); mb(); return data; } static int poll_for_status_set0(uint16_t status_bit) { uint64_t retry = 0; uint32_t temp = read_cpu_i2c_controller(DW_IC_STATUS); temp = read_cpu_i2c_controller(DW_IC_STATUS); while (retry < MAX_RETRY) { if (read_cpu_i2c_controller(DW_IC_STATUS) & status_bit) break; retry++; } if (retry == MAX_RETRY) return -ETIME; return 0; } static uint32_t i2c_dw_scl_lcnt(uint32_t ic_clk, uint32_t t_low, uint32_t tf, uint32_t offset) { /* * Conditional expression: * * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (t_low + tf) * * DW I2C core starts counting the SCL CNTs for the LOW period * of the SCL clock (t_low) as soon as it pulls the SCL line. * In order to meet the t_low timing spec, we need to take into * account the fall time of SCL signal (tf). Default tf value * should be 0.3 us, for safety. */ return ((ic_clk * (t_low + tf) + 500000) / 1000000) - 1 + offset; } static uint32_t i2c_dw_scl_hcnt(uint32_t ic_clk, uint32_t t_symbol, uint32_t tf, uint32_t cond, uint32_t offset) { /* * DesignWare I2C core doesn't seem to have solid strategy to meet * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec * will result in violation of the tHD;STA spec. */ if (cond) /* * Conditional expression: * * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH * * This is based on the DW manuals, and represents an ideal * configuration. The resulting I2C bus speed will be faster * than any of the others. * * If your hardware is free from tHD;STA issue, try this one. */ return (ic_clk * t_symbol + 500000) / 1000000 - 8 + offset; /* * Conditional expression: * * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) * * This is just experimental rule; the tHD;STA period turned * out to be proportinal to (_HCNT + 3). With this setting, * we could meet both tHIGH and tHD;STA timing specs. * * If unsure, you'd better to take this alternative. * * The reason why we need to take into account "tf" here, * is the same as described in i2c_dw_scl_lcnt(). */ return (ic_clk * (t_symbol + tf) + 500000) / 1000000 - 3 + offset; } static int wait_for_cpu_i2c_bus_busy(void) { uint64_t retry = 0; uint32_t status = 0; do { retry++; status = !!(read_cpu_i2c_controller(DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY); } while ((retry < MAX_RETRY) && status); if (retry == MAX_RETRY) return -ETIME; return 0; } static int i2c_read(uint8_t reg_offset, uint8_t *buffer, uint32_t length) { int status; uint32_t i; status = poll_for_status_set0(DW_IC_STATUS_TFE); if (status) return status; write_cpu_i2c_controller(DW_IC_DATA_CMD, reg_offset); for (i = 0; i < length; i++) { if (i == length - 1) write_cpu_i2c_controller(DW_IC_DATA_CMD, DW_IC_CMD | DW_IC_STOP); else write_cpu_i2c_controller(DW_IC_DATA_CMD, DW_IC_CMD); if (poll_for_status_set0(DW_IC_STATUS_RFNE) == 0) buffer[i] = *(uint8_t *) (m_i2c_base_address + DW_IC_DATA_CMD); else pr_err("Read timeout line %d.\n", __LINE__); } return 0; } static int i2c_write(uint8_t reg_offset, uint8_t *buffer, uint32_t length) { int status; uint32_t i; /* Data transfer, poll till transmit ready bit is set */ status = poll_for_status_set0(DW_IC_STATUS_TFE); if (status) { pr_err("In i2c-lib.c, line %d.\n", __LINE__); return status; } write_cpu_i2c_controller(DW_IC_DATA_CMD, reg_offset); for (i = 0; i < length; i++) { if (poll_for_status_set0(DW_IC_STATUS_TFNF) == 0) { if (i == length - 1) write_cpu_i2c_controller(DW_IC_DATA_CMD, buffer[i] | DW_IC_STOP); else write_cpu_i2c_controller(DW_IC_DATA_CMD, buffer[i]); } else { pr_err("Write timeout %d.\n", __LINE__); } } mdelay(200); status = poll_for_status_set0(DW_IC_STATUS_TFE); if (status) { pr_err("In i2c-lib.c, line %d.\n", __LINE__); return status; } return 0; } /* Initialize I2c controller */ void init_cpu_i2c_controller(void) { uint32_t h_cnt; uint32_t l_cnt; uint32_t input_ic_clk_rate = IC_CLK_KHZ; /* by unit KHz ie. 25MHz */ uint32_t sda_falling_time = 300; uint32_t scl_falling_time = 300; /* * The I2C protocol specification requires 300ns of hold time on the * SDA signal (tHD;DAT) in standard and fast speed modes, and a hold * time long enough to bridge the undefined part between logic 1 and * logic 0 of the falling edge of SCL in high speed mode. */ uint32_t sda_hold_time = 432; uint32_t sda_hold = 0; /* Firstly disable the controller. */ ICC_DEBUG("Initialize CPU I2C controller\n"); write_cpu_i2c_controller(DW_IC_ENABLE, 0); sda_hold = (input_ic_clk_rate * sda_hold_time + 500000) / 1000000; write_cpu_i2c_controller(DW_IC_SDA_HOLD, sda_hold); /* Set standard and fast speed deviders for high/low periods. */ /* Standard-mode */ h_cnt = i2c_dw_scl_hcnt(input_ic_clk_rate, 4000, sda_falling_time, 0, 0); l_cnt = i2c_dw_scl_lcnt(input_ic_clk_rate, 4700, scl_falling_time, 0); write_cpu_i2c_controller(DW_IC_SS_SCL_HCNT, h_cnt); write_cpu_i2c_controller(DW_IC_SS_SCL_LCNT, l_cnt); ICC_DEBUG("Standard-mode HCNT=%x, LCNT=%x\n", h_cnt, l_cnt); /* Fast-mode */ h_cnt = i2c_dw_scl_hcnt(input_ic_clk_rate, 600, sda_falling_time, 0, 0); l_cnt = i2c_dw_scl_lcnt(input_ic_clk_rate, 1300, scl_falling_time, 0); write_cpu_i2c_controller(DW_IC_FS_SCL_HCNT, h_cnt); write_cpu_i2c_controller(DW_IC_FS_SCL_LCNT, l_cnt); ICC_DEBUG("Fast-mode HCNT=%x, LCNT=%d\n\n", h_cnt, l_cnt); /* Configure Tx/Rx FIFO threshold levels, since we will be working * in polling mode set both thresholds to their minimum */ write_cpu_i2c_controller(DW_IC_TX_TL, 0); write_cpu_i2c_controller(DW_IC_RX_TL, 0); write_cpu_i2c_controller(DW_IC_INTR_MASK, DW_IC_INTR_DEFAULT_MASK); /* Configure the i2c master */ write_cpu_i2c_controller(DW_IC_CON, INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD); } /* * This function enables I2C controllers. * * @param i2c_controller_index Bus Number of I2C controllers. */ void enable_i2c_controller(uint8_t i2c_controller_index) { m_i2c_base_address = get_i2c_bar_addr(i2c_controller_index); init_cpu_i2c_controller(); } /* * Write/Read data from I2C device. * * @i2c_controller_index: i2c bus number * @slave_address: slave address * @operation: to read or write * @length: number of bytes * @reg_offset: register offset * @buffer: in/out buffer */ int i2c_bus_rw(uint8_t i2c_controller_index, uint8_t slave_address, enum i2c_bus_operation operation, uint32_t length, uint8_t reg_offset, void *buffer) { uint8_t *byte_buffer = buffer; int status = 0; uint32_t databuffer, temp; m_i2c_base_address = get_i2c_bar_addr(i2c_controller_index); status = wait_for_cpu_i2c_bus_busy(); if (status) { pr_err("%d\n", __LINE__); return status; } mdelay(1000); /* Set the slave address. */ write_cpu_i2c_controller(DW_IC_ENABLE, 0x0); /* Disable controller */ databuffer = read_cpu_i2c_controller(DW_IC_CON); databuffer &= ~DW_IC_CON_10BITADDR_MASTER; write_cpu_i2c_controller(DW_IC_CON, databuffer); /* Fill the target addr. */ write_cpu_i2c_controller(DW_IC_TAR, slave_address); temp = read_cpu_i2c_controller(DW_IC_TAR); /* Configure Tx/Rx FIFO threshold levels. */ write_cpu_i2c_controller(DW_IC_ENABLE, 0x1); /* Enable the adapter */ write_cpu_i2c_controller(DW_IC_INTR_MASK, DW_IC_INTR_DEFAULT_MASK); if (operation == I2C_BUS_READ) status = i2c_read(reg_offset, byte_buffer, length); else if (operation == I2C_BUS_WRITE) status = i2c_write(reg_offset, byte_buffer, length); /* Disable controller */ write_cpu_i2c_controller(DW_IC_ENABLE, 0x0); return status; } void disable_i2c_controller(uint8_t i2c_controller_index) { m_i2c_base_address = get_i2c_bar_addr(i2c_controller_index); /* Disable controller */ write_cpu_i2c_controller(DW_IC_ENABLE, 0x0); m_i2c_base_address = 0; } void cpld_write(uint8_t slave_addr, uint8_t reg, uint8_t data) { enable_i2c_controller(CPLD_BUSNR); i2c_bus_rw(CPLD_BUSNR, slave_addr, I2C_BUS_WRITE, sizeof(uint8_t), reg, &data); disable_i2c_controller(CPLD_BUSNR); }