18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * CBUS I2C driver for Nokia Internet Tablets. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010 Nokia Corporation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on code written by Juha Yrjölä, David Weinehall, Mikko Ylinen and 78c2ecf20Sopenharmony_ci * Felipe Balbi. Converted to I2C driver by Aaro Koskinen. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 108c2ecf20Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this 118c2ecf20Sopenharmony_ci * archive for more details. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 168c2ecf20Sopenharmony_ci * GNU General Public License for more details. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/i2c.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/errno.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Bit counts are derived from Nokia implementation. These should be checked 328c2ecf20Sopenharmony_ci * if other CBUS implementations appear. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define CBUS_ADDR_BITS 3 358c2ecf20Sopenharmony_ci#define CBUS_REG_BITS 5 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct cbus_host { 388c2ecf20Sopenharmony_ci spinlock_t lock; /* host lock */ 398c2ecf20Sopenharmony_ci struct device *dev; 408c2ecf20Sopenharmony_ci struct gpio_desc *clk; 418c2ecf20Sopenharmony_ci struct gpio_desc *dat; 428c2ecf20Sopenharmony_ci struct gpio_desc *sel; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * cbus_send_bit - sends one bit over the bus 478c2ecf20Sopenharmony_ci * @host: the host we're using 488c2ecf20Sopenharmony_ci * @bit: one bit of information to send 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic void cbus_send_bit(struct cbus_host *host, unsigned bit) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci gpiod_set_value(host->dat, bit ? 1 : 0); 538c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 1); 548c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 0); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * cbus_send_data - sends @len amount of data over the bus 598c2ecf20Sopenharmony_ci * @host: the host we're using 608c2ecf20Sopenharmony_ci * @data: the data to send 618c2ecf20Sopenharmony_ci * @len: size of the transfer 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic void cbus_send_data(struct cbus_host *host, unsigned data, unsigned len) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int i; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci for (i = len; i > 0; i--) 688c2ecf20Sopenharmony_ci cbus_send_bit(host, data & (1 << (i - 1))); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * cbus_receive_bit - receives one bit from the bus 738c2ecf20Sopenharmony_ci * @host: the host we're using 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic int cbus_receive_bit(struct cbus_host *host) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 1); 808c2ecf20Sopenharmony_ci ret = gpiod_get_value(host->dat); 818c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 0); 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * cbus_receive_word - receives 16-bit word from the bus 878c2ecf20Sopenharmony_ci * @host: the host we're using 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int cbus_receive_word(struct cbus_host *host) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int ret = 0; 928c2ecf20Sopenharmony_ci int i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci for (i = 16; i > 0; i--) { 958c2ecf20Sopenharmony_ci int bit = cbus_receive_bit(host); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (bit < 0) 988c2ecf20Sopenharmony_ci return bit; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (bit) 1018c2ecf20Sopenharmony_ci ret |= 1 << (i - 1); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/** 1078c2ecf20Sopenharmony_ci * cbus_transfer - transfers data over the bus 1088c2ecf20Sopenharmony_ci * @host: the host we're using 1098c2ecf20Sopenharmony_ci * @rw: read/write flag 1108c2ecf20Sopenharmony_ci * @dev: device address 1118c2ecf20Sopenharmony_ci * @reg: register address 1128c2ecf20Sopenharmony_ci * @data: if @rw == I2C_SBUS_WRITE data to send otherwise 0 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, 1158c2ecf20Sopenharmony_ci unsigned reg, unsigned data) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* We don't want interrupts disturbing our transfer */ 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Reset state and start of transfer, SEL stays down during transfer */ 1248c2ecf20Sopenharmony_ci gpiod_set_value(host->sel, 0); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Set the DAT pin to output */ 1278c2ecf20Sopenharmony_ci gpiod_direction_output(host->dat, 1); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Send the device address */ 1308c2ecf20Sopenharmony_ci cbus_send_data(host, dev, CBUS_ADDR_BITS); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Send the rw flag */ 1338c2ecf20Sopenharmony_ci cbus_send_bit(host, rw == I2C_SMBUS_READ); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Send the register address */ 1368c2ecf20Sopenharmony_ci cbus_send_data(host, reg, CBUS_REG_BITS); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (rw == I2C_SMBUS_WRITE) { 1398c2ecf20Sopenharmony_ci cbus_send_data(host, data, 16); 1408c2ecf20Sopenharmony_ci ret = 0; 1418c2ecf20Sopenharmony_ci } else { 1428c2ecf20Sopenharmony_ci ret = gpiod_direction_input(host->dat); 1438c2ecf20Sopenharmony_ci if (ret) { 1448c2ecf20Sopenharmony_ci dev_dbg(host->dev, "failed setting direction\n"); 1458c2ecf20Sopenharmony_ci goto out; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 1); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = cbus_receive_word(host); 1508c2ecf20Sopenharmony_ci if (ret < 0) { 1518c2ecf20Sopenharmony_ci dev_dbg(host->dev, "failed receiving data\n"); 1528c2ecf20Sopenharmony_ci goto out; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Indicate end of transfer, SEL goes up until next transfer */ 1578c2ecf20Sopenharmony_ci gpiod_set_value(host->sel, 1); 1588c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 1); 1598c2ecf20Sopenharmony_ci gpiod_set_value(host->clk, 0); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciout: 1628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int cbus_i2c_smbus_xfer(struct i2c_adapter *adapter, 1688c2ecf20Sopenharmony_ci u16 addr, 1698c2ecf20Sopenharmony_ci unsigned short flags, 1708c2ecf20Sopenharmony_ci char read_write, 1718c2ecf20Sopenharmony_ci u8 command, 1728c2ecf20Sopenharmony_ci int size, 1738c2ecf20Sopenharmony_ci union i2c_smbus_data *data) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct cbus_host *chost = i2c_get_adapdata(adapter); 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (size != I2C_SMBUS_WORD_DATA) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = cbus_transfer(chost, read_write == I2C_SMBUS_READ, addr, 1828c2ecf20Sopenharmony_ci command, data->word); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_READ) 1878c2ecf20Sopenharmony_ci data->word = ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic u32 cbus_i2c_func(struct i2c_adapter *adapter) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const struct i2c_algorithm cbus_i2c_algo = { 1988c2ecf20Sopenharmony_ci .smbus_xfer = cbus_i2c_smbus_xfer, 1998c2ecf20Sopenharmony_ci .smbus_xfer_atomic = cbus_i2c_smbus_xfer, 2008c2ecf20Sopenharmony_ci .functionality = cbus_i2c_func, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int cbus_i2c_remove(struct platform_device *pdev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = platform_get_drvdata(pdev); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci i2c_del_adapter(adapter); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int cbus_i2c_probe(struct platform_device *pdev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 2158c2ecf20Sopenharmony_ci struct cbus_host *chost; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), 2188c2ecf20Sopenharmony_ci GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!adapter) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci chost = devm_kzalloc(&pdev->dev, sizeof(*chost), GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!chost) 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (gpiod_count(&pdev->dev, NULL) != 3) 2278c2ecf20Sopenharmony_ci return -ENODEV; 2288c2ecf20Sopenharmony_ci chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW); 2298c2ecf20Sopenharmony_ci if (IS_ERR(chost->clk)) 2308c2ecf20Sopenharmony_ci return PTR_ERR(chost->clk); 2318c2ecf20Sopenharmony_ci chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN); 2328c2ecf20Sopenharmony_ci if (IS_ERR(chost->dat)) 2338c2ecf20Sopenharmony_ci return PTR_ERR(chost->dat); 2348c2ecf20Sopenharmony_ci chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH); 2358c2ecf20Sopenharmony_ci if (IS_ERR(chost->sel)) 2368c2ecf20Sopenharmony_ci return PTR_ERR(chost->sel); 2378c2ecf20Sopenharmony_ci gpiod_set_consumer_name(chost->clk, "CBUS clk"); 2388c2ecf20Sopenharmony_ci gpiod_set_consumer_name(chost->dat, "CBUS dat"); 2398c2ecf20Sopenharmony_ci gpiod_set_consumer_name(chost->sel, "CBUS sel"); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci adapter->owner = THIS_MODULE; 2428c2ecf20Sopenharmony_ci adapter->class = I2C_CLASS_HWMON; 2438c2ecf20Sopenharmony_ci adapter->dev.parent = &pdev->dev; 2448c2ecf20Sopenharmony_ci adapter->dev.of_node = pdev->dev.of_node; 2458c2ecf20Sopenharmony_ci adapter->nr = pdev->id; 2468c2ecf20Sopenharmony_ci adapter->timeout = HZ; 2478c2ecf20Sopenharmony_ci adapter->algo = &cbus_i2c_algo; 2488c2ecf20Sopenharmony_ci strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci spin_lock_init(&chost->lock); 2518c2ecf20Sopenharmony_ci chost->dev = &pdev->dev; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci i2c_set_adapdata(adapter, chost); 2548c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, adapter); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return i2c_add_numbered_adapter(adapter); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) 2608c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_cbus_dt_ids[] = { 2618c2ecf20Sopenharmony_ci { .compatible = "i2c-cbus-gpio", }, 2628c2ecf20Sopenharmony_ci { } 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids); 2658c2ecf20Sopenharmony_ci#endif 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic struct platform_driver cbus_i2c_driver = { 2688c2ecf20Sopenharmony_ci .probe = cbus_i2c_probe, 2698c2ecf20Sopenharmony_ci .remove = cbus_i2c_remove, 2708c2ecf20Sopenharmony_ci .driver = { 2718c2ecf20Sopenharmony_ci .name = "i2c-cbus-gpio", 2728c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(i2c_cbus_dt_ids), 2738c2ecf20Sopenharmony_ci }, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_cimodule_platform_driver(cbus_i2c_driver); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:i2c-cbus-gpio"); 2788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CBUS I2C driver"); 2798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juha Yrjölä"); 2808c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Weinehall"); 2818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mikko Ylinen"); 2828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi"); 2838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 2848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 285