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-octeon-platdrv.c (7154B)


      1/*
      2 * (C) Copyright 2009-2010
      3 * Nokia Siemens Networks, michael.lawnick.ext@nsn.com
      4 *
      5 * Portions Copyright (C) 2010 - 2016 Cavium, Inc.
      6 *
      7 * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
      8 *
      9 * This file is licensed under the terms of the GNU General Public
     10 * License version 2. This program is licensed "as is" without any
     11 * warranty of any kind, whether express or implied.
     12 */
     13
     14#include <linux/atomic.h>
     15#include <linux/delay.h>
     16#include <linux/i2c.h>
     17#include <linux/interrupt.h>
     18#include <linux/io.h>
     19#include <linux/kernel.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/platform_device.h>
     23#include <linux/sched.h>
     24#include <linux/slab.h>
     25
     26#include <asm/octeon/octeon.h>
     27#include "i2c-octeon-core.h"
     28
     29#define DRV_NAME "i2c-octeon"
     30
     31/**
     32 * octeon_i2c_int_enable - enable the CORE interrupt
     33 * @i2c: The struct octeon_i2c
     34 *
     35 * The interrupt will be asserted when there is non-STAT_IDLE state in
     36 * the SW_TWSI_EOP_TWSI_STAT register.
     37 */
     38static void octeon_i2c_int_enable(struct octeon_i2c *i2c)
     39{
     40	octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN);
     41}
     42
     43/* disable the CORE interrupt */
     44static void octeon_i2c_int_disable(struct octeon_i2c *i2c)
     45{
     46	/* clear TS/ST/IFLG events */
     47	octeon_i2c_write_int(i2c, 0);
     48}
     49
     50/**
     51 * octeon_i2c_int_enable78 - enable the CORE interrupt
     52 * @i2c: The struct octeon_i2c
     53 *
     54 * The interrupt will be asserted when there is non-STAT_IDLE state in the
     55 * SW_TWSI_EOP_TWSI_STAT register.
     56 */
     57static void octeon_i2c_int_enable78(struct octeon_i2c *i2c)
     58{
     59	atomic_inc_return(&i2c->int_enable_cnt);
     60	enable_irq(i2c->irq);
     61}
     62
     63static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq)
     64{
     65	int count;
     66
     67	/*
     68	 * The interrupt can be disabled in two places, but we only
     69	 * want to make the disable_irq_nosync() call once, so keep
     70	 * track with the atomic variable.
     71	 */
     72	count = atomic_dec_if_positive(cnt);
     73	if (count >= 0)
     74		disable_irq_nosync(irq);
     75}
     76
     77/* disable the CORE interrupt */
     78static void octeon_i2c_int_disable78(struct octeon_i2c *i2c)
     79{
     80	__octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq);
     81}
     82
     83/**
     84 * octeon_i2c_hlc_int_enable78 - enable the ST interrupt
     85 * @i2c: The struct octeon_i2c
     86 *
     87 * The interrupt will be asserted when there is non-STAT_IDLE state in
     88 * the SW_TWSI_EOP_TWSI_STAT register.
     89 */
     90static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c)
     91{
     92	atomic_inc_return(&i2c->hlc_int_enable_cnt);
     93	enable_irq(i2c->hlc_irq);
     94}
     95
     96/* disable the ST interrupt */
     97static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c)
     98{
     99	__octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq);
    100}
    101
    102/* HLC interrupt service routine */
    103static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id)
    104{
    105	struct octeon_i2c *i2c = dev_id;
    106
    107	i2c->hlc_int_disable(i2c);
    108	wake_up(&i2c->queue);
    109
    110	return IRQ_HANDLED;
    111}
    112
    113static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c)
    114{
    115	octeon_i2c_write_int(i2c, TWSI_INT_ST_EN);
    116}
    117
    118static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
    119{
    120	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
    121	       I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL;
    122}
    123
    124static const struct i2c_algorithm octeon_i2c_algo = {
    125	.master_xfer = octeon_i2c_xfer,
    126	.functionality = octeon_i2c_functionality,
    127};
    128
    129static const struct i2c_adapter octeon_i2c_ops = {
    130	.owner = THIS_MODULE,
    131	.name = "OCTEON adapter",
    132	.algo = &octeon_i2c_algo,
    133};
    134
    135static int octeon_i2c_probe(struct platform_device *pdev)
    136{
    137	struct device_node *node = pdev->dev.of_node;
    138	int irq, result = 0, hlc_irq = 0;
    139	struct octeon_i2c *i2c;
    140	bool cn78xx_style;
    141
    142	cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi");
    143	if (cn78xx_style) {
    144		hlc_irq = platform_get_irq(pdev, 0);
    145		if (hlc_irq < 0)
    146			return hlc_irq;
    147
    148		irq = platform_get_irq(pdev, 2);
    149		if (irq < 0)
    150			return irq;
    151	} else {
    152		/* All adaptors have an irq.  */
    153		irq = platform_get_irq(pdev, 0);
    154		if (irq < 0)
    155			return irq;
    156	}
    157
    158	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
    159	if (!i2c) {
    160		result = -ENOMEM;
    161		goto out;
    162	}
    163	i2c->dev = &pdev->dev;
    164
    165	i2c->roff.sw_twsi = 0x00;
    166	i2c->roff.twsi_int = 0x10;
    167	i2c->roff.sw_twsi_ext = 0x18;
    168
    169	i2c->twsi_base = devm_platform_ioremap_resource(pdev, 0);
    170	if (IS_ERR(i2c->twsi_base)) {
    171		result = PTR_ERR(i2c->twsi_base);
    172		goto out;
    173	}
    174
    175	/*
    176	 * "clock-rate" is a legacy binding, the official binding is
    177	 * "clock-frequency".  Try the official one first and then
    178	 * fall back if it doesn't exist.
    179	 */
    180	if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) &&
    181	    of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) {
    182		dev_err(i2c->dev,
    183			"no I2C 'clock-rate' or 'clock-frequency' property\n");
    184		result = -ENXIO;
    185		goto out;
    186	}
    187
    188	i2c->sys_freq = octeon_get_io_clock_rate();
    189
    190	init_waitqueue_head(&i2c->queue);
    191
    192	i2c->irq = irq;
    193
    194	if (cn78xx_style) {
    195		i2c->hlc_irq = hlc_irq;
    196
    197		i2c->int_enable = octeon_i2c_int_enable78;
    198		i2c->int_disable = octeon_i2c_int_disable78;
    199		i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78;
    200		i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78;
    201
    202		irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN);
    203		irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN);
    204
    205		result = devm_request_irq(&pdev->dev, i2c->hlc_irq,
    206					  octeon_i2c_hlc_isr78, 0,
    207					  DRV_NAME, i2c);
    208		if (result < 0) {
    209			dev_err(i2c->dev, "failed to attach interrupt\n");
    210			goto out;
    211		}
    212	} else {
    213		i2c->int_enable = octeon_i2c_int_enable;
    214		i2c->int_disable = octeon_i2c_int_disable;
    215		i2c->hlc_int_enable = octeon_i2c_hlc_int_enable;
    216		i2c->hlc_int_disable = octeon_i2c_int_disable;
    217	}
    218
    219	result = devm_request_irq(&pdev->dev, i2c->irq,
    220				  octeon_i2c_isr, 0, DRV_NAME, i2c);
    221	if (result < 0) {
    222		dev_err(i2c->dev, "failed to attach interrupt\n");
    223		goto out;
    224	}
    225
    226	if (OCTEON_IS_MODEL(OCTEON_CN38XX))
    227		i2c->broken_irq_check = true;
    228
    229	result = octeon_i2c_init_lowlevel(i2c);
    230	if (result) {
    231		dev_err(i2c->dev, "init low level failed\n");
    232		goto  out;
    233	}
    234
    235	octeon_i2c_set_clock(i2c);
    236
    237	i2c->adap = octeon_i2c_ops;
    238	i2c->adap.timeout = msecs_to_jiffies(2);
    239	i2c->adap.retries = 5;
    240	i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;
    241	i2c->adap.dev.parent = &pdev->dev;
    242	i2c->adap.dev.of_node = node;
    243	i2c_set_adapdata(&i2c->adap, i2c);
    244	platform_set_drvdata(pdev, i2c);
    245
    246	result = i2c_add_adapter(&i2c->adap);
    247	if (result < 0)
    248		goto out;
    249	dev_info(i2c->dev, "probed\n");
    250	return 0;
    251
    252out:
    253	return result;
    254};
    255
    256static int octeon_i2c_remove(struct platform_device *pdev)
    257{
    258	struct octeon_i2c *i2c = platform_get_drvdata(pdev);
    259
    260	i2c_del_adapter(&i2c->adap);
    261	return 0;
    262};
    263
    264static const struct of_device_id octeon_i2c_match[] = {
    265	{ .compatible = "cavium,octeon-3860-twsi", },
    266	{ .compatible = "cavium,octeon-7890-twsi", },
    267	{},
    268};
    269MODULE_DEVICE_TABLE(of, octeon_i2c_match);
    270
    271static struct platform_driver octeon_i2c_driver = {
    272	.probe		= octeon_i2c_probe,
    273	.remove		= octeon_i2c_remove,
    274	.driver		= {
    275		.name	= DRV_NAME,
    276		.of_match_table = octeon_i2c_match,
    277	},
    278};
    279
    280module_platform_driver(octeon_i2c_driver);
    281
    282MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
    283MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
    284MODULE_LICENSE("GPL");