/* * Juniper Networks TMC I2C Accelerator driver * * Copyright (C) 2020 Juniper Networks * Author: Ashish Bhensdadia * * This driver implement the I2C functionality * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "jnx-tmc.h" #define TMC_I2C_MASTER_I2C_SCAN_RESET_BIT BIT(31) #define TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET 0x0 #define TMC_I2C_MSTR_AUTOMATION_I2C_DPMEM_OFFSET 0x10 #define TMC_I2C_MSTR_AUTOMATION_I2C(adap, offset) \ ((adap)->membase + offset) #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_0 0x0 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8 0x8 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_4 0x4 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_C 0xC #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10 0x10 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_14 0x14 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_18 0x18 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_1C 0x1C #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_20 0x20 #define TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_24 0x24 #define TMC_I2C_MSTR_I2C_DPMEM(adap, offset) \ ((adap)->dpmbase + offset) #define TMC_I2C_TRANS_LEN 2 #define tmc_iowrite(val, addr) iowrite32((val), (addr)) #define TMC_I2C_CTRL_GROUP(g) (((g) & 0xFF) << 8) #define TMC_I2C_CTRL_WRCNT(w) (((w) & 0x3F) << 16) #define TMC_I2C_CTRL_RDCNT(r) (((r) & 0x3F) << 16) #define TMC_I2C_CTRL_DEVADDR(d) (((d) & 0xFF) << 8) #define TMC_I2C_CTRL_OFFSET(o) ((o) & 0xFF) #define TMC_I2C_MEM_CTRL_VLD BIT(31) #define TMC_I2C_CTRL_ERR(s) ((s) & 0x0F000000) #define TMC_I2C_CTRL_DONE_BIT(s) ((s) & BIT(31)) #define TMC_I2C_CTRL_STATUS_OK(s) (TMC_I2C_CTRL_DONE_BIT(s) & \ TMC_I2C_CTRL_ERR(s)) #define TMC_I2C_CTRL_DONE(s) (TMC_I2C_CTRL_DONE_BIT(s) == BIT(31)) #define TMC_I2C_STAT_INC(adap, s) (((adap)->stat.s)++) #define TMC_I2C_STAT_INCN(adap, s, n) (((adap)->stat.s) += (n)) #define TMC_I2C_GET_MASTER(tadap) ((tadap)->tctrl) #define TMC_I2C_READ 0x1 #define TMC_I2C_WRITE 0x2 #define TMC_I2C_MASTER_LOCK(s, flags) \ do { \ spin_lock_irqsave(&(s)->lock, flags); \ } while (0) #define TMC_I2C_MASTER_UNLOCK(s, flags) \ do { \ spin_unlock_irqrestore(&(s)->lock, flags); \ } while (0) #define tmc_i2c_dbg(dev, fmt, args...) \ do { \ if (tmc_i2c_debug >= 1) \ dev_err(dev, fmt, ## args); \ } while (0) /* pfe TMC i2c channel */ static int pfe_channel = 32; module_param(pfe_channel, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(pfe_channel, "Maximum number of channel for PFE TMC"); /* chassid TMC i2c channel */ static int chd_channel = 11; module_param(chd_channel, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(chd_channel, "Maximum number of channel for CHASSID TMC"); static u32 wr_index_to_oper[] = {0x01000000, 0x02000000, 0x84000000, 0x85000000, 0x83000000}; static u32 rd_index_to_oper[] = {0x08000000, 0x09000000, 0x0A000000, 0x8B000000, 0x8C000000, 0x8D000000, 0x83000000}; struct tmc_i2c_adapter_stats { u32 abort; u32 go; u32 mstr_rdy; u32 mstr_busy; u32 trans_compl; u32 msg_cnt; u32 rd_cnt; u32 wr_cnt; u32 byte_cnt; u32 slave_timeo; u32 scl_bus_loss; u32 sda_bus_loss; u32 ack_ptimeo; u32 rd_cnt_0; u32 rd_cnt_gt32; u32 rst_tgt; u32 rst_mstr; }; struct tmc_i2c_adapter { void __iomem *membase; void __iomem *dpmbase; struct i2c_adapter adap; struct i2c_mux_core *muxc; struct tmc_i2c_ctrl *tctrl; int mux_channels; int mux_select; u32 i2c_delay; int entries; int master; u32 control; u32 speed; bool done; bool polling; bool use_block; wait_queue_head_t wait; struct tmc_i2c_adapter_stats stat; }; struct tmc_i2c_ctrl { void __iomem *membase; void __iomem *dpmbase; struct i2c_adapter **adap; struct device *dev; int num_masters; int mux_channels; u32 i2c_delay; u32 master_mask; spinlock_t lock; /* master lock */ }; /* * Reset the Tmc I2C master */ static void tmc_i2c_reset_master(struct i2c_adapter *adap) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); struct tmc_i2c_ctrl *tmc = TMC_I2C_GET_MASTER(tadap); u32 val, master = tadap->master; unsigned long flags; void __iomem *addr; dev_warn(&adap->dev, "Re-setting i2c master: %d\n", master); TMC_I2C_MASTER_LOCK(tmc, flags); addr = tmc->membase + TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET; val = ioread32(addr); tmc_iowrite(val | (TMC_I2C_MASTER_I2C_SCAN_RESET_BIT), addr); tmc_iowrite(val & ~(TMC_I2C_MASTER_I2C_SCAN_RESET_BIT), addr); TMC_I2C_STAT_INC(tadap, rst_mstr); TMC_I2C_MASTER_UNLOCK(tmc, flags); } /* * check if the Tmc I2C master is ready */ static int tmc_i2c_mstr_wait_rdy(struct i2c_adapter *adap, u8 rw, u32 delay) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); unsigned long timeout; u32 val; val = ioread32(TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); if (val) { tmc_iowrite(0x80000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); mdelay(5); tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); val = ioread32(TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); } else { tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); } if ((rw == TMC_I2C_READ) && (delay)) { tmc_iowrite(0x80000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return 0; } timeout = jiffies + adap->timeout; do { val = ioread32(TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); if (!val) return 0; if (tadap->polling) { usleep_range(50, 100); } else { tadap->done = false; wait_event_timeout(tadap->wait, tadap->done, adap->timeout); } } while (time_before(jiffies, timeout)); TMC_I2C_STAT_INC(tadap, mstr_busy); return -EBUSY; } /* * Wait for master completion */ static u32 tmc_i2c_mstr_wait_completion(struct i2c_adapter *adap, u32 dp_entry_offset) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); u32 val; if (tadap->polling) { /* Poll for the results */ unsigned long timeout = jiffies + adap->timeout; do { usleep_range(1000, 1200); val = ioread32(TMC_I2C_MSTR_I2C_DPMEM(tadap, dp_entry_offset)); if (TMC_I2C_CTRL_DONE(val)) break; } while (time_before(jiffies, timeout)); } else { wait_event_timeout(tadap->wait, tadap->done, adap->timeout); val = ioread32(TMC_I2C_MSTR_I2C_DPMEM(tadap, dp_entry_offset)); } return TMC_I2C_CTRL_STATUS_OK(val); } /* * TMC I2C delay read/write operation */ static int tmc_i2c_delay_rw_op(struct i2c_adapter *adap, u8 rw, u32 mux, u32 addr, u32 offset, u32 len, u32 delay, u8 *buf) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); struct device *dev = &adap->dev; int err, n; u32 control = 0, data = 0; u32 val; err = tmc_i2c_mstr_wait_rdy(adap, rw, 0); if (err < 0) { tmc_i2c_reset_master(adap); return err; } TMC_I2C_STAT_INC(tadap, mstr_rdy); /* initialize the start address and mux */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_DPMEM_OFFSET)); tmc_iowrite(mux, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_0)); tmc_iowrite(0x84400000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_4)); /* populate delay */ if (delay) { if (delay > 1000) { delay = delay/1000; delay |= (1 << 16); } } tmc_iowrite(delay, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8)); tmc_iowrite(0x86000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_C)); /* prepare control command */ control |= TMC_I2C_CTRL_DEVADDR(addr); control |= TMC_I2C_CTRL_OFFSET(offset); if (rw == TMC_I2C_WRITE) { for (n = 0; n < len; n++) data |= (buf[n] << ((len - 1 - n) * 8)); tmc_iowrite(data, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10)); control |= TMC_I2C_CTRL_WRCNT(len); control |= wr_index_to_oper[len-1]; dev_dbg(dev, "WR Data: [%#04x, %#04x, %#04x, %#04x]\n", ((data >> 24) & 0xff), ((data >> 16) & 0xff), ((data >> 8) & 0xff), (data & 0xff)); } else { /* read */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10)); control |= TMC_I2C_CTRL_RDCNT(len); control |= rd_index_to_oper[len-1]; } /* * valid this transaction as well */ control |= TMC_I2C_MEM_CTRL_VLD; tadap->control = control; /* * operation control command */ tmc_iowrite(control, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_14)); /* * End commands */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_18)); tmc_iowrite(0x8E000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_1C)); tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_20)); tmc_iowrite(0x8F000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_24)); dev_dbg(dev, "Control (%#x): RD_WR_TYPE:%#02x, RD_WR_LEN:%d," "Addr:%#01x, Offset:%#02x\n", control, ((control >> 24) & 0x3F), ((control >> 16) & 0x3F), ((control >> 8) & 0xff), ((control) & 0xff)); tadap->done = false; /* fire the transaction */ tmc_iowrite(0x00000001, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); TMC_I2C_STAT_INC(tadap, go); val = tmc_i2c_mstr_wait_completion(adap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10); if (val) { dev_err(&adap->dev, "i2c transaction error (0x%08x)\n", val); /* stop the transaction */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return -EIO; } /* * read a word of data */ if (rw == TMC_I2C_READ) { data = ioread32(TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10)); for (n = 0; n < len; n++) buf[n] = (data >> (n * 8)) & 0xff; dev_dbg(dev, "RD Data: [%#04x, %#04x, %#04x, %#04x]\n", ((data >> 24) & 0xff), ((data >> 16) & 0xff), ((data >> 8) & 0xff), (data & 0xff)); TMC_I2C_STAT_INC(tadap, rd_cnt); } else { /* write */ TMC_I2C_STAT_INC(tadap, wr_cnt); } /* stop the transaction */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return 0; } /* *TMC I2C none delay Read/write opertion */ static int tmc_i2c_none_delay_rw_op(struct i2c_adapter *adap, u8 rw, u32 mux, u32 addr, u32 offset, u32 len, u8 *buf) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); struct device *dev = &adap->dev; int err, n; u32 control = 0, data = 0; u32 val; err = tmc_i2c_mstr_wait_rdy(adap, rw, 0); if (err < 0) { tmc_i2c_reset_master(adap); return err; } TMC_I2C_STAT_INC(tadap, mstr_rdy); /* initialize the start address and mux */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_DPMEM_OFFSET)); tmc_iowrite(mux, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_0)); tmc_iowrite(0x84400000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_4)); /* prepare control command */ control |= TMC_I2C_CTRL_DEVADDR(addr); control |= TMC_I2C_CTRL_OFFSET(offset); if (rw == TMC_I2C_WRITE) { for (n = 0; n < len; n++) data |= (buf[n] << (n * 8)); tmc_iowrite(data, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8)); control |= wr_index_to_oper[len-1]; dev_dbg(dev, "WR Data: [%#04x, %#04x, %#04x, %#04x]\n", ((data >> 24) & 0xff), ((data >> 16) & 0xff), ((data >> 8) & 0xff), (data & 0xff)); } else { /* read */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8)); control |= rd_index_to_oper[len-1]; } /* * valid this transaction as well */ control |= TMC_I2C_MEM_CTRL_VLD; tadap->control = control; /* * operation control command */ tmc_iowrite(control, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_C)); /* * End commands */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10)); tmc_iowrite(0x8E000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_14)); tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_18)); tmc_iowrite(0x8F000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_1C)); dev_dbg(dev, "Control (%#x): RD_WR_TYPE:%#02x, RD_WR_LEN:%d," "Addr:%#01x, Offset:%#02x\n", control, ((control >> 24) & 0x3F), ((control >> 16) & 0x3F), ((control >> 8) & 0xff), ((control) & 0xff)); tadap->done = false; /* fire the transaction */ tmc_iowrite(0x00000001, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); TMC_I2C_STAT_INC(tadap, go); /* wait till transaction complete */ val = tmc_i2c_mstr_wait_completion(adap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8); if (val) { dev_err(&adap->dev, "i2c transaction error (0x%08x)\n", val); /* stop the transaction */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return -EIO; } /* * read a word of data */ if (rw == TMC_I2C_READ) { data = ioread32(TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8)); for (n = 0; n < len; n++) buf[n] = (data >> (n * 8)) & 0xff; dev_dbg(dev, "RD Data: [%#04x, %#04x, %#04x, %#04x]\n", ((data >> 24) & 0xff), ((data >> 16) & 0xff), ((data >> 8) & 0xff), (data & 0xff)); TMC_I2C_STAT_INC(tadap, rd_cnt); } else { /* write */ TMC_I2C_STAT_INC(tadap, wr_cnt); } /* stop the transaction */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return 0; } /* * TMC I2C read/write operation */ static int tmc_i2c_rw_op(struct i2c_adapter *adap, u8 rw, u32 mux, u32 addr, u32 offset, u32 len, u8 *buf) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); u32 i2c_delay = tadap->i2c_delay; if (i2c_delay) { return tmc_i2c_delay_rw_op(adap, rw, mux, addr, offset, len, i2c_delay, buf); } else { return tmc_i2c_none_delay_rw_op(adap, rw, mux, addr, offset, len, buf); } } static int tmc_i2c_calc_entries(int msglen) { int entries = msglen / TMC_I2C_TRANS_LEN; return (entries += (msglen % TMC_I2C_TRANS_LEN) ? 1 : 0); } static int tmc_i2c_block_read(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); struct device *dev = &adap->dev; int curmsg, entries, len; int offset = 0; struct i2c_msg *msg; int err, n = 0; u8 rwbuf[4] = {0}; dev_dbg(dev, "Read i2c Block\n"); for (curmsg = 0, offset = 0; curmsg < num; curmsg++) { msg = &msgs[curmsg]; len = msg->len; if (msg->flags & I2C_M_RECV_LEN) len = (I2C_SMBUS_BLOCK_MAX + 1); entries = tmc_i2c_calc_entries(len); if (msg->flags & I2C_M_RD) { if (curmsg == 1 && ((msg->flags & I2C_M_RECV_LEN && !(msgs[0].flags & I2C_M_RD)) || (msg->len > TMC_I2C_TRANS_LEN))) { offset = msgs[0].buf[0]; } while (entries) { err = tmc_i2c_rw_op(adap, TMC_I2C_READ, tadap->mux_select, msgs[0].addr, offset, TMC_I2C_TRANS_LEN, rwbuf); if (err < 0) return err; msg = &msgs[num - 1]; msg->buf[n] = rwbuf[0]; msg->buf[n+1] = rwbuf[1]; n = n + TMC_I2C_TRANS_LEN; offset = offset + TMC_I2C_TRANS_LEN; entries--; } } } return 0; } /* *TMC I2C SMB Read opertion */ static int tmc_i2c_smb_block_read_op(struct i2c_adapter *adap, u8 rw, u32 mux, u32 addr, u32 offset, u32 len, u8 *buf) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); int err, i = 0; u32 control = 0, data = 0; u32 start_add; err = tmc_i2c_mstr_wait_rdy(adap, rw, 0); if (err < 0) { tmc_i2c_reset_master(adap); return err; } TMC_I2C_STAT_INC(tadap, mstr_rdy); /* initialize the start address and mux */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_DPMEM_OFFSET)); tmc_iowrite(mux, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_0)); tmc_iowrite(0x84400000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_4)); /* prepare control command */ control |= TMC_I2C_CTRL_DEVADDR(addr); control |= TMC_I2C_CTRL_OFFSET(offset); /* read */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8)); control |= TMC_I2C_CTRL_RDCNT(len);; control |= rd_index_to_oper[6]; tadap->control = control; /* * operation control command */ tmc_iowrite(control, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_C)); /* * End commands */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_10)); tmc_iowrite(0x8E000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_14)); tmc_iowrite(0x00000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_18)); tmc_iowrite(0x8F000000, TMC_I2C_MSTR_I2C_DPMEM(tadap, TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_1C)); tadap->done = false; /* fire the transaction */ tmc_iowrite(0x00000001, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); TMC_I2C_STAT_INC(tadap, go); /* * read a block of data */ start_add = TMC_I2C_MSTR_I2C_DPMEM_ENTRY_OFFSET_8; while (len > 0) { usleep_range(10000, 12000); data = ioread32(TMC_I2C_MSTR_I2C_DPMEM(tadap, start_add)); buf[i] = data & 0xff; buf[i + 1] = (data >> 8) & 0xff; start_add = start_add + 8; i = i + 2; len = len - 2; TMC_I2C_STAT_INC(tadap, rd_cnt); } /* stop the transaction */ tmc_iowrite(0x00000000, TMC_I2C_MSTR_AUTOMATION_I2C(tadap, TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET)); return 0; } static int tmc_i2c_smb_block_read(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); int curmsg, len; int offset = 0; struct i2c_msg *msg; int err, i = 0; u8 rwbuf[32] = {0}; for (curmsg = 0, offset = 0; curmsg < num; curmsg++) { msg = &msgs[curmsg]; len = msg->len; if (msg->flags & I2C_M_RECV_LEN) len = (I2C_SMBUS_BLOCK_MAX + 1); if (msg->flags & I2C_M_RD) { if ((curmsg == 1) && (msg->flags & I2C_M_RECV_LEN) && !(msgs[0].flags & I2C_M_RD)) { offset = msgs[0].buf[0]; } err = tmc_i2c_smb_block_read_op(adap, TMC_I2C_READ, tadap->mux_select, msgs[0].addr, offset, 32, rwbuf); if (err < 0) { return err; } msg = &msgs[num - 1]; for (i = 0; i < len - 1; i++) { msg->buf[i] = rwbuf[i]; } } } return 0; } static int tmc_i2c_mstr_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); struct device *dev = &adap->dev; int n, curmsg, len = 0; int err = 0; struct i2c_msg *msg; bool read; u8 rwbuf[4] = {0}; dev_dbg(dev, "Num messages -> %d\n", num); /* * Initialize all vars */ tadap->entries = 0; tadap->use_block = false; for (curmsg = 0; curmsg < num; curmsg++) { msg = &msgs[curmsg]; dev_dbg(dev, "[%02d] %d bytes, flag %#02x buf %#02x\n", curmsg, msg->len, msg->flags, msg->buf[0]); if ((msg->len > TMC_I2C_TRANS_LEN) || ((msg->flags & I2C_M_RD) && (msg->flags & I2C_M_RECV_LEN))) { /* If PEC is enabled len will come as 3 for a word read. * We don't want to use block read for this case. */ if ((msg->flags & I2C_CLIENT_PEC) && (msg->len == TMC_I2C_TRANS_LEN+1)) { tadap->use_block = false; } else { tadap->use_block = true; } break; } } if (tadap->use_block) { /* Read Block */ if ((msg->flags & I2C_M_RD) && (msg->flags & I2C_M_RECV_LEN)) { err = tmc_i2c_smb_block_read(adap, msgs, num); } else { err = tmc_i2c_block_read(adap, msgs, num); } if (err < 0) return err; } else { read = msgs[num - 1].flags & I2C_M_RD; for (curmsg = 0; curmsg < num; curmsg++) { msg = &msgs[curmsg]; len = msg->len; dev_dbg(dev, " [%02d] %s %d bytes addr %#02x, " "flag %#02x, buf[0] %#02x\n", curmsg, read ? "RD" : "WR", len, msg->addr, msg->flags, msg->buf[0]); /* SMBus quick read/write command */ if (len == 0 && curmsg == 0 && num == 1) { if (read) { len = 1; } break; } if (curmsg == 0) { if (!read) { for (n = 1; n < len; n++) rwbuf[n-1] = (msg->buf[n]); len--; } else { /* read operation */ continue; } } } if (!read) { /* write */ err = tmc_i2c_rw_op(adap, TMC_I2C_WRITE, tadap->mux_select, msgs[0].addr, msgs[0].buf[0], len, rwbuf); } else { /* read */ /* * If PEC is enabled read only 2 bytes as expected in * case of a word read instead of 3 to make it compatible * with word write implementation. */ if (msg->flags & I2C_CLIENT_PEC && (len == TMC_I2C_TRANS_LEN + 1)) { len--; } err = tmc_i2c_rw_op(adap, TMC_I2C_READ, tadap->mux_select, msgs[0].addr, msgs[0].buf[0], len, rwbuf); msg = &msgs[num - 1]; len = msg->len; /* * To avoid failure in PEC enabled case clear flag. */ if (len == TMC_I2C_TRANS_LEN + 1) { msgs[num - 1].flags &= ~I2C_M_RD; } for (n = 0; n < len; n++) msg->buf[n] = rwbuf[n]; } if (err < 0) return err; } TMC_I2C_STAT_INCN(tadap, msg_cnt, num); return num; } static u32 tmc_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA; } static const struct i2c_algorithm tmc_i2c_algo = { .master_xfer = tmc_i2c_mstr_xfer, .functionality = tmc_i2c_func, }; static int tmc_i2c_mux_group_sel(struct i2c_mux_core *muxc, u32 chan) { struct tmc_i2c_adapter *tadap = i2c_mux_priv(muxc); dev_dbg(muxc->dev, "chan = %d\n", chan); if (!tadap || chan > TMC_I2C_MSTR_MAX_GROUPS) return -ENODEV; tadap->mux_select = chan; return 0; } static int tmc_i2c_mux_init(struct i2c_adapter *adap) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); int chan, ret; tadap->muxc = i2c_mux_alloc(adap, &adap->dev, tadap->mux_channels, 0, 0, tmc_i2c_mux_group_sel, NULL); if (!tadap->muxc) return -ENOMEM; tadap->muxc->priv = tadap; for (chan = 0; chan < tadap->mux_channels; chan++) { ret = i2c_mux_add_adapter(tadap->muxc, 0, chan, 0); if (ret) { dev_err(&adap->dev, "Failed to add adapter %d\n", chan); i2c_mux_del_adapters(tadap->muxc); return ret; } } return 0; } static struct i2c_adapter * tmc_i2c_init_one(struct tmc_i2c_ctrl *tmc, int master, int id) { struct tmc_i2c_adapter *adapter; struct device *dev = tmc->dev; int err; adapter = devm_kzalloc(dev, sizeof(*adapter), GFP_KERNEL); if (!adapter) return ERR_PTR(-ENOMEM); init_waitqueue_head(&adapter->wait); adapter->adap.owner = THIS_MODULE; adapter->adap.algo = &tmc_i2c_algo; adapter->adap.nr = -1; adapter->adap.timeout = HZ / 5; adapter->master = master; adapter->mux_channels = tmc->mux_channels; adapter->i2c_delay = tmc->i2c_delay; adapter->membase = tmc->membase; adapter->dpmbase = tmc->dpmbase; adapter->polling = 1; adapter->tctrl = tmc; i2c_set_adapdata(&adapter->adap, adapter); snprintf(adapter->adap.name, sizeof(adapter->adap.name), "%s:%d", dev_name(dev), master); adapter->adap.dev.parent = dev; err = i2c_add_numbered_adapter(&adapter->adap); if (err) goto error; err = tmc_i2c_mux_init(&adapter->adap); if (err) goto err_remove; dev_dbg(dev, "Adapter[%02d-%02d]: " "dpmbase: 0x%lx\n", id, master, (unsigned long)adapter->dpmbase); return &adapter->adap; err_remove: i2c_del_adapter(&adapter->adap); error: return ERR_PTR(err); } static void tmc_i2c_cleanup_one(struct i2c_adapter *adap) { struct tmc_i2c_adapter *tadap = i2c_get_adapdata(adap); i2c_mux_del_adapters(tadap->muxc); i2c_del_adapter(adap); } static int tmc_i2c_of_init(struct device *dev, struct tmc_i2c_ctrl *tmc, int id) { u32 mux_channels, master, num_masters = 0, master_mask = 0; u32 i2c_delay = 0; if (!(master_mask & BIT(master))) num_masters++; master_mask |= BIT(master); if (id == 0) { /* chassisd */ mux_channels = chd_channel; num_masters = 1; i2c_delay = 0; } else if (id == 1) { /* pfe */ mux_channels = pfe_channel; num_masters = 1; i2c_delay = 20; } else { return -EINVAL; } tmc->adap = devm_kcalloc(dev, num_masters, sizeof(struct i2c_adapter *), GFP_KERNEL); if (!tmc->adap) return -ENOMEM; tmc->num_masters = num_masters; tmc->master_mask = master_mask; tmc->mux_channels = mux_channels; tmc->i2c_delay = i2c_delay; return 0; } static int tmc_i2c_probe(struct platform_device *pdev) { int i, n, err; struct resource *res; struct i2c_adapter *adap; struct device *dev = &pdev->dev; struct tmc_i2c_ctrl *tmc; const struct mfd_cell *cell = mfd_get_cell(pdev); /* * Allocate memory for the Tmc FPGA */ tmc = devm_kzalloc(dev, sizeof(*tmc), GFP_KERNEL); if (!tmc) return -ENOMEM; platform_set_drvdata(pdev, tmc); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; dev_info(dev, "Tmc I2C Accel resource 0x%llx, %llu\n", res->start, resource_size(res)); tmc->membase = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (!tmc->membase) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) return -ENODEV; dev_info(dev, "Tmc I2C Mem resource 0x%llx, %llu\n", res->start, resource_size(res)); tmc->dpmbase = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (!tmc->dpmbase) return -ENOMEM; tmc->dev = dev; spin_lock_init(&tmc->lock); err = tmc_i2c_of_init(dev, tmc, cell->id); if (err) return err; dev_info(dev, "Tmc I2C Masters: %d\n", tmc->num_masters); dev_info(dev, "Tmc I2C Delay: %d\n", tmc->i2c_delay); for (n = 0, i = 0; i < TMC_I2C_MASTER_NR_MSTRS; i++) { if (tmc->master_mask & BIT(i)) { adap = tmc_i2c_init_one(tmc, i, n); if (IS_ERR(adap)) { err = PTR_ERR(adap); dev_err(dev, "Failed to initialize master " "adapter %d: %d\n", i, err); goto err_remove; } tmc->adap[n++] = adap; } } return 0; err_remove: for (n--; n >= 0; n--) tmc_i2c_cleanup_one(tmc->adap[n]); return err; } static int tmc_i2c_remove(struct platform_device *pdev) { struct tmc_i2c_ctrl *tmc = platform_get_drvdata(pdev); int i; /* Disable all masters */ tmc_iowrite(0, tmc->membase + TMC_I2C_MSTR_AUTOMATION_I2C_SCAN_OFFSET); for (i = 0; i < tmc->num_masters; i++) tmc_i2c_cleanup_one(tmc->adap[i]); return 0; } static struct platform_driver tmc_i2c_driver = { .driver = { .name = "i2c-tmc", .owner = THIS_MODULE, }, .probe = tmc_i2c_probe, .remove = tmc_i2c_remove, }; module_platform_driver(tmc_i2c_driver); MODULE_DESCRIPTION("Juniper Networks TMC FPGA I2C Accelerator driver"); MODULE_AUTHOR("Ashish Bhensdadia "); MODULE_LICENSE("GPL");