162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/err.h> 562306a36Sopenharmony_ci#include <linux/io.h> 662306a36Sopenharmony_ci#include <linux/i2c.h> 762306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/regmap.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define GXP_MAX_I2C_ENGINE 10 1462306a36Sopenharmony_cistatic const char * const gxp_i2c_name[] = { 1562306a36Sopenharmony_ci "gxp-i2c0", "gxp-i2c1", "gxp-i2c2", "gxp-i2c3", 1662306a36Sopenharmony_ci "gxp-i2c4", "gxp-i2c5", "gxp-i2c6", "gxp-i2c7", 1762306a36Sopenharmony_ci "gxp-i2c8", "gxp-i2c9" }; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* GXP I2C Global interrupt status/enable register*/ 2062306a36Sopenharmony_ci#define GXP_I2CINTSTAT 0x00 2162306a36Sopenharmony_ci#define GXP_I2CINTEN 0x04 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* GXP I2C registers */ 2462306a36Sopenharmony_ci#define GXP_I2CSTAT 0x00 2562306a36Sopenharmony_ci#define MASK_STOP_EVENT 0x20 2662306a36Sopenharmony_ci#define MASK_ACK 0x08 2762306a36Sopenharmony_ci#define MASK_RW 0x04 2862306a36Sopenharmony_ci#define GXP_I2CEVTERR 0x01 2962306a36Sopenharmony_ci#define MASK_SLAVE_CMD_EVENT 0x01 3062306a36Sopenharmony_ci#define MASK_SLAVE_DATA_EVENT 0x02 3162306a36Sopenharmony_ci#define MASK_MASTER_EVENT 0x10 3262306a36Sopenharmony_ci#define GXP_I2CSNPDAT 0x02 3362306a36Sopenharmony_ci#define GXP_I2CMCMD 0x04 3462306a36Sopenharmony_ci#define GXP_I2CSCMD 0x06 3562306a36Sopenharmony_ci#define GXP_I2CSNPAA 0x09 3662306a36Sopenharmony_ci#define GXP_I2CADVFEAT 0x0A 3762306a36Sopenharmony_ci#define GXP_I2COWNADR 0x0B 3862306a36Sopenharmony_ci#define GXP_I2CFREQDIV 0x0C 3962306a36Sopenharmony_ci#define GXP_I2CFLTFAIR 0x0D 4062306a36Sopenharmony_ci#define GXP_I2CTMOEDG 0x0E 4162306a36Sopenharmony_ci#define GXP_I2CCYCTIM 0x0F 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* I2CSCMD Bits */ 4462306a36Sopenharmony_ci#define SNOOP_EVT_CLR 0x80 4562306a36Sopenharmony_ci#define SLAVE_EVT_CLR 0x40 4662306a36Sopenharmony_ci#define SNOOP_EVT_MASK 0x20 4762306a36Sopenharmony_ci#define SLAVE_EVT_MASK 0x10 4862306a36Sopenharmony_ci#define SLAVE_ACK_ENAB 0x08 4962306a36Sopenharmony_ci#define SLAVE_EVT_STALL 0x01 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* I2CMCMD Bits */ 5262306a36Sopenharmony_ci#define MASTER_EVT_CLR 0x80 5362306a36Sopenharmony_ci#define MASTER_ACK_ENAB 0x08 5462306a36Sopenharmony_ci#define RW_CMD 0x04 5562306a36Sopenharmony_ci#define STOP_CMD 0x02 5662306a36Sopenharmony_ci#define START_CMD 0x01 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* I2CTMOEDG value */ 5962306a36Sopenharmony_ci#define GXP_DATA_EDGE_RST_CTRL 0x0a /* 30ns */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* I2CFLTFAIR Bits */ 6262306a36Sopenharmony_ci#define FILTER_CNT 0x30 6362306a36Sopenharmony_ci#define FAIRNESS_CNT 0x02 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cienum { 6662306a36Sopenharmony_ci GXP_I2C_IDLE = 0, 6762306a36Sopenharmony_ci GXP_I2C_ADDR_PHASE, 6862306a36Sopenharmony_ci GXP_I2C_RDATA_PHASE, 6962306a36Sopenharmony_ci GXP_I2C_WDATA_PHASE, 7062306a36Sopenharmony_ci GXP_I2C_ADDR_NACK, 7162306a36Sopenharmony_ci GXP_I2C_DATA_NACK, 7262306a36Sopenharmony_ci GXP_I2C_ERROR, 7362306a36Sopenharmony_ci GXP_I2C_COMP 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct gxp_i2c_drvdata { 7762306a36Sopenharmony_ci struct device *dev; 7862306a36Sopenharmony_ci void __iomem *base; 7962306a36Sopenharmony_ci struct i2c_timings t; 8062306a36Sopenharmony_ci u32 engine; 8162306a36Sopenharmony_ci int irq; 8262306a36Sopenharmony_ci struct completion completion; 8362306a36Sopenharmony_ci struct i2c_adapter adapter; 8462306a36Sopenharmony_ci struct i2c_msg *curr_msg; 8562306a36Sopenharmony_ci int msgs_remaining; 8662306a36Sopenharmony_ci int msgs_num; 8762306a36Sopenharmony_ci u8 *buf; 8862306a36Sopenharmony_ci size_t buf_remaining; 8962306a36Sopenharmony_ci unsigned char state; 9062306a36Sopenharmony_ci struct i2c_client *slave; 9162306a36Sopenharmony_ci unsigned char stopped; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct regmap *i2cg_map; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void gxp_i2c_start(struct gxp_i2c_drvdata *drvdata) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci u16 value; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci drvdata->buf = drvdata->curr_msg->buf; 10162306a36Sopenharmony_ci drvdata->buf_remaining = drvdata->curr_msg->len; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Note: Address in struct i2c_msg is 7 bits */ 10462306a36Sopenharmony_ci value = drvdata->curr_msg->addr << 9; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Read or Write */ 10762306a36Sopenharmony_ci value |= drvdata->curr_msg->flags & I2C_M_RD ? RW_CMD | START_CMD : START_CMD; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci drvdata->state = GXP_I2C_ADDR_PHASE; 11062306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CMCMD); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int gxp_i2c_master_xfer(struct i2c_adapter *adapter, 11462306a36Sopenharmony_ci struct i2c_msg *msgs, int num) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(adapter); 11862306a36Sopenharmony_ci unsigned long time_left; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci drvdata->msgs_remaining = num; 12162306a36Sopenharmony_ci drvdata->curr_msg = msgs; 12262306a36Sopenharmony_ci drvdata->msgs_num = num; 12362306a36Sopenharmony_ci reinit_completion(&drvdata->completion); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci gxp_i2c_start(drvdata); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&drvdata->completion, 12862306a36Sopenharmony_ci adapter->timeout); 12962306a36Sopenharmony_ci ret = num - drvdata->msgs_remaining; 13062306a36Sopenharmony_ci if (time_left == 0) 13162306a36Sopenharmony_ci return -ETIMEDOUT; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (drvdata->state == GXP_I2C_ADDR_NACK) 13462306a36Sopenharmony_ci return -ENXIO; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (drvdata->state == GXP_I2C_DATA_NACK) 13762306a36Sopenharmony_ci return -EIO; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic u32 gxp_i2c_func(struct i2c_adapter *adap) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_I2C_SLAVE)) 14562306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 15162306a36Sopenharmony_cistatic int gxp_i2c_reg_slave(struct i2c_client *slave) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (drvdata->slave) 15662306a36Sopenharmony_ci return -EBUSY; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (slave->flags & I2C_CLIENT_TEN) 15962306a36Sopenharmony_ci return -EAFNOSUPPORT; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci drvdata->slave = slave; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci writeb(slave->addr << 1, drvdata->base + GXP_I2COWNADR); 16462306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | SLAVE_ACK_ENAB | 16562306a36Sopenharmony_ci SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int gxp_i2c_unreg_slave(struct i2c_client *slave) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci WARN_ON(!drvdata->slave); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2COWNADR); 17762306a36Sopenharmony_ci writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK | 17862306a36Sopenharmony_ci SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci drvdata->slave = NULL; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic const struct i2c_algorithm gxp_i2c_algo = { 18762306a36Sopenharmony_ci .master_xfer = gxp_i2c_master_xfer, 18862306a36Sopenharmony_ci .functionality = gxp_i2c_func, 18962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 19062306a36Sopenharmony_ci .reg_slave = gxp_i2c_reg_slave, 19162306a36Sopenharmony_ci .unreg_slave = gxp_i2c_unreg_slave, 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void gxp_i2c_stop(struct gxp_i2c_drvdata *drvdata) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci /* Clear event and send stop */ 19862306a36Sopenharmony_ci writeb(MASTER_EVT_CLR | STOP_CMD, drvdata->base + GXP_I2CMCMD); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci complete(&drvdata->completion); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void gxp_i2c_restart(struct gxp_i2c_drvdata *drvdata) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u16 value; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci drvdata->buf = drvdata->curr_msg->buf; 20862306a36Sopenharmony_ci drvdata->buf_remaining = drvdata->curr_msg->len; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci value = drvdata->curr_msg->addr << 9; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (drvdata->curr_msg->flags & I2C_M_RD) { 21362306a36Sopenharmony_ci /* Read and clear master event */ 21462306a36Sopenharmony_ci value |= MASTER_EVT_CLR | RW_CMD | START_CMD; 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci /* Write and clear master event */ 21762306a36Sopenharmony_ci value |= MASTER_EVT_CLR | START_CMD; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci drvdata->state = GXP_I2C_ADDR_PHASE; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CMCMD); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void gxp_i2c_chk_addr_ack(struct gxp_i2c_drvdata *drvdata) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u16 value; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSTAT); 23062306a36Sopenharmony_ci if (!(value & MASK_ACK)) { 23162306a36Sopenharmony_ci /* Got no ack, stop */ 23262306a36Sopenharmony_ci drvdata->state = GXP_I2C_ADDR_NACK; 23362306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (drvdata->curr_msg->flags & I2C_M_RD) { 23862306a36Sopenharmony_ci /* Start to read data from slave */ 23962306a36Sopenharmony_ci if (drvdata->buf_remaining == 0) { 24062306a36Sopenharmony_ci /* No more data to read, stop */ 24162306a36Sopenharmony_ci drvdata->msgs_remaining--; 24262306a36Sopenharmony_ci drvdata->state = GXP_I2C_COMP; 24362306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci drvdata->state = GXP_I2C_RDATA_PHASE; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (drvdata->buf_remaining == 1) { 24962306a36Sopenharmony_ci /* The last data, do not ack */ 25062306a36Sopenharmony_ci writeb(MASTER_EVT_CLR | RW_CMD, 25162306a36Sopenharmony_ci drvdata->base + GXP_I2CMCMD); 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci /* Read data and ack it */ 25462306a36Sopenharmony_ci writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB | 25562306a36Sopenharmony_ci RW_CMD, drvdata->base + GXP_I2CMCMD); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci /* Start to write first data to slave */ 25962306a36Sopenharmony_ci if (drvdata->buf_remaining == 0) { 26062306a36Sopenharmony_ci /* No more data to write, stop */ 26162306a36Sopenharmony_ci drvdata->msgs_remaining--; 26262306a36Sopenharmony_ci drvdata->state = GXP_I2C_COMP; 26362306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci value = *drvdata->buf; 26762306a36Sopenharmony_ci value = value << 8; 26862306a36Sopenharmony_ci /* Clear master event */ 26962306a36Sopenharmony_ci value |= MASTER_EVT_CLR; 27062306a36Sopenharmony_ci drvdata->buf++; 27162306a36Sopenharmony_ci drvdata->buf_remaining--; 27262306a36Sopenharmony_ci drvdata->state = GXP_I2C_WDATA_PHASE; 27362306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CMCMD); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void gxp_i2c_ack_data(struct gxp_i2c_drvdata *drvdata) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci u8 value; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Store the data returned */ 28262306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSNPDAT); 28362306a36Sopenharmony_ci *drvdata->buf = value; 28462306a36Sopenharmony_ci drvdata->buf++; 28562306a36Sopenharmony_ci drvdata->buf_remaining--; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (drvdata->buf_remaining == 0) { 28862306a36Sopenharmony_ci /* No more data, this message is completed. */ 28962306a36Sopenharmony_ci drvdata->msgs_remaining--; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (drvdata->msgs_remaining == 0) { 29262306a36Sopenharmony_ci /* No more messages, stop */ 29362306a36Sopenharmony_ci drvdata->state = GXP_I2C_COMP; 29462306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci /* Move to next message and start transfer */ 29862306a36Sopenharmony_ci drvdata->curr_msg++; 29962306a36Sopenharmony_ci gxp_i2c_restart(drvdata); 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Ack the slave to make it send next byte */ 30462306a36Sopenharmony_ci drvdata->state = GXP_I2C_RDATA_PHASE; 30562306a36Sopenharmony_ci if (drvdata->buf_remaining == 1) { 30662306a36Sopenharmony_ci /* The last data, do not ack */ 30762306a36Sopenharmony_ci writeb(MASTER_EVT_CLR | RW_CMD, 30862306a36Sopenharmony_ci drvdata->base + GXP_I2CMCMD); 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci /* Read data and ack it */ 31162306a36Sopenharmony_ci writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB | 31262306a36Sopenharmony_ci RW_CMD, drvdata->base + GXP_I2CMCMD); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void gxp_i2c_chk_data_ack(struct gxp_i2c_drvdata *drvdata) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci u16 value; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSTAT); 32162306a36Sopenharmony_ci if (!(value & MASK_ACK)) { 32262306a36Sopenharmony_ci /* Received No ack, stop */ 32362306a36Sopenharmony_ci drvdata->state = GXP_I2C_DATA_NACK; 32462306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 32562306a36Sopenharmony_ci return; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Got ack, check if there is more data to write */ 32962306a36Sopenharmony_ci if (drvdata->buf_remaining == 0) { 33062306a36Sopenharmony_ci /* No more data, this message is completed */ 33162306a36Sopenharmony_ci drvdata->msgs_remaining--; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (drvdata->msgs_remaining == 0) { 33462306a36Sopenharmony_ci /* No more messages, stop */ 33562306a36Sopenharmony_ci drvdata->state = GXP_I2C_COMP; 33662306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci /* Move to next message and start transfer */ 34062306a36Sopenharmony_ci drvdata->curr_msg++; 34162306a36Sopenharmony_ci gxp_i2c_restart(drvdata); 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Write data to slave */ 34662306a36Sopenharmony_ci value = *drvdata->buf; 34762306a36Sopenharmony_ci value = value << 8; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Clear master event */ 35062306a36Sopenharmony_ci value |= MASTER_EVT_CLR; 35162306a36Sopenharmony_ci drvdata->buf++; 35262306a36Sopenharmony_ci drvdata->buf_remaining--; 35362306a36Sopenharmony_ci drvdata->state = GXP_I2C_WDATA_PHASE; 35462306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CMCMD); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic bool gxp_i2c_slave_irq_handler(struct gxp_i2c_drvdata *drvdata) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci u8 value; 36062306a36Sopenharmony_ci u8 buf; 36162306a36Sopenharmony_ci int ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CEVTERR); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Received start or stop event */ 36662306a36Sopenharmony_ci if (value & MASK_SLAVE_CMD_EVENT) { 36762306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSTAT); 36862306a36Sopenharmony_ci /* Master sent stop */ 36962306a36Sopenharmony_ci if (value & MASK_STOP_EVENT) { 37062306a36Sopenharmony_ci if (drvdata->stopped == 0) 37162306a36Sopenharmony_ci i2c_slave_event(drvdata->slave, I2C_SLAVE_STOP, &buf); 37262306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 37362306a36Sopenharmony_ci SLAVE_ACK_ENAB | SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD); 37462306a36Sopenharmony_ci drvdata->stopped = 1; 37562306a36Sopenharmony_ci } else { 37662306a36Sopenharmony_ci /* Master sent start and wants to read */ 37762306a36Sopenharmony_ci drvdata->stopped = 0; 37862306a36Sopenharmony_ci if (value & MASK_RW) { 37962306a36Sopenharmony_ci i2c_slave_event(drvdata->slave, 38062306a36Sopenharmony_ci I2C_SLAVE_READ_REQUESTED, &buf); 38162306a36Sopenharmony_ci value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK | 38262306a36Sopenharmony_ci SLAVE_EVT_STALL); 38362306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CSCMD); 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci /* Master wants to write to us */ 38662306a36Sopenharmony_ci ret = i2c_slave_event(drvdata->slave, 38762306a36Sopenharmony_ci I2C_SLAVE_WRITE_REQUESTED, &buf); 38862306a36Sopenharmony_ci if (!ret) { 38962306a36Sopenharmony_ci /* Ack next byte from master */ 39062306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 39162306a36Sopenharmony_ci SLAVE_ACK_ENAB | SLAVE_EVT_STALL, 39262306a36Sopenharmony_ci drvdata->base + GXP_I2CSCMD); 39362306a36Sopenharmony_ci } else { 39462306a36Sopenharmony_ci /* Nack next byte from master */ 39562306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 39662306a36Sopenharmony_ci SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } else if (value & MASK_SLAVE_DATA_EVENT) { 40162306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSTAT); 40262306a36Sopenharmony_ci /* Master wants to read */ 40362306a36Sopenharmony_ci if (value & MASK_RW) { 40462306a36Sopenharmony_ci /* Master wants another byte */ 40562306a36Sopenharmony_ci if (value & MASK_ACK) { 40662306a36Sopenharmony_ci i2c_slave_event(drvdata->slave, 40762306a36Sopenharmony_ci I2C_SLAVE_READ_PROCESSED, &buf); 40862306a36Sopenharmony_ci value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK | 40962306a36Sopenharmony_ci SLAVE_EVT_STALL); 41062306a36Sopenharmony_ci writew(value, drvdata->base + GXP_I2CSCMD); 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci /* No more bytes needed */ 41362306a36Sopenharmony_ci writew(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 41462306a36Sopenharmony_ci SLAVE_ACK_ENAB | SLAVE_EVT_STALL, 41562306a36Sopenharmony_ci drvdata->base + GXP_I2CSCMD); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci } else { 41862306a36Sopenharmony_ci /* Master wants to write to us */ 41962306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CSNPDAT); 42062306a36Sopenharmony_ci buf = (uint8_t)value; 42162306a36Sopenharmony_ci ret = i2c_slave_event(drvdata->slave, 42262306a36Sopenharmony_ci I2C_SLAVE_WRITE_RECEIVED, &buf); 42362306a36Sopenharmony_ci if (!ret) { 42462306a36Sopenharmony_ci /* Ack next byte from master */ 42562306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 42662306a36Sopenharmony_ci SLAVE_ACK_ENAB | SLAVE_EVT_STALL, 42762306a36Sopenharmony_ci drvdata->base + GXP_I2CSCMD); 42862306a36Sopenharmony_ci } else { 42962306a36Sopenharmony_ci /* Nack next byte from master */ 43062306a36Sopenharmony_ci writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | 43162306a36Sopenharmony_ci SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci } else { 43562306a36Sopenharmony_ci return false; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return true; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic irqreturn_t gxp_i2c_irq_handler(int irq, void *_drvdata) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata = (struct gxp_i2c_drvdata *)_drvdata; 44462306a36Sopenharmony_ci u32 value; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Check if the interrupt is for the current engine */ 44762306a36Sopenharmony_ci regmap_read(i2cg_map, GXP_I2CINTSTAT, &value); 44862306a36Sopenharmony_ci if (!(value & BIT(drvdata->engine))) 44962306a36Sopenharmony_ci return IRQ_NONE; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci value = readb(drvdata->base + GXP_I2CEVTERR); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Error */ 45462306a36Sopenharmony_ci if (value & ~(MASK_MASTER_EVENT | MASK_SLAVE_CMD_EVENT | 45562306a36Sopenharmony_ci MASK_SLAVE_DATA_EVENT)) { 45662306a36Sopenharmony_ci /* Clear all events */ 45762306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2CEVTERR); 45862306a36Sopenharmony_ci drvdata->state = GXP_I2C_ERROR; 45962306a36Sopenharmony_ci gxp_i2c_stop(drvdata); 46062306a36Sopenharmony_ci return IRQ_HANDLED; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_I2C_SLAVE)) { 46462306a36Sopenharmony_ci /* Slave mode */ 46562306a36Sopenharmony_ci if (value & (MASK_SLAVE_CMD_EVENT | MASK_SLAVE_DATA_EVENT)) { 46662306a36Sopenharmony_ci if (gxp_i2c_slave_irq_handler(drvdata)) 46762306a36Sopenharmony_ci return IRQ_HANDLED; 46862306a36Sopenharmony_ci return IRQ_NONE; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Master mode */ 47362306a36Sopenharmony_ci switch (drvdata->state) { 47462306a36Sopenharmony_ci case GXP_I2C_ADDR_PHASE: 47562306a36Sopenharmony_ci gxp_i2c_chk_addr_ack(drvdata); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci case GXP_I2C_RDATA_PHASE: 47962306a36Sopenharmony_ci gxp_i2c_ack_data(drvdata); 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci case GXP_I2C_WDATA_PHASE: 48362306a36Sopenharmony_ci gxp_i2c_chk_data_ack(drvdata); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return IRQ_HANDLED; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void gxp_i2c_init(struct gxp_i2c_drvdata *drvdata) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci drvdata->state = GXP_I2C_IDLE; 49362306a36Sopenharmony_ci writeb(2000000 / drvdata->t.bus_freq_hz, 49462306a36Sopenharmony_ci drvdata->base + GXP_I2CFREQDIV); 49562306a36Sopenharmony_ci writeb(FILTER_CNT | FAIRNESS_CNT, 49662306a36Sopenharmony_ci drvdata->base + GXP_I2CFLTFAIR); 49762306a36Sopenharmony_ci writeb(GXP_DATA_EDGE_RST_CTRL, drvdata->base + GXP_I2CTMOEDG); 49862306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2CCYCTIM); 49962306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2CSNPAA); 50062306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2CADVFEAT); 50162306a36Sopenharmony_ci writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK | 50262306a36Sopenharmony_ci SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD); 50362306a36Sopenharmony_ci writeb(MASTER_EVT_CLR, drvdata->base + GXP_I2CMCMD); 50462306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2CEVTERR); 50562306a36Sopenharmony_ci writeb(0x00, drvdata->base + GXP_I2COWNADR); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int gxp_i2c_probe(struct platform_device *pdev) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata; 51162306a36Sopenharmony_ci int rc; 51262306a36Sopenharmony_ci struct i2c_adapter *adapter; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (!i2cg_map) { 51562306a36Sopenharmony_ci i2cg_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 51662306a36Sopenharmony_ci "hpe,sysreg"); 51762306a36Sopenharmony_ci if (IS_ERR(i2cg_map)) { 51862306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(i2cg_map), 51962306a36Sopenharmony_ci "failed to map i2cg_handle\n"); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Disable interrupt */ 52362306a36Sopenharmony_ci regmap_update_bits(i2cg_map, GXP_I2CINTEN, 0x00000FFF, 0); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), 52762306a36Sopenharmony_ci GFP_KERNEL); 52862306a36Sopenharmony_ci if (!drvdata) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 53262306a36Sopenharmony_ci drvdata->dev = &pdev->dev; 53362306a36Sopenharmony_ci init_completion(&drvdata->completion); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci drvdata->base = devm_platform_ioremap_resource(pdev, 0); 53662306a36Sopenharmony_ci if (IS_ERR(drvdata->base)) 53762306a36Sopenharmony_ci return PTR_ERR(drvdata->base); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Use physical memory address to determine which I2C engine this is. */ 54062306a36Sopenharmony_ci drvdata->engine = ((size_t)drvdata->base & 0xf00) >> 8; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (drvdata->engine >= GXP_MAX_I2C_ENGINE) { 54362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -EINVAL, "i2c engine% is unsupported\n", 54462306a36Sopenharmony_ci drvdata->engine); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci rc = platform_get_irq(pdev, 0); 54862306a36Sopenharmony_ci if (rc < 0) 54962306a36Sopenharmony_ci return rc; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci drvdata->irq = rc; 55262306a36Sopenharmony_ci rc = devm_request_irq(&pdev->dev, drvdata->irq, gxp_i2c_irq_handler, 55362306a36Sopenharmony_ci IRQF_SHARED, gxp_i2c_name[drvdata->engine], drvdata); 55462306a36Sopenharmony_ci if (rc < 0) 55562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, rc, "irq request failed\n"); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci i2c_parse_fw_timings(&pdev->dev, &drvdata->t, true); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci gxp_i2c_init(drvdata); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Enable interrupt */ 56262306a36Sopenharmony_ci regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine), 56362306a36Sopenharmony_ci BIT(drvdata->engine)); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci adapter = &drvdata->adapter; 56662306a36Sopenharmony_ci i2c_set_adapdata(adapter, drvdata); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci adapter->owner = THIS_MODULE; 56962306a36Sopenharmony_ci strscpy(adapter->name, "HPE GXP I2C adapter", sizeof(adapter->name)); 57062306a36Sopenharmony_ci adapter->algo = &gxp_i2c_algo; 57162306a36Sopenharmony_ci adapter->dev.parent = &pdev->dev; 57262306a36Sopenharmony_ci adapter->dev.of_node = pdev->dev.of_node; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci rc = i2c_add_adapter(adapter); 57562306a36Sopenharmony_ci if (rc) 57662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, rc, "i2c add adapter failed\n"); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void gxp_i2c_remove(struct platform_device *pdev) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct gxp_i2c_drvdata *drvdata = platform_get_drvdata(pdev); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Disable interrupt */ 58662306a36Sopenharmony_ci regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine), 0); 58762306a36Sopenharmony_ci i2c_del_adapter(&drvdata->adapter); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const struct of_device_id gxp_i2c_of_match[] = { 59162306a36Sopenharmony_ci { .compatible = "hpe,gxp-i2c" }, 59262306a36Sopenharmony_ci {}, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gxp_i2c_of_match); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic struct platform_driver gxp_i2c_driver = { 59762306a36Sopenharmony_ci .probe = gxp_i2c_probe, 59862306a36Sopenharmony_ci .remove_new = gxp_i2c_remove, 59962306a36Sopenharmony_ci .driver = { 60062306a36Sopenharmony_ci .name = "gxp-i2c", 60162306a36Sopenharmony_ci .of_match_table = gxp_i2c_of_match, 60262306a36Sopenharmony_ci }, 60362306a36Sopenharmony_ci}; 60462306a36Sopenharmony_cimodule_platform_driver(gxp_i2c_driver); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciMODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>"); 60762306a36Sopenharmony_ciMODULE_DESCRIPTION("HPE GXP I2C bus driver"); 60862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 609