18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Provides I2C support for Philips PNX010x/PNX4008 boards. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Authors: Dennis Kovalev <dkovalev@ru.mvista.com> 58c2ecf20Sopenharmony_ci * Vitaly Wool <vwool@ru.mvista.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 2004-2006 (c) MontaVista Software, Inc. This file is licensed under 88c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program 98c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express 108c2ecf20Sopenharmony_ci * or implied. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/timer.h> 198c2ecf20Sopenharmony_ci#include <linux/completion.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/clk.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ 288c2ecf20Sopenharmony_ci#define I2C_PNX_SPEED_KHZ_DEFAULT 100 298c2ecf20Sopenharmony_ci#define I2C_PNX_REGION_SIZE 0x100 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct i2c_pnx_mif { 328c2ecf20Sopenharmony_ci int ret; /* Return value */ 338c2ecf20Sopenharmony_ci int mode; /* Interface mode */ 348c2ecf20Sopenharmony_ci struct completion complete; /* I/O completion */ 358c2ecf20Sopenharmony_ci struct timer_list timer; /* Timeout */ 368c2ecf20Sopenharmony_ci u8 * buf; /* Data buffer */ 378c2ecf20Sopenharmony_ci int len; /* Length of data buffer */ 388c2ecf20Sopenharmony_ci int order; /* RX Bytes to order via TX */ 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct i2c_pnx_algo_data { 428c2ecf20Sopenharmony_ci void __iomem *ioaddr; 438c2ecf20Sopenharmony_ci struct i2c_pnx_mif mif; 448c2ecf20Sopenharmony_ci int last; 458c2ecf20Sopenharmony_ci struct clk *clk; 468c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 478c2ecf20Sopenharmony_ci int irq; 488c2ecf20Sopenharmony_ci u32 timeout; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cienum { 528c2ecf20Sopenharmony_ci mstatus_tdi = 0x00000001, 538c2ecf20Sopenharmony_ci mstatus_afi = 0x00000002, 548c2ecf20Sopenharmony_ci mstatus_nai = 0x00000004, 558c2ecf20Sopenharmony_ci mstatus_drmi = 0x00000008, 568c2ecf20Sopenharmony_ci mstatus_active = 0x00000020, 578c2ecf20Sopenharmony_ci mstatus_scl = 0x00000040, 588c2ecf20Sopenharmony_ci mstatus_sda = 0x00000080, 598c2ecf20Sopenharmony_ci mstatus_rff = 0x00000100, 608c2ecf20Sopenharmony_ci mstatus_rfe = 0x00000200, 618c2ecf20Sopenharmony_ci mstatus_tff = 0x00000400, 628c2ecf20Sopenharmony_ci mstatus_tfe = 0x00000800, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cienum { 668c2ecf20Sopenharmony_ci mcntrl_tdie = 0x00000001, 678c2ecf20Sopenharmony_ci mcntrl_afie = 0x00000002, 688c2ecf20Sopenharmony_ci mcntrl_naie = 0x00000004, 698c2ecf20Sopenharmony_ci mcntrl_drmie = 0x00000008, 708c2ecf20Sopenharmony_ci mcntrl_drsie = 0x00000010, 718c2ecf20Sopenharmony_ci mcntrl_rffie = 0x00000020, 728c2ecf20Sopenharmony_ci mcntrl_daie = 0x00000040, 738c2ecf20Sopenharmony_ci mcntrl_tffie = 0x00000080, 748c2ecf20Sopenharmony_ci mcntrl_reset = 0x00000100, 758c2ecf20Sopenharmony_ci mcntrl_cdbmode = 0x00000400, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cienum { 798c2ecf20Sopenharmony_ci rw_bit = 1 << 0, 808c2ecf20Sopenharmony_ci start_bit = 1 << 8, 818c2ecf20Sopenharmony_ci stop_bit = 1 << 9, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define I2C_REG_RX(a) ((a)->ioaddr) /* Rx FIFO reg (RO) */ 858c2ecf20Sopenharmony_ci#define I2C_REG_TX(a) ((a)->ioaddr) /* Tx FIFO reg (WO) */ 868c2ecf20Sopenharmony_ci#define I2C_REG_STS(a) ((a)->ioaddr + 0x04) /* Status reg (RO) */ 878c2ecf20Sopenharmony_ci#define I2C_REG_CTL(a) ((a)->ioaddr + 0x08) /* Ctl reg */ 888c2ecf20Sopenharmony_ci#define I2C_REG_CKL(a) ((a)->ioaddr + 0x0c) /* Clock divider low */ 898c2ecf20Sopenharmony_ci#define I2C_REG_CKH(a) ((a)->ioaddr + 0x10) /* Clock divider high */ 908c2ecf20Sopenharmony_ci#define I2C_REG_ADR(a) ((a)->ioaddr + 0x14) /* I2C address */ 918c2ecf20Sopenharmony_ci#define I2C_REG_RFL(a) ((a)->ioaddr + 0x18) /* Rx FIFO level (RO) */ 928c2ecf20Sopenharmony_ci#define I2C_REG_TFL(a) ((a)->ioaddr + 0x1c) /* Tx FIFO level (RO) */ 938c2ecf20Sopenharmony_ci#define I2C_REG_RXB(a) ((a)->ioaddr + 0x20) /* Num of bytes Rx-ed (RO) */ 948c2ecf20Sopenharmony_ci#define I2C_REG_TXB(a) ((a)->ioaddr + 0x24) /* Num of bytes Tx-ed (RO) */ 958c2ecf20Sopenharmony_ci#define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ 968c2ecf20Sopenharmony_ci#define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline int wait_timeout(struct i2c_pnx_algo_data *data) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci long timeout = data->timeout; 1018c2ecf20Sopenharmony_ci while (timeout > 0 && 1028c2ecf20Sopenharmony_ci (ioread32(I2C_REG_STS(data)) & mstatus_active)) { 1038c2ecf20Sopenharmony_ci mdelay(1); 1048c2ecf20Sopenharmony_ci timeout--; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci return (timeout <= 0); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline int wait_reset(struct i2c_pnx_algo_data *data) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci long timeout = data->timeout; 1128c2ecf20Sopenharmony_ci while (timeout > 0 && 1138c2ecf20Sopenharmony_ci (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { 1148c2ecf20Sopenharmony_ci mdelay(1); 1158c2ecf20Sopenharmony_ci timeout--; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return (timeout <= 0); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct timer_list *timer = &alg_data->mif.timer; 1238c2ecf20Sopenharmony_ci unsigned long expires = msecs_to_jiffies(alg_data->timeout); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (expires <= 1) 1268c2ecf20Sopenharmony_ci expires = 2; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci del_timer_sync(timer); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n", 1318c2ecf20Sopenharmony_ci jiffies, expires); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci timer->expires = jiffies + expires; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci add_timer(timer); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * i2c_pnx_start - start a device 1408c2ecf20Sopenharmony_ci * @slave_addr: slave address 1418c2ecf20Sopenharmony_ci * @adap: pointer to adapter structure 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Generate a START signal in the desired mode. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic int i2c_pnx_start(unsigned char slave_addr, 1468c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): addr 0x%x mode %d\n", __func__, 1498c2ecf20Sopenharmony_ci slave_addr, alg_data->mif.mode); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Check for 7 bit slave addresses only */ 1528c2ecf20Sopenharmony_ci if (slave_addr & ~0x7f) { 1538c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 1548c2ecf20Sopenharmony_ci "%s: Invalid slave address %x. Only 7-bit addresses are supported\n", 1558c2ecf20Sopenharmony_ci alg_data->adapter.name, slave_addr); 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* First, make sure bus is idle */ 1608c2ecf20Sopenharmony_ci if (wait_timeout(alg_data)) { 1618c2ecf20Sopenharmony_ci /* Somebody else is monopolizing the bus */ 1628c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 1638c2ecf20Sopenharmony_ci "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", 1648c2ecf20Sopenharmony_ci alg_data->adapter.name, slave_addr, 1658c2ecf20Sopenharmony_ci ioread32(I2C_REG_CTL(alg_data)), 1668c2ecf20Sopenharmony_ci ioread32(I2C_REG_STS(alg_data))); 1678c2ecf20Sopenharmony_ci return -EBUSY; 1688c2ecf20Sopenharmony_ci } else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) { 1698c2ecf20Sopenharmony_ci /* Sorry, we lost the bus */ 1708c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 1718c2ecf20Sopenharmony_ci "%s: Arbitration failure. Slave addr = %02x\n", 1728c2ecf20Sopenharmony_ci alg_data->adapter.name, slave_addr); 1738c2ecf20Sopenharmony_ci return -EIO; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * OK, I2C is enabled and we have the bus. 1788c2ecf20Sopenharmony_ci * Clear the current TDI and AFI status flags. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi, 1818c2ecf20Sopenharmony_ci I2C_REG_STS(alg_data)); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): sending %#x\n", __func__, 1848c2ecf20Sopenharmony_ci (slave_addr << 1) | start_bit | alg_data->mif.mode); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Write the slave address, START bit and R/W bit */ 1878c2ecf20Sopenharmony_ci iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode, 1888c2ecf20Sopenharmony_ci I2C_REG_TX(alg_data)); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): exit\n", __func__); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * i2c_pnx_stop - stop a device 1978c2ecf20Sopenharmony_ci * @adap: pointer to I2C adapter structure 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * Generate a STOP signal to terminate the master transaction. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci /* Only 1 msec max timeout due to interrupt context */ 2048c2ecf20Sopenharmony_ci long timeout = 1000; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", 2078c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Write a STOP bit to TX FIFO */ 2108c2ecf20Sopenharmony_ci iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data)); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Wait until the STOP is seen. */ 2138c2ecf20Sopenharmony_ci while (timeout > 0 && 2148c2ecf20Sopenharmony_ci (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) { 2158c2ecf20Sopenharmony_ci /* may be called from interrupt context */ 2168c2ecf20Sopenharmony_ci udelay(1); 2178c2ecf20Sopenharmony_ci timeout--; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", 2218c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * i2c_pnx_master_xmit - transmit data to slave 2268c2ecf20Sopenharmony_ci * @adap: pointer to I2C adapter structure 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * Sends one byte of data to the slave 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistatic int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci u32 val; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", 2358c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (alg_data->mif.len > 0) { 2388c2ecf20Sopenharmony_ci /* We still have something to talk about... */ 2398c2ecf20Sopenharmony_ci val = *alg_data->mif.buf++; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (alg_data->mif.len == 1) 2428c2ecf20Sopenharmony_ci val |= stop_bit; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci alg_data->mif.len--; 2458c2ecf20Sopenharmony_ci iowrite32(val, I2C_REG_TX(alg_data)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): xmit %#x [%d]\n", 2488c2ecf20Sopenharmony_ci __func__, val, alg_data->mif.len + 1); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (alg_data->mif.len == 0) { 2518c2ecf20Sopenharmony_ci if (alg_data->last) { 2528c2ecf20Sopenharmony_ci /* Wait until the STOP is seen. */ 2538c2ecf20Sopenharmony_ci if (wait_timeout(alg_data)) 2548c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 2558c2ecf20Sopenharmony_ci "The bus is still active after timeout\n"); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci /* Disable master interrupts */ 2588c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) & 2598c2ecf20Sopenharmony_ci ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), 2608c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci del_timer_sync(&alg_data->mif.timer); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 2658c2ecf20Sopenharmony_ci "%s(): Waking up xfer routine.\n", 2668c2ecf20Sopenharmony_ci __func__); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } else if (alg_data->mif.len == 0) { 2718c2ecf20Sopenharmony_ci /* zero-sized transfer */ 2728c2ecf20Sopenharmony_ci i2c_pnx_stop(alg_data); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Disable master interrupts. */ 2758c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) & 2768c2ecf20Sopenharmony_ci ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), 2778c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Stop timer. */ 2808c2ecf20Sopenharmony_ci del_timer_sync(&alg_data->mif.timer); 2818c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 2828c2ecf20Sopenharmony_ci "%s(): Waking up xfer routine after zero-xfer.\n", 2838c2ecf20Sopenharmony_ci __func__); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", 2898c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * i2c_pnx_master_rcv - receive data from slave 2968c2ecf20Sopenharmony_ci * @adap: pointer to I2C adapter structure 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * Reads one byte data from the slave 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned int val = 0; 3038c2ecf20Sopenharmony_ci u32 ctl = 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", 3068c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Check, whether there is already data, 3098c2ecf20Sopenharmony_ci * or we didn't 'ask' for it yet. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { 3128c2ecf20Sopenharmony_ci /* 'Asking' is done asynchronously, e.g. dummy TX of several 3138c2ecf20Sopenharmony_ci * bytes is done before the first actual RX arrives in FIFO. 3148c2ecf20Sopenharmony_ci * Therefore, ordered bytes (via TX) are counted separately. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci if (alg_data->mif.order) { 3178c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 3188c2ecf20Sopenharmony_ci "%s(): Write dummy data to fill Rx-fifo...\n", 3198c2ecf20Sopenharmony_ci __func__); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (alg_data->mif.order == 1) { 3228c2ecf20Sopenharmony_ci /* Last byte, do not acknowledge next rcv. */ 3238c2ecf20Sopenharmony_ci val |= stop_bit; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Enable interrupt RFDAIE (data in Rx fifo), 3278c2ecf20Sopenharmony_ci * and disable DRMIE (need data for Tx) 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci ctl = ioread32(I2C_REG_CTL(alg_data)); 3308c2ecf20Sopenharmony_ci ctl |= mcntrl_rffie | mcntrl_daie; 3318c2ecf20Sopenharmony_ci ctl &= ~mcntrl_drmie; 3328c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * Now we'll 'ask' for data: 3378c2ecf20Sopenharmony_ci * For each byte we want to receive, we must 3388c2ecf20Sopenharmony_ci * write a (dummy) byte to the Tx-FIFO. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci iowrite32(val, I2C_REG_TX(alg_data)); 3418c2ecf20Sopenharmony_ci alg_data->mif.order--; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Handle data. */ 3478c2ecf20Sopenharmony_ci if (alg_data->mif.len > 0) { 3488c2ecf20Sopenharmony_ci val = ioread32(I2C_REG_RX(alg_data)); 3498c2ecf20Sopenharmony_ci *alg_data->mif.buf++ = (u8) (val & 0xff); 3508c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): rcv 0x%x [%d]\n", 3518c2ecf20Sopenharmony_ci __func__, val, alg_data->mif.len); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci alg_data->mif.len--; 3548c2ecf20Sopenharmony_ci if (alg_data->mif.len == 0) { 3558c2ecf20Sopenharmony_ci if (alg_data->last) 3568c2ecf20Sopenharmony_ci /* Wait until the STOP is seen. */ 3578c2ecf20Sopenharmony_ci if (wait_timeout(alg_data)) 3588c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 3598c2ecf20Sopenharmony_ci "The bus is still active after timeout\n"); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Disable master interrupts */ 3628c2ecf20Sopenharmony_ci ctl = ioread32(I2C_REG_CTL(alg_data)); 3638c2ecf20Sopenharmony_ci ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | 3648c2ecf20Sopenharmony_ci mcntrl_drmie | mcntrl_daie); 3658c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Kill timer. */ 3688c2ecf20Sopenharmony_ci del_timer_sync(&alg_data->mif.timer); 3698c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", 3748c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = dev_id; 3828c2ecf20Sopenharmony_ci u32 stat, ctl; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 3858c2ecf20Sopenharmony_ci "%s(): mstat = %x mctrl = %x, mode = %d\n", 3868c2ecf20Sopenharmony_ci __func__, 3878c2ecf20Sopenharmony_ci ioread32(I2C_REG_STS(alg_data)), 3888c2ecf20Sopenharmony_ci ioread32(I2C_REG_CTL(alg_data)), 3898c2ecf20Sopenharmony_ci alg_data->mif.mode); 3908c2ecf20Sopenharmony_ci stat = ioread32(I2C_REG_STS(alg_data)); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* let's see what kind of event this is */ 3938c2ecf20Sopenharmony_ci if (stat & mstatus_afi) { 3948c2ecf20Sopenharmony_ci /* We lost arbitration in the midst of a transfer */ 3958c2ecf20Sopenharmony_ci alg_data->mif.ret = -EIO; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* Disable master interrupts. */ 3988c2ecf20Sopenharmony_ci ctl = ioread32(I2C_REG_CTL(alg_data)); 3998c2ecf20Sopenharmony_ci ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | 4008c2ecf20Sopenharmony_ci mcntrl_drmie); 4018c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Stop timer, to prevent timeout. */ 4048c2ecf20Sopenharmony_ci del_timer_sync(&alg_data->mif.timer); 4058c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 4068c2ecf20Sopenharmony_ci } else if (stat & mstatus_nai) { 4078c2ecf20Sopenharmony_ci /* Slave did not acknowledge, generate a STOP */ 4088c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 4098c2ecf20Sopenharmony_ci "%s(): Slave did not acknowledge, generating a STOP.\n", 4108c2ecf20Sopenharmony_ci __func__); 4118c2ecf20Sopenharmony_ci i2c_pnx_stop(alg_data); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Disable master interrupts. */ 4148c2ecf20Sopenharmony_ci ctl = ioread32(I2C_REG_CTL(alg_data)); 4158c2ecf20Sopenharmony_ci ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | 4168c2ecf20Sopenharmony_ci mcntrl_drmie); 4178c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Our return value. */ 4208c2ecf20Sopenharmony_ci alg_data->mif.ret = -EIO; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Stop timer, to prevent timeout. */ 4238c2ecf20Sopenharmony_ci del_timer_sync(&alg_data->mif.timer); 4248c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Two options: 4288c2ecf20Sopenharmony_ci * - Master Tx needs data. 4298c2ecf20Sopenharmony_ci * - There is data in the Rx-fifo 4308c2ecf20Sopenharmony_ci * The latter is only the case if we have requested for data, 4318c2ecf20Sopenharmony_ci * via a dummy write. (See 'i2c_pnx_master_rcv'.) 4328c2ecf20Sopenharmony_ci * We therefore check, as a sanity check, whether that interrupt 4338c2ecf20Sopenharmony_ci * has been enabled. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) { 4368c2ecf20Sopenharmony_ci if (alg_data->mif.mode == I2C_SMBUS_WRITE) { 4378c2ecf20Sopenharmony_ci i2c_pnx_master_xmit(alg_data); 4388c2ecf20Sopenharmony_ci } else if (alg_data->mif.mode == I2C_SMBUS_READ) { 4398c2ecf20Sopenharmony_ci i2c_pnx_master_rcv(alg_data); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Clear TDI and AFI bits */ 4458c2ecf20Sopenharmony_ci stat = ioread32(I2C_REG_STS(alg_data)); 4468c2ecf20Sopenharmony_ci iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data)); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 4498c2ecf20Sopenharmony_ci "%s(): exiting, stat = %x ctrl = %x.\n", 4508c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data)), 4518c2ecf20Sopenharmony_ci ioread32(I2C_REG_CTL(alg_data))); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void i2c_pnx_timeout(struct timer_list *t) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer); 4598c2ecf20Sopenharmony_ci u32 ctl; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 4628c2ecf20Sopenharmony_ci "Master timed out. stat = %04x, cntrl = %04x. Resetting master...\n", 4638c2ecf20Sopenharmony_ci ioread32(I2C_REG_STS(alg_data)), 4648c2ecf20Sopenharmony_ci ioread32(I2C_REG_CTL(alg_data))); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Reset master and disable interrupts */ 4678c2ecf20Sopenharmony_ci ctl = ioread32(I2C_REG_CTL(alg_data)); 4688c2ecf20Sopenharmony_ci ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie); 4698c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ctl |= mcntrl_reset; 4728c2ecf20Sopenharmony_ci iowrite32(ctl, I2C_REG_CTL(alg_data)); 4738c2ecf20Sopenharmony_ci wait_reset(alg_data); 4748c2ecf20Sopenharmony_ci alg_data->mif.ret = -EIO; 4758c2ecf20Sopenharmony_ci complete(&alg_data->mif.complete); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci u32 stat; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) { 4838c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 4848c2ecf20Sopenharmony_ci "%s: Bus is still active after xfer. Reset it...\n", 4858c2ecf20Sopenharmony_ci alg_data->adapter.name); 4868c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, 4878c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 4888c2ecf20Sopenharmony_ci wait_reset(alg_data); 4898c2ecf20Sopenharmony_ci } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { 4908c2ecf20Sopenharmony_ci /* If there is data in the fifo's after transfer, 4918c2ecf20Sopenharmony_ci * flush fifo's by reset. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, 4948c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 4958c2ecf20Sopenharmony_ci wait_reset(alg_data); 4968c2ecf20Sopenharmony_ci } else if (stat & mstatus_nai) { 4978c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, 4988c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 4998c2ecf20Sopenharmony_ci wait_reset(alg_data); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * i2c_pnx_xfer - generic transfer entry point 5058c2ecf20Sopenharmony_ci * @adap: pointer to I2C adapter structure 5068c2ecf20Sopenharmony_ci * @msgs: array of messages 5078c2ecf20Sopenharmony_ci * @num: number of messages 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * Initiates the transfer 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic int 5128c2ecf20Sopenharmony_cii2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 5158c2ecf20Sopenharmony_ci int rc = 0, completed = 0, i; 5168c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = adap->algo_data; 5178c2ecf20Sopenharmony_ci u32 stat; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 5208c2ecf20Sopenharmony_ci "%s(): entering: %d messages, stat = %04x.\n", 5218c2ecf20Sopenharmony_ci __func__, num, ioread32(I2C_REG_STS(alg_data))); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci bus_reset_if_active(alg_data); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Process transactions in a loop. */ 5268c2ecf20Sopenharmony_ci for (i = 0; rc >= 0 && i < num; i++) { 5278c2ecf20Sopenharmony_ci u8 addr; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 5308c2ecf20Sopenharmony_ci addr = pmsg->addr; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_TEN) { 5338c2ecf20Sopenharmony_ci dev_err(&alg_data->adapter.dev, 5348c2ecf20Sopenharmony_ci "%s: 10 bits addr not supported!\n", 5358c2ecf20Sopenharmony_ci alg_data->adapter.name); 5368c2ecf20Sopenharmony_ci rc = -EINVAL; 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci alg_data->mif.buf = pmsg->buf; 5418c2ecf20Sopenharmony_ci alg_data->mif.len = pmsg->len; 5428c2ecf20Sopenharmony_ci alg_data->mif.order = pmsg->len; 5438c2ecf20Sopenharmony_ci alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? 5448c2ecf20Sopenharmony_ci I2C_SMBUS_READ : I2C_SMBUS_WRITE; 5458c2ecf20Sopenharmony_ci alg_data->mif.ret = 0; 5468c2ecf20Sopenharmony_ci alg_data->last = (i == num - 1); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n", 5498c2ecf20Sopenharmony_ci __func__, alg_data->mif.mode, alg_data->mif.len); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci i2c_pnx_arm_timer(alg_data); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* initialize the completion var */ 5548c2ecf20Sopenharmony_ci init_completion(&alg_data->mif.complete); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Enable master interrupt */ 5578c2ecf20Sopenharmony_ci iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie | 5588c2ecf20Sopenharmony_ci mcntrl_naie | mcntrl_drmie, 5598c2ecf20Sopenharmony_ci I2C_REG_CTL(alg_data)); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Put start-code and slave-address on the bus. */ 5628c2ecf20Sopenharmony_ci rc = i2c_pnx_start(addr, alg_data); 5638c2ecf20Sopenharmony_ci if (rc < 0) 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Wait for completion */ 5678c2ecf20Sopenharmony_ci wait_for_completion(&alg_data->mif.complete); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!(rc = alg_data->mif.ret)) 5708c2ecf20Sopenharmony_ci completed++; 5718c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 5728c2ecf20Sopenharmony_ci "%s(): Complete, return code = %d.\n", 5738c2ecf20Sopenharmony_ci __func__, rc); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Clear TDI and AFI bits in case they are set. */ 5768c2ecf20Sopenharmony_ci if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) { 5778c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 5788c2ecf20Sopenharmony_ci "%s: TDI still set... clearing now.\n", 5798c2ecf20Sopenharmony_ci alg_data->adapter.name); 5808c2ecf20Sopenharmony_ci iowrite32(stat, I2C_REG_STS(alg_data)); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) { 5838c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, 5848c2ecf20Sopenharmony_ci "%s: AFI still set... clearing now.\n", 5858c2ecf20Sopenharmony_ci alg_data->adapter.name); 5868c2ecf20Sopenharmony_ci iowrite32(stat, I2C_REG_STS(alg_data)); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci bus_reset_if_active(alg_data); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Cleanup to be sure... */ 5938c2ecf20Sopenharmony_ci alg_data->mif.buf = NULL; 5948c2ecf20Sopenharmony_ci alg_data->mif.len = 0; 5958c2ecf20Sopenharmony_ci alg_data->mif.order = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", 5988c2ecf20Sopenharmony_ci __func__, ioread32(I2C_REG_STS(alg_data))); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (completed != num) 6018c2ecf20Sopenharmony_ci return ((rc < 0) ? rc : -EREMOTEIO); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return num; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic u32 i2c_pnx_func(struct i2c_adapter *adapter) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic const struct i2c_algorithm pnx_algorithm = { 6128c2ecf20Sopenharmony_ci .master_xfer = i2c_pnx_xfer, 6138c2ecf20Sopenharmony_ci .functionality = i2c_pnx_func, 6148c2ecf20Sopenharmony_ci}; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6178c2ecf20Sopenharmony_cistatic int i2c_pnx_controller_suspend(struct device *dev) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci clk_disable_unprepare(alg_data->clk); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int i2c_pnx_controller_resume(struct device *dev) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return clk_prepare_enable(alg_data->clk); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(i2c_pnx_pm, 6348c2ecf20Sopenharmony_ci i2c_pnx_controller_suspend, i2c_pnx_controller_resume); 6358c2ecf20Sopenharmony_ci#define PNX_I2C_PM (&i2c_pnx_pm) 6368c2ecf20Sopenharmony_ci#else 6378c2ecf20Sopenharmony_ci#define PNX_I2C_PM NULL 6388c2ecf20Sopenharmony_ci#endif 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int i2c_pnx_probe(struct platform_device *pdev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci unsigned long tmp; 6438c2ecf20Sopenharmony_ci int ret = 0; 6448c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data; 6458c2ecf20Sopenharmony_ci unsigned long freq; 6468c2ecf20Sopenharmony_ci struct resource *res; 6478c2ecf20Sopenharmony_ci u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL); 6508c2ecf20Sopenharmony_ci if (!alg_data) 6518c2ecf20Sopenharmony_ci return -ENOMEM; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, alg_data); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci alg_data->adapter.dev.parent = &pdev->dev; 6568c2ecf20Sopenharmony_ci alg_data->adapter.algo = &pnx_algorithm; 6578c2ecf20Sopenharmony_ci alg_data->adapter.algo_data = alg_data; 6588c2ecf20Sopenharmony_ci alg_data->adapter.nr = pdev->id; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT; 6618c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6628c2ecf20Sopenharmony_ci alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node); 6638c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 6648c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "clock-frequency", 6658c2ecf20Sopenharmony_ci &speed); 6668c2ecf20Sopenharmony_ci /* 6678c2ecf20Sopenharmony_ci * At this point, it is planned to add an OF timeout property. 6688c2ecf20Sopenharmony_ci * As soon as there is a consensus about how to call and handle 6698c2ecf20Sopenharmony_ci * this, sth. like the following can be put here: 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * of_property_read_u32(pdev->dev.of_node, "timeout", 6728c2ecf20Sopenharmony_ci * &alg_data->timeout); 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci#endif 6768c2ecf20Sopenharmony_ci alg_data->clk = devm_clk_get(&pdev->dev, NULL); 6778c2ecf20Sopenharmony_ci if (IS_ERR(alg_data->clk)) 6788c2ecf20Sopenharmony_ci return PTR_ERR(alg_data->clk); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), 6838c2ecf20Sopenharmony_ci "%s", pdev->name); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Register I/O resource */ 6868c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6878c2ecf20Sopenharmony_ci alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res); 6888c2ecf20Sopenharmony_ci if (IS_ERR(alg_data->ioaddr)) 6898c2ecf20Sopenharmony_ci return PTR_ERR(alg_data->ioaddr); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = clk_prepare_enable(alg_data->clk); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci freq = clk_get_rate(alg_data->clk); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * Clock Divisor High This value is the number of system clocks 6998c2ecf20Sopenharmony_ci * the serial clock (SCL) will be high. 7008c2ecf20Sopenharmony_ci * For example, if the system clock period is 50 ns and the maximum 7018c2ecf20Sopenharmony_ci * desired serial period is 10000 ns (100 kHz), then CLKHI would be 7028c2ecf20Sopenharmony_ci * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value 7038c2ecf20Sopenharmony_ci * programmed into CLKHI will vary from this slightly due to 7048c2ecf20Sopenharmony_ci * variations in the output pad's rise and fall times as well as 7058c2ecf20Sopenharmony_ci * the deglitching filter length. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci tmp = (freq / speed) / 2 - 2; 7098c2ecf20Sopenharmony_ci if (tmp > 0x3FF) 7108c2ecf20Sopenharmony_ci tmp = 0x3FF; 7118c2ecf20Sopenharmony_ci iowrite32(tmp, I2C_REG_CKH(alg_data)); 7128c2ecf20Sopenharmony_ci iowrite32(tmp, I2C_REG_CKL(alg_data)); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); 7158c2ecf20Sopenharmony_ci if (wait_reset(alg_data)) { 7168c2ecf20Sopenharmony_ci ret = -ENODEV; 7178c2ecf20Sopenharmony_ci goto out_clock; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci init_completion(&alg_data->mif.complete); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci alg_data->irq = platform_get_irq(pdev, 0); 7228c2ecf20Sopenharmony_ci if (alg_data->irq < 0) { 7238c2ecf20Sopenharmony_ci ret = alg_data->irq; 7248c2ecf20Sopenharmony_ci goto out_clock; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt, 7278c2ecf20Sopenharmony_ci 0, pdev->name, alg_data); 7288c2ecf20Sopenharmony_ci if (ret) 7298c2ecf20Sopenharmony_ci goto out_clock; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Register this adapter with the I2C subsystem */ 7328c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(&alg_data->adapter); 7338c2ecf20Sopenharmony_ci if (ret < 0) 7348c2ecf20Sopenharmony_ci goto out_clock; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s: Master at %pap, irq %d.\n", 7378c2ecf20Sopenharmony_ci alg_data->adapter.name, &res->start, alg_data->irq); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ciout_clock: 7428c2ecf20Sopenharmony_ci clk_disable_unprepare(alg_data->clk); 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int i2c_pnx_remove(struct platform_device *pdev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci i2c_del_adapter(&alg_data->adapter); 7518c2ecf20Sopenharmony_ci clk_disable_unprepare(alg_data->clk); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 7578c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_pnx_of_match[] = { 7588c2ecf20Sopenharmony_ci { .compatible = "nxp,pnx-i2c" }, 7598c2ecf20Sopenharmony_ci { }, 7608c2ecf20Sopenharmony_ci}; 7618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_pnx_of_match); 7628c2ecf20Sopenharmony_ci#endif 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic struct platform_driver i2c_pnx_driver = { 7658c2ecf20Sopenharmony_ci .driver = { 7668c2ecf20Sopenharmony_ci .name = "pnx-i2c", 7678c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(i2c_pnx_of_match), 7688c2ecf20Sopenharmony_ci .pm = PNX_I2C_PM, 7698c2ecf20Sopenharmony_ci }, 7708c2ecf20Sopenharmony_ci .probe = i2c_pnx_probe, 7718c2ecf20Sopenharmony_ci .remove = i2c_pnx_remove, 7728c2ecf20Sopenharmony_ci}; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic int __init i2c_adap_pnx_init(void) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci return platform_driver_register(&i2c_pnx_driver); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic void __exit i2c_adap_pnx_exit(void) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci platform_driver_unregister(&i2c_pnx_driver); 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Wool"); 7858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dennis Kovalev <source@mvista.com>"); 7868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses"); 7878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7888c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pnx-i2c"); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/* We need to make sure I2C is initialized before USB */ 7918c2ecf20Sopenharmony_cisubsys_initcall(i2c_adap_pnx_init); 7928c2ecf20Sopenharmony_cimodule_exit(i2c_adap_pnx_exit); 793