18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SuperH Mobile I2C Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Portions of the code based on out-of-tree driver i2c-sh7343.c 98c2ecf20Sopenharmony_ci * Copyright (c) 2006 Carlos Munoz <carlos@kenati.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/of_device.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Transmit operation: */ 298c2ecf20Sopenharmony_ci/* */ 308c2ecf20Sopenharmony_ci/* 0 byte transmit */ 318c2ecf20Sopenharmony_ci/* BUS: S A8 ACK P(*) */ 328c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT */ 338c2ecf20Sopenharmony_ci/* ICIC: */ 348c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x90 */ 358c2ecf20Sopenharmony_ci/* ICDR: A8 */ 368c2ecf20Sopenharmony_ci/* */ 378c2ecf20Sopenharmony_ci/* 1 byte transmit */ 388c2ecf20Sopenharmony_ci/* BUS: S A8 ACK D8(1) ACK P(*) */ 398c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT WAIT */ 408c2ecf20Sopenharmony_ci/* ICIC: -DTE */ 418c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x90 */ 428c2ecf20Sopenharmony_ci/* ICDR: A8 D8(1) */ 438c2ecf20Sopenharmony_ci/* */ 448c2ecf20Sopenharmony_ci/* 2 byte transmit */ 458c2ecf20Sopenharmony_ci/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */ 468c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT WAIT WAIT */ 478c2ecf20Sopenharmony_ci/* ICIC: -DTE */ 488c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x90 */ 498c2ecf20Sopenharmony_ci/* ICDR: A8 D8(1) D8(2) */ 508c2ecf20Sopenharmony_ci/* */ 518c2ecf20Sopenharmony_ci/* 3 bytes or more, +---------+ gets repeated */ 528c2ecf20Sopenharmony_ci/* */ 538c2ecf20Sopenharmony_ci/* */ 548c2ecf20Sopenharmony_ci/* Receive operation: */ 558c2ecf20Sopenharmony_ci/* */ 568c2ecf20Sopenharmony_ci/* 0 byte receive - not supported since slave may hold SDA low */ 578c2ecf20Sopenharmony_ci/* */ 588c2ecf20Sopenharmony_ci/* 1 byte receive [TX] | [RX] */ 598c2ecf20Sopenharmony_ci/* BUS: S A8 ACK | D8(1) ACK P(*) */ 608c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT | WAIT DTE */ 618c2ecf20Sopenharmony_ci/* ICIC: -DTE | +DTE */ 628c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x81 | 0xc0 */ 638c2ecf20Sopenharmony_ci/* ICDR: A8 | D8(1) */ 648c2ecf20Sopenharmony_ci/* */ 658c2ecf20Sopenharmony_ci/* 2 byte receive [TX]| [RX] */ 668c2ecf20Sopenharmony_ci/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P(*) */ 678c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT | WAIT WAIT DTE */ 688c2ecf20Sopenharmony_ci/* ICIC: -DTE | +DTE */ 698c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x81 | 0xc0 */ 708c2ecf20Sopenharmony_ci/* ICDR: A8 | D8(1) D8(2) */ 718c2ecf20Sopenharmony_ci/* */ 728c2ecf20Sopenharmony_ci/* 3 byte receive [TX] | [RX] (*) */ 738c2ecf20Sopenharmony_ci/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */ 748c2ecf20Sopenharmony_ci/* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */ 758c2ecf20Sopenharmony_ci/* ICIC: -DTE | +DTE */ 768c2ecf20Sopenharmony_ci/* ICCR: 0x94 0x81 | 0xc0 */ 778c2ecf20Sopenharmony_ci/* ICDR: A8 | D8(1) D8(2) D8(3) */ 788c2ecf20Sopenharmony_ci/* */ 798c2ecf20Sopenharmony_ci/* 4 bytes or more, this part is repeated +---------+ */ 808c2ecf20Sopenharmony_ci/* */ 818c2ecf20Sopenharmony_ci/* */ 828c2ecf20Sopenharmony_ci/* Interrupt order and BUSY flag */ 838c2ecf20Sopenharmony_ci/* ___ _ */ 848c2ecf20Sopenharmony_ci/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ 858c2ecf20Sopenharmony_ci/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ 868c2ecf20Sopenharmony_ci/* */ 878c2ecf20Sopenharmony_ci/* S D7 D6 D5 D4 D3 D2 D1 D0 P(*) */ 888c2ecf20Sopenharmony_ci/* ___ */ 898c2ecf20Sopenharmony_ci/* WAIT IRQ ________________________________/ \___________ */ 908c2ecf20Sopenharmony_ci/* TACK IRQ ____________________________________/ \_______ */ 918c2ecf20Sopenharmony_ci/* DTE IRQ __________________________________________/ \_ */ 928c2ecf20Sopenharmony_ci/* AL IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ 938c2ecf20Sopenharmony_ci/* _______________________________________________ */ 948c2ecf20Sopenharmony_ci/* BUSY __/ \_ */ 958c2ecf20Sopenharmony_ci/* */ 968c2ecf20Sopenharmony_ci/* (*) The STOP condition is only sent by the master at the end of the last */ 978c2ecf20Sopenharmony_ci/* I2C message or if the I2C_M_STOP flag is set. Similarly, the BUSY bit is */ 988c2ecf20Sopenharmony_ci/* only cleared after the STOP condition, so, between messages we have to */ 998c2ecf20Sopenharmony_ci/* poll for the DTE bit. */ 1008c2ecf20Sopenharmony_ci/* */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cienum sh_mobile_i2c_op { 1038c2ecf20Sopenharmony_ci OP_START = 0, 1048c2ecf20Sopenharmony_ci OP_TX_FIRST, 1058c2ecf20Sopenharmony_ci OP_TX, 1068c2ecf20Sopenharmony_ci OP_TX_STOP, 1078c2ecf20Sopenharmony_ci OP_TX_TO_RX, 1088c2ecf20Sopenharmony_ci OP_RX, 1098c2ecf20Sopenharmony_ci OP_RX_STOP, 1108c2ecf20Sopenharmony_ci OP_RX_STOP_DATA, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct sh_mobile_i2c_data { 1148c2ecf20Sopenharmony_ci struct device *dev; 1158c2ecf20Sopenharmony_ci void __iomem *reg; 1168c2ecf20Sopenharmony_ci struct i2c_adapter adap; 1178c2ecf20Sopenharmony_ci unsigned long bus_speed; 1188c2ecf20Sopenharmony_ci unsigned int clks_per_count; 1198c2ecf20Sopenharmony_ci struct clk *clk; 1208c2ecf20Sopenharmony_ci u_int8_t icic; 1218c2ecf20Sopenharmony_ci u_int8_t flags; 1228c2ecf20Sopenharmony_ci u_int16_t iccl; 1238c2ecf20Sopenharmony_ci u_int16_t icch; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci spinlock_t lock; 1268c2ecf20Sopenharmony_ci wait_queue_head_t wait; 1278c2ecf20Sopenharmony_ci struct i2c_msg *msg; 1288c2ecf20Sopenharmony_ci int pos; 1298c2ecf20Sopenharmony_ci int sr; 1308c2ecf20Sopenharmony_ci bool send_stop; 1318c2ecf20Sopenharmony_ci bool stop_after_dma; 1328c2ecf20Sopenharmony_ci bool atomic_xfer; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci struct resource *res; 1358c2ecf20Sopenharmony_ci struct dma_chan *dma_tx; 1368c2ecf20Sopenharmony_ci struct dma_chan *dma_rx; 1378c2ecf20Sopenharmony_ci struct scatterlist sg; 1388c2ecf20Sopenharmony_ci enum dma_data_direction dma_direction; 1398c2ecf20Sopenharmony_ci u8 *dma_buf; 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistruct sh_mobile_dt_config { 1438c2ecf20Sopenharmony_ci int clks_per_count; 1448c2ecf20Sopenharmony_ci int (*setup)(struct sh_mobile_i2c_data *pd); 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define IIC_FLAG_HAS_ICIC67 (1 << 0) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* Register offsets */ 1508c2ecf20Sopenharmony_ci#define ICDR 0x00 1518c2ecf20Sopenharmony_ci#define ICCR 0x04 1528c2ecf20Sopenharmony_ci#define ICSR 0x08 1538c2ecf20Sopenharmony_ci#define ICIC 0x0c 1548c2ecf20Sopenharmony_ci#define ICCL 0x10 1558c2ecf20Sopenharmony_ci#define ICCH 0x14 1568c2ecf20Sopenharmony_ci#define ICSTART 0x70 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* Register bits */ 1598c2ecf20Sopenharmony_ci#define ICCR_ICE 0x80 1608c2ecf20Sopenharmony_ci#define ICCR_RACK 0x40 1618c2ecf20Sopenharmony_ci#define ICCR_TRS 0x10 1628c2ecf20Sopenharmony_ci#define ICCR_BBSY 0x04 1638c2ecf20Sopenharmony_ci#define ICCR_SCP 0x01 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define ICSR_SCLM 0x80 1668c2ecf20Sopenharmony_ci#define ICSR_SDAM 0x40 1678c2ecf20Sopenharmony_ci#define SW_DONE 0x20 1688c2ecf20Sopenharmony_ci#define ICSR_BUSY 0x10 1698c2ecf20Sopenharmony_ci#define ICSR_AL 0x08 1708c2ecf20Sopenharmony_ci#define ICSR_TACK 0x04 1718c2ecf20Sopenharmony_ci#define ICSR_WAIT 0x02 1728c2ecf20Sopenharmony_ci#define ICSR_DTE 0x01 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define ICIC_ICCLB8 0x80 1758c2ecf20Sopenharmony_ci#define ICIC_ICCHB8 0x40 1768c2ecf20Sopenharmony_ci#define ICIC_TDMAE 0x20 1778c2ecf20Sopenharmony_ci#define ICIC_RDMAE 0x10 1788c2ecf20Sopenharmony_ci#define ICIC_ALE 0x08 1798c2ecf20Sopenharmony_ci#define ICIC_TACKE 0x04 1808c2ecf20Sopenharmony_ci#define ICIC_WAITE 0x02 1818c2ecf20Sopenharmony_ci#define ICIC_DTEE 0x01 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#define ICSTART_ICSTART 0x10 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci if (offs == ICIC) 1888c2ecf20Sopenharmony_ci data |= pd->icic; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci iowrite8(data, pd->reg + offs); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return ioread8(pd->reg + offs); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs, 1998c2ecf20Sopenharmony_ci unsigned char set, unsigned char clr) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic u32 sh_mobile_i2c_iccl(unsigned long count_khz, u32 tLOW, u32 tf) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Conditional expression: 2088c2ecf20Sopenharmony_ci * ICCL >= COUNT_CLK * (tLOW + tf) 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * SH-Mobile IIC hardware starts counting the LOW period of 2118c2ecf20Sopenharmony_ci * the SCL signal (tLOW) as soon as it pulls the SCL line. 2128c2ecf20Sopenharmony_ci * In order to meet the tLOW timing spec, we need to take into 2138c2ecf20Sopenharmony_ci * account the fall time of SCL signal (tf). Default tf value 2148c2ecf20Sopenharmony_ci * should be 0.3 us, for safety. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci return (((count_khz * (tLOW + tf)) + 5000) / 10000); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic u32 sh_mobile_i2c_icch(unsigned long count_khz, u32 tHIGH, u32 tf) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * Conditional expression: 2238c2ecf20Sopenharmony_ci * ICCH >= COUNT_CLK * (tHIGH + tf) 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * SH-Mobile IIC hardware is aware of SCL transition period 'tr', 2268c2ecf20Sopenharmony_ci * and can ignore it. SH-Mobile IIC controller starts counting 2278c2ecf20Sopenharmony_ci * the HIGH period of the SCL signal (tHIGH) after the SCL input 2288c2ecf20Sopenharmony_ci * voltage increases at VIH. 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Afterward it turned out calculating ICCH using only tHIGH spec 2318c2ecf20Sopenharmony_ci * will result in violation of the tHD;STA timing spec. We need 2328c2ecf20Sopenharmony_ci * to take into account the fall time of SDA signal (tf) at START 2338c2ecf20Sopenharmony_ci * condition, in order to meet both tHIGH and tHD;STA specs. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci return (((count_khz * (tHIGH + tf)) + 5000) / 10000); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_check_timing(struct sh_mobile_i2c_data *pd) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u16 max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (pd->iccl > max_val || pd->icch > max_val) { 2438c2ecf20Sopenharmony_ci dev_err(pd->dev, "timing values out of range: L/H=0x%x/0x%x\n", 2448c2ecf20Sopenharmony_ci pd->iccl, pd->icch); 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* one more bit of ICCL in ICIC */ 2498c2ecf20Sopenharmony_ci if (pd->iccl & 0x100) 2508c2ecf20Sopenharmony_ci pd->icic |= ICIC_ICCLB8; 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci pd->icic &= ~ICIC_ICCLB8; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* one more bit of ICCH in ICIC */ 2558c2ecf20Sopenharmony_ci if (pd->icch & 0x100) 2568c2ecf20Sopenharmony_ci pd->icic |= ICIC_ICCHB8; 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci pd->icic &= ~ICIC_ICCHB8; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "timing values: L/H=0x%x/0x%x\n", pd->iccl, pd->icch); 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci unsigned long i2c_clk_khz; 2678c2ecf20Sopenharmony_ci u32 tHIGH, tLOW, tf; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci i2c_clk_khz = clk_get_rate(pd->clk) / 1000 / pd->clks_per_count; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (pd->bus_speed == I2C_MAX_STANDARD_MODE_FREQ) { 2728c2ecf20Sopenharmony_ci tLOW = 47; /* tLOW = 4.7 us */ 2738c2ecf20Sopenharmony_ci tHIGH = 40; /* tHD;STA = tHIGH = 4.0 us */ 2748c2ecf20Sopenharmony_ci tf = 3; /* tf = 0.3 us */ 2758c2ecf20Sopenharmony_ci } else if (pd->bus_speed == I2C_MAX_FAST_MODE_FREQ) { 2768c2ecf20Sopenharmony_ci tLOW = 13; /* tLOW = 1.3 us */ 2778c2ecf20Sopenharmony_ci tHIGH = 6; /* tHD;STA = tHIGH = 0.6 us */ 2788c2ecf20Sopenharmony_ci tf = 3; /* tf = 0.3 us */ 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci dev_err(pd->dev, "unrecognized bus speed %lu Hz\n", 2818c2ecf20Sopenharmony_ci pd->bus_speed); 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf); 2868c2ecf20Sopenharmony_ci pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return sh_mobile_i2c_check_timing(pd); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci unsigned long clks_per_cycle; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* L = 5, H = 4, L + H = 9 */ 2968c2ecf20Sopenharmony_ci clks_per_cycle = clk_get_rate(pd->clk) / pd->bus_speed; 2978c2ecf20Sopenharmony_ci pd->iccl = DIV_ROUND_UP(clks_per_cycle * 5 / 9 - 1, pd->clks_per_count); 2988c2ecf20Sopenharmony_ci pd->icch = DIV_ROUND_UP(clks_per_cycle * 4 / 9 - 5, pd->clks_per_count); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return sh_mobile_i2c_check_timing(pd); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned char ret = 0; 3068c2ecf20Sopenharmony_ci unsigned long flags; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "op %d\n", op); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci spin_lock_irqsave(&pd->lock, flags); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci switch (op) { 3138c2ecf20Sopenharmony_ci case OP_START: /* issue start and trigger DTE interrupt */ 3148c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY); 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case OP_TX_FIRST: /* disable DTE interrupt and write client address */ 3178c2ecf20Sopenharmony_ci iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); 3188c2ecf20Sopenharmony_ci iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg)); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci case OP_TX: /* write data */ 3218c2ecf20Sopenharmony_ci iic_wr(pd, ICDR, pd->msg->buf[pd->pos]); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case OP_TX_STOP: /* issue a stop (or rep_start) */ 3248c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS 3258c2ecf20Sopenharmony_ci : ICCR_ICE | ICCR_TRS | ICCR_BBSY); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case OP_TX_TO_RX: /* select read mode */ 3288c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP); 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case OP_RX: /* just read data */ 3318c2ecf20Sopenharmony_ci ret = iic_rd(pd, ICDR); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case OP_RX_STOP: /* enable DTE interrupt, issue stop */ 3348c2ecf20Sopenharmony_ci if (!pd->atomic_xfer) 3358c2ecf20Sopenharmony_ci iic_wr(pd, ICIC, 3368c2ecf20Sopenharmony_ci ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); 3378c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */ 3408c2ecf20Sopenharmony_ci if (!pd->atomic_xfer) 3418c2ecf20Sopenharmony_ci iic_wr(pd, ICIC, 3428c2ecf20Sopenharmony_ci ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); 3438c2ecf20Sopenharmony_ci ret = iic_rd(pd, ICDR); 3448c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK); 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pd->lock, flags); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "op %d, data out 0x%02x\n", op, ret); 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci if (pd->pos == pd->msg->len) { 3578c2ecf20Sopenharmony_ci i2c_op(pd, OP_TX_STOP); 3588c2ecf20Sopenharmony_ci return 1; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (pd->pos == -1) 3628c2ecf20Sopenharmony_ci i2c_op(pd, OP_TX_FIRST); 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci i2c_op(pd, OP_TX); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pd->pos++; 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int real_pos; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* switch from TX (address) to RX (data) adds two interrupts */ 3758c2ecf20Sopenharmony_ci real_pos = pd->pos - 2; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (pd->pos == -1) { 3788c2ecf20Sopenharmony_ci i2c_op(pd, OP_TX_FIRST); 3798c2ecf20Sopenharmony_ci } else if (pd->pos == 0) { 3808c2ecf20Sopenharmony_ci i2c_op(pd, OP_TX_TO_RX); 3818c2ecf20Sopenharmony_ci } else if (pd->pos == pd->msg->len) { 3828c2ecf20Sopenharmony_ci if (pd->stop_after_dma) { 3838c2ecf20Sopenharmony_ci /* Simulate PIO end condition after DMA transfer */ 3848c2ecf20Sopenharmony_ci i2c_op(pd, OP_RX_STOP); 3858c2ecf20Sopenharmony_ci pd->pos++; 3868c2ecf20Sopenharmony_ci goto done; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (real_pos < 0) 3908c2ecf20Sopenharmony_ci i2c_op(pd, OP_RX_STOP); 3918c2ecf20Sopenharmony_ci else 3928c2ecf20Sopenharmony_ci pd->msg->buf[real_pos] = i2c_op(pd, OP_RX_STOP_DATA); 3938c2ecf20Sopenharmony_ci } else if (real_pos >= 0) { 3948c2ecf20Sopenharmony_ci pd->msg->buf[real_pos] = i2c_op(pd, OP_RX); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci done: 3988c2ecf20Sopenharmony_ci pd->pos++; 3998c2ecf20Sopenharmony_ci return pd->pos == (pd->msg->len + 2); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd = dev_id; 4058c2ecf20Sopenharmony_ci unsigned char sr; 4068c2ecf20Sopenharmony_ci int wakeup = 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci sr = iic_rd(pd, ICSR); 4098c2ecf20Sopenharmony_ci pd->sr |= sr; /* remember state */ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr, 4128c2ecf20Sopenharmony_ci (pd->msg->flags & I2C_M_RD) ? "read" : "write", 4138c2ecf20Sopenharmony_ci pd->pos, pd->msg->len); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Kick off TxDMA after preface was done */ 4168c2ecf20Sopenharmony_ci if (pd->dma_direction == DMA_TO_DEVICE && pd->pos == 0) 4178c2ecf20Sopenharmony_ci iic_set_clr(pd, ICIC, ICIC_TDMAE, 0); 4188c2ecf20Sopenharmony_ci else if (sr & (ICSR_AL | ICSR_TACK)) 4198c2ecf20Sopenharmony_ci /* don't interrupt transaction - continue to issue stop */ 4208c2ecf20Sopenharmony_ci iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK)); 4218c2ecf20Sopenharmony_ci else if (pd->msg->flags & I2C_M_RD) 4228c2ecf20Sopenharmony_ci wakeup = sh_mobile_i2c_isr_rx(pd); 4238c2ecf20Sopenharmony_ci else 4248c2ecf20Sopenharmony_ci wakeup = sh_mobile_i2c_isr_tx(pd); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Kick off RxDMA after preface was done */ 4278c2ecf20Sopenharmony_ci if (pd->dma_direction == DMA_FROM_DEVICE && pd->pos == 1) 4288c2ecf20Sopenharmony_ci iic_set_clr(pd, ICIC, ICIC_RDMAE, 0); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */ 4318c2ecf20Sopenharmony_ci iic_wr(pd, ICSR, sr & ~ICSR_WAIT); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (wakeup) { 4348c2ecf20Sopenharmony_ci pd->sr |= SW_DONE; 4358c2ecf20Sopenharmony_ci if (!pd->atomic_xfer) 4368c2ecf20Sopenharmony_ci wake_up(&pd->wait); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* defeat write posting to avoid spurious WAIT interrupts */ 4408c2ecf20Sopenharmony_ci iic_rd(pd, ICSR); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE 4488c2ecf20Sopenharmony_ci ? pd->dma_rx : pd->dma_tx; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg), 4518c2ecf20Sopenharmony_ci pd->msg->len, pd->dma_direction); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci pd->dma_direction = DMA_NONE; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci if (pd->dma_direction == DMA_NONE) 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci else if (pd->dma_direction == DMA_FROM_DEVICE) 4618c2ecf20Sopenharmony_ci dmaengine_terminate_all(pd->dma_rx); 4628c2ecf20Sopenharmony_ci else if (pd->dma_direction == DMA_TO_DEVICE) 4638c2ecf20Sopenharmony_ci dmaengine_terminate_all(pd->dma_tx); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci sh_mobile_i2c_dma_unmap(pd); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void sh_mobile_i2c_dma_callback(void *data) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd = data; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci sh_mobile_i2c_dma_unmap(pd); 4738c2ecf20Sopenharmony_ci pd->pos = pd->msg->len; 4748c2ecf20Sopenharmony_ci pd->stop_after_dma = true; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, 4808c2ecf20Sopenharmony_ci enum dma_transfer_direction dir, dma_addr_t port_addr) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct dma_chan *chan; 4838c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 4848c2ecf20Sopenharmony_ci char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; 4858c2ecf20Sopenharmony_ci int ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci chan = dma_request_chan(dev, chan_name); 4888c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 4898c2ecf20Sopenharmony_ci dev_dbg(dev, "request_channel failed for %s (%ld)\n", chan_name, 4908c2ecf20Sopenharmony_ci PTR_ERR(chan)); 4918c2ecf20Sopenharmony_ci return chan; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 4958c2ecf20Sopenharmony_ci cfg.direction = dir; 4968c2ecf20Sopenharmony_ci if (dir == DMA_MEM_TO_DEV) { 4978c2ecf20Sopenharmony_ci cfg.dst_addr = port_addr; 4988c2ecf20Sopenharmony_ci cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 4998c2ecf20Sopenharmony_ci } else { 5008c2ecf20Sopenharmony_ci cfg.src_addr = port_addr; 5018c2ecf20Sopenharmony_ci cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(chan, &cfg); 5058c2ecf20Sopenharmony_ci if (ret) { 5068c2ecf20Sopenharmony_ci dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); 5078c2ecf20Sopenharmony_ci dma_release_channel(chan); 5088c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci dev_dbg(dev, "got DMA channel for %s\n", chan_name); 5128c2ecf20Sopenharmony_ci return chan; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci bool read = pd->msg->flags & I2C_M_RD; 5188c2ecf20Sopenharmony_ci enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 5198c2ecf20Sopenharmony_ci struct dma_chan *chan = read ? pd->dma_rx : pd->dma_tx; 5208c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txdesc; 5218c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 5228c2ecf20Sopenharmony_ci dma_cookie_t cookie; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (PTR_ERR(chan) == -EPROBE_DEFER) { 5258c2ecf20Sopenharmony_ci if (read) 5268c2ecf20Sopenharmony_ci chan = pd->dma_rx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, 5278c2ecf20Sopenharmony_ci pd->res->start + ICDR); 5288c2ecf20Sopenharmony_ci else 5298c2ecf20Sopenharmony_ci chan = pd->dma_tx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, 5308c2ecf20Sopenharmony_ci pd->res->start + ICDR); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (IS_ERR(chan)) 5348c2ecf20Sopenharmony_ci return; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci dma_addr = dma_map_single(chan->device->dev, pd->dma_buf, pd->msg->len, dir); 5378c2ecf20Sopenharmony_ci if (dma_mapping_error(chan->device->dev, dma_addr)) { 5388c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "dma map failed, using PIO\n"); 5398c2ecf20Sopenharmony_ci return; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci sg_dma_len(&pd->sg) = pd->msg->len; 5438c2ecf20Sopenharmony_ci sg_dma_address(&pd->sg) = dma_addr; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci pd->dma_direction = dir; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci txdesc = dmaengine_prep_slave_sg(chan, &pd->sg, 1, 5488c2ecf20Sopenharmony_ci read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, 5498c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 5508c2ecf20Sopenharmony_ci if (!txdesc) { 5518c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n"); 5528c2ecf20Sopenharmony_ci sh_mobile_i2c_cleanup_dma(pd); 5538c2ecf20Sopenharmony_ci return; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci txdesc->callback = sh_mobile_i2c_dma_callback; 5578c2ecf20Sopenharmony_ci txdesc->callback_param = pd; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci cookie = dmaengine_submit(txdesc); 5608c2ecf20Sopenharmony_ci if (dma_submit_error(cookie)) { 5618c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "submitting dma failed, using PIO\n"); 5628c2ecf20Sopenharmony_ci sh_mobile_i2c_cleanup_dma(pd); 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci dma_async_issue_pending(chan); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, 5708c2ecf20Sopenharmony_ci bool do_init) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci if (do_init) { 5738c2ecf20Sopenharmony_ci /* Initialize channel registers */ 5748c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_SCP); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* Enable channel and configure rx ack */ 5778c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Set the clock */ 5808c2ecf20Sopenharmony_ci iic_wr(pd, ICCL, pd->iccl & 0xff); 5818c2ecf20Sopenharmony_ci iic_wr(pd, ICCH, pd->icch & 0xff); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci pd->msg = usr_msg; 5858c2ecf20Sopenharmony_ci pd->pos = -1; 5868c2ecf20Sopenharmony_ci pd->sr = 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (pd->atomic_xfer) 5898c2ecf20Sopenharmony_ci return; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8); 5928c2ecf20Sopenharmony_ci if (pd->dma_buf) 5938c2ecf20Sopenharmony_ci sh_mobile_i2c_xfer_dma(pd); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Enable all interrupts to begin with */ 5968c2ecf20Sopenharmony_ci iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic int poll_dte(struct sh_mobile_i2c_data *pd) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci int i; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci for (i = 1000; i; i--) { 6048c2ecf20Sopenharmony_ci u_int8_t val = iic_rd(pd, ICSR); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (val & ICSR_DTE) 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (val & ICSR_TACK) 6108c2ecf20Sopenharmony_ci return -ENXIO; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci udelay(10); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return i ? 0 : -ETIMEDOUT; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int poll_busy(struct sh_mobile_i2c_data *pd) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci int i; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci for (i = 1000; i; i--) { 6238c2ecf20Sopenharmony_ci u_int8_t val = iic_rd(pd, ICSR); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* the interrupt handler may wake us up before the 6288c2ecf20Sopenharmony_ci * transfer is finished, so poll the hardware 6298c2ecf20Sopenharmony_ci * until we're done. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci if (!(val & ICSR_BUSY)) { 6328c2ecf20Sopenharmony_ci /* handle missing acknowledge and arbitration lost */ 6338c2ecf20Sopenharmony_ci val |= pd->sr; 6348c2ecf20Sopenharmony_ci if (val & ICSR_TACK) 6358c2ecf20Sopenharmony_ci return -ENXIO; 6368c2ecf20Sopenharmony_ci if (val & ICSR_AL) 6378c2ecf20Sopenharmony_ci return -EAGAIN; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci udelay(10); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return i ? 0 : -ETIMEDOUT; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int sh_mobile_xfer(struct sh_mobile_i2c_data *pd, 6488c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct i2c_msg *msg; 6518c2ecf20Sopenharmony_ci int err = 0; 6528c2ecf20Sopenharmony_ci int i; 6538c2ecf20Sopenharmony_ci long time_left; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Wake up device and enable clock */ 6568c2ecf20Sopenharmony_ci pm_runtime_get_sync(pd->dev); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Process all messages */ 6598c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 6608c2ecf20Sopenharmony_ci bool do_start = pd->send_stop || !i; 6618c2ecf20Sopenharmony_ci msg = &msgs[i]; 6628c2ecf20Sopenharmony_ci pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP; 6638c2ecf20Sopenharmony_ci pd->stop_after_dma = false; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci start_ch(pd, msg, do_start); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (do_start) 6688c2ecf20Sopenharmony_ci i2c_op(pd, OP_START); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (pd->atomic_xfer) { 6718c2ecf20Sopenharmony_ci unsigned long j = jiffies + pd->adap.timeout; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci time_left = time_before_eq(jiffies, j); 6748c2ecf20Sopenharmony_ci while (time_left && 6758c2ecf20Sopenharmony_ci !(pd->sr & (ICSR_TACK | SW_DONE))) { 6768c2ecf20Sopenharmony_ci unsigned char sr = iic_rd(pd, ICSR); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (sr & (ICSR_AL | ICSR_TACK | 6798c2ecf20Sopenharmony_ci ICSR_WAIT | ICSR_DTE)) { 6808c2ecf20Sopenharmony_ci sh_mobile_i2c_isr(0, pd); 6818c2ecf20Sopenharmony_ci udelay(150); 6828c2ecf20Sopenharmony_ci } else { 6838c2ecf20Sopenharmony_ci cpu_relax(); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci time_left = time_before_eq(jiffies, j); 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci } else { 6888c2ecf20Sopenharmony_ci /* The interrupt handler takes care of the rest... */ 6898c2ecf20Sopenharmony_ci time_left = wait_event_timeout(pd->wait, 6908c2ecf20Sopenharmony_ci pd->sr & (ICSR_TACK | SW_DONE), 6918c2ecf20Sopenharmony_ci pd->adap.timeout); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* 'stop_after_dma' tells if DMA xfer was complete */ 6948c2ecf20Sopenharmony_ci i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg, 6958c2ecf20Sopenharmony_ci pd->stop_after_dma); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!time_left) { 6998c2ecf20Sopenharmony_ci dev_err(pd->dev, "Transfer request timed out\n"); 7008c2ecf20Sopenharmony_ci if (pd->dma_direction != DMA_NONE) 7018c2ecf20Sopenharmony_ci sh_mobile_i2c_cleanup_dma(pd); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 7048c2ecf20Sopenharmony_ci break; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (pd->send_stop) 7088c2ecf20Sopenharmony_ci err = poll_busy(pd); 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci err = poll_dte(pd); 7118c2ecf20Sopenharmony_ci if (err < 0) 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Disable channel */ 7168c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_SCP); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Disable clock and mark device as idle */ 7198c2ecf20Sopenharmony_ci pm_runtime_put_sync(pd->dev); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return err ?: num; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, 7258c2ecf20Sopenharmony_ci struct i2c_msg *msgs, 7268c2ecf20Sopenharmony_ci int num) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci pd->atomic_xfer = false; 7318c2ecf20Sopenharmony_ci return sh_mobile_xfer(pd, msgs, num); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_xfer_atomic(struct i2c_adapter *adapter, 7358c2ecf20Sopenharmony_ci struct i2c_msg *msgs, 7368c2ecf20Sopenharmony_ci int num) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci pd->atomic_xfer = true; 7418c2ecf20Sopenharmony_ci return sh_mobile_xfer(pd, msgs, num); 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic const struct i2c_algorithm sh_mobile_i2c_algorithm = { 7508c2ecf20Sopenharmony_ci .functionality = sh_mobile_i2c_func, 7518c2ecf20Sopenharmony_ci .master_xfer = sh_mobile_i2c_xfer, 7528c2ecf20Sopenharmony_ci .master_xfer_atomic = sh_mobile_i2c_xfer_atomic, 7538c2ecf20Sopenharmony_ci}; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks sh_mobile_i2c_quirks = { 7568c2ecf20Sopenharmony_ci .flags = I2C_AQ_NO_ZERO_LEN_READ, 7578c2ecf20Sopenharmony_ci}; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/* 7608c2ecf20Sopenharmony_ci * r8a7740 has an errata regarding I2C I/O pad reset needing this workaround. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci iic_set_clr(pd, ICCR, ICCR_ICE, 0); 7658c2ecf20Sopenharmony_ci iic_rd(pd, ICCR); /* dummy read */ 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci iic_set_clr(pd, ICSTART, ICSTART_ICSTART, 0); 7688c2ecf20Sopenharmony_ci iic_rd(pd, ICSTART); /* dummy read */ 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci udelay(10); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_SCP); 7738c2ecf20Sopenharmony_ci iic_wr(pd, ICSTART, 0); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci udelay(10); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_TRS); 7788c2ecf20Sopenharmony_ci udelay(10); 7798c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, 0); 7808c2ecf20Sopenharmony_ci udelay(10); 7818c2ecf20Sopenharmony_ci iic_wr(pd, ICCR, ICCR_TRS); 7828c2ecf20Sopenharmony_ci udelay(10); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci return sh_mobile_i2c_init(pd); 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic const struct sh_mobile_dt_config default_dt_config = { 7888c2ecf20Sopenharmony_ci .clks_per_count = 1, 7898c2ecf20Sopenharmony_ci .setup = sh_mobile_i2c_init, 7908c2ecf20Sopenharmony_ci}; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic const struct sh_mobile_dt_config fast_clock_dt_config = { 7938c2ecf20Sopenharmony_ci .clks_per_count = 2, 7948c2ecf20Sopenharmony_ci .setup = sh_mobile_i2c_init, 7958c2ecf20Sopenharmony_ci}; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic const struct sh_mobile_dt_config v2_freq_calc_dt_config = { 7988c2ecf20Sopenharmony_ci .clks_per_count = 2, 7998c2ecf20Sopenharmony_ci .setup = sh_mobile_i2c_v2_init, 8008c2ecf20Sopenharmony_ci}; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic const struct sh_mobile_dt_config r8a7740_dt_config = { 8038c2ecf20Sopenharmony_ci .clks_per_count = 1, 8048c2ecf20Sopenharmony_ci .setup = sh_mobile_i2c_r8a7740_workaround, 8058c2ecf20Sopenharmony_ci}; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic const struct of_device_id sh_mobile_i2c_dt_ids[] = { 8088c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config }, 8098c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, 8108c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a774c0", .data = &v2_freq_calc_dt_config }, 8118c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config }, 8128c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config }, 8138c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config }, 8148c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config }, 8158c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config }, 8168c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, 8178c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, 8188c2ecf20Sopenharmony_ci { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, 8198c2ecf20Sopenharmony_ci { .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config }, 8208c2ecf20Sopenharmony_ci { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, 8218c2ecf20Sopenharmony_ci { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, 8228c2ecf20Sopenharmony_ci {}, 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci if (!IS_ERR(pd->dma_tx)) { 8298c2ecf20Sopenharmony_ci dma_release_channel(pd->dma_tx); 8308c2ecf20Sopenharmony_ci pd->dma_tx = ERR_PTR(-EPROBE_DEFER); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (!IS_ERR(pd->dma_rx)) { 8348c2ecf20Sopenharmony_ci dma_release_channel(pd->dma_rx); 8358c2ecf20Sopenharmony_ci pd->dma_rx = ERR_PTR(-EPROBE_DEFER); 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct resource *res; 8428c2ecf20Sopenharmony_ci resource_size_t n; 8438c2ecf20Sopenharmony_ci int k = 0, ret; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { 8468c2ecf20Sopenharmony_ci for (n = res->start; n <= res->end; n++) { 8478c2ecf20Sopenharmony_ci ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr, 8488c2ecf20Sopenharmony_ci 0, dev_name(&dev->dev), pd); 8498c2ecf20Sopenharmony_ci if (ret) { 8508c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot request IRQ %pa\n", &n); 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci k++; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return k > 0 ? 0 : -ENOENT; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_probe(struct platform_device *dev) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd; 8638c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 8648c2ecf20Sopenharmony_ci struct resource *res; 8658c2ecf20Sopenharmony_ci const struct sh_mobile_dt_config *config; 8668c2ecf20Sopenharmony_ci int ret; 8678c2ecf20Sopenharmony_ci u32 bus_speed; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci pd = devm_kzalloc(&dev->dev, sizeof(struct sh_mobile_i2c_data), GFP_KERNEL); 8708c2ecf20Sopenharmony_ci if (!pd) 8718c2ecf20Sopenharmony_ci return -ENOMEM; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci pd->clk = devm_clk_get(&dev->dev, NULL); 8748c2ecf20Sopenharmony_ci if (IS_ERR(pd->clk)) { 8758c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot get clock\n"); 8768c2ecf20Sopenharmony_ci return PTR_ERR(pd->clk); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci ret = sh_mobile_i2c_hook_irqs(dev, pd); 8808c2ecf20Sopenharmony_ci if (ret) 8818c2ecf20Sopenharmony_ci return ret; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci pd->dev = &dev->dev; 8848c2ecf20Sopenharmony_ci platform_set_drvdata(dev, pd); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci pd->res = res; 8898c2ecf20Sopenharmony_ci pd->reg = devm_ioremap_resource(&dev->dev, res); 8908c2ecf20Sopenharmony_ci if (IS_ERR(pd->reg)) 8918c2ecf20Sopenharmony_ci return PTR_ERR(pd->reg); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed); 8948c2ecf20Sopenharmony_ci pd->bus_speed = (ret || !bus_speed) ? I2C_MAX_STANDARD_MODE_FREQ : bus_speed; 8958c2ecf20Sopenharmony_ci pd->clks_per_count = 1; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Newer variants come with two new bits in ICIC */ 8988c2ecf20Sopenharmony_ci if (resource_size(res) > 0x17) 8998c2ecf20Sopenharmony_ci pd->flags |= IIC_FLAG_HAS_ICIC67; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci pm_runtime_enable(&dev->dev); 9028c2ecf20Sopenharmony_ci pm_runtime_get_sync(&dev->dev); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci config = of_device_get_match_data(&dev->dev); 9058c2ecf20Sopenharmony_ci if (config) { 9068c2ecf20Sopenharmony_ci pd->clks_per_count = config->clks_per_count; 9078c2ecf20Sopenharmony_ci ret = config->setup(pd); 9088c2ecf20Sopenharmony_ci } else { 9098c2ecf20Sopenharmony_ci ret = sh_mobile_i2c_init(pd); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci pm_runtime_put_sync(&dev->dev); 9138c2ecf20Sopenharmony_ci if (ret) 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Init DMA */ 9178c2ecf20Sopenharmony_ci sg_init_table(&pd->sg, 1); 9188c2ecf20Sopenharmony_ci pd->dma_direction = DMA_NONE; 9198c2ecf20Sopenharmony_ci pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* setup the private data */ 9228c2ecf20Sopenharmony_ci adap = &pd->adap; 9238c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, pd); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci adap->owner = THIS_MODULE; 9268c2ecf20Sopenharmony_ci adap->algo = &sh_mobile_i2c_algorithm; 9278c2ecf20Sopenharmony_ci adap->quirks = &sh_mobile_i2c_quirks; 9288c2ecf20Sopenharmony_ci adap->dev.parent = &dev->dev; 9298c2ecf20Sopenharmony_ci adap->retries = 5; 9308c2ecf20Sopenharmony_ci adap->nr = dev->id; 9318c2ecf20Sopenharmony_ci adap->dev.of_node = dev->dev.of_node; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci strlcpy(adap->name, dev->name, sizeof(adap->name)); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci spin_lock_init(&pd->lock); 9368c2ecf20Sopenharmony_ci init_waitqueue_head(&pd->wait); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(adap); 9398c2ecf20Sopenharmony_ci if (ret < 0) { 9408c2ecf20Sopenharmony_ci sh_mobile_i2c_release_dma(pd); 9418c2ecf20Sopenharmony_ci return ret; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz\n", adap->nr, pd->bus_speed); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return 0; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int sh_mobile_i2c_remove(struct platform_device *dev) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci i2c_del_adapter(&pd->adap); 9548c2ecf20Sopenharmony_ci sh_mobile_i2c_release_dma(pd); 9558c2ecf20Sopenharmony_ci pm_runtime_disable(&dev->dev); 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic struct platform_driver sh_mobile_i2c_driver = { 9608c2ecf20Sopenharmony_ci .driver = { 9618c2ecf20Sopenharmony_ci .name = "i2c-sh_mobile", 9628c2ecf20Sopenharmony_ci .of_match_table = sh_mobile_i2c_dt_ids, 9638c2ecf20Sopenharmony_ci }, 9648c2ecf20Sopenharmony_ci .probe = sh_mobile_i2c_probe, 9658c2ecf20Sopenharmony_ci .remove = sh_mobile_i2c_remove, 9668c2ecf20Sopenharmony_ci}; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic int __init sh_mobile_i2c_adap_init(void) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci return platform_driver_register(&sh_mobile_i2c_driver); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_cisubsys_initcall(sh_mobile_i2c_adap_init); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic void __exit sh_mobile_i2c_adap_exit(void) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci platform_driver_unregister(&sh_mobile_i2c_driver); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_cimodule_exit(sh_mobile_i2c_adap_exit); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); 9818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm"); 9828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfram Sang"); 9838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 9848c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:i2c-sh_mobile"); 985