10857ba3cSAaro Koskinen /* 20857ba3cSAaro Koskinen * CBUS I2C driver for Nokia Internet Tablets. 30857ba3cSAaro Koskinen * 40857ba3cSAaro Koskinen * Copyright (C) 2004-2010 Nokia Corporation 50857ba3cSAaro Koskinen * 60857ba3cSAaro Koskinen * Based on code written by Juha Yrjölä, David Weinehall, Mikko Ylinen and 70857ba3cSAaro Koskinen * Felipe Balbi. Converted to I2C driver by Aaro Koskinen. 80857ba3cSAaro Koskinen * 90857ba3cSAaro Koskinen * This file is subject to the terms and conditions of the GNU General 100857ba3cSAaro Koskinen * Public License. See the file "COPYING" in the main directory of this 110857ba3cSAaro Koskinen * archive for more details. 120857ba3cSAaro Koskinen * 130857ba3cSAaro Koskinen * This program is distributed in the hope that it will be useful, 140857ba3cSAaro Koskinen * but WITHOUT ANY WARRANTY; without even the implied warranty of 150857ba3cSAaro Koskinen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 160857ba3cSAaro Koskinen * GNU General Public License for more details. 170857ba3cSAaro Koskinen */ 180857ba3cSAaro Koskinen 190857ba3cSAaro Koskinen #include <linux/io.h> 200857ba3cSAaro Koskinen #include <linux/i2c.h> 210857ba3cSAaro Koskinen #include <linux/gpio.h> 220857ba3cSAaro Koskinen #include <linux/init.h> 230857ba3cSAaro Koskinen #include <linux/slab.h> 240857ba3cSAaro Koskinen #include <linux/delay.h> 250857ba3cSAaro Koskinen #include <linux/errno.h> 260857ba3cSAaro Koskinen #include <linux/kernel.h> 270857ba3cSAaro Koskinen #include <linux/module.h> 280857ba3cSAaro Koskinen #include <linux/of_gpio.h> 290857ba3cSAaro Koskinen #include <linux/interrupt.h> 300857ba3cSAaro Koskinen #include <linux/platform_device.h> 310857ba3cSAaro Koskinen #include <linux/platform_data/i2c-cbus-gpio.h> 320857ba3cSAaro Koskinen 330857ba3cSAaro Koskinen /* 340857ba3cSAaro Koskinen * Bit counts are derived from Nokia implementation. These should be checked 350857ba3cSAaro Koskinen * if other CBUS implementations appear. 360857ba3cSAaro Koskinen */ 370857ba3cSAaro Koskinen #define CBUS_ADDR_BITS 3 380857ba3cSAaro Koskinen #define CBUS_REG_BITS 5 390857ba3cSAaro Koskinen 400857ba3cSAaro Koskinen struct cbus_host { 410857ba3cSAaro Koskinen spinlock_t lock; /* host lock */ 420857ba3cSAaro Koskinen struct device *dev; 430857ba3cSAaro Koskinen int clk_gpio; 440857ba3cSAaro Koskinen int dat_gpio; 450857ba3cSAaro Koskinen int sel_gpio; 460857ba3cSAaro Koskinen }; 470857ba3cSAaro Koskinen 480857ba3cSAaro Koskinen /** 490857ba3cSAaro Koskinen * cbus_send_bit - sends one bit over the bus 500857ba3cSAaro Koskinen * @host: the host we're using 510857ba3cSAaro Koskinen * @bit: one bit of information to send 520857ba3cSAaro Koskinen */ 530857ba3cSAaro Koskinen static void cbus_send_bit(struct cbus_host *host, unsigned bit) 540857ba3cSAaro Koskinen { 550857ba3cSAaro Koskinen gpio_set_value(host->dat_gpio, bit ? 1 : 0); 560857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 1); 570857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 0); 580857ba3cSAaro Koskinen } 590857ba3cSAaro Koskinen 600857ba3cSAaro Koskinen /** 610857ba3cSAaro Koskinen * cbus_send_data - sends @len amount of data over the bus 620857ba3cSAaro Koskinen * @host: the host we're using 630857ba3cSAaro Koskinen * @data: the data to send 640857ba3cSAaro Koskinen * @len: size of the transfer 650857ba3cSAaro Koskinen */ 660857ba3cSAaro Koskinen static void cbus_send_data(struct cbus_host *host, unsigned data, unsigned len) 670857ba3cSAaro Koskinen { 680857ba3cSAaro Koskinen int i; 690857ba3cSAaro Koskinen 700857ba3cSAaro Koskinen for (i = len; i > 0; i--) 710857ba3cSAaro Koskinen cbus_send_bit(host, data & (1 << (i - 1))); 720857ba3cSAaro Koskinen } 730857ba3cSAaro Koskinen 740857ba3cSAaro Koskinen /** 750857ba3cSAaro Koskinen * cbus_receive_bit - receives one bit from the bus 760857ba3cSAaro Koskinen * @host: the host we're using 770857ba3cSAaro Koskinen */ 780857ba3cSAaro Koskinen static int cbus_receive_bit(struct cbus_host *host) 790857ba3cSAaro Koskinen { 800857ba3cSAaro Koskinen int ret; 810857ba3cSAaro Koskinen 820857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 1); 830857ba3cSAaro Koskinen ret = gpio_get_value(host->dat_gpio); 840857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 0); 850857ba3cSAaro Koskinen return ret; 860857ba3cSAaro Koskinen } 870857ba3cSAaro Koskinen 880857ba3cSAaro Koskinen /** 890857ba3cSAaro Koskinen * cbus_receive_word - receives 16-bit word from the bus 900857ba3cSAaro Koskinen * @host: the host we're using 910857ba3cSAaro Koskinen */ 920857ba3cSAaro Koskinen static int cbus_receive_word(struct cbus_host *host) 930857ba3cSAaro Koskinen { 940857ba3cSAaro Koskinen int ret = 0; 950857ba3cSAaro Koskinen int i; 960857ba3cSAaro Koskinen 970857ba3cSAaro Koskinen for (i = 16; i > 0; i--) { 980857ba3cSAaro Koskinen int bit = cbus_receive_bit(host); 990857ba3cSAaro Koskinen 1000857ba3cSAaro Koskinen if (bit < 0) 1010857ba3cSAaro Koskinen return bit; 1020857ba3cSAaro Koskinen 1030857ba3cSAaro Koskinen if (bit) 1040857ba3cSAaro Koskinen ret |= 1 << (i - 1); 1050857ba3cSAaro Koskinen } 1060857ba3cSAaro Koskinen return ret; 1070857ba3cSAaro Koskinen } 1080857ba3cSAaro Koskinen 1090857ba3cSAaro Koskinen /** 1100857ba3cSAaro Koskinen * cbus_transfer - transfers data over the bus 1110857ba3cSAaro Koskinen * @host: the host we're using 1120857ba3cSAaro Koskinen * @rw: read/write flag 1130857ba3cSAaro Koskinen * @dev: device address 1140857ba3cSAaro Koskinen * @reg: register address 1150857ba3cSAaro Koskinen * @data: if @rw == I2C_SBUS_WRITE data to send otherwise 0 1160857ba3cSAaro Koskinen */ 1170857ba3cSAaro Koskinen static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, 1180857ba3cSAaro Koskinen unsigned reg, unsigned data) 1190857ba3cSAaro Koskinen { 1200857ba3cSAaro Koskinen unsigned long flags; 1210857ba3cSAaro Koskinen int ret; 1220857ba3cSAaro Koskinen 1230857ba3cSAaro Koskinen /* We don't want interrupts disturbing our transfer */ 1240857ba3cSAaro Koskinen spin_lock_irqsave(&host->lock, flags); 1250857ba3cSAaro Koskinen 1260857ba3cSAaro Koskinen /* Reset state and start of transfer, SEL stays down during transfer */ 1270857ba3cSAaro Koskinen gpio_set_value(host->sel_gpio, 0); 1280857ba3cSAaro Koskinen 1290857ba3cSAaro Koskinen /* Set the DAT pin to output */ 1300857ba3cSAaro Koskinen gpio_direction_output(host->dat_gpio, 1); 1310857ba3cSAaro Koskinen 1320857ba3cSAaro Koskinen /* Send the device address */ 1330857ba3cSAaro Koskinen cbus_send_data(host, dev, CBUS_ADDR_BITS); 1340857ba3cSAaro Koskinen 1350857ba3cSAaro Koskinen /* Send the rw flag */ 1360857ba3cSAaro Koskinen cbus_send_bit(host, rw == I2C_SMBUS_READ); 1370857ba3cSAaro Koskinen 1380857ba3cSAaro Koskinen /* Send the register address */ 1390857ba3cSAaro Koskinen cbus_send_data(host, reg, CBUS_REG_BITS); 1400857ba3cSAaro Koskinen 1410857ba3cSAaro Koskinen if (rw == I2C_SMBUS_WRITE) { 1420857ba3cSAaro Koskinen cbus_send_data(host, data, 16); 1430857ba3cSAaro Koskinen ret = 0; 1440857ba3cSAaro Koskinen } else { 1450857ba3cSAaro Koskinen ret = gpio_direction_input(host->dat_gpio); 1460857ba3cSAaro Koskinen if (ret) { 1470857ba3cSAaro Koskinen dev_dbg(host->dev, "failed setting direction\n"); 1480857ba3cSAaro Koskinen goto out; 1490857ba3cSAaro Koskinen } 1500857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 1); 1510857ba3cSAaro Koskinen 1520857ba3cSAaro Koskinen ret = cbus_receive_word(host); 1530857ba3cSAaro Koskinen if (ret < 0) { 1540857ba3cSAaro Koskinen dev_dbg(host->dev, "failed receiving data\n"); 1550857ba3cSAaro Koskinen goto out; 1560857ba3cSAaro Koskinen } 1570857ba3cSAaro Koskinen } 1580857ba3cSAaro Koskinen 1590857ba3cSAaro Koskinen /* Indicate end of transfer, SEL goes up until next transfer */ 1600857ba3cSAaro Koskinen gpio_set_value(host->sel_gpio, 1); 1610857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 1); 1620857ba3cSAaro Koskinen gpio_set_value(host->clk_gpio, 0); 1630857ba3cSAaro Koskinen 1640857ba3cSAaro Koskinen out: 1650857ba3cSAaro Koskinen spin_unlock_irqrestore(&host->lock, flags); 1660857ba3cSAaro Koskinen 1670857ba3cSAaro Koskinen return ret; 1680857ba3cSAaro Koskinen } 1690857ba3cSAaro Koskinen 1700857ba3cSAaro Koskinen static int cbus_i2c_smbus_xfer(struct i2c_adapter *adapter, 1710857ba3cSAaro Koskinen u16 addr, 1720857ba3cSAaro Koskinen unsigned short flags, 1730857ba3cSAaro Koskinen char read_write, 1740857ba3cSAaro Koskinen u8 command, 1750857ba3cSAaro Koskinen int size, 1760857ba3cSAaro Koskinen union i2c_smbus_data *data) 1770857ba3cSAaro Koskinen { 1780857ba3cSAaro Koskinen struct cbus_host *chost = i2c_get_adapdata(adapter); 1790857ba3cSAaro Koskinen int ret; 1800857ba3cSAaro Koskinen 1810857ba3cSAaro Koskinen if (size != I2C_SMBUS_WORD_DATA) 1820857ba3cSAaro Koskinen return -EINVAL; 1830857ba3cSAaro Koskinen 1840857ba3cSAaro Koskinen ret = cbus_transfer(chost, read_write == I2C_SMBUS_READ, addr, 1850857ba3cSAaro Koskinen command, data->word); 1860857ba3cSAaro Koskinen if (ret < 0) 1870857ba3cSAaro Koskinen return ret; 1880857ba3cSAaro Koskinen 1890857ba3cSAaro Koskinen if (read_write == I2C_SMBUS_READ) 1900857ba3cSAaro Koskinen data->word = ret; 1910857ba3cSAaro Koskinen 1920857ba3cSAaro Koskinen return 0; 1930857ba3cSAaro Koskinen } 1940857ba3cSAaro Koskinen 1950857ba3cSAaro Koskinen static u32 cbus_i2c_func(struct i2c_adapter *adapter) 1960857ba3cSAaro Koskinen { 1970857ba3cSAaro Koskinen return I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; 1980857ba3cSAaro Koskinen } 1990857ba3cSAaro Koskinen 2000857ba3cSAaro Koskinen static const struct i2c_algorithm cbus_i2c_algo = { 2010857ba3cSAaro Koskinen .smbus_xfer = cbus_i2c_smbus_xfer, 2020857ba3cSAaro Koskinen .functionality = cbus_i2c_func, 2030857ba3cSAaro Koskinen }; 2040857ba3cSAaro Koskinen 2050857ba3cSAaro Koskinen static int cbus_i2c_remove(struct platform_device *pdev) 2060857ba3cSAaro Koskinen { 2070857ba3cSAaro Koskinen struct i2c_adapter *adapter = platform_get_drvdata(pdev); 2080857ba3cSAaro Koskinen 2090857ba3cSAaro Koskinen return i2c_del_adapter(adapter); 2100857ba3cSAaro Koskinen } 2110857ba3cSAaro Koskinen 2120857ba3cSAaro Koskinen static int cbus_i2c_probe(struct platform_device *pdev) 2130857ba3cSAaro Koskinen { 2140857ba3cSAaro Koskinen struct i2c_adapter *adapter; 2150857ba3cSAaro Koskinen struct cbus_host *chost; 2160857ba3cSAaro Koskinen int ret; 2170857ba3cSAaro Koskinen 2180857ba3cSAaro Koskinen adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), 2190857ba3cSAaro Koskinen GFP_KERNEL); 2200857ba3cSAaro Koskinen if (!adapter) 2210857ba3cSAaro Koskinen return -ENOMEM; 2220857ba3cSAaro Koskinen 2230857ba3cSAaro Koskinen chost = devm_kzalloc(&pdev->dev, sizeof(*chost), GFP_KERNEL); 2240857ba3cSAaro Koskinen if (!chost) 2250857ba3cSAaro Koskinen return -ENOMEM; 2260857ba3cSAaro Koskinen 2270857ba3cSAaro Koskinen if (pdev->dev.of_node) { 2280857ba3cSAaro Koskinen struct device_node *dnode = pdev->dev.of_node; 2290857ba3cSAaro Koskinen if (of_gpio_count(dnode) != 3) 2300857ba3cSAaro Koskinen return -ENODEV; 2310857ba3cSAaro Koskinen chost->clk_gpio = of_get_gpio(dnode, 0); 2320857ba3cSAaro Koskinen chost->dat_gpio = of_get_gpio(dnode, 1); 2330857ba3cSAaro Koskinen chost->sel_gpio = of_get_gpio(dnode, 2); 2340857ba3cSAaro Koskinen } else if (pdev->dev.platform_data) { 2350857ba3cSAaro Koskinen struct i2c_cbus_platform_data *pdata = pdev->dev.platform_data; 2360857ba3cSAaro Koskinen chost->clk_gpio = pdata->clk_gpio; 2370857ba3cSAaro Koskinen chost->dat_gpio = pdata->dat_gpio; 2380857ba3cSAaro Koskinen chost->sel_gpio = pdata->sel_gpio; 2390857ba3cSAaro Koskinen } else { 2400857ba3cSAaro Koskinen return -ENODEV; 2410857ba3cSAaro Koskinen } 2420857ba3cSAaro Koskinen 2430857ba3cSAaro Koskinen adapter->owner = THIS_MODULE; 2440857ba3cSAaro Koskinen adapter->class = I2C_CLASS_HWMON; 2450857ba3cSAaro Koskinen adapter->dev.parent = &pdev->dev; 2460857ba3cSAaro Koskinen adapter->nr = pdev->id; 2470857ba3cSAaro Koskinen adapter->timeout = HZ; 2480857ba3cSAaro Koskinen adapter->algo = &cbus_i2c_algo; 2490857ba3cSAaro Koskinen strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); 2500857ba3cSAaro Koskinen 2510857ba3cSAaro Koskinen spin_lock_init(&chost->lock); 2520857ba3cSAaro Koskinen chost->dev = &pdev->dev; 2530857ba3cSAaro Koskinen 2540857ba3cSAaro Koskinen ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio, 2550857ba3cSAaro Koskinen GPIOF_OUT_INIT_LOW, "CBUS clk"); 2560857ba3cSAaro Koskinen if (ret) 2570857ba3cSAaro Koskinen return ret; 2580857ba3cSAaro Koskinen 2590857ba3cSAaro Koskinen ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN, 2600857ba3cSAaro Koskinen "CBUS data"); 2610857ba3cSAaro Koskinen if (ret) 2620857ba3cSAaro Koskinen return ret; 2630857ba3cSAaro Koskinen 2640857ba3cSAaro Koskinen ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio, 2650857ba3cSAaro Koskinen GPIOF_OUT_INIT_HIGH, "CBUS sel"); 2660857ba3cSAaro Koskinen if (ret) 2670857ba3cSAaro Koskinen return ret; 2680857ba3cSAaro Koskinen 2690857ba3cSAaro Koskinen i2c_set_adapdata(adapter, chost); 2700857ba3cSAaro Koskinen platform_set_drvdata(pdev, adapter); 2710857ba3cSAaro Koskinen 2720857ba3cSAaro Koskinen return i2c_add_numbered_adapter(adapter); 2730857ba3cSAaro Koskinen } 2740857ba3cSAaro Koskinen 2750857ba3cSAaro Koskinen #if defined(CONFIG_OF) 2760857ba3cSAaro Koskinen static const struct of_device_id i2c_cbus_dt_ids[] = { 2770857ba3cSAaro Koskinen { .compatible = "i2c-cbus-gpio", }, 2780857ba3cSAaro Koskinen { } 2790857ba3cSAaro Koskinen }; 2800857ba3cSAaro Koskinen MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids); 2810857ba3cSAaro Koskinen #endif 2820857ba3cSAaro Koskinen 2830857ba3cSAaro Koskinen static struct platform_driver cbus_i2c_driver = { 2840857ba3cSAaro Koskinen .probe = cbus_i2c_probe, 2850857ba3cSAaro Koskinen .remove = cbus_i2c_remove, 2860857ba3cSAaro Koskinen .driver = { 2870857ba3cSAaro Koskinen .owner = THIS_MODULE, 2880857ba3cSAaro Koskinen .name = "i2c-cbus-gpio", 2890857ba3cSAaro Koskinen }, 2900857ba3cSAaro Koskinen }; 2910857ba3cSAaro Koskinen module_platform_driver(cbus_i2c_driver); 2920857ba3cSAaro Koskinen 2930857ba3cSAaro Koskinen MODULE_ALIAS("platform:i2c-cbus-gpio"); 2940857ba3cSAaro Koskinen MODULE_DESCRIPTION("CBUS I2C driver"); 2950857ba3cSAaro Koskinen MODULE_AUTHOR("Juha Yrjölä"); 2960857ba3cSAaro Koskinen MODULE_AUTHOR("David Weinehall"); 2970857ba3cSAaro Koskinen MODULE_AUTHOR("Mikko Ylinen"); 2980857ba3cSAaro Koskinen MODULE_AUTHOR("Felipe Balbi"); 2990857ba3cSAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 3000857ba3cSAaro Koskinen MODULE_LICENSE("GPL"); 301