linux/drivers/i2c/busses/i2c-octeon-platdrv.c
<<
>>
Prefs
   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");
 285