root/drivers/i2c/busses/i2c-octeon-platdrv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. octeon_i2c_int_enable
  2. octeon_i2c_int_disable
  3. octeon_i2c_int_enable78
  4. __octeon_i2c_irq_disable
  5. octeon_i2c_int_disable78
  6. octeon_i2c_hlc_int_enable78
  7. octeon_i2c_hlc_int_disable78
  8. octeon_i2c_hlc_isr78
  9. octeon_i2c_hlc_int_enable
  10. octeon_i2c_functionality
  11. octeon_i2c_probe
  12. octeon_i2c_remove

   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  */
  38 static 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 */
  44 static 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  */
  57 static 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 
  63 static 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 */
  78 static 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  */
  90 static 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 */
  97 static 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 */
 103 static 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 
 113 static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c)
 114 {
 115         octeon_i2c_write_int(i2c, TWSI_INT_ST_EN);
 116 }
 117 
 118 static 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 
 124 static const struct i2c_algorithm octeon_i2c_algo = {
 125         .master_xfer = octeon_i2c_xfer,
 126         .functionality = octeon_i2c_functionality,
 127 };
 128 
 129 static const struct i2c_adapter octeon_i2c_ops = {
 130         .owner = THIS_MODULE,
 131         .name = "OCTEON adapter",
 132         .algo = &octeon_i2c_algo,
 133 };
 134 
 135 static 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 resource *res_mem;
 140         struct octeon_i2c *i2c;
 141         bool cn78xx_style;
 142 
 143         cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi");
 144         if (cn78xx_style) {
 145                 hlc_irq = platform_get_irq(pdev, 0);
 146                 if (hlc_irq < 0)
 147                         return hlc_irq;
 148 
 149                 irq = platform_get_irq(pdev, 2);
 150                 if (irq < 0)
 151                         return irq;
 152         } else {
 153                 /* All adaptors have an irq.  */
 154                 irq = platform_get_irq(pdev, 0);
 155                 if (irq < 0)
 156                         return irq;
 157         }
 158 
 159         i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
 160         if (!i2c) {
 161                 result = -ENOMEM;
 162                 goto out;
 163         }
 164         i2c->dev = &pdev->dev;
 165 
 166         i2c->roff.sw_twsi = 0x00;
 167         i2c->roff.twsi_int = 0x10;
 168         i2c->roff.sw_twsi_ext = 0x18;
 169 
 170         res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 171         i2c->twsi_base = devm_ioremap_resource(&pdev->dev, res_mem);
 172         if (IS_ERR(i2c->twsi_base)) {
 173                 result = PTR_ERR(i2c->twsi_base);
 174                 goto out;
 175         }
 176 
 177         /*
 178          * "clock-rate" is a legacy binding, the official binding is
 179          * "clock-frequency".  Try the official one first and then
 180          * fall back if it doesn't exist.
 181          */
 182         if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) &&
 183             of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) {
 184                 dev_err(i2c->dev,
 185                         "no I2C 'clock-rate' or 'clock-frequency' property\n");
 186                 result = -ENXIO;
 187                 goto out;
 188         }
 189 
 190         i2c->sys_freq = octeon_get_io_clock_rate();
 191 
 192         init_waitqueue_head(&i2c->queue);
 193 
 194         i2c->irq = irq;
 195 
 196         if (cn78xx_style) {
 197                 i2c->hlc_irq = hlc_irq;
 198 
 199                 i2c->int_enable = octeon_i2c_int_enable78;
 200                 i2c->int_disable = octeon_i2c_int_disable78;
 201                 i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78;
 202                 i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78;
 203 
 204                 irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN);
 205                 irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN);
 206 
 207                 result = devm_request_irq(&pdev->dev, i2c->hlc_irq,
 208                                           octeon_i2c_hlc_isr78, 0,
 209                                           DRV_NAME, i2c);
 210                 if (result < 0) {
 211                         dev_err(i2c->dev, "failed to attach interrupt\n");
 212                         goto out;
 213                 }
 214         } else {
 215                 i2c->int_enable = octeon_i2c_int_enable;
 216                 i2c->int_disable = octeon_i2c_int_disable;
 217                 i2c->hlc_int_enable = octeon_i2c_hlc_int_enable;
 218                 i2c->hlc_int_disable = octeon_i2c_int_disable;
 219         }
 220 
 221         result = devm_request_irq(&pdev->dev, i2c->irq,
 222                                   octeon_i2c_isr, 0, DRV_NAME, i2c);
 223         if (result < 0) {
 224                 dev_err(i2c->dev, "failed to attach interrupt\n");
 225                 goto out;
 226         }
 227 
 228         if (OCTEON_IS_MODEL(OCTEON_CN38XX))
 229                 i2c->broken_irq_check = true;
 230 
 231         result = octeon_i2c_init_lowlevel(i2c);
 232         if (result) {
 233                 dev_err(i2c->dev, "init low level failed\n");
 234                 goto  out;
 235         }
 236 
 237         octeon_i2c_set_clock(i2c);
 238 
 239         i2c->adap = octeon_i2c_ops;
 240         i2c->adap.timeout = msecs_to_jiffies(2);
 241         i2c->adap.retries = 5;
 242         i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;
 243         i2c->adap.dev.parent = &pdev->dev;
 244         i2c->adap.dev.of_node = node;
 245         i2c_set_adapdata(&i2c->adap, i2c);
 246         platform_set_drvdata(pdev, i2c);
 247 
 248         result = i2c_add_adapter(&i2c->adap);
 249         if (result < 0)
 250                 goto out;
 251         dev_info(i2c->dev, "probed\n");
 252         return 0;
 253 
 254 out:
 255         return result;
 256 };
 257 
 258 static int octeon_i2c_remove(struct platform_device *pdev)
 259 {
 260         struct octeon_i2c *i2c = platform_get_drvdata(pdev);
 261 
 262         i2c_del_adapter(&i2c->adap);
 263         return 0;
 264 };
 265 
 266 static const struct of_device_id octeon_i2c_match[] = {
 267         { .compatible = "cavium,octeon-3860-twsi", },
 268         { .compatible = "cavium,octeon-7890-twsi", },
 269         {},
 270 };
 271 MODULE_DEVICE_TABLE(of, octeon_i2c_match);
 272 
 273 static struct platform_driver octeon_i2c_driver = {
 274         .probe          = octeon_i2c_probe,
 275         .remove         = octeon_i2c_remove,
 276         .driver         = {
 277                 .name   = DRV_NAME,
 278                 .of_match_table = octeon_i2c_match,
 279         },
 280 };
 281 
 282 module_platform_driver(octeon_i2c_driver);
 283 
 284 MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
 285 MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
 286 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */