18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * I2C bus driver for the SH7760 I2C Interfaces. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * licensed under the terms outlined in the file COPYING. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/clock.h> 228c2ecf20Sopenharmony_ci#include <asm/i2c-sh7760.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* register offsets */ 258c2ecf20Sopenharmony_ci#define I2CSCR 0x0 /* slave ctrl */ 268c2ecf20Sopenharmony_ci#define I2CMCR 0x4 /* master ctrl */ 278c2ecf20Sopenharmony_ci#define I2CSSR 0x8 /* slave status */ 288c2ecf20Sopenharmony_ci#define I2CMSR 0xC /* master status */ 298c2ecf20Sopenharmony_ci#define I2CSIER 0x10 /* slave irq enable */ 308c2ecf20Sopenharmony_ci#define I2CMIER 0x14 /* master irq enable */ 318c2ecf20Sopenharmony_ci#define I2CCCR 0x18 /* clock dividers */ 328c2ecf20Sopenharmony_ci#define I2CSAR 0x1c /* slave address */ 338c2ecf20Sopenharmony_ci#define I2CMAR 0x20 /* master address */ 348c2ecf20Sopenharmony_ci#define I2CRXTX 0x24 /* data port */ 358c2ecf20Sopenharmony_ci#define I2CFCR 0x28 /* fifo control */ 368c2ecf20Sopenharmony_ci#define I2CFSR 0x2C /* fifo status */ 378c2ecf20Sopenharmony_ci#define I2CFIER 0x30 /* fifo irq enable */ 388c2ecf20Sopenharmony_ci#define I2CRFDR 0x34 /* rx fifo count */ 398c2ecf20Sopenharmony_ci#define I2CTFDR 0x38 /* tx fifo count */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define REGSIZE 0x3C 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MCR_MDBS 0x80 /* non-fifo mode switch */ 448c2ecf20Sopenharmony_ci#define MCR_FSCL 0x40 /* override SCL pin */ 458c2ecf20Sopenharmony_ci#define MCR_FSDA 0x20 /* override SDA pin */ 468c2ecf20Sopenharmony_ci#define MCR_OBPC 0x10 /* override pins */ 478c2ecf20Sopenharmony_ci#define MCR_MIE 0x08 /* master if enable */ 488c2ecf20Sopenharmony_ci#define MCR_TSBE 0x04 498c2ecf20Sopenharmony_ci#define MCR_FSB 0x02 /* force stop bit */ 508c2ecf20Sopenharmony_ci#define MCR_ESG 0x01 /* en startbit gen. */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define MSR_MNR 0x40 /* nack received */ 538c2ecf20Sopenharmony_ci#define MSR_MAL 0x20 /* arbitration lost */ 548c2ecf20Sopenharmony_ci#define MSR_MST 0x10 /* sent a stop */ 558c2ecf20Sopenharmony_ci#define MSR_MDE 0x08 568c2ecf20Sopenharmony_ci#define MSR_MDT 0x04 578c2ecf20Sopenharmony_ci#define MSR_MDR 0x02 588c2ecf20Sopenharmony_ci#define MSR_MAT 0x01 /* slave addr xfer done */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define MIE_MNRE 0x40 /* nack irq en */ 618c2ecf20Sopenharmony_ci#define MIE_MALE 0x20 /* arblos irq en */ 628c2ecf20Sopenharmony_ci#define MIE_MSTE 0x10 /* stop irq en */ 638c2ecf20Sopenharmony_ci#define MIE_MDEE 0x08 648c2ecf20Sopenharmony_ci#define MIE_MDTE 0x04 658c2ecf20Sopenharmony_ci#define MIE_MDRE 0x02 668c2ecf20Sopenharmony_ci#define MIE_MATE 0x01 /* address sent irq en */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define FCR_RFRST 0x02 /* reset rx fifo */ 698c2ecf20Sopenharmony_ci#define FCR_TFRST 0x01 /* reset tx fifo */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define FSR_TEND 0x04 /* last byte sent */ 728c2ecf20Sopenharmony_ci#define FSR_RDF 0x02 /* rx fifo trigger */ 738c2ecf20Sopenharmony_ci#define FSR_TDFE 0x01 /* tx fifo empty */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define FIER_TEIE 0x04 /* tx fifo empty irq en */ 768c2ecf20Sopenharmony_ci#define FIER_RXIE 0x02 /* rx fifo trig irq en */ 778c2ecf20Sopenharmony_ci#define FIER_TXIE 0x01 /* tx fifo trig irq en */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define FIFO_SIZE 16 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistruct cami2c { 828c2ecf20Sopenharmony_ci void __iomem *iobase; 838c2ecf20Sopenharmony_ci struct i2c_adapter adap; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* message processing */ 868c2ecf20Sopenharmony_ci struct i2c_msg *msg; 878c2ecf20Sopenharmony_ci#define IDF_SEND 1 888c2ecf20Sopenharmony_ci#define IDF_RECV 2 898c2ecf20Sopenharmony_ci#define IDF_STOP 4 908c2ecf20Sopenharmony_ci int flags; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define IDS_DONE 1 938c2ecf20Sopenharmony_ci#define IDS_ARBLOST 2 948c2ecf20Sopenharmony_ci#define IDS_NACK 4 958c2ecf20Sopenharmony_ci int status; 968c2ecf20Sopenharmony_ci struct completion xfer_done; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci int irq; 998c2ecf20Sopenharmony_ci struct resource *ioarea; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void OUT32(struct cami2c *cam, int reg, unsigned long val) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci __raw_writel(val, (unsigned long)cam->iobase + reg); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline unsigned long IN32(struct cami2c *cam, int reg) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci return __raw_readl((unsigned long)cam->iobase + reg); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic irqreturn_t sh7760_i2c_irq(int irq, void *ptr) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct cami2c *id = ptr; 1158c2ecf20Sopenharmony_ci struct i2c_msg *msg = id->msg; 1168c2ecf20Sopenharmony_ci char *data = msg->buf; 1178c2ecf20Sopenharmony_ci unsigned long msr, fsr, fier, len; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci msr = IN32(id, I2CMSR); 1208c2ecf20Sopenharmony_ci fsr = IN32(id, I2CFSR); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* arbitration lost */ 1238c2ecf20Sopenharmony_ci if (msr & MSR_MAL) { 1248c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, 0); 1258c2ecf20Sopenharmony_ci OUT32(id, I2CSCR, 0); 1268c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0); 1278c2ecf20Sopenharmony_ci id->status |= IDS_DONE | IDS_ARBLOST; 1288c2ecf20Sopenharmony_ci goto out; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (msr & MSR_MNR) { 1328c2ecf20Sopenharmony_ci /* NACK handling is very screwed up. After receiving a 1338c2ecf20Sopenharmony_ci * NAK IRQ one has to wait a bit before writing to any 1348c2ecf20Sopenharmony_ci * registers, or the ctl will lock up. After that delay 1358c2ecf20Sopenharmony_ci * do a normal i2c stop. Then wait at least 1 ms before 1368c2ecf20Sopenharmony_ci * attempting another transfer or ctl will stop working 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci udelay(100); /* wait or risk ctl hang */ 1398c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); 1408c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); 1418c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, 0); 1428c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, MIE_MSTE); 1438c2ecf20Sopenharmony_ci OUT32(id, I2CSCR, 0); 1448c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0); 1458c2ecf20Sopenharmony_ci id->status |= IDS_NACK; 1468c2ecf20Sopenharmony_ci msr &= ~MSR_MAT; 1478c2ecf20Sopenharmony_ci fsr = 0; 1488c2ecf20Sopenharmony_ci /* In some cases the MST bit is also set. */ 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* i2c-stop was sent */ 1528c2ecf20Sopenharmony_ci if (msr & MSR_MST) { 1538c2ecf20Sopenharmony_ci id->status |= IDS_DONE; 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* i2c slave addr was sent; set to "normal" operation */ 1588c2ecf20Sopenharmony_ci if (msr & MSR_MAT) 1598c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci fier = IN32(id, I2CFIER); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (fsr & FSR_RDF) { 1648c2ecf20Sopenharmony_ci len = IN32(id, I2CRFDR); 1658c2ecf20Sopenharmony_ci if (msg->len <= len) { 1668c2ecf20Sopenharmony_ci if (id->flags & IDF_STOP) { 1678c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); 1688c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, 0); 1698c2ecf20Sopenharmony_ci /* manual says: wait >= 0.5 SCL times */ 1708c2ecf20Sopenharmony_ci udelay(5); 1718c2ecf20Sopenharmony_ci /* next int should be MST */ 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci id->status |= IDS_DONE; 1748c2ecf20Sopenharmony_ci /* keep the RDF bit: ctrl holds SCL low 1758c2ecf20Sopenharmony_ci * until the setup for the next i2c_msg 1768c2ecf20Sopenharmony_ci * clears this bit. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci fsr &= ~FSR_RDF; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci while (msg->len && len) { 1828c2ecf20Sopenharmony_ci *data++ = IN32(id, I2CRXTX); 1838c2ecf20Sopenharmony_ci msg->len--; 1848c2ecf20Sopenharmony_ci len--; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (msg->len) { 1888c2ecf20Sopenharmony_ci len = (msg->len >= FIFO_SIZE) ? FIFO_SIZE - 1 1898c2ecf20Sopenharmony_ci : msg->len - 1; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xf) << 4)); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci } else if (id->flags & IDF_SEND) { 1958c2ecf20Sopenharmony_ci if ((fsr & FSR_TEND) && (msg->len < 1)) { 1968c2ecf20Sopenharmony_ci if (id->flags & IDF_STOP) { 1978c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci id->status |= IDS_DONE; 2008c2ecf20Sopenharmony_ci /* keep the TEND bit: ctl holds SCL low 2018c2ecf20Sopenharmony_ci * until the setup for the next i2c_msg 2028c2ecf20Sopenharmony_ci * clears this bit. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci fsr &= ~FSR_TEND; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci if (fsr & FSR_TDFE) { 2088c2ecf20Sopenharmony_ci while (msg->len && (IN32(id, I2CTFDR) < FIFO_SIZE)) { 2098c2ecf20Sopenharmony_ci OUT32(id, I2CRXTX, *data++); 2108c2ecf20Sopenharmony_ci msg->len--; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (msg->len < 1) { 2148c2ecf20Sopenharmony_ci fier &= ~FIER_TXIE; 2158c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, fier); 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci len = (msg->len >= FIFO_SIZE) ? 2 : 0; 2188c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, 2198c2ecf20Sopenharmony_ci FCR_RFRST | ((len & 3) << 2)); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ciout: 2248c2ecf20Sopenharmony_ci if (id->status & IDS_DONE) { 2258c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, 0); 2268c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, 0); 2278c2ecf20Sopenharmony_ci id->msg = NULL; 2288c2ecf20Sopenharmony_ci complete(&id->xfer_done); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci /* clear status flags and ctrl resumes work */ 2318c2ecf20Sopenharmony_ci OUT32(id, I2CMSR, ~msr); 2328c2ecf20Sopenharmony_ci OUT32(id, I2CFSR, ~fsr); 2338c2ecf20Sopenharmony_ci OUT32(id, I2CSSR, 0); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* prepare and start a master receive operation */ 2408c2ecf20Sopenharmony_cistatic void sh7760_i2c_mrecv(struct cami2c *id) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int len; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci id->flags |= IDF_RECV; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* set the slave addr reg; otherwise rcv wont work! */ 2478c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0xfe); 2488c2ecf20Sopenharmony_ci OUT32(id, I2CMAR, (id->msg->addr << 1) | 1); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* adjust rx fifo trigger */ 2518c2ecf20Sopenharmony_ci if (id->msg->len >= FIFO_SIZE) 2528c2ecf20Sopenharmony_ci len = FIFO_SIZE - 1; /* trigger at fifo full */ 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci len = id->msg->len - 1; /* trigger before all received */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); 2578c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xF) << 4)); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci OUT32(id, I2CMSR, 0); 2608c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); 2618c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); 2628c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, FIER_RXIE); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* prepare and start a master send operation */ 2668c2ecf20Sopenharmony_cistatic void sh7760_i2c_msend(struct cami2c *id) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int len; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci id->flags |= IDF_SEND; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* set the slave addr reg; otherwise xmit wont work! */ 2738c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0xfe); 2748c2ecf20Sopenharmony_ci OUT32(id, I2CMAR, (id->msg->addr << 1) | 0); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* adjust tx fifo trigger */ 2778c2ecf20Sopenharmony_ci if (id->msg->len >= FIFO_SIZE) 2788c2ecf20Sopenharmony_ci len = 2; /* trig: 2 bytes left in TX fifo */ 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci len = 0; /* trig: 8 bytes left in TX fifo */ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); 2838c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_RFRST | ((len & 3) << 2)); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci while (id->msg->len && IN32(id, I2CTFDR) < FIFO_SIZE) { 2868c2ecf20Sopenharmony_ci OUT32(id, I2CRXTX, *(id->msg->buf)); 2878c2ecf20Sopenharmony_ci (id->msg->len)--; 2888c2ecf20Sopenharmony_ci (id->msg->buf)++; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci OUT32(id, I2CMSR, 0); 2928c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); 2938c2ecf20Sopenharmony_ci OUT32(id, I2CFSR, 0); 2948c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); 2958c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, FIER_TEIE | (id->msg->len ? FIER_TXIE : 0)); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic inline int sh7760_i2c_busy_check(struct cami2c *id) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci return (IN32(id, I2CMCR) & MCR_FSDA); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int sh7760_i2c_master_xfer(struct i2c_adapter *adap, 3048c2ecf20Sopenharmony_ci struct i2c_msg *msgs, 3058c2ecf20Sopenharmony_ci int num) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct cami2c *id = adap->algo_data; 3088c2ecf20Sopenharmony_ci int i, retr; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (sh7760_i2c_busy_check(id)) { 3118c2ecf20Sopenharmony_ci dev_err(&adap->dev, "sh7760-i2c%d: bus busy!\n", adap->nr); 3128c2ecf20Sopenharmony_ci return -EBUSY; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci i = 0; 3168c2ecf20Sopenharmony_ci while (i < num) { 3178c2ecf20Sopenharmony_ci retr = adap->retries; 3188c2ecf20Sopenharmony_ciretry: 3198c2ecf20Sopenharmony_ci id->flags = ((i == (num-1)) ? IDF_STOP : 0); 3208c2ecf20Sopenharmony_ci id->status = 0; 3218c2ecf20Sopenharmony_ci id->msg = msgs; 3228c2ecf20Sopenharmony_ci init_completion(&id->xfer_done); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (msgs->flags & I2C_M_RD) 3258c2ecf20Sopenharmony_ci sh7760_i2c_mrecv(id); 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci sh7760_i2c_msend(id); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci wait_for_completion(&id->xfer_done); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (id->status == 0) { 3328c2ecf20Sopenharmony_ci num = -EIO; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (id->status & IDS_NACK) { 3378c2ecf20Sopenharmony_ci /* wait a bit or i2c module stops working */ 3388c2ecf20Sopenharmony_ci mdelay(1); 3398c2ecf20Sopenharmony_ci num = -EREMOTEIO; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (id->status & IDS_ARBLOST) { 3448c2ecf20Sopenharmony_ci if (retr--) { 3458c2ecf20Sopenharmony_ci mdelay(2); 3468c2ecf20Sopenharmony_ci goto retry; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci num = -EREMOTEIO; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci msgs++; 3538c2ecf20Sopenharmony_ci i++; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci id->msg = NULL; 3578c2ecf20Sopenharmony_ci id->flags = 0; 3588c2ecf20Sopenharmony_ci id->status = 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, 0); 3618c2ecf20Sopenharmony_ci OUT32(id, I2CMSR, 0); 3628c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, 0); 3638c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, 0); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* reset slave module registers too: master mode enables slave 3668c2ecf20Sopenharmony_ci * module for receive ops (ack, data). Without this reset, 3678c2ecf20Sopenharmony_ci * eternal bus activity might be reported after NACK / ARBLOST. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci OUT32(id, I2CSCR, 0); 3708c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0); 3718c2ecf20Sopenharmony_ci OUT32(id, I2CSSR, 0); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return num; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic u32 sh7760_i2c_func(struct i2c_adapter *adap) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic const struct i2c_algorithm sh7760_i2c_algo = { 3828c2ecf20Sopenharmony_ci .master_xfer = sh7760_i2c_master_xfer, 3838c2ecf20Sopenharmony_ci .functionality = sh7760_i2c_func, 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* calculate CCR register setting for a desired scl clock. SCL clock is 3878c2ecf20Sopenharmony_ci * derived from I2C module clock (iclk) which in turn is derived from 3888c2ecf20Sopenharmony_ci * peripheral module clock (mclk, usually around 33MHz): 3898c2ecf20Sopenharmony_ci * iclk = mclk/(CDF + 1). iclk must be < 20MHz. 3908c2ecf20Sopenharmony_ci * scl = iclk/(SCGD*8 + 20). 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistatic int calc_CCR(unsigned long scl_hz) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct clk *mclk; 3958c2ecf20Sopenharmony_ci unsigned long mck, m1, dff, odff, iclk; 3968c2ecf20Sopenharmony_ci signed char cdf, cdfm; 3978c2ecf20Sopenharmony_ci int scgd, scgdm, scgds; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci mclk = clk_get(NULL, "peripheral_clk"); 4008c2ecf20Sopenharmony_ci if (IS_ERR(mclk)) { 4018c2ecf20Sopenharmony_ci return PTR_ERR(mclk); 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci mck = mclk->rate; 4048c2ecf20Sopenharmony_ci clk_put(mclk); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci odff = scl_hz; 4088c2ecf20Sopenharmony_ci scgdm = cdfm = m1 = 0; 4098c2ecf20Sopenharmony_ci for (cdf = 3; cdf >= 0; cdf--) { 4108c2ecf20Sopenharmony_ci iclk = mck / (1 + cdf); 4118c2ecf20Sopenharmony_ci if (iclk >= 20000000) 4128c2ecf20Sopenharmony_ci continue; 4138c2ecf20Sopenharmony_ci scgds = ((iclk / scl_hz) - 20) >> 3; 4148c2ecf20Sopenharmony_ci for (scgd = scgds; (scgd < 63) && scgd <= scgds + 1; scgd++) { 4158c2ecf20Sopenharmony_ci m1 = iclk / (20 + (scgd << 3)); 4168c2ecf20Sopenharmony_ci dff = abs(scl_hz - m1); 4178c2ecf20Sopenharmony_ci if (dff < odff) { 4188c2ecf20Sopenharmony_ci odff = dff; 4198c2ecf20Sopenharmony_ci cdfm = cdf; 4208c2ecf20Sopenharmony_ci scgdm = scgd; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci /* fail if more than 25% off of requested SCL */ 4258c2ecf20Sopenharmony_ci if (odff > (scl_hz >> 2)) 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* create a CCR register value */ 4298c2ecf20Sopenharmony_ci return ((scgdm << 2) | cdfm); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int sh7760_i2c_probe(struct platform_device *pdev) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct sh7760_i2c_platdata *pd; 4358c2ecf20Sopenharmony_ci struct resource *res; 4368c2ecf20Sopenharmony_ci struct cami2c *id; 4378c2ecf20Sopenharmony_ci int ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 4408c2ecf20Sopenharmony_ci if (!pd) { 4418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform_data!\n"); 4428c2ecf20Sopenharmony_ci ret = -ENODEV; 4438c2ecf20Sopenharmony_ci goto out0; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci id = kzalloc(sizeof(*id), GFP_KERNEL); 4478c2ecf20Sopenharmony_ci if (!id) { 4488c2ecf20Sopenharmony_ci ret = -ENOMEM; 4498c2ecf20Sopenharmony_ci goto out0; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4538c2ecf20Sopenharmony_ci if (!res) { 4548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no mmio resources\n"); 4558c2ecf20Sopenharmony_ci ret = -ENODEV; 4568c2ecf20Sopenharmony_ci goto out1; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci id->ioarea = request_mem_region(res->start, REGSIZE, pdev->name); 4608c2ecf20Sopenharmony_ci if (!id->ioarea) { 4618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "mmio already reserved\n"); 4628c2ecf20Sopenharmony_ci ret = -EBUSY; 4638c2ecf20Sopenharmony_ci goto out1; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci id->iobase = ioremap(res->start, REGSIZE); 4678c2ecf20Sopenharmony_ci if (!id->iobase) { 4688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot ioremap\n"); 4698c2ecf20Sopenharmony_ci ret = -ENODEV; 4708c2ecf20Sopenharmony_ci goto out2; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 4748c2ecf20Sopenharmony_ci if (ret < 0) 4758c2ecf20Sopenharmony_ci goto out3; 4768c2ecf20Sopenharmony_ci id->irq = ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci id->adap.nr = pdev->id; 4798c2ecf20Sopenharmony_ci id->adap.algo = &sh7760_i2c_algo; 4808c2ecf20Sopenharmony_ci id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 4818c2ecf20Sopenharmony_ci id->adap.retries = 3; 4828c2ecf20Sopenharmony_ci id->adap.algo_data = id; 4838c2ecf20Sopenharmony_ci id->adap.dev.parent = &pdev->dev; 4848c2ecf20Sopenharmony_ci snprintf(id->adap.name, sizeof(id->adap.name), 4858c2ecf20Sopenharmony_ci "SH7760 I2C at %08lx", (unsigned long)res->start); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci OUT32(id, I2CMCR, 0); 4888c2ecf20Sopenharmony_ci OUT32(id, I2CMSR, 0); 4898c2ecf20Sopenharmony_ci OUT32(id, I2CMIER, 0); 4908c2ecf20Sopenharmony_ci OUT32(id, I2CMAR, 0); 4918c2ecf20Sopenharmony_ci OUT32(id, I2CSIER, 0); 4928c2ecf20Sopenharmony_ci OUT32(id, I2CSAR, 0); 4938c2ecf20Sopenharmony_ci OUT32(id, I2CSCR, 0); 4948c2ecf20Sopenharmony_ci OUT32(id, I2CSSR, 0); 4958c2ecf20Sopenharmony_ci OUT32(id, I2CFIER, 0); 4968c2ecf20Sopenharmony_ci OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); 4978c2ecf20Sopenharmony_ci OUT32(id, I2CFSR, 0); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ret = calc_CCR(pd->speed_khz * 1000); 5008c2ecf20Sopenharmony_ci if (ret < 0) { 5018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", 5028c2ecf20Sopenharmony_ci pd->speed_khz); 5038c2ecf20Sopenharmony_ci goto out3; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci OUT32(id, I2CCCR, ret); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (request_irq(id->irq, sh7760_i2c_irq, 0, 5088c2ecf20Sopenharmony_ci SH7760_I2C_DEVNAME, id)) { 5098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); 5108c2ecf20Sopenharmony_ci ret = -EBUSY; 5118c2ecf20Sopenharmony_ci goto out3; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(&id->adap); 5158c2ecf20Sopenharmony_ci if (ret < 0) 5168c2ecf20Sopenharmony_ci goto out4; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, id); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%d kHz mmio %08x irq %d\n", 5218c2ecf20Sopenharmony_ci pd->speed_khz, res->start, id->irq); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ciout4: 5268c2ecf20Sopenharmony_ci free_irq(id->irq, id); 5278c2ecf20Sopenharmony_ciout3: 5288c2ecf20Sopenharmony_ci iounmap(id->iobase); 5298c2ecf20Sopenharmony_ciout2: 5308c2ecf20Sopenharmony_ci release_resource(id->ioarea); 5318c2ecf20Sopenharmony_ci kfree(id->ioarea); 5328c2ecf20Sopenharmony_ciout1: 5338c2ecf20Sopenharmony_ci kfree(id); 5348c2ecf20Sopenharmony_ciout0: 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int sh7760_i2c_remove(struct platform_device *pdev) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct cami2c *id = platform_get_drvdata(pdev); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci i2c_del_adapter(&id->adap); 5438c2ecf20Sopenharmony_ci free_irq(id->irq, id); 5448c2ecf20Sopenharmony_ci iounmap(id->iobase); 5458c2ecf20Sopenharmony_ci release_resource(id->ioarea); 5468c2ecf20Sopenharmony_ci kfree(id->ioarea); 5478c2ecf20Sopenharmony_ci kfree(id); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic struct platform_driver sh7760_i2c_drv = { 5538c2ecf20Sopenharmony_ci .driver = { 5548c2ecf20Sopenharmony_ci .name = SH7760_I2C_DEVNAME, 5558c2ecf20Sopenharmony_ci }, 5568c2ecf20Sopenharmony_ci .probe = sh7760_i2c_probe, 5578c2ecf20Sopenharmony_ci .remove = sh7760_i2c_remove, 5588c2ecf20Sopenharmony_ci}; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cimodule_platform_driver(sh7760_i2c_drv); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SH7760 I2C bus driver"); 5648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); 565