cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2c-cbus-gpio.c (7028B)


      1/*
      2 * CBUS I2C driver for Nokia Internet Tablets.
      3 *
      4 * Copyright (C) 2004-2010 Nokia Corporation
      5 *
      6 * Based on code written by Juha Yrjölä, David Weinehall, Mikko Ylinen and
      7 * Felipe Balbi. Converted to I2C driver by Aaro Koskinen.
      8 *
      9 * This file is subject to the terms and conditions of the GNU General
     10 * Public License. See the file "COPYING" in the main directory of this
     11 * archive for more details.
     12 *
     13 * This program is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     16 * GNU General Public License for more details.
     17 */
     18
     19#include <linux/io.h>
     20#include <linux/i2c.h>
     21#include <linux/slab.h>
     22#include <linux/delay.h>
     23#include <linux/errno.h>
     24#include <linux/kernel.h>
     25#include <linux/module.h>
     26#include <linux/gpio/consumer.h>
     27#include <linux/interrupt.h>
     28#include <linux/platform_device.h>
     29
     30/*
     31 * Bit counts are derived from Nokia implementation. These should be checked
     32 * if other CBUS implementations appear.
     33 */
     34#define CBUS_ADDR_BITS	3
     35#define CBUS_REG_BITS	5
     36
     37struct cbus_host {
     38	spinlock_t	lock;		/* host lock */
     39	struct device	*dev;
     40	struct gpio_desc *clk;
     41	struct gpio_desc *dat;
     42	struct gpio_desc *sel;
     43};
     44
     45/**
     46 * cbus_send_bit - sends one bit over the bus
     47 * @host: the host we're using
     48 * @bit: one bit of information to send
     49 */
     50static void cbus_send_bit(struct cbus_host *host, unsigned bit)
     51{
     52	gpiod_set_value(host->dat, bit ? 1 : 0);
     53	gpiod_set_value(host->clk, 1);
     54	gpiod_set_value(host->clk, 0);
     55}
     56
     57/**
     58 * cbus_send_data - sends @len amount of data over the bus
     59 * @host: the host we're using
     60 * @data: the data to send
     61 * @len: size of the transfer
     62 */
     63static void cbus_send_data(struct cbus_host *host, unsigned data, unsigned len)
     64{
     65	int i;
     66
     67	for (i = len; i > 0; i--)
     68		cbus_send_bit(host, data & (1 << (i - 1)));
     69}
     70
     71/**
     72 * cbus_receive_bit - receives one bit from the bus
     73 * @host: the host we're using
     74 */
     75static int cbus_receive_bit(struct cbus_host *host)
     76{
     77	int ret;
     78
     79	gpiod_set_value(host->clk, 1);
     80	ret = gpiod_get_value(host->dat);
     81	gpiod_set_value(host->clk, 0);
     82	return ret;
     83}
     84
     85/**
     86 * cbus_receive_word - receives 16-bit word from the bus
     87 * @host: the host we're using
     88 */
     89static int cbus_receive_word(struct cbus_host *host)
     90{
     91	int ret = 0;
     92	int i;
     93
     94	for (i = 16; i > 0; i--) {
     95		int bit = cbus_receive_bit(host);
     96
     97		if (bit < 0)
     98			return bit;
     99
    100		if (bit)
    101			ret |= 1 << (i - 1);
    102	}
    103	return ret;
    104}
    105
    106/**
    107 * cbus_transfer - transfers data over the bus
    108 * @host: the host we're using
    109 * @rw: read/write flag
    110 * @dev: device address
    111 * @reg: register address
    112 * @data: if @rw == I2C_SBUS_WRITE data to send otherwise 0
    113 */
    114static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
    115			 unsigned reg, unsigned data)
    116{
    117	unsigned long flags;
    118	int ret;
    119
    120	/* We don't want interrupts disturbing our transfer */
    121	spin_lock_irqsave(&host->lock, flags);
    122
    123	/* Reset state and start of transfer, SEL stays down during transfer */
    124	gpiod_set_value(host->sel, 0);
    125
    126	/* Set the DAT pin to output */
    127	gpiod_direction_output(host->dat, 1);
    128
    129	/* Send the device address */
    130	cbus_send_data(host, dev, CBUS_ADDR_BITS);
    131
    132	/* Send the rw flag */
    133	cbus_send_bit(host, rw == I2C_SMBUS_READ);
    134
    135	/* Send the register address */
    136	cbus_send_data(host, reg, CBUS_REG_BITS);
    137
    138	if (rw == I2C_SMBUS_WRITE) {
    139		cbus_send_data(host, data, 16);
    140		ret = 0;
    141	} else {
    142		ret = gpiod_direction_input(host->dat);
    143		if (ret) {
    144			dev_dbg(host->dev, "failed setting direction\n");
    145			goto out;
    146		}
    147		gpiod_set_value(host->clk, 1);
    148
    149		ret = cbus_receive_word(host);
    150		if (ret < 0) {
    151			dev_dbg(host->dev, "failed receiving data\n");
    152			goto out;
    153		}
    154	}
    155
    156	/* Indicate end of transfer, SEL goes up until next transfer */
    157	gpiod_set_value(host->sel, 1);
    158	gpiod_set_value(host->clk, 1);
    159	gpiod_set_value(host->clk, 0);
    160
    161out:
    162	spin_unlock_irqrestore(&host->lock, flags);
    163
    164	return ret;
    165}
    166
    167static int cbus_i2c_smbus_xfer(struct i2c_adapter	*adapter,
    168			       u16			addr,
    169			       unsigned short		flags,
    170			       char			read_write,
    171			       u8			command,
    172			       int			size,
    173			       union i2c_smbus_data	*data)
    174{
    175	struct cbus_host *chost = i2c_get_adapdata(adapter);
    176	int ret;
    177
    178	if (size != I2C_SMBUS_WORD_DATA)
    179		return -EINVAL;
    180
    181	ret = cbus_transfer(chost, read_write == I2C_SMBUS_READ, addr,
    182			    command, data->word);
    183	if (ret < 0)
    184		return ret;
    185
    186	if (read_write == I2C_SMBUS_READ)
    187		data->word = ret;
    188
    189	return 0;
    190}
    191
    192static u32 cbus_i2c_func(struct i2c_adapter *adapter)
    193{
    194	return I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA;
    195}
    196
    197static const struct i2c_algorithm cbus_i2c_algo = {
    198	.smbus_xfer		= cbus_i2c_smbus_xfer,
    199	.smbus_xfer_atomic	= cbus_i2c_smbus_xfer,
    200	.functionality		= cbus_i2c_func,
    201};
    202
    203static int cbus_i2c_remove(struct platform_device *pdev)
    204{
    205	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
    206
    207	i2c_del_adapter(adapter);
    208
    209	return 0;
    210}
    211
    212static int cbus_i2c_probe(struct platform_device *pdev)
    213{
    214	struct i2c_adapter *adapter;
    215	struct cbus_host *chost;
    216
    217	adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter),
    218			       GFP_KERNEL);
    219	if (!adapter)
    220		return -ENOMEM;
    221
    222	chost = devm_kzalloc(&pdev->dev, sizeof(*chost), GFP_KERNEL);
    223	if (!chost)
    224		return -ENOMEM;
    225
    226	if (gpiod_count(&pdev->dev, NULL) != 3)
    227		return -ENODEV;
    228	chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW);
    229	if (IS_ERR(chost->clk))
    230		return PTR_ERR(chost->clk);
    231	chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN);
    232	if (IS_ERR(chost->dat))
    233		return PTR_ERR(chost->dat);
    234	chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH);
    235	if (IS_ERR(chost->sel))
    236		return PTR_ERR(chost->sel);
    237	gpiod_set_consumer_name(chost->clk, "CBUS clk");
    238	gpiod_set_consumer_name(chost->dat, "CBUS dat");
    239	gpiod_set_consumer_name(chost->sel, "CBUS sel");
    240
    241	adapter->owner		= THIS_MODULE;
    242	adapter->class		= I2C_CLASS_HWMON;
    243	adapter->dev.parent	= &pdev->dev;
    244	adapter->dev.of_node	= pdev->dev.of_node;
    245	adapter->nr		= pdev->id;
    246	adapter->timeout	= HZ;
    247	adapter->algo		= &cbus_i2c_algo;
    248	strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name));
    249
    250	spin_lock_init(&chost->lock);
    251	chost->dev = &pdev->dev;
    252
    253	i2c_set_adapdata(adapter, chost);
    254	platform_set_drvdata(pdev, adapter);
    255
    256	return i2c_add_numbered_adapter(adapter);
    257}
    258
    259#if defined(CONFIG_OF)
    260static const struct of_device_id i2c_cbus_dt_ids[] = {
    261	{ .compatible = "i2c-cbus-gpio", },
    262	{ }
    263};
    264MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids);
    265#endif
    266
    267static struct platform_driver cbus_i2c_driver = {
    268	.probe	= cbus_i2c_probe,
    269	.remove	= cbus_i2c_remove,
    270	.driver	= {
    271		.name	= "i2c-cbus-gpio",
    272		.of_match_table = of_match_ptr(i2c_cbus_dt_ids),
    273	},
    274};
    275module_platform_driver(cbus_i2c_driver);
    276
    277MODULE_ALIAS("platform:i2c-cbus-gpio");
    278MODULE_DESCRIPTION("CBUS I2C driver");
    279MODULE_AUTHOR("Juha Yrjölä");
    280MODULE_AUTHOR("David Weinehall");
    281MODULE_AUTHOR("Mikko Ylinen");
    282MODULE_AUTHOR("Felipe Balbi");
    283MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
    284MODULE_LICENSE("GPL");