18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * I2C bus driver for Kontron COM modules 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013 Kontron Europe GmbH 68c2ecf20Sopenharmony_ci * Author: Michael Brunner <michael.brunner@kontron.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * The driver is based on the i2c-ocores driver by Peter Korsgaard. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/kempld.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define KEMPLD_I2C_PRELOW 0x0b 188c2ecf20Sopenharmony_ci#define KEMPLD_I2C_PREHIGH 0x0c 198c2ecf20Sopenharmony_ci#define KEMPLD_I2C_DATA 0x0e 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define KEMPLD_I2C_CTRL 0x0d 228c2ecf20Sopenharmony_ci#define I2C_CTRL_IEN 0x40 238c2ecf20Sopenharmony_ci#define I2C_CTRL_EN 0x80 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define KEMPLD_I2C_STAT 0x0f 268c2ecf20Sopenharmony_ci#define I2C_STAT_IF 0x01 278c2ecf20Sopenharmony_ci#define I2C_STAT_TIP 0x02 288c2ecf20Sopenharmony_ci#define I2C_STAT_ARBLOST 0x20 298c2ecf20Sopenharmony_ci#define I2C_STAT_BUSY 0x40 308c2ecf20Sopenharmony_ci#define I2C_STAT_NACK 0x80 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define KEMPLD_I2C_CMD 0x0f 338c2ecf20Sopenharmony_ci#define I2C_CMD_START 0x91 348c2ecf20Sopenharmony_ci#define I2C_CMD_STOP 0x41 358c2ecf20Sopenharmony_ci#define I2C_CMD_READ 0x21 368c2ecf20Sopenharmony_ci#define I2C_CMD_WRITE 0x11 378c2ecf20Sopenharmony_ci#define I2C_CMD_READ_ACK 0x21 388c2ecf20Sopenharmony_ci#define I2C_CMD_READ_NACK 0x29 398c2ecf20Sopenharmony_ci#define I2C_CMD_IACK 0x01 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define KEMPLD_I2C_FREQ_MAX 2700 /* 2.7 mHz */ 428c2ecf20Sopenharmony_ci#define KEMPLD_I2C_FREQ_STD 100 /* 100 kHz */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cienum { 458c2ecf20Sopenharmony_ci STATE_DONE = 0, 468c2ecf20Sopenharmony_ci STATE_INIT, 478c2ecf20Sopenharmony_ci STATE_ADDR, 488c2ecf20Sopenharmony_ci STATE_ADDR10, 498c2ecf20Sopenharmony_ci STATE_START, 508c2ecf20Sopenharmony_ci STATE_WRITE, 518c2ecf20Sopenharmony_ci STATE_READ, 528c2ecf20Sopenharmony_ci STATE_ERROR, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct kempld_i2c_data { 568c2ecf20Sopenharmony_ci struct device *dev; 578c2ecf20Sopenharmony_ci struct kempld_device_data *pld; 588c2ecf20Sopenharmony_ci struct i2c_adapter adap; 598c2ecf20Sopenharmony_ci struct i2c_msg *msg; 608c2ecf20Sopenharmony_ci int pos; 618c2ecf20Sopenharmony_ci int nmsgs; 628c2ecf20Sopenharmony_ci int state; 638c2ecf20Sopenharmony_ci bool was_active; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic unsigned int bus_frequency = KEMPLD_I2C_FREQ_STD; 678c2ecf20Sopenharmony_cimodule_param(bus_frequency, uint, 0); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bus_frequency, "Set I2C bus frequency in kHz (default=" 698c2ecf20Sopenharmony_ci __MODULE_STRING(KEMPLD_I2C_FREQ_STD)")"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int i2c_bus = -1; 728c2ecf20Sopenharmony_cimodule_param(i2c_bus, int, 0); 738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(i2c_bus, "Set I2C bus number (default=-1 for dynamic assignment)"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic bool i2c_gpio_mux; 768c2ecf20Sopenharmony_cimodule_param(i2c_gpio_mux, bool, 0); 778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(i2c_gpio_mux, "Enable I2C port on GPIO out (default=false)"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * kempld_get_mutex must be called prior to calling this function. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic int kempld_i2c_process(struct kempld_i2c_data *i2c) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 858c2ecf20Sopenharmony_ci u8 stat = kempld_read8(pld, KEMPLD_I2C_STAT); 868c2ecf20Sopenharmony_ci struct i2c_msg *msg = i2c->msg; 878c2ecf20Sopenharmony_ci u8 addr; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Ready? */ 908c2ecf20Sopenharmony_ci if (stat & I2C_STAT_TIP) 918c2ecf20Sopenharmony_ci return -EBUSY; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { 948c2ecf20Sopenharmony_ci /* Stop has been sent */ 958c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); 968c2ecf20Sopenharmony_ci if (i2c->state == STATE_ERROR) 978c2ecf20Sopenharmony_ci return -EIO; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Error? */ 1028c2ecf20Sopenharmony_ci if (stat & I2C_STAT_ARBLOST) { 1038c2ecf20Sopenharmony_ci i2c->state = STATE_ERROR; 1048c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); 1058c2ecf20Sopenharmony_ci return -EAGAIN; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (i2c->state == STATE_INIT) { 1098c2ecf20Sopenharmony_ci if (stat & I2C_STAT_BUSY) 1108c2ecf20Sopenharmony_ci return -EBUSY; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci i2c->state = STATE_ADDR; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (i2c->state == STATE_ADDR) { 1168c2ecf20Sopenharmony_ci /* 10 bit address? */ 1178c2ecf20Sopenharmony_ci if (i2c->msg->flags & I2C_M_TEN) { 1188c2ecf20Sopenharmony_ci addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6); 1198c2ecf20Sopenharmony_ci /* Set read bit if necessary */ 1208c2ecf20Sopenharmony_ci addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0; 1218c2ecf20Sopenharmony_ci i2c->state = STATE_ADDR10; 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci addr = i2c_8bit_addr_from_msg(i2c->msg); 1248c2ecf20Sopenharmony_ci i2c->state = STATE_START; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_DATA, addr); 1288c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Second part of 10 bit addressing */ 1348c2ecf20Sopenharmony_ci if (i2c->state == STATE_ADDR10) { 1358c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff); 1368c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci i2c->state = STATE_START; 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (i2c->state == STATE_START || i2c->state == STATE_WRITE) { 1438c2ecf20Sopenharmony_ci i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (stat & I2C_STAT_NACK) { 1468c2ecf20Sopenharmony_ci i2c->state = STATE_ERROR; 1478c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); 1488c2ecf20Sopenharmony_ci return -ENXIO; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci msg->buf[i2c->pos++] = kempld_read8(pld, KEMPLD_I2C_DATA); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (i2c->pos >= msg->len) { 1558c2ecf20Sopenharmony_ci i2c->nmsgs--; 1568c2ecf20Sopenharmony_ci i2c->msg++; 1578c2ecf20Sopenharmony_ci i2c->pos = 0; 1588c2ecf20Sopenharmony_ci msg = i2c->msg; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (i2c->nmsgs) { 1618c2ecf20Sopenharmony_ci if (!(msg->flags & I2C_M_NOSTART)) { 1628c2ecf20Sopenharmony_ci i2c->state = STATE_ADDR; 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } else { 1658c2ecf20Sopenharmony_ci i2c->state = (msg->flags & I2C_M_RD) 1668c2ecf20Sopenharmony_ci ? STATE_READ : STATE_WRITE; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci i2c->state = STATE_DONE; 1708c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (i2c->state == STATE_READ) { 1768c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, i2c->pos == (msg->len - 1) ? 1778c2ecf20Sopenharmony_ci I2C_CMD_READ_NACK : I2C_CMD_READ_ACK); 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_DATA, msg->buf[i2c->pos++]); 1808c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int kempld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 1878c2ecf20Sopenharmony_ci int num) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct kempld_i2c_data *i2c = i2c_get_adapdata(adap); 1908c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 1918c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + HZ; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci i2c->msg = msgs; 1958c2ecf20Sopenharmony_ci i2c->pos = 0; 1968c2ecf20Sopenharmony_ci i2c->nmsgs = num; 1978c2ecf20Sopenharmony_ci i2c->state = STATE_INIT; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Handle the transfer */ 2008c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 2018c2ecf20Sopenharmony_ci kempld_get_mutex(pld); 2028c2ecf20Sopenharmony_ci ret = kempld_i2c_process(i2c); 2038c2ecf20Sopenharmony_ci kempld_release_mutex(pld); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) 2068c2ecf20Sopenharmony_ci return (i2c->state == STATE_DONE) ? num : ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (ret == 0) 2098c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci usleep_range(5, 15); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci i2c->state = STATE_ERROR; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * kempld_get_mutex must be called prior to calling this function. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic void kempld_i2c_device_init(struct kempld_i2c_data *i2c) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 2258c2ecf20Sopenharmony_ci u16 prescale_corr; 2268c2ecf20Sopenharmony_ci long prescale; 2278c2ecf20Sopenharmony_ci u8 ctrl; 2288c2ecf20Sopenharmony_ci u8 stat; 2298c2ecf20Sopenharmony_ci u8 cfg; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Make sure the device is disabled */ 2328c2ecf20Sopenharmony_ci ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); 2338c2ecf20Sopenharmony_ci ctrl &= ~(I2C_CTRL_EN | I2C_CTRL_IEN); 2348c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (bus_frequency > KEMPLD_I2C_FREQ_MAX) 2378c2ecf20Sopenharmony_ci bus_frequency = KEMPLD_I2C_FREQ_MAX; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (pld->info.spec_major == 1) 2408c2ecf20Sopenharmony_ci prescale = pld->pld_clock / (bus_frequency * 5) - 1000; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci prescale = pld->pld_clock / (bus_frequency * 4) - 3000; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (prescale < 0) 2458c2ecf20Sopenharmony_ci prescale = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Round to the best matching value */ 2488c2ecf20Sopenharmony_ci prescale_corr = prescale / 1000; 2498c2ecf20Sopenharmony_ci if (prescale % 1000 >= 500) 2508c2ecf20Sopenharmony_ci prescale_corr++; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_PRELOW, prescale_corr & 0xff); 2538c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_PREHIGH, prescale_corr >> 8); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Activate I2C bus output on GPIO pins */ 2568c2ecf20Sopenharmony_ci cfg = kempld_read8(pld, KEMPLD_CFG); 2578c2ecf20Sopenharmony_ci if (i2c_gpio_mux) 2588c2ecf20Sopenharmony_ci cfg |= KEMPLD_CFG_GPIO_I2C_MUX; 2598c2ecf20Sopenharmony_ci else 2608c2ecf20Sopenharmony_ci cfg &= ~KEMPLD_CFG_GPIO_I2C_MUX; 2618c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_CFG, cfg); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Enable the device */ 2648c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); 2658c2ecf20Sopenharmony_ci ctrl |= I2C_CTRL_EN; 2668c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci stat = kempld_read8(pld, KEMPLD_I2C_STAT); 2698c2ecf20Sopenharmony_ci if (stat & I2C_STAT_BUSY) 2708c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic u32 kempld_i2c_func(struct i2c_adapter *adap) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic const struct i2c_algorithm kempld_i2c_algorithm = { 2798c2ecf20Sopenharmony_ci .master_xfer = kempld_i2c_xfer, 2808c2ecf20Sopenharmony_ci .functionality = kempld_i2c_func, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic const struct i2c_adapter kempld_i2c_adapter = { 2848c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2858c2ecf20Sopenharmony_ci .name = "i2c-kempld", 2868c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 2878c2ecf20Sopenharmony_ci .algo = &kempld_i2c_algorithm, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int kempld_i2c_probe(struct platform_device *pdev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); 2938c2ecf20Sopenharmony_ci struct kempld_i2c_data *i2c; 2948c2ecf20Sopenharmony_ci int ret; 2958c2ecf20Sopenharmony_ci u8 ctrl; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); 2988c2ecf20Sopenharmony_ci if (!i2c) 2998c2ecf20Sopenharmony_ci return -ENOMEM; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci i2c->pld = pld; 3028c2ecf20Sopenharmony_ci i2c->dev = &pdev->dev; 3038c2ecf20Sopenharmony_ci i2c->adap = kempld_i2c_adapter; 3048c2ecf20Sopenharmony_ci i2c->adap.dev.parent = i2c->dev; 3058c2ecf20Sopenharmony_ci i2c_set_adapdata(&i2c->adap, i2c); 3068c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, i2c); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci kempld_get_mutex(pld); 3098c2ecf20Sopenharmony_ci ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (ctrl & I2C_CTRL_EN) 3128c2ecf20Sopenharmony_ci i2c->was_active = true; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci kempld_i2c_device_init(i2c); 3158c2ecf20Sopenharmony_ci kempld_release_mutex(pld); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Add I2C adapter to I2C tree */ 3188c2ecf20Sopenharmony_ci if (i2c_bus >= -1) 3198c2ecf20Sopenharmony_ci i2c->adap.nr = i2c_bus; 3208c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(&i2c->adap); 3218c2ecf20Sopenharmony_ci if (ret) 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci dev_info(i2c->dev, "I2C bus initialized at %dkHz\n", 3258c2ecf20Sopenharmony_ci bus_frequency); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int kempld_i2c_remove(struct platform_device *pdev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); 3338c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 3348c2ecf20Sopenharmony_ci u8 ctrl; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci kempld_get_mutex(pld); 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * Disable I2C logic if it was not activated before the 3398c2ecf20Sopenharmony_ci * driver loaded 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (!i2c->was_active) { 3428c2ecf20Sopenharmony_ci ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); 3438c2ecf20Sopenharmony_ci ctrl &= ~I2C_CTRL_EN; 3448c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci kempld_release_mutex(pld); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci i2c_del_adapter(&i2c->adap); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3548c2ecf20Sopenharmony_cistatic int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); 3578c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 3588c2ecf20Sopenharmony_ci u8 ctrl; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci kempld_get_mutex(pld); 3618c2ecf20Sopenharmony_ci ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); 3628c2ecf20Sopenharmony_ci ctrl &= ~I2C_CTRL_EN; 3638c2ecf20Sopenharmony_ci kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); 3648c2ecf20Sopenharmony_ci kempld_release_mutex(pld); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int kempld_i2c_resume(struct platform_device *pdev) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); 3728c2ecf20Sopenharmony_ci struct kempld_device_data *pld = i2c->pld; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci kempld_get_mutex(pld); 3758c2ecf20Sopenharmony_ci kempld_i2c_device_init(i2c); 3768c2ecf20Sopenharmony_ci kempld_release_mutex(pld); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci#else 3818c2ecf20Sopenharmony_ci#define kempld_i2c_suspend NULL 3828c2ecf20Sopenharmony_ci#define kempld_i2c_resume NULL 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic struct platform_driver kempld_i2c_driver = { 3868c2ecf20Sopenharmony_ci .driver = { 3878c2ecf20Sopenharmony_ci .name = "kempld-i2c", 3888c2ecf20Sopenharmony_ci }, 3898c2ecf20Sopenharmony_ci .probe = kempld_i2c_probe, 3908c2ecf20Sopenharmony_ci .remove = kempld_i2c_remove, 3918c2ecf20Sopenharmony_ci .suspend = kempld_i2c_suspend, 3928c2ecf20Sopenharmony_ci .resume = kempld_i2c_resume, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cimodule_platform_driver(kempld_i2c_driver); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("KEM PLD I2C Driver"); 3988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>"); 3998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4008c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:kempld_i2c"); 401