162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Aspeed 24XX/25XX I2C Controller. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012-2017 ASPEED Technology Inc. 662306a36Sopenharmony_ci * Copyright 2017 IBM Corporation 762306a36Sopenharmony_ci * Copyright 2017 Google, Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/completion.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/irq.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/of_address.h> 2262306a36Sopenharmony_ci#include <linux/of_irq.h> 2362306a36Sopenharmony_ci#include <linux/of_platform.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/reset.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* I2C Register */ 2962306a36Sopenharmony_ci#define ASPEED_I2C_FUN_CTRL_REG 0x00 3062306a36Sopenharmony_ci#define ASPEED_I2C_AC_TIMING_REG1 0x04 3162306a36Sopenharmony_ci#define ASPEED_I2C_AC_TIMING_REG2 0x08 3262306a36Sopenharmony_ci#define ASPEED_I2C_INTR_CTRL_REG 0x0c 3362306a36Sopenharmony_ci#define ASPEED_I2C_INTR_STS_REG 0x10 3462306a36Sopenharmony_ci#define ASPEED_I2C_CMD_REG 0x14 3562306a36Sopenharmony_ci#define ASPEED_I2C_DEV_ADDR_REG 0x18 3662306a36Sopenharmony_ci#define ASPEED_I2C_BYTE_BUF_REG 0x20 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Global Register Definition */ 3962306a36Sopenharmony_ci/* 0x00 : I2C Interrupt Status Register */ 4062306a36Sopenharmony_ci/* 0x08 : I2C Interrupt Target Assignment */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Device Register Definition */ 4362306a36Sopenharmony_ci/* 0x00 : I2CD Function Control Register */ 4462306a36Sopenharmony_ci#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15) 4562306a36Sopenharmony_ci#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) 4662306a36Sopenharmony_ci#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) 4762306a36Sopenharmony_ci#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) 4862306a36Sopenharmony_ci#define ASPEED_I2CD_SLAVE_EN BIT(1) 4962306a36Sopenharmony_ci#define ASPEED_I2CD_MASTER_EN BIT(0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 0x04 : I2CD Clock and AC Timing Control Register #1 */ 5262306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_TBUF_MASK GENMASK(31, 28) 5362306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_THDSTA_MASK GENMASK(27, 24) 5462306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_TACST_MASK GENMASK(23, 20) 5562306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_SCL_HIGH_SHIFT 16 5662306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16) 5762306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12 5862306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12) 5962306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0) 6062306a36Sopenharmony_ci#define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0) 6162306a36Sopenharmony_ci/* 0x08 : I2CD Clock and AC Timing Control Register #2 */ 6262306a36Sopenharmony_ci#define ASPEED_NO_TIMEOUT_CTRL 0 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 0x0c : I2CD Interrupt Control Register & 6562306a36Sopenharmony_ci * 0x10 : I2CD Interrupt Status Register 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * These share bit definitions, so use the same values for the enable & 6862306a36Sopenharmony_ci * status bits. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_RECV_MASK 0xf000ffff 7162306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) 7262306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) 7362306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7) 7462306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6) 7562306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_ABNORMAL BIT(5) 7662306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4) 7762306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3) 7862306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_RX_DONE BIT(2) 7962306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_TX_NAK BIT(1) 8062306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_TX_ACK BIT(0) 8162306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_MASTER_ERRORS \ 8262306a36Sopenharmony_ci (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ 8362306a36Sopenharmony_ci ASPEED_I2CD_INTR_SCL_TIMEOUT | \ 8462306a36Sopenharmony_ci ASPEED_I2CD_INTR_ABNORMAL | \ 8562306a36Sopenharmony_ci ASPEED_I2CD_INTR_ARBIT_LOSS) 8662306a36Sopenharmony_ci#define ASPEED_I2CD_INTR_ALL \ 8762306a36Sopenharmony_ci (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \ 8862306a36Sopenharmony_ci ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \ 8962306a36Sopenharmony_ci ASPEED_I2CD_INTR_SCL_TIMEOUT | \ 9062306a36Sopenharmony_ci ASPEED_I2CD_INTR_ABNORMAL | \ 9162306a36Sopenharmony_ci ASPEED_I2CD_INTR_NORMAL_STOP | \ 9262306a36Sopenharmony_ci ASPEED_I2CD_INTR_ARBIT_LOSS | \ 9362306a36Sopenharmony_ci ASPEED_I2CD_INTR_RX_DONE | \ 9462306a36Sopenharmony_ci ASPEED_I2CD_INTR_TX_NAK | \ 9562306a36Sopenharmony_ci ASPEED_I2CD_INTR_TX_ACK) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 0x14 : I2CD Command/Status Register */ 9862306a36Sopenharmony_ci#define ASPEED_I2CD_SCL_LINE_STS BIT(18) 9962306a36Sopenharmony_ci#define ASPEED_I2CD_SDA_LINE_STS BIT(17) 10062306a36Sopenharmony_ci#define ASPEED_I2CD_BUS_BUSY_STS BIT(16) 10162306a36Sopenharmony_ci#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Command Bit */ 10462306a36Sopenharmony_ci#define ASPEED_I2CD_M_STOP_CMD BIT(5) 10562306a36Sopenharmony_ci#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4) 10662306a36Sopenharmony_ci#define ASPEED_I2CD_M_RX_CMD BIT(3) 10762306a36Sopenharmony_ci#define ASPEED_I2CD_S_TX_CMD BIT(2) 10862306a36Sopenharmony_ci#define ASPEED_I2CD_M_TX_CMD BIT(1) 10962306a36Sopenharmony_ci#define ASPEED_I2CD_M_START_CMD BIT(0) 11062306a36Sopenharmony_ci#define ASPEED_I2CD_MASTER_CMDS_MASK \ 11162306a36Sopenharmony_ci (ASPEED_I2CD_M_STOP_CMD | \ 11262306a36Sopenharmony_ci ASPEED_I2CD_M_S_RX_CMD_LAST | \ 11362306a36Sopenharmony_ci ASPEED_I2CD_M_RX_CMD | \ 11462306a36Sopenharmony_ci ASPEED_I2CD_M_TX_CMD | \ 11562306a36Sopenharmony_ci ASPEED_I2CD_M_START_CMD) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 0x18 : I2CD Slave Device Address Register */ 11862306a36Sopenharmony_ci#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cienum aspeed_i2c_master_state { 12162306a36Sopenharmony_ci ASPEED_I2C_MASTER_INACTIVE, 12262306a36Sopenharmony_ci ASPEED_I2C_MASTER_PENDING, 12362306a36Sopenharmony_ci ASPEED_I2C_MASTER_START, 12462306a36Sopenharmony_ci ASPEED_I2C_MASTER_TX_FIRST, 12562306a36Sopenharmony_ci ASPEED_I2C_MASTER_TX, 12662306a36Sopenharmony_ci ASPEED_I2C_MASTER_RX_FIRST, 12762306a36Sopenharmony_ci ASPEED_I2C_MASTER_RX, 12862306a36Sopenharmony_ci ASPEED_I2C_MASTER_STOP, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cienum aspeed_i2c_slave_state { 13262306a36Sopenharmony_ci ASPEED_I2C_SLAVE_INACTIVE, 13362306a36Sopenharmony_ci ASPEED_I2C_SLAVE_START, 13462306a36Sopenharmony_ci ASPEED_I2C_SLAVE_READ_REQUESTED, 13562306a36Sopenharmony_ci ASPEED_I2C_SLAVE_READ_PROCESSED, 13662306a36Sopenharmony_ci ASPEED_I2C_SLAVE_WRITE_REQUESTED, 13762306a36Sopenharmony_ci ASPEED_I2C_SLAVE_WRITE_RECEIVED, 13862306a36Sopenharmony_ci ASPEED_I2C_SLAVE_STOP, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct aspeed_i2c_bus { 14262306a36Sopenharmony_ci struct i2c_adapter adap; 14362306a36Sopenharmony_ci struct device *dev; 14462306a36Sopenharmony_ci void __iomem *base; 14562306a36Sopenharmony_ci struct reset_control *rst; 14662306a36Sopenharmony_ci /* Synchronizes I/O mem access to base. */ 14762306a36Sopenharmony_ci spinlock_t lock; 14862306a36Sopenharmony_ci struct completion cmd_complete; 14962306a36Sopenharmony_ci u32 (*get_clk_reg_val)(struct device *dev, 15062306a36Sopenharmony_ci u32 divisor); 15162306a36Sopenharmony_ci unsigned long parent_clk_frequency; 15262306a36Sopenharmony_ci u32 bus_frequency; 15362306a36Sopenharmony_ci /* Transaction state. */ 15462306a36Sopenharmony_ci enum aspeed_i2c_master_state master_state; 15562306a36Sopenharmony_ci struct i2c_msg *msgs; 15662306a36Sopenharmony_ci size_t buf_index; 15762306a36Sopenharmony_ci size_t msgs_index; 15862306a36Sopenharmony_ci size_t msgs_count; 15962306a36Sopenharmony_ci bool send_stop; 16062306a36Sopenharmony_ci int cmd_err; 16162306a36Sopenharmony_ci /* Protected only by i2c_lock_bus */ 16262306a36Sopenharmony_ci int master_xfer_result; 16362306a36Sopenharmony_ci /* Multi-master */ 16462306a36Sopenharmony_ci bool multi_master; 16562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 16662306a36Sopenharmony_ci struct i2c_client *slave; 16762306a36Sopenharmony_ci enum aspeed_i2c_slave_state slave_state; 16862306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long time_left, flags; 17662306a36Sopenharmony_ci int ret = 0; 17762306a36Sopenharmony_ci u32 command; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 18062306a36Sopenharmony_ci command = readl(bus->base + ASPEED_I2C_CMD_REG); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (command & ASPEED_I2CD_SDA_LINE_STS) { 18362306a36Sopenharmony_ci /* Bus is idle: no recovery needed. */ 18462306a36Sopenharmony_ci if (command & ASPEED_I2CD_SCL_LINE_STS) 18562306a36Sopenharmony_ci goto out; 18662306a36Sopenharmony_ci dev_dbg(bus->dev, "SCL hung (state %x), attempting recovery\n", 18762306a36Sopenharmony_ci command); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci reinit_completion(&bus->cmd_complete); 19062306a36Sopenharmony_ci writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG); 19162306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci time_left = wait_for_completion_timeout( 19462306a36Sopenharmony_ci &bus->cmd_complete, bus->adap.timeout); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 19762306a36Sopenharmony_ci if (time_left == 0) 19862306a36Sopenharmony_ci goto reset_out; 19962306a36Sopenharmony_ci else if (bus->cmd_err) 20062306a36Sopenharmony_ci goto reset_out; 20162306a36Sopenharmony_ci /* Recovery failed. */ 20262306a36Sopenharmony_ci else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) & 20362306a36Sopenharmony_ci ASPEED_I2CD_SCL_LINE_STS)) 20462306a36Sopenharmony_ci goto reset_out; 20562306a36Sopenharmony_ci /* Bus error. */ 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci dev_dbg(bus->dev, "SDA hung (state %x), attempting recovery\n", 20862306a36Sopenharmony_ci command); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci reinit_completion(&bus->cmd_complete); 21162306a36Sopenharmony_ci /* Writes 1 to 8 SCL clock cycles until SDA is released. */ 21262306a36Sopenharmony_ci writel(ASPEED_I2CD_BUS_RECOVER_CMD, 21362306a36Sopenharmony_ci bus->base + ASPEED_I2C_CMD_REG); 21462306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci time_left = wait_for_completion_timeout( 21762306a36Sopenharmony_ci &bus->cmd_complete, bus->adap.timeout); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 22062306a36Sopenharmony_ci if (time_left == 0) 22162306a36Sopenharmony_ci goto reset_out; 22262306a36Sopenharmony_ci else if (bus->cmd_err) 22362306a36Sopenharmony_ci goto reset_out; 22462306a36Sopenharmony_ci /* Recovery failed. */ 22562306a36Sopenharmony_ci else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) & 22662306a36Sopenharmony_ci ASPEED_I2CD_SDA_LINE_STS)) 22762306a36Sopenharmony_ci goto reset_out; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciout: 23162306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cireset_out: 23662306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return aspeed_i2c_reset(bus); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 24262306a36Sopenharmony_cistatic u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci u32 command, irq_handled = 0; 24562306a36Sopenharmony_ci struct i2c_client *slave = bus->slave; 24662306a36Sopenharmony_ci u8 value; 24762306a36Sopenharmony_ci int ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!slave) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * Handle stop conditions early, prior to SLAVE_MATCH. Some masters may drive 25462306a36Sopenharmony_ci * transfers with low enough latency between the nak/stop phase of the current 25562306a36Sopenharmony_ci * command and the start/address phase of the following command that the 25662306a36Sopenharmony_ci * interrupts are coalesced by the time we process them. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { 25962306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; 26062306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_STOP; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_TX_NAK && 26462306a36Sopenharmony_ci bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { 26562306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_NAK; 26662306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_STOP; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Propagate any stop conditions to the slave implementation. */ 27062306a36Sopenharmony_ci if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) { 27162306a36Sopenharmony_ci i2c_slave_event(slave, I2C_SLAVE_STOP, &value); 27262306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * Now that we've dealt with any potentially coalesced stop conditions, 27762306a36Sopenharmony_ci * address any start conditions. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) { 28062306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_SLAVE_MATCH; 28162306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_START; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * If the slave has been stopped and not started then slave interrupt 28662306a36Sopenharmony_ci * handling is complete. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) 28962306a36Sopenharmony_ci return irq_handled; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci command = readl(bus->base + ASPEED_I2C_CMD_REG); 29262306a36Sopenharmony_ci dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", 29362306a36Sopenharmony_ci irq_status, command); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Slave was sent something. */ 29662306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { 29762306a36Sopenharmony_ci value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; 29862306a36Sopenharmony_ci /* Handle address frame. */ 29962306a36Sopenharmony_ci if (bus->slave_state == ASPEED_I2C_SLAVE_START) { 30062306a36Sopenharmony_ci if (value & 0x1) 30162306a36Sopenharmony_ci bus->slave_state = 30262306a36Sopenharmony_ci ASPEED_I2C_SLAVE_READ_REQUESTED; 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci bus->slave_state = 30562306a36Sopenharmony_ci ASPEED_I2C_SLAVE_WRITE_REQUESTED; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_RX_DONE; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci switch (bus->slave_state) { 31162306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_READ_REQUESTED: 31262306a36Sopenharmony_ci if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) 31362306a36Sopenharmony_ci dev_err(bus->dev, "Unexpected ACK on read request.\n"); 31462306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; 31562306a36Sopenharmony_ci i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); 31662306a36Sopenharmony_ci writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); 31762306a36Sopenharmony_ci writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_READ_PROCESSED: 32062306a36Sopenharmony_ci if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { 32162306a36Sopenharmony_ci dev_err(bus->dev, 32262306a36Sopenharmony_ci "Expected ACK after processed read.\n"); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_ACK; 32662306a36Sopenharmony_ci i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); 32762306a36Sopenharmony_ci writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); 32862306a36Sopenharmony_ci writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_WRITE_REQUESTED: 33162306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED; 33262306a36Sopenharmony_ci ret = i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value); 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * Slave ACK's on this address phase already but as the backend driver 33562306a36Sopenharmony_ci * returns an errno, the bus driver should nack the next incoming byte. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci if (ret < 0) 33862306a36Sopenharmony_ci writel(ASPEED_I2CD_M_S_RX_CMD_LAST, bus->base + ASPEED_I2C_CMD_REG); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_WRITE_RECEIVED: 34162306a36Sopenharmony_ci i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_STOP: 34462306a36Sopenharmony_ci /* Stop event handling is done early. Unreachable. */ 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci case ASPEED_I2C_SLAVE_START: 34762306a36Sopenharmony_ci /* Slave was just started. Waiting for the next event. */; 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci dev_err(bus->dev, "unknown slave_state: %d\n", 35162306a36Sopenharmony_ci bus->slave_state); 35262306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return irq_handled; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 36162306a36Sopenharmony_cistatic void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD; 36462306a36Sopenharmony_ci struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; 36562306a36Sopenharmony_ci u8 slave_addr = i2c_8bit_addr_from_msg(msg); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * If it's requested in the middle of a slave session, set the master 37062306a36Sopenharmony_ci * state to 'pending' then H/W will continue handling this master 37162306a36Sopenharmony_ci * command when the bus comes back to the idle state. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) { 37462306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_PENDING; 37562306a36Sopenharmony_ci return; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_START; 38062306a36Sopenharmony_ci bus->buf_index = 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (msg->flags & I2C_M_RD) { 38362306a36Sopenharmony_ci command |= ASPEED_I2CD_M_RX_CMD; 38462306a36Sopenharmony_ci /* Need to let the hardware know to NACK after RX. */ 38562306a36Sopenharmony_ci if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) 38662306a36Sopenharmony_ci command |= ASPEED_I2CD_M_S_RX_CMD_LAST; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG); 39062306a36Sopenharmony_ci writel(command, bus->base + ASPEED_I2C_CMD_REG); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 39462306a36Sopenharmony_cistatic void aspeed_i2c_do_stop(struct aspeed_i2c_bus *bus) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_STOP; 39762306a36Sopenharmony_ci writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 40162306a36Sopenharmony_cistatic void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci if (bus->msgs_index + 1 < bus->msgs_count) { 40462306a36Sopenharmony_ci bus->msgs_index++; 40562306a36Sopenharmony_ci aspeed_i2c_do_start(bus); 40662306a36Sopenharmony_ci } else { 40762306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int aspeed_i2c_is_irq_error(u32 irq_status) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS) 41462306a36Sopenharmony_ci return -EAGAIN; 41562306a36Sopenharmony_ci if (irq_status & (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | 41662306a36Sopenharmony_ci ASPEED_I2CD_INTR_SCL_TIMEOUT)) 41762306a36Sopenharmony_ci return -EBUSY; 41862306a36Sopenharmony_ci if (irq_status & (ASPEED_I2CD_INTR_ABNORMAL)) 41962306a36Sopenharmony_ci return -EPROTO; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci u32 irq_handled = 0, command = 0; 42762306a36Sopenharmony_ci struct i2c_msg *msg; 42862306a36Sopenharmony_ci u8 recv_byte; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) { 43262306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 43362306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; 43462306a36Sopenharmony_ci goto out_complete; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * We encountered an interrupt that reports an error: the hardware 43962306a36Sopenharmony_ci * should clear the command queue effectively taking us back to the 44062306a36Sopenharmony_ci * INACTIVE state. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci ret = aspeed_i2c_is_irq_error(irq_status); 44362306a36Sopenharmony_ci if (ret) { 44462306a36Sopenharmony_ci dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", 44562306a36Sopenharmony_ci irq_status); 44662306a36Sopenharmony_ci irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); 44762306a36Sopenharmony_ci if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { 44862306a36Sopenharmony_ci bus->cmd_err = ret; 44962306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 45062306a36Sopenharmony_ci goto out_complete; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* Master is not currently active, irq was for someone else. */ 45562306a36Sopenharmony_ci if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || 45662306a36Sopenharmony_ci bus->master_state == ASPEED_I2C_MASTER_PENDING) 45762306a36Sopenharmony_ci goto out_no_complete; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* We are in an invalid state; reset bus to a known state. */ 46062306a36Sopenharmony_ci if (!bus->msgs) { 46162306a36Sopenharmony_ci dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", 46262306a36Sopenharmony_ci irq_status); 46362306a36Sopenharmony_ci bus->cmd_err = -EIO; 46462306a36Sopenharmony_ci if (bus->master_state != ASPEED_I2C_MASTER_STOP && 46562306a36Sopenharmony_ci bus->master_state != ASPEED_I2C_MASTER_INACTIVE) 46662306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 46762306a36Sopenharmony_ci goto out_no_complete; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci msg = &bus->msgs[bus->msgs_index]; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * START is a special case because we still have to handle a subsequent 47362306a36Sopenharmony_ci * TX or RX immediately after we handle it, so we handle it here and 47462306a36Sopenharmony_ci * then update the state and handle the new state below. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci if (bus->master_state == ASPEED_I2C_MASTER_START) { 47762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * If a peer master starts a xfer immediately after it queues a 48062306a36Sopenharmony_ci * master command, clear the queued master command and change 48162306a36Sopenharmony_ci * its state to 'pending'. To simplify handling of pending 48262306a36Sopenharmony_ci * cases, it uses S/W solution instead of H/W command queue 48362306a36Sopenharmony_ci * handling. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { 48662306a36Sopenharmony_ci writel(readl(bus->base + ASPEED_I2C_CMD_REG) & 48762306a36Sopenharmony_ci ~ASPEED_I2CD_MASTER_CMDS_MASK, 48862306a36Sopenharmony_ci bus->base + ASPEED_I2C_CMD_REG); 48962306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_PENDING; 49062306a36Sopenharmony_ci dev_dbg(bus->dev, 49162306a36Sopenharmony_ci "master goes pending due to a slave start\n"); 49262306a36Sopenharmony_ci goto out_no_complete; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 49562306a36Sopenharmony_ci if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { 49662306a36Sopenharmony_ci if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { 49762306a36Sopenharmony_ci bus->cmd_err = -ENXIO; 49862306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 49962306a36Sopenharmony_ci goto out_complete; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci pr_devel("no slave present at %02x\n", msg->addr); 50262306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_NAK; 50362306a36Sopenharmony_ci bus->cmd_err = -ENXIO; 50462306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 50562306a36Sopenharmony_ci goto out_no_complete; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_ACK; 50862306a36Sopenharmony_ci if (msg->len == 0) { /* SMBUS_QUICK */ 50962306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 51062306a36Sopenharmony_ci goto out_no_complete; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci if (msg->flags & I2C_M_RD) 51362306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_RX_FIRST; 51462306a36Sopenharmony_ci else 51562306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_TX_FIRST; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci switch (bus->master_state) { 51962306a36Sopenharmony_ci case ASPEED_I2C_MASTER_TX: 52062306a36Sopenharmony_ci if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) { 52162306a36Sopenharmony_ci dev_dbg(bus->dev, "slave NACKed TX\n"); 52262306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_NAK; 52362306a36Sopenharmony_ci goto error_and_stop; 52462306a36Sopenharmony_ci } else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { 52562306a36Sopenharmony_ci dev_err(bus->dev, "slave failed to ACK TX\n"); 52662306a36Sopenharmony_ci goto error_and_stop; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_TX_ACK; 52962306a36Sopenharmony_ci fallthrough; 53062306a36Sopenharmony_ci case ASPEED_I2C_MASTER_TX_FIRST: 53162306a36Sopenharmony_ci if (bus->buf_index < msg->len) { 53262306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_TX; 53362306a36Sopenharmony_ci writel(msg->buf[bus->buf_index++], 53462306a36Sopenharmony_ci bus->base + ASPEED_I2C_BYTE_BUF_REG); 53562306a36Sopenharmony_ci writel(ASPEED_I2CD_M_TX_CMD, 53662306a36Sopenharmony_ci bus->base + ASPEED_I2C_CMD_REG); 53762306a36Sopenharmony_ci } else { 53862306a36Sopenharmony_ci aspeed_i2c_next_msg_or_stop(bus); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci goto out_no_complete; 54162306a36Sopenharmony_ci case ASPEED_I2C_MASTER_RX_FIRST: 54262306a36Sopenharmony_ci /* RX may not have completed yet (only address cycle) */ 54362306a36Sopenharmony_ci if (!(irq_status & ASPEED_I2CD_INTR_RX_DONE)) 54462306a36Sopenharmony_ci goto out_no_complete; 54562306a36Sopenharmony_ci fallthrough; 54662306a36Sopenharmony_ci case ASPEED_I2C_MASTER_RX: 54762306a36Sopenharmony_ci if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) { 54862306a36Sopenharmony_ci dev_err(bus->dev, "master failed to RX\n"); 54962306a36Sopenharmony_ci goto error_and_stop; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_RX_DONE; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; 55462306a36Sopenharmony_ci msg->buf[bus->buf_index++] = recv_byte; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (msg->flags & I2C_M_RECV_LEN) { 55762306a36Sopenharmony_ci if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { 55862306a36Sopenharmony_ci bus->cmd_err = -EPROTO; 55962306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 56062306a36Sopenharmony_ci goto out_no_complete; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci msg->len = recv_byte + 56362306a36Sopenharmony_ci ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1); 56462306a36Sopenharmony_ci msg->flags &= ~I2C_M_RECV_LEN; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (bus->buf_index < msg->len) { 56862306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_RX; 56962306a36Sopenharmony_ci command = ASPEED_I2CD_M_RX_CMD; 57062306a36Sopenharmony_ci if (bus->buf_index + 1 == msg->len) 57162306a36Sopenharmony_ci command |= ASPEED_I2CD_M_S_RX_CMD_LAST; 57262306a36Sopenharmony_ci writel(command, bus->base + ASPEED_I2C_CMD_REG); 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci aspeed_i2c_next_msg_or_stop(bus); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci goto out_no_complete; 57762306a36Sopenharmony_ci case ASPEED_I2C_MASTER_STOP: 57862306a36Sopenharmony_ci if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) { 57962306a36Sopenharmony_ci dev_err(bus->dev, 58062306a36Sopenharmony_ci "master failed to STOP. irq_status:0x%x\n", 58162306a36Sopenharmony_ci irq_status); 58262306a36Sopenharmony_ci bus->cmd_err = -EIO; 58362306a36Sopenharmony_ci /* Do not STOP as we have already tried. */ 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 58962306a36Sopenharmony_ci goto out_complete; 59062306a36Sopenharmony_ci case ASPEED_I2C_MASTER_INACTIVE: 59162306a36Sopenharmony_ci dev_err(bus->dev, 59262306a36Sopenharmony_ci "master received interrupt 0x%08x, but is inactive\n", 59362306a36Sopenharmony_ci irq_status); 59462306a36Sopenharmony_ci bus->cmd_err = -EIO; 59562306a36Sopenharmony_ci /* Do not STOP as we should be inactive. */ 59662306a36Sopenharmony_ci goto out_complete; 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci WARN(1, "unknown master state\n"); 59962306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 60062306a36Sopenharmony_ci bus->cmd_err = -EINVAL; 60162306a36Sopenharmony_ci goto out_complete; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_cierror_and_stop: 60462306a36Sopenharmony_ci bus->cmd_err = -EIO; 60562306a36Sopenharmony_ci aspeed_i2c_do_stop(bus); 60662306a36Sopenharmony_ci goto out_no_complete; 60762306a36Sopenharmony_ciout_complete: 60862306a36Sopenharmony_ci bus->msgs = NULL; 60962306a36Sopenharmony_ci if (bus->cmd_err) 61062306a36Sopenharmony_ci bus->master_xfer_result = bus->cmd_err; 61162306a36Sopenharmony_ci else 61262306a36Sopenharmony_ci bus->master_xfer_result = bus->msgs_index + 1; 61362306a36Sopenharmony_ci complete(&bus->cmd_complete); 61462306a36Sopenharmony_ciout_no_complete: 61562306a36Sopenharmony_ci return irq_handled; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct aspeed_i2c_bus *bus = dev_id; 62162306a36Sopenharmony_ci u32 irq_received, irq_remaining, irq_handled; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci spin_lock(&bus->lock); 62462306a36Sopenharmony_ci irq_received = readl(bus->base + ASPEED_I2C_INTR_STS_REG); 62562306a36Sopenharmony_ci /* Ack all interrupts except for Rx done */ 62662306a36Sopenharmony_ci writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE, 62762306a36Sopenharmony_ci bus->base + ASPEED_I2C_INTR_STS_REG); 62862306a36Sopenharmony_ci readl(bus->base + ASPEED_I2C_INTR_STS_REG); 62962306a36Sopenharmony_ci irq_received &= ASPEED_I2CD_INTR_RECV_MASK; 63062306a36Sopenharmony_ci irq_remaining = irq_received; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 63362306a36Sopenharmony_ci /* 63462306a36Sopenharmony_ci * In most cases, interrupt bits will be set one by one, although 63562306a36Sopenharmony_ci * multiple interrupt bits could be set at the same time. It's also 63662306a36Sopenharmony_ci * possible that master interrupt bits could be set along with slave 63762306a36Sopenharmony_ci * interrupt bits. Each case needs to be handled using corresponding 63862306a36Sopenharmony_ci * handlers depending on the current state. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && 64162306a36Sopenharmony_ci bus->master_state != ASPEED_I2C_MASTER_PENDING) { 64262306a36Sopenharmony_ci irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); 64362306a36Sopenharmony_ci irq_remaining &= ~irq_handled; 64462306a36Sopenharmony_ci if (irq_remaining) 64562306a36Sopenharmony_ci irq_handled |= aspeed_i2c_slave_irq(bus, irq_remaining); 64662306a36Sopenharmony_ci } else { 64762306a36Sopenharmony_ci irq_handled = aspeed_i2c_slave_irq(bus, irq_remaining); 64862306a36Sopenharmony_ci irq_remaining &= ~irq_handled; 64962306a36Sopenharmony_ci if (irq_remaining) 65062306a36Sopenharmony_ci irq_handled |= aspeed_i2c_master_irq(bus, 65162306a36Sopenharmony_ci irq_remaining); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * Start a pending master command at here if a slave operation is 65662306a36Sopenharmony_ci * completed. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci if (bus->master_state == ASPEED_I2C_MASTER_PENDING && 65962306a36Sopenharmony_ci bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) 66062306a36Sopenharmony_ci aspeed_i2c_do_start(bus); 66162306a36Sopenharmony_ci#else 66262306a36Sopenharmony_ci irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); 66362306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci irq_remaining &= ~irq_handled; 66662306a36Sopenharmony_ci if (irq_remaining) 66762306a36Sopenharmony_ci dev_err(bus->dev, 66862306a36Sopenharmony_ci "irq handled != irq. expected 0x%08x, but was 0x%08x\n", 66962306a36Sopenharmony_ci irq_received, irq_handled); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Ack Rx done */ 67262306a36Sopenharmony_ci if (irq_received & ASPEED_I2CD_INTR_RX_DONE) { 67362306a36Sopenharmony_ci writel(ASPEED_I2CD_INTR_RX_DONE, 67462306a36Sopenharmony_ci bus->base + ASPEED_I2C_INTR_STS_REG); 67562306a36Sopenharmony_ci readl(bus->base + ASPEED_I2C_INTR_STS_REG); 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci spin_unlock(&bus->lock); 67862306a36Sopenharmony_ci return irq_remaining ? IRQ_NONE : IRQ_HANDLED; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int aspeed_i2c_master_xfer(struct i2c_adapter *adap, 68262306a36Sopenharmony_ci struct i2c_msg *msgs, int num) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); 68562306a36Sopenharmony_ci unsigned long time_left, flags; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 68862306a36Sopenharmony_ci bus->cmd_err = 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* If bus is busy in a single master environment, attempt recovery. */ 69162306a36Sopenharmony_ci if (!bus->multi_master && 69262306a36Sopenharmony_ci (readl(bus->base + ASPEED_I2C_CMD_REG) & 69362306a36Sopenharmony_ci ASPEED_I2CD_BUS_BUSY_STS)) { 69462306a36Sopenharmony_ci int ret; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 69762306a36Sopenharmony_ci ret = aspeed_i2c_recover_bus(bus); 69862306a36Sopenharmony_ci if (ret) 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci bus->cmd_err = 0; 70462306a36Sopenharmony_ci bus->msgs = msgs; 70562306a36Sopenharmony_ci bus->msgs_index = 0; 70662306a36Sopenharmony_ci bus->msgs_count = num; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci reinit_completion(&bus->cmd_complete); 70962306a36Sopenharmony_ci aspeed_i2c_do_start(bus); 71062306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&bus->cmd_complete, 71362306a36Sopenharmony_ci bus->adap.timeout); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (time_left == 0) { 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * In a multi-master setup, if a timeout occurs, attempt 71862306a36Sopenharmony_ci * recovery. But if the bus is idle, we still need to reset the 71962306a36Sopenharmony_ci * i2c controller to clear the remaining interrupts. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci if (bus->multi_master && 72262306a36Sopenharmony_ci (readl(bus->base + ASPEED_I2C_CMD_REG) & 72362306a36Sopenharmony_ci ASPEED_I2CD_BUS_BUSY_STS)) 72462306a36Sopenharmony_ci aspeed_i2c_recover_bus(bus); 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci aspeed_i2c_reset(bus); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* 72962306a36Sopenharmony_ci * If timed out and the state is still pending, drop the pending 73062306a36Sopenharmony_ci * master command. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 73362306a36Sopenharmony_ci if (bus->master_state == ASPEED_I2C_MASTER_PENDING) 73462306a36Sopenharmony_ci bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 73562306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return -ETIMEDOUT; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return bus->master_xfer_result; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic u32 aspeed_i2c_functionality(struct i2c_adapter *adap) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 74962306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 75062306a36Sopenharmony_cistatic void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci u32 addr_reg_val, func_ctrl_reg_val; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* 75562306a36Sopenharmony_ci * Set slave addr. Reserved bits can all safely be written with zeros 75662306a36Sopenharmony_ci * on all of ast2[456]00, so zero everything else to ensure we only 75762306a36Sopenharmony_ci * enable a single slave address (ast2500 has two, ast2600 has three, 75862306a36Sopenharmony_ci * the enable bits for which are also in this register) so that we don't 75962306a36Sopenharmony_ci * end up with additional phantom devices responding on the bus. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci addr_reg_val = slave_addr & ASPEED_I2CD_DEV_ADDR_MASK; 76262306a36Sopenharmony_ci writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Turn on slave mode. */ 76562306a36Sopenharmony_ci func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); 76662306a36Sopenharmony_ci func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN; 76762306a36Sopenharmony_ci writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int aspeed_i2c_reg_slave(struct i2c_client *client) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); 77562306a36Sopenharmony_ci unsigned long flags; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 77862306a36Sopenharmony_ci if (bus->slave) { 77962306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci __aspeed_i2c_reg_slave(bus, client->addr); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci bus->slave = client; 78662306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int aspeed_i2c_unreg_slave(struct i2c_client *client) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); 79462306a36Sopenharmony_ci u32 func_ctrl_reg_val; 79562306a36Sopenharmony_ci unsigned long flags; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 79862306a36Sopenharmony_ci if (!bus->slave) { 79962306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 80062306a36Sopenharmony_ci return -EINVAL; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* Turn off slave mode. */ 80462306a36Sopenharmony_ci func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); 80562306a36Sopenharmony_ci func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN; 80662306a36Sopenharmony_ci writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci bus->slave = NULL; 80962306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic const struct i2c_algorithm aspeed_i2c_algo = { 81662306a36Sopenharmony_ci .master_xfer = aspeed_i2c_master_xfer, 81762306a36Sopenharmony_ci .functionality = aspeed_i2c_functionality, 81862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 81962306a36Sopenharmony_ci .reg_slave = aspeed_i2c_reg_slave, 82062306a36Sopenharmony_ci .unreg_slave = aspeed_i2c_unreg_slave, 82162306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic u32 aspeed_i2c_get_clk_reg_val(struct device *dev, 82562306a36Sopenharmony_ci u32 clk_high_low_mask, 82662306a36Sopenharmony_ci u32 divisor) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci u32 base_clk_divisor, clk_high_low_max, clk_high, clk_low, tmp; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* 83162306a36Sopenharmony_ci * SCL_high and SCL_low represent a value 1 greater than what is stored 83262306a36Sopenharmony_ci * since a zero divider is meaningless. Thus, the max value each can 83362306a36Sopenharmony_ci * store is every bit set + 1. Since SCL_high and SCL_low are added 83462306a36Sopenharmony_ci * together (see below), the max value of both is the max value of one 83562306a36Sopenharmony_ci * them times two. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci clk_high_low_max = (clk_high_low_mask + 1) * 2; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* 84062306a36Sopenharmony_ci * The actual clock frequency of SCL is: 84162306a36Sopenharmony_ci * SCL_freq = APB_freq / (base_freq * (SCL_high + SCL_low)) 84262306a36Sopenharmony_ci * = APB_freq / divisor 84362306a36Sopenharmony_ci * where base_freq is a programmable clock divider; its value is 84462306a36Sopenharmony_ci * base_freq = 1 << base_clk_divisor 84562306a36Sopenharmony_ci * SCL_high is the number of base_freq clock cycles that SCL stays high 84662306a36Sopenharmony_ci * and SCL_low is the number of base_freq clock cycles that SCL stays 84762306a36Sopenharmony_ci * low for a period of SCL. 84862306a36Sopenharmony_ci * The actual register has a minimum SCL_high and SCL_low minimum of 1; 84962306a36Sopenharmony_ci * thus, they start counting at zero. So 85062306a36Sopenharmony_ci * SCL_high = clk_high + 1 85162306a36Sopenharmony_ci * SCL_low = clk_low + 1 85262306a36Sopenharmony_ci * Thus, 85362306a36Sopenharmony_ci * SCL_freq = APB_freq / 85462306a36Sopenharmony_ci * ((1 << base_clk_divisor) * (clk_high + 1 + clk_low + 1)) 85562306a36Sopenharmony_ci * The documentation recommends clk_high >= clk_high_max / 2 and 85662306a36Sopenharmony_ci * clk_low >= clk_low_max / 2 - 1 when possible; this last constraint 85762306a36Sopenharmony_ci * gives us the following solution: 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci base_clk_divisor = divisor > clk_high_low_max ? 86062306a36Sopenharmony_ci ilog2((divisor - 1) / clk_high_low_max) + 1 : 0; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (base_clk_divisor > ASPEED_I2CD_TIME_BASE_DIVISOR_MASK) { 86362306a36Sopenharmony_ci base_clk_divisor = ASPEED_I2CD_TIME_BASE_DIVISOR_MASK; 86462306a36Sopenharmony_ci clk_low = clk_high_low_mask; 86562306a36Sopenharmony_ci clk_high = clk_high_low_mask; 86662306a36Sopenharmony_ci dev_err(dev, 86762306a36Sopenharmony_ci "clamping clock divider: divider requested, %u, is greater than largest possible divider, %u.\n", 86862306a36Sopenharmony_ci divisor, (1 << base_clk_divisor) * clk_high_low_max); 86962306a36Sopenharmony_ci } else { 87062306a36Sopenharmony_ci tmp = (divisor + (1 << base_clk_divisor) - 1) 87162306a36Sopenharmony_ci >> base_clk_divisor; 87262306a36Sopenharmony_ci clk_low = tmp / 2; 87362306a36Sopenharmony_ci clk_high = tmp - clk_low; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (clk_high) 87662306a36Sopenharmony_ci clk_high--; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (clk_low) 87962306a36Sopenharmony_ci clk_low--; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT) 88462306a36Sopenharmony_ci & ASPEED_I2CD_TIME_SCL_HIGH_MASK) 88562306a36Sopenharmony_ci | ((clk_low << ASPEED_I2CD_TIME_SCL_LOW_SHIFT) 88662306a36Sopenharmony_ci & ASPEED_I2CD_TIME_SCL_LOW_MASK) 88762306a36Sopenharmony_ci | (base_clk_divisor 88862306a36Sopenharmony_ci & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic u32 aspeed_i2c_24xx_get_clk_reg_val(struct device *dev, u32 divisor) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci /* 89462306a36Sopenharmony_ci * clk_high and clk_low are each 3 bits wide, so each can hold a max 89562306a36Sopenharmony_ci * value of 8 giving a clk_high_low_max of 16. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci return aspeed_i2c_get_clk_reg_val(dev, GENMASK(2, 0), divisor); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * clk_high and clk_low are each 4 bits wide, so each can hold a max 90462306a36Sopenharmony_ci * value of 16 giving a clk_high_low_max of 32. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci return aspeed_i2c_get_clk_reg_val(dev, GENMASK(3, 0), divisor); 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 91062306a36Sopenharmony_cistatic int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci u32 divisor, clk_reg_val; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); 91562306a36Sopenharmony_ci clk_reg_val = readl(bus->base + ASPEED_I2C_AC_TIMING_REG1); 91662306a36Sopenharmony_ci clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK | 91762306a36Sopenharmony_ci ASPEED_I2CD_TIME_THDSTA_MASK | 91862306a36Sopenharmony_ci ASPEED_I2CD_TIME_TACST_MASK); 91962306a36Sopenharmony_ci clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor); 92062306a36Sopenharmony_ci writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1); 92162306a36Sopenharmony_ci writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* precondition: bus.lock has been acquired. */ 92762306a36Sopenharmony_cistatic int aspeed_i2c_init(struct aspeed_i2c_bus *bus, 92862306a36Sopenharmony_ci struct platform_device *pdev) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci u32 fun_ctrl_reg = ASPEED_I2CD_MASTER_EN; 93162306a36Sopenharmony_ci int ret; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Disable everything. */ 93462306a36Sopenharmony_ci writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ret = aspeed_i2c_init_clk(bus); 93762306a36Sopenharmony_ci if (ret < 0) 93862306a36Sopenharmony_ci return ret; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (of_property_read_bool(pdev->dev.of_node, "multi-master")) 94162306a36Sopenharmony_ci bus->multi_master = true; 94262306a36Sopenharmony_ci else 94362306a36Sopenharmony_ci fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Enable Master Mode */ 94662306a36Sopenharmony_ci writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg, 94762306a36Sopenharmony_ci bus->base + ASPEED_I2C_FUN_CTRL_REG); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 95062306a36Sopenharmony_ci /* If slave has already been registered, re-enable it. */ 95162306a36Sopenharmony_ci if (bus->slave) 95262306a36Sopenharmony_ci __aspeed_i2c_reg_slave(bus, bus->slave->addr); 95362306a36Sopenharmony_ci#endif /* CONFIG_I2C_SLAVE */ 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* Set interrupt generation of I2C controller */ 95662306a36Sopenharmony_ci writel(ASPEED_I2CD_INTR_ALL, bus->base + ASPEED_I2C_INTR_CTRL_REG); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int aspeed_i2c_reset(struct aspeed_i2c_bus *bus) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(bus->dev); 96462306a36Sopenharmony_ci unsigned long flags; 96562306a36Sopenharmony_ci int ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* Disable and ack all interrupts. */ 97062306a36Sopenharmony_ci writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); 97162306a36Sopenharmony_ci writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = aspeed_i2c_init(bus, pdev); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return ret; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic const struct of_device_id aspeed_i2c_bus_of_table[] = { 98162306a36Sopenharmony_ci { 98262306a36Sopenharmony_ci .compatible = "aspeed,ast2400-i2c-bus", 98362306a36Sopenharmony_ci .data = aspeed_i2c_24xx_get_clk_reg_val, 98462306a36Sopenharmony_ci }, 98562306a36Sopenharmony_ci { 98662306a36Sopenharmony_ci .compatible = "aspeed,ast2500-i2c-bus", 98762306a36Sopenharmony_ci .data = aspeed_i2c_25xx_get_clk_reg_val, 98862306a36Sopenharmony_ci }, 98962306a36Sopenharmony_ci { 99062306a36Sopenharmony_ci .compatible = "aspeed,ast2600-i2c-bus", 99162306a36Sopenharmony_ci .data = aspeed_i2c_25xx_get_clk_reg_val, 99262306a36Sopenharmony_ci }, 99362306a36Sopenharmony_ci { }, 99462306a36Sopenharmony_ci}; 99562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic int aspeed_i2c_probe_bus(struct platform_device *pdev) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci const struct of_device_id *match; 100062306a36Sopenharmony_ci struct aspeed_i2c_bus *bus; 100162306a36Sopenharmony_ci struct clk *parent_clk; 100262306a36Sopenharmony_ci int irq, ret; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); 100562306a36Sopenharmony_ci if (!bus) 100662306a36Sopenharmony_ci return -ENOMEM; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci bus->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 100962306a36Sopenharmony_ci if (IS_ERR(bus->base)) 101062306a36Sopenharmony_ci return PTR_ERR(bus->base); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci parent_clk = devm_clk_get(&pdev->dev, NULL); 101362306a36Sopenharmony_ci if (IS_ERR(parent_clk)) 101462306a36Sopenharmony_ci return PTR_ERR(parent_clk); 101562306a36Sopenharmony_ci bus->parent_clk_frequency = clk_get_rate(parent_clk); 101662306a36Sopenharmony_ci /* We just need the clock rate, we don't actually use the clk object. */ 101762306a36Sopenharmony_ci devm_clk_put(&pdev->dev, parent_clk); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL); 102062306a36Sopenharmony_ci if (IS_ERR(bus->rst)) { 102162306a36Sopenharmony_ci dev_err(&pdev->dev, 102262306a36Sopenharmony_ci "missing or invalid reset controller device tree entry\n"); 102362306a36Sopenharmony_ci return PTR_ERR(bus->rst); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci reset_control_deassert(bus->rst); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, 102862306a36Sopenharmony_ci "bus-frequency", &bus->bus_frequency); 102962306a36Sopenharmony_ci if (ret < 0) { 103062306a36Sopenharmony_ci dev_err(&pdev->dev, 103162306a36Sopenharmony_ci "Could not read bus-frequency property\n"); 103262306a36Sopenharmony_ci bus->bus_frequency = I2C_MAX_STANDARD_MODE_FREQ; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node); 103662306a36Sopenharmony_ci if (!match) 103762306a36Sopenharmony_ci bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val; 103862306a36Sopenharmony_ci else 103962306a36Sopenharmony_ci bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) 104062306a36Sopenharmony_ci match->data; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Initialize the I2C adapter */ 104362306a36Sopenharmony_ci spin_lock_init(&bus->lock); 104462306a36Sopenharmony_ci init_completion(&bus->cmd_complete); 104562306a36Sopenharmony_ci bus->adap.owner = THIS_MODULE; 104662306a36Sopenharmony_ci bus->adap.retries = 0; 104762306a36Sopenharmony_ci bus->adap.algo = &aspeed_i2c_algo; 104862306a36Sopenharmony_ci bus->adap.dev.parent = &pdev->dev; 104962306a36Sopenharmony_ci bus->adap.dev.of_node = pdev->dev.of_node; 105062306a36Sopenharmony_ci strscpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); 105162306a36Sopenharmony_ci i2c_set_adapdata(&bus->adap, bus); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci bus->dev = &pdev->dev; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* Clean up any left over interrupt state. */ 105662306a36Sopenharmony_ci writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); 105762306a36Sopenharmony_ci writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); 105862306a36Sopenharmony_ci /* 105962306a36Sopenharmony_ci * bus.lock does not need to be held because the interrupt handler has 106062306a36Sopenharmony_ci * not been enabled yet. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci ret = aspeed_i2c_init(bus, pdev); 106362306a36Sopenharmony_ci if (ret < 0) 106462306a36Sopenharmony_ci return ret; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 106762306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, 106862306a36Sopenharmony_ci 0, dev_name(&pdev->dev), bus); 106962306a36Sopenharmony_ci if (ret < 0) 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci ret = i2c_add_adapter(&bus->adap); 107362306a36Sopenharmony_ci if (ret < 0) 107462306a36Sopenharmony_ci return ret; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci platform_set_drvdata(pdev, bus); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci dev_info(bus->dev, "i2c bus %d registered, irq %d\n", 107962306a36Sopenharmony_ci bus->adap.nr, irq); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return 0; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic void aspeed_i2c_remove_bus(struct platform_device *pdev) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev); 108762306a36Sopenharmony_ci unsigned long flags; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci spin_lock_irqsave(&bus->lock, flags); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci /* Disable everything. */ 109262306a36Sopenharmony_ci writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG); 109362306a36Sopenharmony_ci writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci spin_unlock_irqrestore(&bus->lock, flags); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci reset_control_assert(bus->rst); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci i2c_del_adapter(&bus->adap); 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic struct platform_driver aspeed_i2c_bus_driver = { 110362306a36Sopenharmony_ci .probe = aspeed_i2c_probe_bus, 110462306a36Sopenharmony_ci .remove_new = aspeed_i2c_remove_bus, 110562306a36Sopenharmony_ci .driver = { 110662306a36Sopenharmony_ci .name = "aspeed-i2c-bus", 110762306a36Sopenharmony_ci .of_match_table = aspeed_i2c_bus_of_table, 110862306a36Sopenharmony_ci }, 110962306a36Sopenharmony_ci}; 111062306a36Sopenharmony_cimodule_platform_driver(aspeed_i2c_bus_driver); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ciMODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>"); 111362306a36Sopenharmony_ciMODULE_DESCRIPTION("Aspeed I2C Bus Driver"); 111462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1115